Coverage Report

Created: 2026-10-28 07:08

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/ropey-1.6.1/src/slice.rs
Line
Count
Source
1
use std::ops::RangeBounds;
2
use std::sync::Arc;
3
4
use crate::iter::{Bytes, Chars, Chunks, Lines};
5
use crate::rope::Rope;
6
use crate::str_utils::{
7
    byte_to_char_idx, byte_to_line_idx, byte_to_utf16_surrogate_idx, char_to_byte_idx,
8
    char_to_line_idx, count_chars, count_line_breaks, count_utf16_surrogates, line_to_byte_idx,
9
    line_to_char_idx, utf16_code_unit_to_char_idx,
10
};
11
use crate::tree::{Count, Node, TextInfo};
12
use crate::{end_bound_to_num, start_bound_to_num, Error, Result};
13
14
/// An immutable view into part of a `Rope`.
15
///
16
/// Just like standard `&str` slices, `RopeSlice`s behave as if the text in
17
/// their range is the only text that exists.  All indexing is relative to
18
/// the start of their range, and all iterators and methods that return text
19
/// truncate that text to the range of the slice.
20
///
21
/// In other words, the behavior of a `RopeSlice` is always identical to that
22
/// of a full `Rope` created from the same text range.  Nothing should be
23
/// surprising here.
24
#[derive(Copy, Clone)]
25
pub struct RopeSlice<'a>(pub(crate) RSEnum<'a>);
26
27
#[derive(Copy, Clone, Debug)]
28
pub(crate) enum RSEnum<'a> {
29
    Full {
30
        node: &'a Arc<Node>,
31
        start_info: TextInfo,
32
        end_info: TextInfo,
33
    },
34
    Light {
35
        text: &'a str,
36
        char_count: Count,
37
        utf16_surrogate_count: Count,
38
        line_break_count: Count,
39
    },
40
}
41
42
impl<'a> RopeSlice<'a> {
43
    /// Used for tests and debugging purposes.
44
    #[allow(dead_code)]
45
0
    pub(crate) fn is_light(&self) -> bool {
46
0
        matches!(&self.0, RSEnum::Light { .. })
47
0
    }
48
49
0
    pub(crate) fn new_with_range(node: &'a Arc<Node>, start: usize, end: usize) -> RopeSlice<'a> {
50
0
        assert!(start <= end);
51
0
        assert!(end <= node.text_info().chars as usize);
52
53
        // Early-out shortcut for taking a slice of the full thing.
54
0
        if start == 0 && end == node.char_count() {
55
0
            if node.is_leaf() {
56
0
                let text = node.leaf_text();
57
0
                return RopeSlice(RSEnum::Light {
58
0
                    text: text,
59
0
                    char_count: (end - start) as Count,
60
0
                    utf16_surrogate_count: count_utf16_surrogates(text) as Count,
61
0
                    line_break_count: count_line_breaks(text) as Count,
62
0
                });
63
            } else {
64
0
                return RopeSlice(RSEnum::Full {
65
0
                    node: node,
66
0
                    start_info: TextInfo {
67
0
                        bytes: 0,
68
0
                        chars: 0,
69
0
                        utf16_surrogates: 0,
70
0
                        line_breaks: 0,
71
0
                    },
72
0
                    end_info: TextInfo {
73
0
                        bytes: node.byte_count() as Count,
74
0
                        chars: node.char_count() as Count,
75
0
                        utf16_surrogates: node.utf16_surrogate_count() as Count,
76
0
                        line_breaks: node.line_break_count() as Count,
77
0
                    },
78
0
                });
79
            }
80
0
        }
81
82
        // Find the deepest node that still contains the full range given.
83
0
        let mut n_start = start;
84
0
        let mut n_end = end;
85
0
        let mut node = node;
86
        'outer: loop {
87
0
            match *(node as &Node) {
88
                // Early out if we reach a leaf, because we can do the
89
                // simpler lightweight slice then.
90
0
                Node::Leaf(ref text) => {
91
0
                    let start_byte = char_to_byte_idx(text, n_start);
92
0
                    let end_byte =
93
0
                        start_byte + char_to_byte_idx(&text[start_byte..], n_end - n_start);
94
0
                    return RopeSlice(RSEnum::Light {
95
0
                        text: &text[start_byte..end_byte],
96
0
                        char_count: (n_end - n_start) as Count,
97
0
                        utf16_surrogate_count: count_utf16_surrogates(&text[start_byte..end_byte])
98
0
                            as Count,
99
0
                        line_break_count: count_line_breaks(&text[start_byte..end_byte]) as Count,
100
0
                    });
101
                }
102
103
0
                Node::Internal(ref children) => {
104
0
                    let mut start_char = 0;
105
0
                    for (i, inf) in children.info().iter().enumerate() {
106
0
                        if n_start >= start_char && n_end <= (start_char + inf.chars as usize) {
107
0
                            n_start -= start_char;
108
0
                            n_end -= start_char;
109
0
                            node = &children.nodes()[i];
110
0
                            continue 'outer;
111
0
                        }
112
0
                        start_char += inf.chars as usize;
113
                    }
114
0
                    break;
115
                }
116
            }
117
        }
118
119
        // Create the slice
120
        RopeSlice(RSEnum::Full {
121
0
            node: node,
122
0
            start_info: node.char_to_text_info(n_start),
123
            end_info: {
124
                #[cfg(any(feature = "cr_lines", feature = "unicode_lines"))]
125
                {
126
0
                    let mut info = node.char_to_text_info(n_end);
127
0
                    if node.is_crlf_split(n_end) {
128
0
                        info.line_breaks += 1;
129
0
                    }
130
0
                    info
131
                }
132
                #[cfg(not(any(feature = "cr_lines", feature = "unicode_lines")))]
133
                node.char_to_text_info(n_end)
134
            },
135
        })
136
0
    }
137
138
0
    pub(crate) fn new_with_byte_range(
139
0
        node: &'a Arc<Node>,
140
0
        start: usize,
141
0
        end: usize,
142
0
    ) -> Result<RopeSlice<'a>> {
143
0
        assert!(start <= end);
144
0
        assert!(end <= node.text_info().bytes as usize);
145
146
        // Early-out shortcut for taking a slice of the full thing.
147
0
        if start == 0 && end == node.byte_count() {
148
0
            if node.is_leaf() {
149
0
                let text = node.leaf_text();
150
0
                return Ok(RopeSlice(RSEnum::Light {
151
0
                    text,
152
0
                    char_count: count_chars(text) as Count,
153
0
                    utf16_surrogate_count: count_utf16_surrogates(text) as Count,
154
0
                    line_break_count: count_line_breaks(text) as Count,
155
0
                }));
156
            } else {
157
0
                return Ok(RopeSlice(RSEnum::Full {
158
0
                    node,
159
0
                    start_info: TextInfo {
160
0
                        bytes: 0,
161
0
                        chars: 0,
162
0
                        utf16_surrogates: 0,
163
0
                        line_breaks: 0,
164
0
                    },
165
0
                    end_info: TextInfo {
166
0
                        bytes: node.byte_count() as Count,
167
0
                        chars: node.char_count() as Count,
168
0
                        utf16_surrogates: node.utf16_surrogate_count() as Count,
169
0
                        line_breaks: node.line_break_count() as Count,
170
0
                    },
171
0
                }));
172
            }
173
0
        }
174
175
        // Find the deepest node that still contains the full range given.
176
0
        let mut n_start = start;
177
0
        let mut n_end = end;
178
0
        let mut node = node;
179
        'outer: loop {
180
0
            match *(node as &Node) {
181
                // Early out if we reach a leaf, because we can do the
182
                // simpler lightweight slice then.
183
0
                Node::Leaf(ref text) => {
184
0
                    let start_byte = n_start;
185
0
                    let end_byte = n_end;
186
0
                    if !text.is_char_boundary(start_byte) || !text.is_char_boundary(end_byte) {
187
0
                        return Err(Error::ByteRangeNotCharBoundary(Some(start), Some(end)));
188
0
                    }
189
0
                    return Ok(RopeSlice(RSEnum::Light {
190
0
                        text: &text[start_byte..end_byte],
191
0
                        char_count: count_chars(&text[start_byte..end_byte]) as Count,
192
0
                        utf16_surrogate_count: count_utf16_surrogates(&text[start_byte..end_byte])
193
0
                            as Count,
194
0
                        line_break_count: count_line_breaks(&text[start_byte..end_byte]) as Count,
195
0
                    }));
196
                }
197
198
0
                Node::Internal(ref children) => {
199
0
                    let mut start_byte = 0;
200
0
                    for (i, inf) in children.info().iter().enumerate() {
201
0
                        if n_start >= start_byte && n_end <= (start_byte + inf.bytes as usize) {
202
0
                            n_start -= start_byte;
203
0
                            n_end -= start_byte;
204
0
                            node = &children.nodes()[i];
205
0
                            continue 'outer;
206
0
                        }
207
0
                        start_byte += inf.bytes as usize;
208
                    }
209
0
                    break;
210
                }
211
            }
212
        }
213
214
        // Make sure the bytes indices are valid char boundaries.
215
0
        if !node.is_char_boundary(n_start) || !node.is_char_boundary(n_end) {
216
0
            return Err(Error::ByteRangeNotCharBoundary(Some(start), Some(end)));
217
0
        }
218
219
        // Create the slice
220
0
        Ok(RopeSlice(RSEnum::Full {
221
0
            node,
222
0
            start_info: node.byte_to_text_info(n_start),
223
0
            end_info: node.byte_to_text_info(n_end),
224
0
        }))
225
0
    }
226
227
    //-----------------------------------------------------------------------
228
    // Informational methods
229
230
    /// Total number of bytes in the `RopeSlice`.
231
    ///
232
    /// Runs in O(1) time.
233
    #[inline]
234
0
    pub fn len_bytes(&self) -> usize {
235
0
        match *self {
236
            RopeSlice(RSEnum::Full {
237
0
                end_info,
238
0
                start_info,
239
                ..
240
0
            }) => (end_info.bytes - start_info.bytes) as usize,
241
0
            RopeSlice(RSEnum::Light { text, .. }) => text.len(),
242
        }
243
0
    }
244
245
    /// Total number of chars in the `RopeSlice`.
246
    ///
247
    /// Runs in O(1) time.
248
    #[inline]
249
0
    pub fn len_chars(&self) -> usize {
250
0
        match *self {
251
            RopeSlice(RSEnum::Full {
252
0
                end_info,
253
0
                start_info,
254
                ..
255
0
            }) => (end_info.chars - start_info.chars) as usize,
256
0
            RopeSlice(RSEnum::Light { char_count, .. }) => char_count as usize,
257
        }
258
0
    }
259
260
    /// Total number of lines in the `RopeSlice`.
261
    ///
262
    /// Runs in O(1) time.
263
    #[inline]
264
0
    pub fn len_lines(&self) -> usize {
265
0
        match *self {
266
            RopeSlice(RSEnum::Full {
267
0
                end_info,
268
0
                start_info,
269
                ..
270
0
            }) => (end_info.line_breaks - start_info.line_breaks) as usize + 1,
271
            RopeSlice(RSEnum::Light {
272
0
                line_break_count, ..
273
0
            }) => line_break_count as usize + 1,
274
        }
275
0
    }
276
277
    /// Total number of utf16 code units that would be in the `RopeSlice` if
278
    /// it were encoded as utf16.
279
    ///
280
    /// Ropey stores text internally as utf8, but sometimes it is necessary
281
    /// to interact with external APIs that still use utf16.  This function is
282
    /// primarily intended for such situations, and is otherwise not very
283
    /// useful.
284
    ///
285
    /// Runs in O(1) time.
286
    #[inline]
287
0
    pub fn len_utf16_cu(&self) -> usize {
288
0
        match *self {
289
            RopeSlice(RSEnum::Full {
290
0
                end_info,
291
0
                start_info,
292
                ..
293
            }) => {
294
0
                ((end_info.chars + end_info.utf16_surrogates)
295
0
                    - (start_info.chars + start_info.utf16_surrogates)) as usize
296
            }
297
            RopeSlice(RSEnum::Light {
298
0
                char_count,
299
0
                utf16_surrogate_count,
300
                ..
301
0
            }) => (char_count + utf16_surrogate_count) as usize,
302
        }
303
0
    }
304
305
    //-----------------------------------------------------------------------
306
    // Index conversion methods
307
308
    /// Returns the char index of the given byte.
309
    ///
310
    /// Notes:
311
    ///
312
    /// - If the byte is in the middle of a multi-byte char, returns the
313
    ///   index of the char that the byte belongs to.
314
    /// - `byte_idx` can be one-past-the-end, which will return one-past-the-end
315
    ///   char index.
316
    ///
317
    /// Runs in O(log N) time.
318
    ///
319
    /// # Panics
320
    ///
321
    /// Panics if `byte_idx` is out of bounds (i.e. `byte_idx > len_bytes()`).
322
    #[inline]
323
0
    pub fn byte_to_char(&self, byte_idx: usize) -> usize {
324
0
        self.try_byte_to_char(byte_idx).unwrap()
325
0
    }
326
327
    /// Returns the line index of the given byte.
328
    ///
329
    /// Notes:
330
    ///
331
    /// - Lines are zero-indexed.  This is functionally equivalent to
332
    ///   counting the line endings before the specified byte.
333
    /// - `byte_idx` can be one-past-the-end, which will return the
334
    ///   last line index.
335
    ///
336
    /// Runs in O(log N) time.
337
    ///
338
    /// # Panics
339
    ///
340
    /// Panics if `byte_idx` is out of bounds (i.e. `byte_idx > len_bytes()`).
341
    #[inline]
342
0
    pub fn byte_to_line(&self, byte_idx: usize) -> usize {
343
0
        self.try_byte_to_line(byte_idx).unwrap()
344
0
    }
345
346
    /// Returns the byte index of the given char.
347
    ///
348
    /// Notes:
349
    ///
350
    /// - `char_idx` can be one-past-the-end, which will return
351
    ///   one-past-the-end byte index.
352
    ///
353
    /// Runs in O(log N) time.
354
    ///
355
    /// # Panics
356
    ///
357
    /// Panics if `char_idx` is out of bounds (i.e. `char_idx > len_chars()`).
358
    #[inline]
359
0
    pub fn char_to_byte(&self, char_idx: usize) -> usize {
360
0
        self.try_char_to_byte(char_idx).unwrap()
361
0
    }
362
363
    /// Returns the line index of the given char.
364
    ///
365
    /// Notes:
366
    ///
367
    /// - Lines are zero-indexed.  This is functionally equivalent to
368
    ///   counting the line endings before the specified char.
369
    /// - `char_idx` can be one-past-the-end, which will return the
370
    ///   last line index.
371
    ///
372
    /// Runs in O(log N) time.
373
    ///
374
    /// # Panics
375
    ///
376
    /// Panics if `char_idx` is out of bounds (i.e. `char_idx > len_chars()`).
377
    #[inline]
378
0
    pub fn char_to_line(&self, char_idx: usize) -> usize {
379
0
        self.try_char_to_line(char_idx).unwrap()
380
0
    }
381
382
    /// Returns the utf16 code unit index of the given char.
383
    ///
384
    /// Ropey stores text internally as utf8, but sometimes it is necessary
385
    /// to interact with external APIs that still use utf16.  This function is
386
    /// primarily intended for such situations, and is otherwise not very
387
    /// useful.
388
    ///
389
    /// Runs in O(log N) time.
390
    ///
391
    /// # Panics
392
    ///
393
    /// Panics if `char_idx` is out of bounds (i.e. `char_idx > len_chars()`).
394
    #[inline]
395
0
    pub fn char_to_utf16_cu(&self, char_idx: usize) -> usize {
396
0
        self.try_char_to_utf16_cu(char_idx).unwrap()
397
0
    }
398
399
    /// Returns the char index of the given utf16 code unit.
400
    ///
401
    /// Ropey stores text internally as utf8, but sometimes it is necessary
402
    /// to interact with external APIs that still use utf16.  This function is
403
    /// primarily intended for such situations, and is otherwise not very
404
    /// useful.
405
    ///
406
    /// Note: if the utf16 code unit is in the middle of a char, returns the
407
    /// index of the char that it belongs to.
408
    ///
409
    /// Runs in O(log N) time.
410
    ///
411
    /// # Panics
412
    ///
413
    /// Panics if `utf16_cu_idx` is out of bounds
414
    /// (i.e. `utf16_cu_idx > len_utf16_cu()`).
415
    #[inline]
416
0
    pub fn utf16_cu_to_char(&self, utf16_cu_idx: usize) -> usize {
417
0
        self.try_utf16_cu_to_char(utf16_cu_idx).unwrap()
418
0
    }
419
420
    /// Returns the byte index of the start of the given line.
421
    ///
422
    /// Notes:
423
    ///
424
    /// - Lines are zero-indexed.
425
    /// - `line_idx` can be one-past-the-end, which will return
426
    ///   one-past-the-end byte index.
427
    ///
428
    /// Runs in O(log N) time.
429
    ///
430
    /// # Panics
431
    ///
432
    /// Panics if `line_idx` is out of bounds (i.e. `line_idx > len_lines()`).
433
    #[inline]
434
0
    pub fn line_to_byte(&self, line_idx: usize) -> usize {
435
0
        self.try_line_to_byte(line_idx).unwrap()
436
0
    }
437
438
    /// Returns the char index of the start of the given line.
439
    ///
440
    /// Notes:
441
    ///
442
    /// - Lines are zero-indexed.
443
    /// - `line_idx` can be one-past-the-end, which will return
444
    ///   one-past-the-end char index.
445
    ///
446
    /// Runs in O(log N) time.
447
    ///
448
    /// # Panics
449
    ///
450
    /// Panics if `line_idx` is out of bounds (i.e. `line_idx > len_lines()`).
451
    #[inline]
452
0
    pub fn line_to_char(&self, line_idx: usize) -> usize {
453
0
        self.try_line_to_char(line_idx).unwrap()
454
0
    }
455
456
    //-----------------------------------------------------------------------
457
    // Fetch methods
458
459
    /// Returns the byte at `byte_idx`.
460
    ///
461
    /// Runs in O(log N) time.
462
    ///
463
    /// # Panics
464
    ///
465
    /// Panics if `byte_idx` is out of bounds (i.e. `byte_idx >= len_bytes()`).
466
    #[inline]
467
0
    pub fn byte(&self, byte_idx: usize) -> u8 {
468
        // Bounds check
469
0
        if let Some(out) = self.get_byte(byte_idx) {
470
0
            out
471
        } else {
472
0
            panic!(
473
                "Attempt to index past end of slice: byte index {}, slice byte length {}",
474
                byte_idx,
475
0
                self.len_bytes()
476
            );
477
        }
478
0
    }
479
480
    /// Returns the char at `char_idx`.
481
    ///
482
    /// Runs in O(log N) time.
483
    ///
484
    /// # Panics
485
    ///
486
    /// Panics if `char_idx` is out of bounds (i.e. `char_idx >= len_chars()`).
487
    #[inline]
488
0
    pub fn char(&self, char_idx: usize) -> char {
489
0
        if let Some(out) = self.get_char(char_idx) {
490
0
            out
491
        } else {
492
0
            panic!(
493
                "Attempt to index past end of slice: char index {}, slice char length {}",
494
                char_idx,
495
0
                self.len_chars()
496
            );
497
        }
498
0
    }
499
500
    /// Returns the line at `line_idx`.
501
    ///
502
    /// Note: lines are zero-indexed.
503
    ///
504
    /// Runs in O(log N) time.
505
    ///
506
    /// # Panics
507
    ///
508
    /// Panics if `line_idx` is out of bounds (i.e. `line_idx >= len_lines()`).
509
    #[inline]
510
0
    pub fn line(&self, line_idx: usize) -> RopeSlice<'a> {
511
0
        if let Some(out) = self.get_line(line_idx) {
512
0
            out
513
        } else {
514
0
            let len_lines = self.len_lines();
515
0
            panic!(
516
                "Attempt to index past end of slice: line index {}, slice line length {}",
517
                line_idx, len_lines
518
            );
519
        }
520
0
    }
521
522
    /// Returns the chunk containing the given byte index.
523
    ///
524
    /// Also returns the byte and char indices of the beginning of the chunk
525
    /// and the index of the line that the chunk starts on.
526
    ///
527
    /// Note: for convenience, a one-past-the-end `byte_idx` returns the last
528
    /// chunk of the `RopeSlice`.
529
    ///
530
    /// The return value is organized as
531
    /// `(chunk, chunk_byte_idx, chunk_char_idx, chunk_line_idx)`.
532
    ///
533
    /// Runs in O(log N) time.
534
    ///
535
    /// # Panics
536
    ///
537
    /// Panics if `byte_idx` is out of bounds (i.e. `byte_idx > len_bytes()`).
538
0
    pub fn chunk_at_byte(&self, byte_idx: usize) -> (&'a str, usize, usize, usize) {
539
0
        self.try_chunk_at_byte(byte_idx).unwrap()
540
0
    }
541
542
    /// Returns the chunk containing the given char index.
543
    ///
544
    /// Also returns the byte and char indices of the beginning of the chunk
545
    /// and the index of the line that the chunk starts on.
546
    ///
547
    /// Note: for convenience, a one-past-the-end `char_idx` returns the last
548
    /// chunk of the `RopeSlice`.
549
    ///
550
    /// The return value is organized as
551
    /// `(chunk, chunk_byte_idx, chunk_char_idx, chunk_line_idx)`.
552
    ///
553
    /// Runs in O(log N) time.
554
    ///
555
    /// # Panics
556
    ///
557
    /// Panics if `char_idx` is out of bounds (i.e. `char_idx > len_chars()`).
558
0
    pub fn chunk_at_char(&self, char_idx: usize) -> (&'a str, usize, usize, usize) {
559
0
        if let Some(out) = self.get_chunk_at_char(char_idx) {
560
0
            out
561
        } else {
562
0
            panic!(
563
                "Attempt to index past end of slice: char index {}, slice char length {}",
564
                char_idx,
565
0
                self.len_chars()
566
            );
567
        }
568
0
    }
569
570
    /// Returns the chunk containing the given line break.
571
    ///
572
    /// Also returns the byte and char indices of the beginning of the chunk
573
    /// and the index of the line that the chunk starts on.
574
    ///
575
    /// Note: for convenience, both the beginning and end of the slice are
576
    /// considered line breaks for the purposes of indexing.  For example, in
577
    /// the string `"Hello \n world!"` 0 would give the first chunk, 1 would
578
    /// give the chunk containing the newline character, and 2 would give the
579
    /// last chunk.
580
    ///
581
    /// The return value is organized as
582
    /// `(chunk, chunk_byte_idx, chunk_char_idx, chunk_line_idx)`.
583
    ///
584
    /// Runs in O(log N) time.
585
    ///
586
    /// # Panics
587
    ///
588
    /// Panics if `line_break_idx` is out of bounds (i.e. `line_break_idx > len_lines()`).
589
0
    pub fn chunk_at_line_break(&self, line_break_idx: usize) -> (&'a str, usize, usize, usize) {
590
0
        if let Some(out) = self.get_chunk_at_line_break(line_break_idx) {
591
0
            out
592
        } else {
593
0
            panic!(
594
                "Attempt to index past end of Rope: line break index {}, max index {}",
595
                line_break_idx,
596
0
                self.len_lines()
597
            );
598
        }
599
0
    }
600
601
    /// Returns the entire contents of the `RopeSlice` as a `&str` if
602
    /// possible.
603
    ///
604
    /// This is useful for optimizing cases where the slice is only a few
605
    /// characters or words, and therefore has a high chance of being
606
    /// contiguous in memory.
607
    ///
608
    /// For large slices this method will typically fail and return `None`
609
    /// because large slices usually cross chunk boundaries in the rope.
610
    ///
611
    /// (Also see the `From` impl for converting to a `Cow<str>`.)
612
    ///
613
    /// Runs in O(1) time.
614
    #[inline]
615
0
    pub fn as_str(&self) -> Option<&'a str> {
616
0
        match *self {
617
0
            RopeSlice(RSEnum::Full { .. }) => None,
618
0
            RopeSlice(RSEnum::Light { text, .. }) => Some(text),
619
        }
620
0
    }
621
622
    //-----------------------------------------------------------------------
623
    // Slice creation
624
625
    /// Returns a sub-slice of the `RopeSlice` in the given char index range.
626
    ///
627
    /// Uses range syntax, e.g. `2..7`, `2..`, etc.
628
    ///
629
    /// Runs in O(log N) time.
630
    ///
631
    /// # Panics
632
    ///
633
    /// Panics if the start of the range is greater than the end, or the end
634
    /// is out of bounds (i.e. `end > len_chars()`).
635
0
    pub fn slice<R>(&self, char_range: R) -> RopeSlice<'a>
636
0
    where
637
0
        R: RangeBounds<usize>,
638
    {
639
0
        let (start, end) = {
640
0
            let start_range = start_bound_to_num(char_range.start_bound());
641
0
            let end_range = end_bound_to_num(char_range.end_bound());
642
643
            // Early-out shortcut for taking a slice of the full thing.
644
0
            if start_range == None && end_range == None {
645
0
                return *self;
646
0
            }
647
648
            (
649
0
                start_range.unwrap_or(0),
650
0
                end_range.unwrap_or_else(|| self.len_chars()),
651
            )
652
        };
653
654
        // Bounds check
655
0
        assert!(start <= end);
656
0
        assert!(
657
0
            end <= self.len_chars(),
658
            "Attempt to slice past end of RopeSlice: slice end {}, RopeSlice length {}",
659
            end,
660
0
            self.len_chars()
661
        );
662
663
0
        match *self {
664
            RopeSlice(RSEnum::Full {
665
0
                node, start_info, ..
666
0
            }) => RopeSlice::new_with_range(
667
0
                node,
668
0
                start_info.chars as usize + start,
669
0
                start_info.chars as usize + end,
670
            ),
671
0
            RopeSlice(RSEnum::Light { text, .. }) => {
672
0
                let start_byte = char_to_byte_idx(text, start);
673
0
                let end_byte = char_to_byte_idx(text, end);
674
0
                let new_text = &text[start_byte..end_byte];
675
0
                RopeSlice(RSEnum::Light {
676
0
                    text: new_text,
677
0
                    char_count: (end - start) as Count,
678
0
                    utf16_surrogate_count: count_utf16_surrogates(new_text) as Count,
679
0
                    line_break_count: count_line_breaks(new_text) as Count,
680
0
                })
681
            }
682
        }
683
0
    }
684
685
    /// Returns a sub-slice of the `RopeSlice` in the given byte index range.
686
    ///
687
    /// Uses range syntax, e.g. `2..7`, `2..`, etc.
688
    ///
689
    /// Runs in O(log N) time.
690
    ///
691
    /// # Panics
692
    ///
693
    /// Panics if:
694
    /// - The start of the range is greater than the end.
695
    /// - The end is out of bounds (i.e. `end > len_bytes()`).
696
    /// - The range doesn't align with char boundaries.
697
0
    pub fn byte_slice<R>(&self, byte_range: R) -> RopeSlice<'a>
698
0
    where
699
0
        R: RangeBounds<usize>,
700
    {
701
0
        match self.get_byte_slice_impl(byte_range) {
702
0
            Ok(s) => return s,
703
0
            Err(e) => panic!("byte_slice(): {}", e),
704
        }
705
0
    }
706
707
    //-----------------------------------------------------------------------
708
    // Iterator methods
709
710
    /// Creates an iterator over the bytes of the `RopeSlice`.
711
    ///
712
    /// Runs in O(log N) time.
713
    #[inline]
714
0
    pub fn bytes(&self) -> Bytes<'a> {
715
0
        match *self {
716
            RopeSlice(RSEnum::Full {
717
0
                node,
718
0
                start_info,
719
0
                end_info,
720
0
            }) => Bytes::new_with_range(
721
0
                node,
722
0
                (start_info.bytes as usize, end_info.bytes as usize),
723
0
                (start_info.chars as usize, end_info.chars as usize),
724
0
                (
725
0
                    start_info.line_breaks as usize,
726
0
                    end_info.line_breaks as usize + 1,
727
0
                ),
728
            ),
729
0
            RopeSlice(RSEnum::Light { text, .. }) => Bytes::from_str(text),
730
        }
731
0
    }
732
733
    /// Creates an iterator over the bytes of the `RopeSlice`, starting at
734
    /// byte `byte_idx`.
735
    ///
736
    /// If `byte_idx == len_bytes()` then an iterator at the end of the
737
    /// `RopeSlice` is created (i.e. `next()` will return `None`).
738
    ///
739
    /// Runs in O(log N) time.
740
    ///
741
    /// # Panics
742
    ///
743
    /// Panics if `byte_idx` is out of bounds (i.e. `byte_idx > len_bytes()`).
744
    #[inline]
745
0
    pub fn bytes_at(&self, byte_idx: usize) -> Bytes<'a> {
746
0
        if let Some(out) = self.get_bytes_at(byte_idx) {
747
0
            out
748
        } else {
749
0
            panic!(
750
                "Attempt to index past end of RopeSlice: byte index {}, RopeSlice byte length {}",
751
                byte_idx,
752
0
                self.len_bytes()
753
            );
754
        }
755
0
    }
756
757
    /// Creates an iterator over the chars of the `RopeSlice`.
758
    ///
759
    /// Runs in O(log N) time.
760
    #[inline]
761
0
    pub fn chars(&self) -> Chars<'a> {
762
0
        match *self {
763
            RopeSlice(RSEnum::Full {
764
0
                node,
765
0
                start_info,
766
0
                end_info,
767
0
            }) => Chars::new_with_range(
768
0
                node,
769
0
                (start_info.bytes as usize, end_info.bytes as usize),
770
0
                (start_info.chars as usize, end_info.chars as usize),
771
0
                (
772
0
                    start_info.line_breaks as usize,
773
0
                    end_info.line_breaks as usize + 1,
774
0
                ),
775
            ),
776
0
            RopeSlice(RSEnum::Light { text, .. }) => Chars::from_str(text),
777
        }
778
0
    }
Unexecuted instantiation: <ropey::slice::RopeSlice>::chars
Unexecuted instantiation: <ropey::slice::RopeSlice>::chars
779
780
    /// Creates an iterator over the chars of the `RopeSlice`, starting at
781
    /// char `char_idx`.
782
    ///
783
    /// If `char_idx == len_chars()` then an iterator at the end of the
784
    /// `RopeSlice` is created (i.e. `next()` will return `None`).
785
    ///
786
    /// Runs in O(log N) time.
787
    ///
788
    /// # Panics
789
    ///
790
    /// Panics if `char_idx` is out of bounds (i.e. `char_idx > len_chars()`).
791
    #[inline]
792
0
    pub fn chars_at(&self, char_idx: usize) -> Chars<'a> {
793
0
        if let Some(out) = self.get_chars_at(char_idx) {
794
0
            out
795
        } else {
796
0
            panic!(
797
                "Attempt to index past end of RopeSlice: char index {}, RopeSlice char length {}",
798
                char_idx,
799
0
                self.len_chars()
800
            );
801
        }
802
0
    }
803
804
    /// Creates an iterator over the lines of the `RopeSlice`.
805
    ///
806
    /// Runs in O(log N) time.
807
    #[inline]
808
0
    pub fn lines(&self) -> Lines<'a> {
809
0
        match *self {
810
            RopeSlice(RSEnum::Full {
811
0
                node,
812
0
                start_info,
813
0
                end_info,
814
0
            }) => Lines::new_with_range(
815
0
                node,
816
0
                (start_info.bytes as usize, end_info.bytes as usize),
817
0
                (
818
0
                    start_info.line_breaks as usize,
819
0
                    end_info.line_breaks as usize + 1,
820
0
                ),
821
            ),
822
            RopeSlice(RSEnum::Light {
823
0
                text,
824
0
                line_break_count,
825
                ..
826
0
            }) => Lines::from_str(text, line_break_count as usize + 1),
827
        }
828
0
    }
829
830
    /// Creates an iterator over the lines of the `RopeSlice`, starting at
831
    /// line `line_idx`.
832
    ///
833
    /// If `line_idx == len_lines()` then an iterator at the end of the
834
    /// `RopeSlice` is created (i.e. `next()` will return `None`).
835
    ///
836
    /// Runs in O(log N) time.
837
    ///
838
    /// # Panics
839
    ///
840
    /// Panics if `line_idx` is out of bounds (i.e. `line_idx > len_lines()`).
841
    #[inline]
842
0
    pub fn lines_at(&self, line_idx: usize) -> Lines<'a> {
843
0
        if let Some(out) = self.get_lines_at(line_idx) {
844
0
            out
845
        } else {
846
0
            panic!(
847
                "Attempt to index past end of RopeSlice: line index {}, RopeSlice line length {}",
848
                line_idx,
849
0
                self.len_lines()
850
            );
851
        }
852
0
    }
853
854
    /// Creates an iterator over the chunks of the `RopeSlice`.
855
    ///
856
    /// Runs in O(log N) time.
857
    #[inline]
858
0
    pub fn chunks(&self) -> Chunks<'a> {
859
0
        match *self {
860
            RopeSlice(RSEnum::Full {
861
0
                node,
862
0
                start_info,
863
0
                end_info,
864
0
            }) => Chunks::new_with_range(
865
0
                node,
866
0
                (start_info.bytes as usize, end_info.bytes as usize),
867
0
                (start_info.chars as usize, end_info.chars as usize),
868
0
                (
869
0
                    start_info.line_breaks as usize,
870
0
                    end_info.line_breaks as usize + 1,
871
0
                ),
872
            ),
873
0
            RopeSlice(RSEnum::Light { text, .. }) => Chunks::from_str(text, false),
874
        }
875
0
    }
876
877
    /// Creates an iterator over the chunks of the `RopeSlice`, with the
878
    /// iterator starting at the byte containing `byte_idx`.
879
    ///
880
    /// Also returns the byte and char indices of the beginning of the first
881
    /// chunk to be yielded, and the index of the line that chunk starts on.
882
    ///
883
    /// If `byte_idx == len_bytes()` an iterator at the end of the `RopeSlice`
884
    /// (yielding `None` on a call to `next()`) is created.
885
    ///
886
    /// The return value is organized as
887
    /// `(iterator, chunk_byte_idx, chunk_char_idx, chunk_line_idx)`.
888
    ///
889
    /// Runs in O(log N) time.
890
    ///
891
    /// # Panics
892
    ///
893
    /// Panics if `byte_idx` is out of bounds (i.e. `byte_idx > len_bytes()`).
894
    #[inline]
895
0
    pub fn chunks_at_byte(&self, byte_idx: usize) -> (Chunks<'a>, usize, usize, usize) {
896
0
        if let Some(out) = self.get_chunks_at_byte(byte_idx) {
897
0
            out
898
        } else {
899
0
            panic!(
900
                "Attempt to index past end of RopeSlice: byte index {}, RopeSlice byte length {}",
901
                byte_idx,
902
0
                self.len_bytes()
903
            );
904
        }
905
0
    }
906
907
    /// Creates an iterator over the chunks of the `RopeSlice`, with the
908
    /// iterator starting on the chunk containing `char_idx`.
909
    ///
910
    /// Also returns the byte and char indices of the beginning of the first
911
    /// chunk to be yielded, and the index of the line that chunk starts on.
912
    ///
913
    /// If `char_idx == len_chars()` an iterator at the end of the `RopeSlice`
914
    /// (yielding `None` on a call to `next()`) is created.
915
    ///
916
    /// The return value is organized as
917
    /// `(iterator, chunk_byte_idx, chunk_char_idx, chunk_line_idx)`.
918
    ///
919
    /// Runs in O(log N) time.
920
    ///
921
    /// # Panics
922
    ///
923
    /// Panics if `char_idx` is out of bounds (i.e. `char_idx > len_chars()`).
924
    #[inline]
925
0
    pub fn chunks_at_char(&self, char_idx: usize) -> (Chunks<'a>, usize, usize, usize) {
926
0
        if let Some(out) = self.get_chunks_at_char(char_idx) {
927
0
            out
928
        } else {
929
0
            panic!(
930
                "Attempt to index past end of RopeSlice: char index {}, RopeSlice char length {}",
931
                char_idx,
932
0
                self.len_chars()
933
            );
934
        }
935
0
    }
936
937
    /// Creates an iterator over the chunks of the `RopeSlice`, with the
938
    /// iterator starting at the chunk containing `line_break_idx`.
939
    ///
940
    /// Also returns the byte and char indices of the beginning of the first
941
    /// chunk to be yielded, and the index of the line that chunk starts on.
942
    ///
943
    /// Note: for convenience, both the beginning and end of the `RopeSlice` are
944
    /// considered line breaks for the purposes of indexing.  For example, in
945
    /// the string `"Hello \n world!"` 0 would create an iterator starting on
946
    /// the first chunk, 1 would create an iterator starting on the chunk
947
    /// containing the newline character, and 2 would create an iterator at
948
    /// the end of the `RopeSlice` (yielding `None` on a call to `next()`).
949
    ///
950
    /// The return value is organized as
951
    /// `(iterator, chunk_byte_idx, chunk_char_idx, chunk_line_idx)`.
952
    ///
953
    /// Runs in O(log N) time.
954
    ///
955
    /// # Panics
956
    ///
957
    /// Panics if `line_break_idx` is out of bounds (i.e. `line_break_idx > len_lines()`).
958
    #[inline]
959
0
    pub fn chunks_at_line_break(&self, line_break_idx: usize) -> (Chunks<'a>, usize, usize, usize) {
960
0
        if let Some(out) = self.get_chunks_at_line_break(line_break_idx) {
961
0
            out
962
        } else {
963
0
            panic!(
964
                "Attempt to index past end of RopeSlice: line break index {}, RopeSlice line break max index {}",
965
                line_break_idx,
966
0
                self.len_lines()
967
            );
968
        }
969
0
    }
970
}
971
972
/// # Non-Panicking
973
///
974
/// The methods in this impl block provide non-panicking versions of
975
/// `RopeSlice`'s panicking methods.  They return either `Option::None` or
976
/// `Result::Err()` when their panicking counterparts would have panicked.
977
impl<'a> RopeSlice<'a> {
978
    /// Non-panicking version of [`byte_to_char()`](RopeSlice::byte_to_char).
979
    #[inline]
980
0
    pub fn try_byte_to_char(&self, byte_idx: usize) -> Result<usize> {
981
        // Bounds check
982
0
        if byte_idx <= self.len_bytes() {
983
0
            let (chunk, b, c, _) = self.chunk_at_byte(byte_idx);
984
0
            Ok(c + byte_to_char_idx(chunk, byte_idx - b))
985
        } else {
986
0
            Err(Error::ByteIndexOutOfBounds(byte_idx, self.len_bytes()))
987
        }
988
0
    }
989
990
    /// Non-panicking version of [`byte_to_line()`](RopeSlice::byte_to_line).
991
    #[inline]
992
0
    pub fn try_byte_to_line(&self, byte_idx: usize) -> Result<usize> {
993
        // Bounds check
994
0
        if byte_idx <= self.len_bytes() {
995
0
            let (chunk, b, _, l) = self.chunk_at_byte(byte_idx);
996
0
            Ok(l + byte_to_line_idx(chunk, byte_idx - b))
997
        } else {
998
0
            Err(Error::ByteIndexOutOfBounds(byte_idx, self.len_bytes()))
999
        }
1000
0
    }
1001
1002
    /// Non-panicking version of [`char_to_byte()`](RopeSlice::char_to_byte).
1003
    #[inline]
1004
0
    pub fn try_char_to_byte(&self, char_idx: usize) -> Result<usize> {
1005
        // Bounds check
1006
0
        if char_idx <= self.len_chars() {
1007
0
            let (chunk, b, c, _) = self.chunk_at_char(char_idx);
1008
0
            Ok(b + char_to_byte_idx(chunk, char_idx - c))
1009
        } else {
1010
0
            Err(Error::CharIndexOutOfBounds(char_idx, self.len_chars()))
1011
        }
1012
0
    }
1013
1014
    /// Non-panicking version of [`char_to_line()`](RopeSlice::char_to_line).
1015
    #[inline]
1016
0
    pub fn try_char_to_line(&self, char_idx: usize) -> Result<usize> {
1017
        // Bounds check
1018
0
        if char_idx <= self.len_chars() {
1019
0
            let (chunk, _, c, l) = self.chunk_at_char(char_idx);
1020
0
            Ok(l + char_to_line_idx(chunk, char_idx - c))
1021
        } else {
1022
0
            Err(Error::CharIndexOutOfBounds(char_idx, self.len_chars()))
1023
        }
1024
0
    }
1025
1026
    /// Non-panicking version of [`char_to_utf16_cu()`](RopeSlice::char_to_utf16_cu).
1027
    #[inline]
1028
0
    pub fn try_char_to_utf16_cu(&self, char_idx: usize) -> Result<usize> {
1029
        // Bounds check
1030
0
        if char_idx <= self.len_chars() {
1031
0
            match *self {
1032
                RopeSlice(RSEnum::Full {
1033
0
                    node, start_info, ..
1034
                }) => {
1035
0
                    let char_idx = char_idx + start_info.chars as usize;
1036
1037
0
                    let (chunk, chunk_start_info) = node.get_chunk_at_char(char_idx);
1038
0
                    let chunk_byte_idx =
1039
0
                        char_to_byte_idx(chunk, char_idx - chunk_start_info.chars as usize);
1040
0
                    let surrogate_count = byte_to_utf16_surrogate_idx(chunk, chunk_byte_idx);
1041
1042
0
                    Ok(
1043
0
                        char_idx + chunk_start_info.utf16_surrogates as usize + surrogate_count
1044
0
                            - start_info.chars as usize
1045
0
                            - start_info.utf16_surrogates as usize,
1046
0
                    )
1047
                }
1048
1049
0
                RopeSlice(RSEnum::Light { text, .. }) => {
1050
0
                    let byte_idx = char_to_byte_idx(text, char_idx);
1051
0
                    let surrogate_count = byte_to_utf16_surrogate_idx(text, byte_idx);
1052
0
                    Ok(char_idx + surrogate_count)
1053
                }
1054
            }
1055
        } else {
1056
0
            Err(Error::CharIndexOutOfBounds(char_idx, self.len_chars()))
1057
        }
1058
0
    }
1059
1060
    /// Non-panicking version of [`utf16_cu_to_char()`](RopeSlice::utf16_cu_to_char).
1061
    #[inline]
1062
0
    pub fn try_utf16_cu_to_char(&self, utf16_cu_idx: usize) -> Result<usize> {
1063
        // Bounds check
1064
0
        if utf16_cu_idx <= self.len_utf16_cu() {
1065
0
            match *self {
1066
                RopeSlice(RSEnum::Full {
1067
0
                    node, start_info, ..
1068
                }) => {
1069
0
                    let utf16_cu_idx =
1070
0
                        utf16_cu_idx + (start_info.chars + start_info.utf16_surrogates) as usize;
1071
1072
0
                    let (chunk, chunk_start_info) = node.get_chunk_at_utf16_code_unit(utf16_cu_idx);
1073
0
                    let chunk_utf16_cu_idx = utf16_cu_idx
1074
0
                        - (chunk_start_info.chars + chunk_start_info.utf16_surrogates) as usize;
1075
0
                    let chunk_char_idx = utf16_code_unit_to_char_idx(chunk, chunk_utf16_cu_idx);
1076
1077
0
                    Ok(
1078
0
                        chunk_start_info.chars as usize + chunk_char_idx
1079
0
                            - start_info.chars as usize,
1080
0
                    )
1081
                }
1082
1083
0
                RopeSlice(RSEnum::Light { text, .. }) => {
1084
0
                    Ok(utf16_code_unit_to_char_idx(text, utf16_cu_idx))
1085
                }
1086
            }
1087
        } else {
1088
0
            Err(Error::Utf16IndexOutOfBounds(
1089
0
                utf16_cu_idx,
1090
0
                self.len_utf16_cu(),
1091
0
            ))
1092
        }
1093
0
    }
1094
1095
    /// Non-panicking version of [`line_to_byte()`](RopeSlice::line_to_byte).
1096
    #[inline]
1097
0
    pub fn try_line_to_byte(&self, line_idx: usize) -> Result<usize> {
1098
        // Bounds check
1099
0
        if line_idx <= self.len_lines() {
1100
0
            if line_idx == self.len_lines() {
1101
0
                Ok(self.len_bytes())
1102
            } else {
1103
0
                let (chunk, b, _, l) = self.chunk_at_line_break(line_idx);
1104
0
                Ok(b + line_to_byte_idx(chunk, line_idx - l))
1105
            }
1106
        } else {
1107
0
            Err(Error::LineIndexOutOfBounds(line_idx, self.len_lines()))
1108
        }
1109
0
    }
1110
1111
    /// Non-panicking version of [`line_to_char()`](RopeSlice::line_to_char).
1112
    #[inline]
1113
0
    pub fn try_line_to_char(&self, line_idx: usize) -> Result<usize> {
1114
        // Bounds check
1115
0
        if line_idx <= self.len_lines() {
1116
0
            if line_idx == self.len_lines() {
1117
0
                Ok(self.len_chars())
1118
            } else {
1119
0
                let (chunk, _, c, l) = self.chunk_at_line_break(line_idx);
1120
0
                Ok(c + line_to_char_idx(chunk, line_idx - l))
1121
            }
1122
        } else {
1123
0
            Err(Error::LineIndexOutOfBounds(line_idx, self.len_lines()))
1124
        }
1125
0
    }
1126
1127
    /// Non-panicking version of [`get_byte()`](RopeSlice::get_byte).
1128
    #[inline]
1129
0
    pub fn get_byte(&self, byte_idx: usize) -> Option<u8> {
1130
        // Bounds check
1131
0
        if byte_idx < self.len_bytes() {
1132
0
            let (chunk, chunk_byte_idx, _, _) = self.chunk_at_byte(byte_idx);
1133
0
            let chunk_rel_byte_idx = byte_idx - chunk_byte_idx;
1134
0
            Some(chunk.as_bytes()[chunk_rel_byte_idx])
1135
        } else {
1136
0
            None
1137
        }
1138
0
    }
1139
1140
    /// Non-panicking version of [`char()`](RopeSlice::char).
1141
    #[inline]
1142
0
    pub fn get_char(&self, char_idx: usize) -> Option<char> {
1143
        // Bounds check
1144
0
        if char_idx < self.len_chars() {
1145
0
            let (chunk, _, chunk_char_idx, _) = self.chunk_at_char(char_idx);
1146
0
            let byte_idx = char_to_byte_idx(chunk, char_idx - chunk_char_idx);
1147
0
            Some(chunk[byte_idx..].chars().next().unwrap())
1148
        } else {
1149
0
            None
1150
        }
1151
0
    }
1152
1153
    /// Non-panicking version of [`line()`](RopeSlice::line).
1154
    #[inline]
1155
0
    pub fn get_line(&self, line_idx: usize) -> Option<RopeSlice<'a>> {
1156
0
        let len_lines = self.len_lines();
1157
        // Bounds check
1158
0
        if line_idx < len_lines {
1159
0
            let (chunk_1, _, c1, l1) = self.chunk_at_line_break(line_idx);
1160
0
            let (chunk_2, _, c2, l2) = self.chunk_at_line_break(line_idx + 1);
1161
0
            if c1 == c2 {
1162
0
                let text1 = &chunk_1[line_to_byte_idx(chunk_1, line_idx - l1)..];
1163
0
                let text2 = &text1[..line_to_byte_idx(text1, 1)];
1164
                Some(RopeSlice(RSEnum::Light {
1165
0
                    text: text2,
1166
0
                    char_count: count_chars(text2) as Count,
1167
0
                    utf16_surrogate_count: count_utf16_surrogates(text2) as Count,
1168
0
                    line_break_count: if line_idx == (len_lines - 1) { 0 } else { 1 },
1169
                }))
1170
            } else {
1171
0
                let start = c1 + line_to_char_idx(chunk_1, line_idx - l1);
1172
0
                let end = c2 + line_to_char_idx(chunk_2, line_idx + 1 - l2);
1173
0
                Some(self.slice(start..end))
1174
            }
1175
        } else {
1176
0
            None
1177
        }
1178
0
    }
1179
1180
    /// Non-panicking version of [`chunk_at_byte()`](RopeSlice::chunk_at_byte).
1181
0
    pub fn try_chunk_at_byte(&self, byte_idx: usize) -> Result<(&'a str, usize, usize, usize)> {
1182
        // Bounds check
1183
0
        if byte_idx <= self.len_bytes() {
1184
0
            match *self {
1185
                RopeSlice(RSEnum::Full {
1186
0
                    node,
1187
0
                    start_info,
1188
0
                    end_info,
1189
                }) => {
1190
                    // Get the chunk.
1191
0
                    let (chunk, chunk_start_info) =
1192
0
                        node.get_chunk_at_byte(byte_idx + start_info.bytes as usize);
1193
1194
                    // Calculate clipped start/end byte indices within the chunk.
1195
0
                    let chunk_start_byte_idx =
1196
0
                        start_info.bytes.saturating_sub(chunk_start_info.bytes);
1197
0
                    let chunk_end_byte_idx =
1198
0
                        (chunk.len() as Count).min(end_info.bytes - chunk_start_info.bytes);
1199
1200
                    // Return the clipped chunk and byte offset.
1201
0
                    Ok((
1202
0
                        &chunk[chunk_start_byte_idx as usize..chunk_end_byte_idx as usize],
1203
0
                        chunk_start_info.bytes.saturating_sub(start_info.bytes) as usize,
1204
0
                        chunk_start_info.chars.saturating_sub(start_info.chars) as usize,
1205
0
                        chunk_start_info
1206
0
                            .line_breaks
1207
0
                            .saturating_sub(start_info.line_breaks)
1208
0
                            as usize,
1209
0
                    ))
1210
                }
1211
0
                RopeSlice(RSEnum::Light { text, .. }) => Ok((text, 0, 0, 0)),
1212
            }
1213
        } else {
1214
0
            Err(Error::ByteIndexOutOfBounds(byte_idx, self.len_bytes()))
1215
        }
1216
0
    }
1217
1218
    /// Non-panicking version of [`chunk_at_char()`](RopeSlice::chunk_at_char).
1219
0
    pub fn get_chunk_at_char(&self, char_idx: usize) -> Option<(&'a str, usize, usize, usize)> {
1220
        // Bounds check
1221
0
        if char_idx <= self.len_chars() {
1222
0
            match *self {
1223
                RopeSlice(RSEnum::Full {
1224
0
                    node,
1225
0
                    start_info,
1226
0
                    end_info,
1227
                }) => {
1228
                    // Get the chunk.
1229
0
                    let (chunk, chunk_start_info) =
1230
0
                        node.get_chunk_at_char(char_idx + start_info.chars as usize);
1231
1232
                    // Calculate clipped start/end byte indices within the chunk.
1233
0
                    let chunk_start_byte_idx =
1234
0
                        start_info.bytes.saturating_sub(chunk_start_info.bytes);
1235
0
                    let chunk_end_byte_idx =
1236
0
                        (chunk.len() as Count).min(end_info.bytes - chunk_start_info.bytes);
1237
1238
                    // Return the clipped chunk and byte offset.
1239
0
                    Some((
1240
0
                        &chunk[chunk_start_byte_idx as usize..chunk_end_byte_idx as usize],
1241
0
                        chunk_start_info.bytes.saturating_sub(start_info.bytes) as usize,
1242
0
                        chunk_start_info.chars.saturating_sub(start_info.chars) as usize,
1243
0
                        chunk_start_info
1244
0
                            .line_breaks
1245
0
                            .saturating_sub(start_info.line_breaks)
1246
0
                            as usize,
1247
0
                    ))
1248
                }
1249
0
                RopeSlice(RSEnum::Light { text, .. }) => Some((text, 0, 0, 0)),
1250
            }
1251
        } else {
1252
0
            None
1253
        }
1254
0
    }
1255
1256
    /// Non-panicking version of [`chunk_at_line_break()`](RopeSlice::chunk_at_line_break).
1257
0
    pub fn get_chunk_at_line_break(
1258
0
        &self,
1259
0
        line_break_idx: usize,
1260
0
    ) -> Option<(&'a str, usize, usize, usize)> {
1261
        // Bounds check
1262
0
        if line_break_idx <= self.len_lines() {
1263
0
            match *self {
1264
                RopeSlice(RSEnum::Full {
1265
0
                    node,
1266
0
                    start_info,
1267
0
                    end_info,
1268
                }) => {
1269
                    // Get the chunk.
1270
0
                    let (chunk, chunk_start_info) = if line_break_idx == 0 {
1271
0
                        node.get_chunk_at_byte(start_info.bytes as usize)
1272
0
                    } else if line_break_idx == self.len_lines() {
1273
0
                        node.get_chunk_at_byte(end_info.bytes as usize)
1274
                    } else {
1275
0
                        node.get_chunk_at_line_break(
1276
0
                            line_break_idx + start_info.line_breaks as usize,
1277
0
                        )
1278
                    };
1279
1280
                    // Calculate clipped start/end byte indices within the chunk.
1281
0
                    let chunk_start_byte_idx =
1282
0
                        start_info.bytes.saturating_sub(chunk_start_info.bytes);
1283
0
                    let chunk_end_byte_idx =
1284
0
                        (chunk.len() as Count).min(end_info.bytes - chunk_start_info.bytes);
1285
1286
                    // Return the clipped chunk and byte offset.
1287
0
                    Some((
1288
0
                        &chunk[chunk_start_byte_idx as usize..chunk_end_byte_idx as usize],
1289
0
                        chunk_start_info.bytes.saturating_sub(start_info.bytes) as usize,
1290
0
                        chunk_start_info.chars.saturating_sub(start_info.chars) as usize,
1291
0
                        chunk_start_info
1292
0
                            .line_breaks
1293
0
                            .saturating_sub(start_info.line_breaks)
1294
0
                            as usize,
1295
0
                    ))
1296
                }
1297
0
                RopeSlice(RSEnum::Light { text, .. }) => Some((text, 0, 0, 0)),
1298
            }
1299
        } else {
1300
0
            None
1301
        }
1302
0
    }
1303
1304
    /// Non-panicking version of [`slice()`](RopeSlice::slice).
1305
0
    pub fn get_slice<R>(&self, char_range: R) -> Option<RopeSlice<'a>>
1306
0
    where
1307
0
        R: RangeBounds<usize>,
1308
    {
1309
0
        let (start, end) = {
1310
0
            let start_range = start_bound_to_num(char_range.start_bound());
1311
0
            let end_range = end_bound_to_num(char_range.end_bound());
1312
1313
            // Early-out shortcut for taking a slice of the full thing.
1314
0
            if start_range == None && end_range == None {
1315
0
                return Some(*self);
1316
0
            }
1317
1318
            (
1319
0
                start_range.unwrap_or(0),
1320
0
                end_range.unwrap_or_else(|| self.len_chars()),
1321
            )
1322
        };
1323
1324
        // Bounds check
1325
0
        if start <= end && end <= self.len_chars() {
1326
0
            match *self {
1327
                RopeSlice(RSEnum::Full {
1328
0
                    node, start_info, ..
1329
0
                }) => Some(RopeSlice::new_with_range(
1330
0
                    node,
1331
0
                    start_info.chars as usize + start,
1332
0
                    start_info.chars as usize + end,
1333
0
                )),
1334
0
                RopeSlice(RSEnum::Light { text, .. }) => {
1335
0
                    let start_byte = char_to_byte_idx(text, start);
1336
0
                    let end_byte = char_to_byte_idx(text, end);
1337
0
                    let new_text = &text[start_byte..end_byte];
1338
0
                    Some(RopeSlice(RSEnum::Light {
1339
0
                        text: new_text,
1340
0
                        char_count: (end - start) as Count,
1341
0
                        utf16_surrogate_count: count_utf16_surrogates(new_text) as Count,
1342
0
                        line_break_count: count_line_breaks(new_text) as Count,
1343
0
                    }))
1344
                }
1345
            }
1346
        } else {
1347
0
            None
1348
        }
1349
0
    }
1350
1351
    /// Non-panicking version of [`byte_slice()`](RopeSlice::byte_slice).
1352
0
    pub fn get_byte_slice<R>(&self, byte_range: R) -> Option<RopeSlice<'a>>
1353
0
    where
1354
0
        R: RangeBounds<usize>,
1355
    {
1356
0
        self.get_byte_slice_impl(byte_range).ok()
1357
0
    }
1358
1359
0
    pub(crate) fn get_byte_slice_impl<R>(&self, byte_range: R) -> Result<RopeSlice<'a>>
1360
0
    where
1361
0
        R: RangeBounds<usize>,
1362
    {
1363
0
        let start_range = start_bound_to_num(byte_range.start_bound());
1364
0
        let end_range = end_bound_to_num(byte_range.end_bound());
1365
1366
        // Bounds checks.
1367
0
        match (start_range, end_range) {
1368
            (None, None) => {
1369
                // Early-out shortcut for taking a slice of the full thing.
1370
0
                return Ok(*self);
1371
            }
1372
0
            (Some(s), Some(e)) => {
1373
0
                if s > e {
1374
0
                    return Err(Error::ByteRangeInvalid(s, e));
1375
0
                } else if e > self.len_bytes() {
1376
0
                    return Err(Error::ByteRangeOutOfBounds(
1377
0
                        start_range,
1378
0
                        end_range,
1379
0
                        self.len_bytes(),
1380
0
                    ));
1381
0
                }
1382
            }
1383
0
            (Some(s), None) => {
1384
0
                if s > self.len_bytes() {
1385
0
                    return Err(Error::ByteRangeOutOfBounds(
1386
0
                        start_range,
1387
0
                        end_range,
1388
0
                        self.len_bytes(),
1389
0
                    ));
1390
0
                }
1391
            }
1392
0
            (None, Some(e)) => {
1393
0
                if e > self.len_bytes() {
1394
0
                    return Err(Error::ByteRangeOutOfBounds(
1395
0
                        start_range,
1396
0
                        end_range,
1397
0
                        self.len_bytes(),
1398
0
                    ));
1399
0
                }
1400
            }
1401
        }
1402
1403
0
        let (start, end) = (
1404
0
            start_range.unwrap_or(0),
1405
0
            end_range.unwrap_or_else(|| self.len_bytes()),
1406
        );
1407
1408
0
        match *self {
1409
            RopeSlice(RSEnum::Full {
1410
0
                node, start_info, ..
1411
0
            }) => RopeSlice::new_with_byte_range(
1412
0
                node,
1413
0
                start_info.bytes as usize + start,
1414
0
                start_info.bytes as usize + end,
1415
            )
1416
0
            .map_err(|e| {
1417
0
                if let Error::ByteRangeNotCharBoundary(_, _) = e {
1418
0
                    Error::ByteRangeNotCharBoundary(start_range, end_range)
1419
                } else {
1420
0
                    e
1421
                }
1422
0
            }),
1423
0
            RopeSlice(RSEnum::Light { text, .. }) => {
1424
0
                if !text.is_char_boundary(start) || !text.is_char_boundary(end) {
1425
0
                    return Err(Error::ByteRangeNotCharBoundary(start_range, end_range));
1426
0
                }
1427
0
                let new_text = &text[start..end];
1428
0
                Ok(RopeSlice(RSEnum::Light {
1429
0
                    text: new_text,
1430
0
                    char_count: count_chars(new_text) as Count,
1431
0
                    utf16_surrogate_count: count_utf16_surrogates(new_text) as Count,
1432
0
                    line_break_count: count_line_breaks(new_text) as Count,
1433
0
                }))
1434
            }
1435
        }
1436
0
    }
1437
1438
    /// Non-panicking version of [`bytes_at()`](RopeSlice::bytes_at).
1439
    #[inline]
1440
0
    pub fn get_bytes_at(&self, byte_idx: usize) -> Option<Bytes<'a>> {
1441
        // Bounds check
1442
0
        if byte_idx <= self.len_bytes() {
1443
0
            match *self {
1444
                RopeSlice(RSEnum::Full {
1445
0
                    node,
1446
0
                    start_info,
1447
0
                    end_info,
1448
0
                }) => Some(Bytes::new_with_range_at(
1449
0
                    node,
1450
0
                    start_info.bytes as usize + byte_idx,
1451
0
                    (start_info.bytes as usize, end_info.bytes as usize),
1452
0
                    (start_info.chars as usize, end_info.chars as usize),
1453
0
                    (
1454
0
                        start_info.line_breaks as usize,
1455
0
                        end_info.line_breaks as usize + 1,
1456
0
                    ),
1457
0
                )),
1458
0
                RopeSlice(RSEnum::Light { text, .. }) => Some(Bytes::from_str_at(text, byte_idx)),
1459
            }
1460
        } else {
1461
0
            None
1462
        }
1463
0
    }
1464
1465
    /// Non-panicking version of [`chars_at()`](RopeSlice::chars_at).
1466
    #[inline]
1467
0
    pub fn get_chars_at(&self, char_idx: usize) -> Option<Chars<'a>> {
1468
        // Bounds check
1469
0
        if char_idx <= self.len_chars() {
1470
0
            match *self {
1471
                RopeSlice(RSEnum::Full {
1472
0
                    node,
1473
0
                    start_info,
1474
0
                    end_info,
1475
0
                }) => Some(Chars::new_with_range_at(
1476
0
                    node,
1477
0
                    start_info.chars as usize + char_idx,
1478
0
                    (start_info.bytes as usize, end_info.bytes as usize),
1479
0
                    (start_info.chars as usize, end_info.chars as usize),
1480
0
                    (
1481
0
                        start_info.line_breaks as usize,
1482
0
                        end_info.line_breaks as usize + 1,
1483
0
                    ),
1484
0
                )),
1485
0
                RopeSlice(RSEnum::Light { text, .. }) => Some(Chars::from_str_at(text, char_idx)),
1486
            }
1487
        } else {
1488
0
            None
1489
        }
1490
0
    }
1491
1492
    /// Non-panicking version of [`lines_at()`](RopeSlice::lines_at).
1493
    #[inline]
1494
0
    pub fn get_lines_at(&self, line_idx: usize) -> Option<Lines<'a>> {
1495
        // Bounds check
1496
0
        if line_idx <= self.len_lines() {
1497
0
            match *self {
1498
                RopeSlice(RSEnum::Full {
1499
0
                    node,
1500
0
                    start_info,
1501
0
                    end_info,
1502
0
                }) => Some(Lines::new_with_range_at(
1503
0
                    node,
1504
0
                    start_info.line_breaks as usize + line_idx,
1505
0
                    (start_info.bytes as usize, end_info.bytes as usize),
1506
0
                    (
1507
0
                        start_info.line_breaks as usize,
1508
0
                        end_info.line_breaks as usize + 1,
1509
0
                    ),
1510
0
                )),
1511
                RopeSlice(RSEnum::Light {
1512
0
                    text,
1513
0
                    line_break_count,
1514
                    ..
1515
0
                }) => Some(Lines::from_str_at(
1516
0
                    text,
1517
0
                    line_idx,
1518
0
                    line_break_count as usize + 1,
1519
0
                )),
1520
            }
1521
        } else {
1522
0
            None
1523
        }
1524
0
    }
1525
1526
    /// Non-panicking version of [`chunks_at_byte()`](RopeSlice::chunks_at_byte).
1527
    #[inline]
1528
0
    pub fn get_chunks_at_byte(&self, byte_idx: usize) -> Option<(Chunks<'a>, usize, usize, usize)> {
1529
        // Bounds check
1530
0
        if byte_idx <= self.len_bytes() {
1531
0
            match *self {
1532
                RopeSlice(RSEnum::Full {
1533
0
                    node,
1534
0
                    start_info,
1535
0
                    end_info,
1536
                }) => {
1537
0
                    let (chunks, chunk_byte_idx, chunk_char_idx, chunk_line_idx) =
1538
0
                        Chunks::new_with_range_at_byte(
1539
0
                            node,
1540
0
                            byte_idx + start_info.bytes as usize,
1541
0
                            (start_info.bytes as usize, end_info.bytes as usize),
1542
0
                            (start_info.chars as usize, end_info.chars as usize),
1543
0
                            (
1544
0
                                start_info.line_breaks as usize,
1545
0
                                end_info.line_breaks as usize + 1,
1546
0
                            ),
1547
0
                        );
1548
1549
0
                    Some((
1550
0
                        chunks,
1551
0
                        chunk_byte_idx.saturating_sub(start_info.bytes as usize),
1552
0
                        chunk_char_idx.saturating_sub(start_info.chars as usize),
1553
0
                        chunk_line_idx.saturating_sub(start_info.line_breaks as usize),
1554
0
                    ))
1555
                }
1556
                RopeSlice(RSEnum::Light {
1557
0
                    text,
1558
0
                    char_count,
1559
0
                    line_break_count,
1560
                    ..
1561
                }) => {
1562
0
                    let chunks = Chunks::from_str(text, byte_idx == text.len());
1563
1564
0
                    if byte_idx == text.len() {
1565
0
                        Some((
1566
0
                            chunks,
1567
0
                            text.len(),
1568
0
                            char_count as usize,
1569
0
                            line_break_count as usize,
1570
0
                        ))
1571
                    } else {
1572
0
                        Some((chunks, 0, 0, 0))
1573
                    }
1574
                }
1575
            }
1576
        } else {
1577
0
            None
1578
        }
1579
0
    }
1580
1581
    /// Non-panicking version of [`chunks_at_char()`](RopeSlice::chunks_at_char).
1582
    #[inline]
1583
0
    pub fn get_chunks_at_char(&self, char_idx: usize) -> Option<(Chunks<'a>, usize, usize, usize)> {
1584
        // Bounds check
1585
0
        if char_idx <= self.len_chars() {
1586
0
            match *self {
1587
                RopeSlice(RSEnum::Full {
1588
0
                    node,
1589
0
                    start_info,
1590
0
                    end_info,
1591
                }) => {
1592
0
                    let (chunks, chunk_byte_idx, chunk_char_idx, chunk_line_idx) =
1593
0
                        Chunks::new_with_range_at_char(
1594
0
                            node,
1595
0
                            char_idx + start_info.chars as usize,
1596
0
                            (start_info.bytes as usize, end_info.bytes as usize),
1597
0
                            (start_info.chars as usize, end_info.chars as usize),
1598
0
                            (
1599
0
                                start_info.line_breaks as usize,
1600
0
                                end_info.line_breaks as usize + 1,
1601
0
                            ),
1602
0
                        );
1603
1604
0
                    Some((
1605
0
                        chunks,
1606
0
                        chunk_byte_idx.saturating_sub(start_info.bytes as usize),
1607
0
                        chunk_char_idx.saturating_sub(start_info.chars as usize),
1608
0
                        chunk_line_idx.saturating_sub(start_info.line_breaks as usize),
1609
0
                    ))
1610
                }
1611
                RopeSlice(RSEnum::Light {
1612
0
                    text,
1613
0
                    char_count,
1614
0
                    line_break_count,
1615
                    ..
1616
                }) => {
1617
0
                    let chunks = Chunks::from_str(text, char_idx == char_count as usize);
1618
1619
0
                    if char_idx == char_count as usize {
1620
0
                        Some((
1621
0
                            chunks,
1622
0
                            text.len(),
1623
0
                            char_count as usize,
1624
0
                            line_break_count as usize,
1625
0
                        ))
1626
                    } else {
1627
0
                        Some((chunks, 0, 0, 0))
1628
                    }
1629
                }
1630
            }
1631
        } else {
1632
0
            None
1633
        }
1634
0
    }
1635
1636
    /// Non-panicking version of [`chunks_at_line_break()`](RopeSlice::chunks_at_line_break).
1637
    #[inline]
1638
0
    pub fn get_chunks_at_line_break(
1639
0
        &self,
1640
0
        line_break_idx: usize,
1641
0
    ) -> Option<(Chunks<'a>, usize, usize, usize)> {
1642
        // Bounds check
1643
0
        if line_break_idx <= self.len_lines() {
1644
0
            match *self {
1645
                RopeSlice(RSEnum::Full {
1646
0
                    node,
1647
0
                    start_info,
1648
0
                    end_info,
1649
                }) => {
1650
                    // Get the chunk.
1651
0
                    let (chunks, chunk_byte_idx, chunk_char_idx, chunk_line_idx) =
1652
0
                        if line_break_idx == 0 {
1653
0
                            Chunks::new_with_range_at_byte(
1654
0
                                node,
1655
0
                                start_info.bytes as usize,
1656
0
                                (start_info.bytes as usize, end_info.bytes as usize),
1657
0
                                (start_info.chars as usize, end_info.chars as usize),
1658
0
                                (
1659
0
                                    start_info.line_breaks as usize,
1660
0
                                    end_info.line_breaks as usize + 1,
1661
0
                                ),
1662
                            )
1663
0
                        } else if line_break_idx == self.len_lines() {
1664
0
                            Chunks::new_with_range_at_byte(
1665
0
                                node,
1666
0
                                end_info.bytes as usize,
1667
0
                                (start_info.bytes as usize, end_info.bytes as usize),
1668
0
                                (start_info.chars as usize, end_info.chars as usize),
1669
0
                                (
1670
0
                                    start_info.line_breaks as usize,
1671
0
                                    end_info.line_breaks as usize + 1,
1672
0
                                ),
1673
                            )
1674
                        } else {
1675
0
                            Chunks::new_with_range_at_line_break(
1676
0
                                node,
1677
0
                                line_break_idx + start_info.line_breaks as usize,
1678
0
                                (start_info.bytes as usize, end_info.bytes as usize),
1679
0
                                (start_info.chars as usize, end_info.chars as usize),
1680
0
                                (
1681
0
                                    start_info.line_breaks as usize,
1682
0
                                    end_info.line_breaks as usize + 1,
1683
0
                                ),
1684
                            )
1685
                        };
1686
0
                    Some((
1687
0
                        chunks,
1688
0
                        chunk_byte_idx.saturating_sub(start_info.bytes as usize),
1689
0
                        chunk_char_idx.saturating_sub(start_info.chars as usize),
1690
0
                        chunk_line_idx.saturating_sub(start_info.line_breaks as usize),
1691
0
                    ))
1692
                }
1693
                RopeSlice(RSEnum::Light {
1694
0
                    text,
1695
0
                    char_count,
1696
0
                    line_break_count,
1697
                    ..
1698
                }) => {
1699
0
                    let chunks =
1700
0
                        Chunks::from_str(text, line_break_idx == line_break_count as usize);
1701
1702
0
                    if line_break_idx == line_break_count as usize {
1703
0
                        Some((
1704
0
                            chunks,
1705
0
                            text.len(),
1706
0
                            char_count as usize,
1707
0
                            line_break_count as usize,
1708
0
                        ))
1709
                    } else {
1710
0
                        Some((chunks, 0, 0, 0))
1711
                    }
1712
                }
1713
            }
1714
        } else {
1715
0
            None
1716
        }
1717
0
    }
1718
}
1719
1720
//==============================================================
1721
// Conversion impls
1722
1723
/// Creates a `RopeSlice` directly from a string slice.
1724
///
1725
/// The useful applications of this are actually somewhat narrow.  It is
1726
/// intended primarily as an aid when implementing additional functionality
1727
/// on top of Ropey, where you may already have access to a rope chunk and
1728
/// want to directly create a `RopeSlice` from it, avoiding the overhead of
1729
/// going through the slicing APIs.
1730
///
1731
/// Although it is possible to use this to create `RopeSlice`s from
1732
/// arbitrary strings, doing so is not especially useful.  For example,
1733
/// `Rope`s and `RopeSlice`s can already be directly compared for
1734
/// equality with strings and string slices.
1735
///
1736
/// Runs in O(N) time, where N is the length of the string slice.
1737
impl<'a> From<&'a str> for RopeSlice<'a> {
1738
    #[inline]
1739
0
    fn from(text: &'a str) -> Self {
1740
0
        RopeSlice(RSEnum::Light {
1741
0
            text: text,
1742
0
            char_count: count_chars(text) as Count,
1743
0
            utf16_surrogate_count: count_utf16_surrogates(text) as Count,
1744
0
            line_break_count: count_line_breaks(text) as Count,
1745
0
        })
1746
0
    }
1747
}
1748
1749
impl<'a> From<RopeSlice<'a>> for String {
1750
    #[inline]
1751
0
    fn from(s: RopeSlice<'a>) -> Self {
1752
0
        let mut text = String::with_capacity(s.len_bytes());
1753
0
        text.extend(s.chunks());
1754
0
        text
1755
0
    }
1756
}
1757
1758
/// Attempts to borrow the contents of the slice, but will convert to an
1759
/// owned string if the contents is not contiguous in memory.
1760
///
1761
/// Runs in best case O(1), worst case O(N).
1762
impl<'a> From<RopeSlice<'a>> for std::borrow::Cow<'a, str> {
1763
    #[inline]
1764
0
    fn from(s: RopeSlice<'a>) -> Self {
1765
0
        if let Some(text) = s.as_str() {
1766
0
            std::borrow::Cow::Borrowed(text)
1767
        } else {
1768
0
            std::borrow::Cow::Owned(String::from(s))
1769
        }
1770
0
    }
1771
}
1772
1773
//==============================================================
1774
// Other impls
1775
1776
impl<'a> std::fmt::Debug for RopeSlice<'a> {
1777
0
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1778
0
        f.debug_list().entries(self.chunks()).finish()
1779
0
    }
1780
}
1781
1782
impl<'a> std::fmt::Display for RopeSlice<'a> {
1783
    #[inline]
1784
0
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1785
0
        for chunk in self.chunks() {
1786
0
            write!(f, "{}", chunk)?
1787
        }
1788
0
        Ok(())
1789
0
    }
1790
}
1791
1792
impl<'a> std::cmp::Eq for RopeSlice<'a> {}
1793
1794
impl<'a, 'b> std::cmp::PartialEq<RopeSlice<'b>> for RopeSlice<'a> {
1795
0
    fn eq(&self, other: &RopeSlice<'b>) -> bool {
1796
0
        if self.len_bytes() != other.len_bytes() {
1797
0
            return false;
1798
0
        }
1799
1800
0
        let mut chunk_itr_1 = self.chunks();
1801
0
        let mut chunk_itr_2 = other.chunks();
1802
0
        let mut chunk1 = chunk_itr_1.next().unwrap_or("").as_bytes();
1803
0
        let mut chunk2 = chunk_itr_2.next().unwrap_or("").as_bytes();
1804
1805
        loop {
1806
0
            if chunk1.len() > chunk2.len() {
1807
0
                if &chunk1[..chunk2.len()] != chunk2 {
1808
0
                    return false;
1809
0
                } else {
1810
0
                    chunk1 = &chunk1[chunk2.len()..];
1811
0
                    chunk2 = &[];
1812
0
                }
1813
0
            } else if &chunk2[..chunk1.len()] != chunk1 {
1814
0
                return false;
1815
0
            } else {
1816
0
                chunk2 = &chunk2[chunk1.len()..];
1817
0
                chunk1 = &[];
1818
0
            }
1819
1820
0
            if chunk1.is_empty() {
1821
0
                if let Some(chunk) = chunk_itr_1.next() {
1822
0
                    chunk1 = chunk.as_bytes();
1823
0
                } else {
1824
0
                    break;
1825
                }
1826
0
            }
1827
1828
0
            if chunk2.is_empty() {
1829
0
                if let Some(chunk) = chunk_itr_2.next() {
1830
0
                    chunk2 = chunk.as_bytes();
1831
0
                } else {
1832
0
                    break;
1833
                }
1834
0
            }
1835
        }
1836
1837
0
        return true;
1838
0
    }
1839
}
1840
1841
impl<'a, 'b> std::cmp::PartialEq<&'b str> for RopeSlice<'a> {
1842
    #[inline]
1843
0
    fn eq(&self, other: &&'b str) -> bool {
1844
0
        match *self {
1845
            RopeSlice(RSEnum::Full { .. }) => {
1846
0
                if self.len_bytes() != other.len() {
1847
0
                    return false;
1848
0
                }
1849
0
                let other = other.as_bytes();
1850
1851
0
                let mut idx = 0;
1852
0
                for chunk in self.chunks() {
1853
0
                    let chunk = chunk.as_bytes();
1854
0
                    if chunk != &other[idx..(idx + chunk.len())] {
1855
0
                        return false;
1856
0
                    }
1857
0
                    idx += chunk.len();
1858
                }
1859
1860
0
                return true;
1861
            }
1862
0
            RopeSlice(RSEnum::Light { text, .. }) => {
1863
0
                return text == *other;
1864
            }
1865
        }
1866
0
    }
1867
}
1868
1869
impl<'a, 'b> std::cmp::PartialEq<RopeSlice<'a>> for &'b str {
1870
    #[inline]
1871
0
    fn eq(&self, other: &RopeSlice<'a>) -> bool {
1872
0
        other == self
1873
0
    }
1874
}
1875
1876
impl<'a> std::cmp::PartialEq<str> for RopeSlice<'a> {
1877
    #[inline]
1878
0
    fn eq(&self, other: &str) -> bool {
1879
0
        std::cmp::PartialEq::<&str>::eq(self, &other)
1880
0
    }
1881
}
1882
1883
impl<'a> std::cmp::PartialEq<RopeSlice<'a>> for str {
1884
    #[inline]
1885
0
    fn eq(&self, other: &RopeSlice<'a>) -> bool {
1886
0
        std::cmp::PartialEq::<&str>::eq(other, &self)
1887
0
    }
1888
}
1889
1890
impl<'a> std::cmp::PartialEq<String> for RopeSlice<'a> {
1891
    #[inline]
1892
0
    fn eq(&self, other: &String) -> bool {
1893
0
        self == other.as_str()
1894
0
    }
1895
}
1896
1897
impl<'a> std::cmp::PartialEq<RopeSlice<'a>> for String {
1898
    #[inline]
1899
0
    fn eq(&self, other: &RopeSlice<'a>) -> bool {
1900
0
        self.as_str() == other
1901
0
    }
1902
}
1903
1904
impl<'a, 'b> std::cmp::PartialEq<std::borrow::Cow<'b, str>> for RopeSlice<'a> {
1905
    #[inline]
1906
0
    fn eq(&self, other: &std::borrow::Cow<'b, str>) -> bool {
1907
0
        *self == **other
1908
0
    }
1909
}
1910
1911
impl<'a, 'b> std::cmp::PartialEq<RopeSlice<'a>> for std::borrow::Cow<'b, str> {
1912
    #[inline]
1913
0
    fn eq(&self, other: &RopeSlice<'a>) -> bool {
1914
0
        **self == *other
1915
0
    }
1916
}
1917
1918
impl<'a> std::cmp::PartialEq<Rope> for RopeSlice<'a> {
1919
    #[inline]
1920
0
    fn eq(&self, other: &Rope) -> bool {
1921
0
        *self == other.slice(..)
1922
0
    }
1923
}
1924
1925
impl<'a> std::cmp::PartialEq<RopeSlice<'a>> for Rope {
1926
    #[inline]
1927
0
    fn eq(&self, other: &RopeSlice<'a>) -> bool {
1928
0
        self.slice(..) == *other
1929
0
    }
1930
}
1931
1932
impl<'a> std::cmp::Ord for RopeSlice<'a> {
1933
    #[allow(clippy::op_ref)] // Erroneously thinks with can directly use a slice.
1934
0
    fn cmp(&self, other: &RopeSlice<'a>) -> std::cmp::Ordering {
1935
0
        let mut chunk_itr_1 = self.chunks();
1936
0
        let mut chunk_itr_2 = other.chunks();
1937
0
        let mut chunk1 = chunk_itr_1.next().unwrap_or("").as_bytes();
1938
0
        let mut chunk2 = chunk_itr_2.next().unwrap_or("").as_bytes();
1939
1940
        loop {
1941
0
            if chunk1.len() >= chunk2.len() {
1942
0
                let compared = chunk1[..chunk2.len()].cmp(chunk2);
1943
0
                if compared != std::cmp::Ordering::Equal {
1944
0
                    return compared;
1945
0
                }
1946
1947
0
                chunk1 = &chunk1[chunk2.len()..];
1948
0
                chunk2 = &[];
1949
            } else {
1950
0
                let compared = chunk1.cmp(&chunk2[..chunk1.len()]);
1951
0
                if compared != std::cmp::Ordering::Equal {
1952
0
                    return compared;
1953
0
                }
1954
1955
0
                chunk1 = &[];
1956
0
                chunk2 = &chunk2[chunk1.len()..];
1957
            }
1958
1959
0
            if chunk1.is_empty() {
1960
0
                if let Some(chunk) = chunk_itr_1.next() {
1961
0
                    chunk1 = chunk.as_bytes();
1962
0
                } else {
1963
0
                    break;
1964
                }
1965
0
            }
1966
1967
0
            if chunk2.is_empty() {
1968
0
                if let Some(chunk) = chunk_itr_2.next() {
1969
0
                    chunk2 = chunk.as_bytes();
1970
0
                } else {
1971
0
                    break;
1972
                }
1973
0
            }
1974
        }
1975
1976
0
        self.len_bytes().cmp(&other.len_bytes())
1977
0
    }
1978
}
1979
1980
impl<'a, 'b> std::cmp::PartialOrd<RopeSlice<'b>> for RopeSlice<'a> {
1981
    #[inline]
1982
0
    fn partial_cmp(&self, other: &RopeSlice<'b>) -> Option<std::cmp::Ordering> {
1983
0
        Some(self.cmp(other))
1984
0
    }
1985
}
1986
1987
impl<'a> std::hash::Hash for RopeSlice<'a> {
1988
0
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
1989
        // `std::hash::Hasher` only guarantees the same hash output for
1990
        // exactly the same calls to `Hasher::write()`.  Just submitting
1991
        // the same data in the same order isn't enough--it also has to
1992
        // be split the same between calls.  So we go to some effort here
1993
        // to ensure that we always submit the text data in the same
1994
        // fixed-size blocks, even if those blocks don't align with chunk
1995
        // boundaries at all.
1996
        //
1997
        // The naive approach is to always copy to a fixed-size buffer
1998
        // and submit the buffer whenever it fills up.  We conceptually
1999
        // follow that approach here, but we do a little better by
2000
        // skipping the buffer and directly passing the data without
2001
        // copying when possible.
2002
        const BLOCK_SIZE: usize = 256;
2003
2004
0
        let mut buffer = [0u8; BLOCK_SIZE];
2005
0
        let mut buffer_len = 0;
2006
2007
0
        for chunk in self.chunks() {
2008
0
            let mut data = chunk.as_bytes();
2009
2010
0
            while !data.is_empty() {
2011
0
                if buffer_len == 0 && data.len() >= BLOCK_SIZE {
2012
0
                    // Process data directly, skipping the buffer.
2013
0
                    let (head, tail) = data.split_at(BLOCK_SIZE);
2014
0
                    state.write(head);
2015
0
                    data = tail;
2016
0
                } else if buffer_len == BLOCK_SIZE {
2017
0
                    // Process the filled buffer.
2018
0
                    state.write(&buffer[..]);
2019
0
                    buffer_len = 0;
2020
0
                } else {
2021
0
                    // Append to the buffer.
2022
0
                    let n = (BLOCK_SIZE - buffer_len).min(data.len());
2023
0
                    let (head, tail) = data.split_at(n);
2024
0
                    (&mut buffer[buffer_len..(buffer_len + n)]).copy_from_slice(head);
2025
0
                    buffer_len += n;
2026
0
                    data = tail;
2027
0
                }
2028
            }
2029
        }
2030
2031
        // Write any remaining unprocessed data in the buffer.
2032
0
        if buffer_len > 0 {
2033
0
            state.write(&buffer[..buffer_len]);
2034
0
        }
2035
2036
        // Same strategy as `&str` in stdlib, so that e.g. two adjacent
2037
        // fields in a `#[derive(Hash)]` struct with "Hi " and "there"
2038
        // vs "Hi t" and "here" give the struct a different hash.
2039
0
        state.write_u8(0xff)
2040
0
    }
2041
}
2042
2043
//===========================================================
2044
2045
#[cfg(test)]
2046
mod tests {
2047
    use crate::str_utils::{
2048
        byte_to_char_idx, byte_to_line_idx, char_to_byte_idx, char_to_line_idx,
2049
    };
2050
    use crate::Rope;
2051
    use std::hash::{Hash, Hasher};
2052
2053
    // 127 bytes, 103 chars, 1 line
2054
    const TEXT: &str = "Hello there!  How're you doing?  It's \
2055
                        a fine day, isn't it?  Aren't you glad \
2056
                        we're alive?  こんにちは、みんなさん!";
2057
    // 124 bytes, 100 chars, 4 lines
2058
    const TEXT_LINES: &str = "Hello there!  How're you doing?\nIt's \
2059
                              a fine day, isn't it?\nAren't you glad \
2060
                              we're alive?\nこんにちは、みんなさん!";
2061
    // 127 bytes, 107 chars, 111 utf16 code units, 1 line
2062
    const TEXT_EMOJI: &str = "Hello there!🐸  How're you doing?🐸  It's \
2063
                              a fine day, isn't it?🐸  Aren't you glad \
2064
                              we're alive?🐸  こんにちは、みんなさん!";
2065
2066
    #[test]
2067
    fn len_bytes_01() {
2068
        let r = Rope::from_str(TEXT);
2069
        let s = r.slice(7..98);
2070
        assert_eq!(s.len_bytes(), 105);
2071
    }
2072
2073
    #[test]
2074
    fn len_bytes_02() {
2075
        let r = Rope::from_str(TEXT);
2076
        let s = r.slice(43..43);
2077
        assert_eq!(s.len_bytes(), 0);
2078
    }
2079
2080
    #[test]
2081
    fn len_chars_01() {
2082
        let r = Rope::from_str(TEXT);
2083
        let s = r.slice(7..98);
2084
        assert_eq!(s.len_chars(), 91);
2085
    }
2086
2087
    #[test]
2088
    fn len_chars_02() {
2089
        let r = Rope::from_str(TEXT);
2090
        let s = r.slice(43..43);
2091
        assert_eq!(s.len_chars(), 0);
2092
    }
2093
2094
    #[test]
2095
    fn len_lines_01() {
2096
        let r = Rope::from_str(TEXT_LINES);
2097
        let s = r.slice(34..98);
2098
        assert_eq!(s.len_lines(), 3);
2099
    }
2100
2101
    #[test]
2102
    fn len_lines_02() {
2103
        let r = Rope::from_str(TEXT_LINES);
2104
        let s = r.slice(43..43);
2105
        assert_eq!(s.len_lines(), 1);
2106
    }
2107
2108
    #[test]
2109
    fn len_lines_03() {
2110
        // Make sure splitting CRLF pairs at the end works properly.
2111
        let r = Rope::from_str("\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n");
2112
        for i in 0..r.len_chars() {
2113
            if cfg!(any(feature = "cr_lines", feature = "unicode_lines")) {
2114
                assert_eq!(r.slice(..i).len_lines(), 1 + ((i + 1) / 2));
2115
            } else {
2116
                assert_eq!(r.slice(..i).len_lines(), 1 + (i / 2));
2117
            }
2118
        }
2119
    }
2120
2121
    #[test]
2122
    fn len_lines_04() {
2123
        // Make sure splitting CRLF pairs at the start works properly.
2124
        let r = Rope::from_str("\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n");
2125
        for i in 0..r.len_chars() {
2126
            assert_eq!(r.slice(i..).len_lines(), 16 - (i / 2));
2127
        }
2128
    }
2129
2130
    #[test]
2131
    fn len_utf16_cu_01() {
2132
        let r = Rope::from_str(TEXT);
2133
        let s = r.slice(..);
2134
        assert_eq!(s.len_utf16_cu(), 103);
2135
    }
2136
2137
    #[test]
2138
    fn len_utf16_cu_02() {
2139
        let r = Rope::from_str(TEXT_EMOJI);
2140
        let s = r.slice(..);
2141
        assert_eq!(s.len_utf16_cu(), 111);
2142
    }
2143
2144
    #[test]
2145
    fn len_utf16_cu_03() {
2146
        let r = Rope::from_str(TEXT_EMOJI);
2147
        let s = r.slice(13..33);
2148
        assert_eq!(s.len_utf16_cu(), 21);
2149
    }
2150
2151
    #[test]
2152
    fn len_utf16_cu_04() {
2153
        let r = Rope::from_str("🐸");
2154
        let s = r.slice(..);
2155
        assert_eq!(s.len_utf16_cu(), 2);
2156
    }
2157
2158
    #[test]
2159
    fn len_utf16_cu_05() {
2160
        let r = Rope::from_str("");
2161
        let s = r.slice(..);
2162
        assert_eq!(s.len_utf16_cu(), 0);
2163
    }
2164
2165
    #[test]
2166
    fn byte_to_char_01() {
2167
        let r = Rope::from_str(TEXT);
2168
        let s = r.slice(88..102);
2169
2170
        // ?  こんにちは、みんなさん
2171
2172
        assert_eq!(0, s.byte_to_char(0));
2173
        assert_eq!(1, s.byte_to_char(1));
2174
        assert_eq!(2, s.byte_to_char(2));
2175
2176
        assert_eq!(3, s.byte_to_char(3));
2177
        assert_eq!(3, s.byte_to_char(4));
2178
        assert_eq!(3, s.byte_to_char(5));
2179
2180
        assert_eq!(4, s.byte_to_char(6));
2181
        assert_eq!(4, s.byte_to_char(7));
2182
        assert_eq!(4, s.byte_to_char(8));
2183
2184
        assert_eq!(13, s.byte_to_char(33));
2185
        assert_eq!(13, s.byte_to_char(34));
2186
        assert_eq!(13, s.byte_to_char(35));
2187
        assert_eq!(14, s.byte_to_char(36));
2188
    }
2189
2190
    #[test]
2191
    fn byte_to_line_01() {
2192
        let r = Rope::from_str(TEXT_LINES);
2193
        let s = r.slice(34..96);
2194
2195
        // 's a fine day, isn't it?\nAren't you glad \
2196
        // we're alive?\nこんにちは、みん
2197
2198
        assert_eq!(0, s.byte_to_line(0));
2199
        assert_eq!(0, s.byte_to_line(1));
2200
2201
        assert_eq!(0, s.byte_to_line(24));
2202
        assert_eq!(1, s.byte_to_line(25));
2203
        assert_eq!(1, s.byte_to_line(26));
2204
2205
        assert_eq!(1, s.byte_to_line(53));
2206
        assert_eq!(2, s.byte_to_line(54));
2207
        assert_eq!(2, s.byte_to_line(57));
2208
2209
        assert_eq!(2, s.byte_to_line(78));
2210
    }
2211
2212
    #[test]
2213
    fn byte_to_line_02() {
2214
        let r = Rope::from_str(TEXT_LINES);
2215
        let s = r.slice(50..50);
2216
        assert_eq!(0, s.byte_to_line(0));
2217
    }
2218
2219
    #[test]
2220
    fn byte_to_line_03() {
2221
        let r = Rope::from_str("Hi there\nstranger!");
2222
        let s = r.slice(0..9);
2223
        assert_eq!(0, s.byte_to_line(0));
2224
        assert_eq!(0, s.byte_to_line(8));
2225
        assert_eq!(1, s.byte_to_line(9));
2226
    }
2227
2228
    #[test]
2229
    #[should_panic]
2230
    fn byte_to_line_04() {
2231
        let r = Rope::from_str(TEXT_LINES);
2232
        let s = r.slice(34..96);
2233
        s.byte_to_line(79);
2234
    }
2235
2236
    #[test]
2237
    fn byte_to_line_05() {
2238
        // Make sure splitting CRLF pairs at the end works properly.
2239
        let r = Rope::from_str("\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n");
2240
        for i in 0..r.len_bytes() {
2241
            if cfg!(any(feature = "cr_lines", feature = "unicode_lines")) {
2242
                assert_eq!(r.byte_slice(..i).byte_to_line(i), (i + 1) / 2);
2243
            } else {
2244
                assert_eq!(r.byte_slice(..i).byte_to_line(i), i / 2);
2245
            }
2246
        }
2247
    }
2248
2249
    #[test]
2250
    fn char_to_byte_01() {
2251
        let r = Rope::from_str(TEXT);
2252
        let s = r.slice(88..102);
2253
2254
        // ?  こんにちは、みんなさん
2255
2256
        assert_eq!(0, s.char_to_byte(0));
2257
        assert_eq!(1, s.char_to_byte(1));
2258
        assert_eq!(2, s.char_to_byte(2));
2259
2260
        assert_eq!(3, s.char_to_byte(3));
2261
        assert_eq!(6, s.char_to_byte(4));
2262
        assert_eq!(33, s.char_to_byte(13));
2263
        assert_eq!(36, s.char_to_byte(14));
2264
    }
2265
2266
    #[test]
2267
    fn char_to_line_01() {
2268
        let r = Rope::from_str(TEXT_LINES);
2269
        let s = r.slice(34..96);
2270
2271
        // 's a fine day, isn't it?\nAren't you glad \
2272
        // we're alive?\nこんにちは、みん
2273
2274
        assert_eq!(0, s.char_to_line(0));
2275
        assert_eq!(0, s.char_to_line(1));
2276
2277
        assert_eq!(0, s.char_to_line(24));
2278
        assert_eq!(1, s.char_to_line(25));
2279
        assert_eq!(1, s.char_to_line(26));
2280
2281
        assert_eq!(1, s.char_to_line(53));
2282
        assert_eq!(2, s.char_to_line(54));
2283
        assert_eq!(2, s.char_to_line(55));
2284
2285
        assert_eq!(2, s.char_to_line(62));
2286
    }
2287
2288
    #[test]
2289
    fn char_to_line_02() {
2290
        let r = Rope::from_str(TEXT_LINES);
2291
        let s = r.slice(43..43);
2292
2293
        assert_eq!(0, s.char_to_line(0));
2294
    }
2295
2296
    #[test]
2297
    fn char_to_line_03() {
2298
        let r = Rope::from_str("Hi there\nstranger!");
2299
        let s = r.slice(0..9);
2300
        assert_eq!(0, s.char_to_line(0));
2301
        assert_eq!(0, s.char_to_line(8));
2302
        assert_eq!(1, s.char_to_line(9));
2303
    }
2304
2305
    #[test]
2306
    #[should_panic]
2307
    fn char_to_line_04() {
2308
        let r = Rope::from_str(TEXT_LINES);
2309
        let s = r.slice(34..96);
2310
2311
        s.char_to_line(63);
2312
    }
2313
2314
    #[test]
2315
    fn char_to_line_05() {
2316
        // Make sure splitting CRLF pairs at the end works properly.
2317
        let r = Rope::from_str("\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n");
2318
        for i in 0..r.len_chars() {
2319
            if cfg!(any(feature = "cr_lines", feature = "unicode_lines")) {
2320
                assert_eq!(r.slice(..i).char_to_line(i), (i + 1) / 2);
2321
            } else {
2322
                assert_eq!(r.slice(..i).char_to_line(i), i / 2);
2323
            }
2324
        }
2325
    }
2326
2327
    #[test]
2328
    fn line_to_byte_01() {
2329
        let r = Rope::from_str(TEXT_LINES);
2330
        let s = r.slice(34..96);
2331
2332
        // 's a fine day, isn't it?\nAren't you glad \
2333
        // we're alive?\nこんにちは、みん
2334
2335
        assert_eq!(0, s.line_to_byte(0));
2336
        assert_eq!(25, s.line_to_byte(1));
2337
        assert_eq!(54, s.line_to_byte(2));
2338
        assert_eq!(78, s.line_to_byte(3));
2339
    }
2340
2341
    #[test]
2342
    fn line_to_byte_02() {
2343
        let r = Rope::from_str(TEXT_LINES);
2344
        let s = r.slice(43..43);
2345
2346
        assert_eq!(0, s.line_to_byte(0));
2347
        assert_eq!(0, s.line_to_byte(1));
2348
    }
2349
2350
    #[test]
2351
    #[should_panic]
2352
    fn line_to_byte_03() {
2353
        let r = Rope::from_str(TEXT_LINES);
2354
        let s = r.slice(34..96);
2355
2356
        s.line_to_byte(4);
2357
    }
2358
2359
    #[test]
2360
    fn line_to_byte_04() {
2361
        // Make sure splitting CRLF pairs at the end works properly.
2362
        let r = Rope::from_str("\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n");
2363
        for i in 0..r.len_bytes() {
2364
            assert_eq!(r.byte_slice(..i).line_to_byte((i + 1) / 2), i);
2365
        }
2366
    }
2367
2368
    #[test]
2369
    fn line_to_char_01() {
2370
        let r = Rope::from_str(TEXT_LINES);
2371
        let s = r.slice(34..96);
2372
2373
        assert_eq!(0, s.line_to_char(0));
2374
        assert_eq!(25, s.line_to_char(1));
2375
        assert_eq!(54, s.line_to_char(2));
2376
        assert_eq!(62, s.line_to_char(3));
2377
    }
2378
2379
    #[test]
2380
    fn line_to_char_02() {
2381
        let r = Rope::from_str(TEXT_LINES);
2382
        let s = r.slice(43..43);
2383
2384
        assert_eq!(0, s.line_to_char(0));
2385
        assert_eq!(0, s.line_to_char(1));
2386
    }
2387
2388
    #[test]
2389
    #[should_panic]
2390
    fn line_to_char_03() {
2391
        let r = Rope::from_str(TEXT_LINES);
2392
        let s = r.slice(34..96);
2393
2394
        s.line_to_char(4);
2395
    }
2396
2397
    #[test]
2398
    fn line_to_char_04() {
2399
        // Make sure splitting CRLF pairs at the end works properly.
2400
        let r = Rope::from_str("\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n");
2401
        for i in 0..r.len_chars() {
2402
            assert_eq!(r.slice(..i).line_to_char((i + 1) / 2), i);
2403
        }
2404
    }
2405
2406
    #[test]
2407
    fn char_to_utf16_cu_01() {
2408
        let r = Rope::from_str("");
2409
        let s = r.slice(..);
2410
        assert_eq!(0, s.char_to_utf16_cu(0));
2411
    }
2412
2413
    #[test]
2414
    #[should_panic]
2415
    fn char_to_utf16_cu_02() {
2416
        let r = Rope::from_str("");
2417
        let s = r.slice(..);
2418
        s.char_to_utf16_cu(1);
2419
    }
2420
2421
    #[test]
2422
    fn char_to_utf16_cu_03() {
2423
        let r = Rope::from_str("🐸");
2424
        let s = r.slice(..);
2425
        assert_eq!(0, s.char_to_utf16_cu(0));
2426
        assert_eq!(2, s.char_to_utf16_cu(1));
2427
    }
2428
2429
    #[test]
2430
    #[should_panic]
2431
    fn char_to_utf16_cu_04() {
2432
        let r = Rope::from_str("🐸");
2433
        let s = r.slice(..);
2434
        s.char_to_utf16_cu(2);
2435
    }
2436
2437
    #[test]
2438
    fn char_to_utf16_cu_05() {
2439
        let r = Rope::from_str(TEXT_EMOJI);
2440
        let s = r.slice(..);
2441
2442
        assert_eq!(0, s.char_to_utf16_cu(0));
2443
2444
        assert_eq!(12, s.char_to_utf16_cu(12));
2445
        assert_eq!(14, s.char_to_utf16_cu(13));
2446
2447
        assert_eq!(33, s.char_to_utf16_cu(32));
2448
        assert_eq!(35, s.char_to_utf16_cu(33));
2449
2450
        assert_eq!(63, s.char_to_utf16_cu(61));
2451
        assert_eq!(65, s.char_to_utf16_cu(62));
2452
2453
        assert_eq!(95, s.char_to_utf16_cu(92));
2454
        assert_eq!(97, s.char_to_utf16_cu(93));
2455
2456
        assert_eq!(111, s.char_to_utf16_cu(107));
2457
    }
2458
2459
    #[test]
2460
    #[should_panic]
2461
    fn char_to_utf16_cu_06() {
2462
        let r = Rope::from_str(TEXT_EMOJI);
2463
        let s = r.slice(..);
2464
        s.char_to_utf16_cu(108);
2465
    }
2466
2467
    #[test]
2468
    fn char_to_utf16_cu_07() {
2469
        let r = Rope::from_str(TEXT_EMOJI);
2470
        let s = r.slice(1..106);
2471
2472
        assert_eq!(0, s.char_to_utf16_cu(0));
2473
2474
        assert_eq!(11, s.char_to_utf16_cu(11));
2475
        assert_eq!(13, s.char_to_utf16_cu(12));
2476
2477
        assert_eq!(32, s.char_to_utf16_cu(31));
2478
        assert_eq!(34, s.char_to_utf16_cu(32));
2479
2480
        assert_eq!(62, s.char_to_utf16_cu(60));
2481
        assert_eq!(64, s.char_to_utf16_cu(61));
2482
2483
        assert_eq!(94, s.char_to_utf16_cu(91));
2484
        assert_eq!(96, s.char_to_utf16_cu(92));
2485
2486
        assert_eq!(109, s.char_to_utf16_cu(105));
2487
    }
2488
2489
    #[test]
2490
    #[should_panic]
2491
    fn char_to_utf16_cu_08() {
2492
        let r = Rope::from_str(TEXT_EMOJI);
2493
        let s = r.slice(1..106);
2494
        s.char_to_utf16_cu(106);
2495
    }
2496
2497
    #[test]
2498
    fn utf16_cu_to_char_01() {
2499
        let r = Rope::from_str("");
2500
        let s = r.slice(..);
2501
        assert_eq!(0, s.utf16_cu_to_char(0));
2502
    }
2503
2504
    #[test]
2505
    #[should_panic]
2506
    fn utf16_cu_to_char_02() {
2507
        let r = Rope::from_str("");
2508
        let s = r.slice(..);
2509
        s.utf16_cu_to_char(1);
2510
    }
2511
2512
    #[test]
2513
    fn utf16_cu_to_char_03() {
2514
        let r = Rope::from_str("🐸");
2515
        let s = r.slice(..);
2516
        assert_eq!(0, s.utf16_cu_to_char(0));
2517
        assert_eq!(0, s.utf16_cu_to_char(1));
2518
        assert_eq!(1, s.utf16_cu_to_char(2));
2519
    }
2520
2521
    #[test]
2522
    #[should_panic]
2523
    fn utf16_cu_to_char_04() {
2524
        let r = Rope::from_str("🐸");
2525
        let s = r.slice(..);
2526
        s.utf16_cu_to_char(3);
2527
    }
2528
2529
    #[test]
2530
    fn utf16_cu_to_char_05() {
2531
        let r = Rope::from_str(TEXT_EMOJI);
2532
        let s = r.slice(..);
2533
2534
        assert_eq!(0, s.utf16_cu_to_char(0));
2535
2536
        assert_eq!(12, s.utf16_cu_to_char(12));
2537
        assert_eq!(12, s.utf16_cu_to_char(13));
2538
        assert_eq!(13, s.utf16_cu_to_char(14));
2539
2540
        assert_eq!(32, s.utf16_cu_to_char(33));
2541
        assert_eq!(32, s.utf16_cu_to_char(34));
2542
        assert_eq!(33, s.utf16_cu_to_char(35));
2543
2544
        assert_eq!(61, s.utf16_cu_to_char(63));
2545
        assert_eq!(61, s.utf16_cu_to_char(64));
2546
        assert_eq!(62, s.utf16_cu_to_char(65));
2547
2548
        assert_eq!(92, s.utf16_cu_to_char(95));
2549
        assert_eq!(92, s.utf16_cu_to_char(96));
2550
        assert_eq!(93, s.utf16_cu_to_char(97));
2551
2552
        assert_eq!(107, s.utf16_cu_to_char(111));
2553
    }
2554
2555
    #[test]
2556
    #[should_panic]
2557
    fn utf16_cu_to_char_06() {
2558
        let r = Rope::from_str(TEXT_EMOJI);
2559
        let s = r.slice(..);
2560
        s.utf16_cu_to_char(112);
2561
    }
2562
2563
    #[test]
2564
    fn utf16_cu_to_char_07() {
2565
        let r = Rope::from_str(TEXT_EMOJI);
2566
        let s = r.slice(1..106);
2567
2568
        assert_eq!(0, s.utf16_cu_to_char(0));
2569
2570
        assert_eq!(11, s.utf16_cu_to_char(11));
2571
        assert_eq!(11, s.utf16_cu_to_char(12));
2572
        assert_eq!(12, s.utf16_cu_to_char(13));
2573
2574
        assert_eq!(31, s.utf16_cu_to_char(32));
2575
        assert_eq!(31, s.utf16_cu_to_char(33));
2576
        assert_eq!(32, s.utf16_cu_to_char(34));
2577
2578
        assert_eq!(60, s.utf16_cu_to_char(62));
2579
        assert_eq!(60, s.utf16_cu_to_char(63));
2580
        assert_eq!(61, s.utf16_cu_to_char(64));
2581
2582
        assert_eq!(91, s.utf16_cu_to_char(94));
2583
        assert_eq!(91, s.utf16_cu_to_char(95));
2584
        assert_eq!(92, s.utf16_cu_to_char(96));
2585
2586
        assert_eq!(105, s.utf16_cu_to_char(109));
2587
    }
2588
2589
    #[test]
2590
    #[should_panic]
2591
    fn utf16_cu_to_char_08() {
2592
        let r = Rope::from_str(TEXT_EMOJI);
2593
        let s = r.slice(1..106);
2594
        s.utf16_cu_to_char(110);
2595
    }
2596
2597
    #[test]
2598
    fn byte_01() {
2599
        let r = Rope::from_str(TEXT);
2600
        let s = r.slice(34..100);
2601
2602
        assert_eq!(s.byte(0), b't');
2603
        assert_eq!(s.byte(10), b' ');
2604
2605
        // UTF-8 encoding of 'な'.
2606
        assert_eq!(s.byte(s.len_bytes() - 3), 0xE3);
2607
        assert_eq!(s.byte(s.len_bytes() - 2), 0x81);
2608
        assert_eq!(s.byte(s.len_bytes() - 1), 0xAA);
2609
    }
2610
2611
    #[test]
2612
    #[should_panic]
2613
    fn byte_02() {
2614
        let r = Rope::from_str(TEXT);
2615
        let s = r.slice(34..100);
2616
        s.byte(s.len_bytes());
2617
    }
2618
2619
    #[test]
2620
    #[should_panic]
2621
    fn byte_03() {
2622
        let r = Rope::from_str(TEXT);
2623
        let s = r.slice(42..42);
2624
        s.byte(0);
2625
    }
2626
2627
    #[test]
2628
    fn char_01() {
2629
        let r = Rope::from_str(TEXT);
2630
        let s = r.slice(34..100);
2631
2632
        // t's \
2633
        // a fine day, isn't it?  Aren't you glad \
2634
        // we're alive?  こんにちは、みんな
2635
2636
        assert_eq!(s.char(0), 't');
2637
        assert_eq!(s.char(10), ' ');
2638
        assert_eq!(s.char(18), 'n');
2639
        assert_eq!(s.char(65), 'な');
2640
    }
2641
2642
    #[test]
2643
    #[should_panic]
2644
    fn char_02() {
2645
        let r = Rope::from_str(TEXT);
2646
        let s = r.slice(34..100);
2647
        s.char(66);
2648
    }
2649
2650
    #[test]
2651
    #[should_panic]
2652
    fn char_03() {
2653
        let r = Rope::from_str(TEXT);
2654
        let s = r.slice(43..43);
2655
        s.char(0);
2656
    }
2657
2658
    #[test]
2659
    fn line_01() {
2660
        let r = Rope::from_str(TEXT_LINES);
2661
        let s = r.slice(34..96);
2662
        // "'s a fine day, isn't it?\nAren't you glad \
2663
        //  we're alive?\nこんにちは、みん"
2664
2665
        let l0 = s.line(0);
2666
        assert_eq!(l0, "'s a fine day, isn't it?\n");
2667
        assert_eq!(l0.len_bytes(), 25);
2668
        assert_eq!(l0.len_chars(), 25);
2669
        assert_eq!(l0.len_lines(), 2);
2670
2671
        let l1 = s.line(1);
2672
        assert_eq!(l1, "Aren't you glad we're alive?\n");
2673
        assert_eq!(l1.len_bytes(), 29);
2674
        assert_eq!(l1.len_chars(), 29);
2675
        assert_eq!(l1.len_lines(), 2);
2676
2677
        let l2 = s.line(2);
2678
        assert_eq!(l2, "こんにちは、みん");
2679
        assert_eq!(l2.len_bytes(), 24);
2680
        assert_eq!(l2.len_chars(), 8);
2681
        assert_eq!(l2.len_lines(), 1);
2682
    }
2683
2684
    #[test]
2685
    fn line_02() {
2686
        let r = Rope::from_str(TEXT_LINES);
2687
        let s = r.slice(34..59);
2688
        // "'s a fine day, isn't it?\n"
2689
2690
        assert_eq!(s.line(0), "'s a fine day, isn't it?\n");
2691
        assert_eq!(s.line(1), "");
2692
    }
2693
2694
    #[test]
2695
    fn line_03() {
2696
        let r = Rope::from_str("Hi\nHi\nHi\nHi\nHi\nHi\n");
2697
        let s = r.slice(1..17);
2698
2699
        assert_eq!(s.line(0), "i\n");
2700
        assert_eq!(s.line(1), "Hi\n");
2701
        assert_eq!(s.line(2), "Hi\n");
2702
        assert_eq!(s.line(3), "Hi\n");
2703
        assert_eq!(s.line(4), "Hi\n");
2704
        assert_eq!(s.line(5), "Hi");
2705
    }
2706
2707
    #[test]
2708
    fn line_04() {
2709
        let r = Rope::from_str(TEXT_LINES);
2710
        let s = r.slice(43..43);
2711
2712
        assert_eq!(s.line(0), "");
2713
    }
2714
2715
    #[test]
2716
    #[should_panic]
2717
    fn line_05() {
2718
        let r = Rope::from_str(TEXT_LINES);
2719
        let s = r.slice(34..96);
2720
        s.line(3);
2721
    }
2722
2723
    #[test]
2724
    fn line_06() {
2725
        let r = Rope::from_str("1\n2\n3\n4\n5\n6\n7\n8");
2726
        let s = r.slice(1..11);
2727
        // "\n2\n3\n4\n5\n6"
2728
2729
        assert_eq!(s.line(0).len_lines(), 2);
2730
        assert_eq!(s.line(1).len_lines(), 2);
2731
        assert_eq!(s.line(2).len_lines(), 2);
2732
        assert_eq!(s.line(3).len_lines(), 2);
2733
        assert_eq!(s.line(4).len_lines(), 2);
2734
        assert_eq!(s.line(5).len_lines(), 1);
2735
    }
2736
2737
    #[test]
2738
    fn chunk_at_byte() {
2739
        let r = Rope::from_str(TEXT_LINES);
2740
        let s = r.slice(34..96);
2741
        let text = &TEXT_LINES[34..112];
2742
        // "'s a fine day, isn't it?\nAren't you glad \
2743
        //  we're alive?\nこんにちは、みん"
2744
2745
        let mut t = text;
2746
        let mut prev_chunk = "";
2747
        for i in 0..s.len_bytes() {
2748
            let (chunk, b, c, l) = s.chunk_at_byte(i);
2749
            assert_eq!(c, byte_to_char_idx(text, b));
2750
            assert_eq!(l, byte_to_line_idx(text, b));
2751
            if chunk != prev_chunk {
2752
                assert_eq!(chunk, &t[..chunk.len()]);
2753
                t = &t[chunk.len()..];
2754
                prev_chunk = chunk;
2755
            }
2756
2757
            let c1 = {
2758
                let i2 = byte_to_char_idx(text, i);
2759
                text.chars().nth(i2).unwrap()
2760
            };
2761
            let c2 = {
2762
                let i2 = i - b;
2763
                let i3 = byte_to_char_idx(chunk, i2);
2764
                chunk.chars().nth(i3).unwrap()
2765
            };
2766
            assert_eq!(c1, c2);
2767
        }
2768
2769
        assert_eq!(t.len(), 0);
2770
    }
2771
2772
    #[test]
2773
    fn chunk_at_char() {
2774
        let r = Rope::from_str(TEXT_LINES);
2775
        let s = r.slice(34..96);
2776
        let text = &TEXT_LINES[34..112];
2777
        // "'s a fine day, isn't it?\nAren't you glad \
2778
        //  we're alive?\nこんにちは、みん"
2779
2780
        let mut t = text;
2781
        let mut prev_chunk = "";
2782
        for i in 0..s.len_chars() {
2783
            let (chunk, b, c, l) = s.chunk_at_char(i);
2784
            assert_eq!(b, char_to_byte_idx(text, c));
2785
            assert_eq!(l, char_to_line_idx(text, c));
2786
            if chunk != prev_chunk {
2787
                assert_eq!(chunk, &t[..chunk.len()]);
2788
                t = &t[chunk.len()..];
2789
                prev_chunk = chunk;
2790
            }
2791
2792
            let c1 = text.chars().nth(i).unwrap();
2793
            let c2 = {
2794
                let i2 = i - c;
2795
                chunk.chars().nth(i2).unwrap()
2796
            };
2797
            assert_eq!(c1, c2);
2798
        }
2799
        assert_eq!(t.len(), 0);
2800
    }
2801
2802
    #[test]
2803
    fn chunk_at_line_break() {
2804
        let r = Rope::from_str(TEXT_LINES);
2805
        let s = r.slice(34..96);
2806
        let text = &TEXT_LINES[34..112];
2807
        // "'s a fine day, isn't it?\nAren't you glad \
2808
        //  we're alive?\nこんにちは、みん"
2809
2810
        // First chunk
2811
        {
2812
            let (chunk, b, c, l) = s.chunk_at_line_break(0);
2813
            assert_eq!(chunk, &text[..chunk.len()]);
2814
            assert_eq!(b, 0);
2815
            assert_eq!(c, 0);
2816
            assert_eq!(l, 0);
2817
        }
2818
2819
        // Middle chunks
2820
        for i in 1..s.len_lines() {
2821
            let (chunk, b, c, l) = s.chunk_at_line_break(i);
2822
            assert_eq!(chunk, &text[b..(b + chunk.len())]);
2823
            assert_eq!(c, byte_to_char_idx(text, b));
2824
            assert_eq!(l, byte_to_line_idx(text, b));
2825
            assert!(l < i);
2826
            assert!(i <= byte_to_line_idx(text, b + chunk.len()));
2827
        }
2828
2829
        // Last chunk
2830
        {
2831
            let (chunk, b, c, l) = s.chunk_at_line_break(s.len_lines());
2832
            assert_eq!(chunk, &text[(text.len() - chunk.len())..]);
2833
            assert_eq!(chunk, &text[b..]);
2834
            assert_eq!(c, byte_to_char_idx(text, b));
2835
            assert_eq!(l, byte_to_line_idx(text, b));
2836
        }
2837
    }
2838
2839
    #[test]
2840
    fn slice_01() {
2841
        let r = Rope::from_str(TEXT);
2842
        let s1 = r.slice(..);
2843
2844
        let s2 = s1.slice(..);
2845
2846
        assert_eq!(TEXT, s2);
2847
    }
2848
2849
    #[test]
2850
    fn slice_02() {
2851
        let r = Rope::from_str(TEXT);
2852
        let s1 = r.slice(5..43);
2853
2854
        let s2 = s1.slice(3..25);
2855
2856
        assert_eq!(&TEXT[8..30], s2);
2857
    }
2858
2859
    #[test]
2860
    fn slice_03() {
2861
        let r = Rope::from_str(TEXT);
2862
        let s1 = r.slice(31..97);
2863
2864
        let s2 = s1.slice(7..64);
2865
2866
        assert_eq!(&TEXT[38..103], s2);
2867
    }
2868
2869
    #[test]
2870
    fn slice_04() {
2871
        let r = Rope::from_str(TEXT);
2872
        let s1 = r.slice(5..43);
2873
2874
        let s2 = s1.slice(21..21);
2875
2876
        assert!(s2.is_light());
2877
        assert_eq!("", s2);
2878
    }
2879
2880
    #[test]
2881
    fn slice_05() {
2882
        let r = Rope::from_str(TEXT);
2883
        let s1 = r.slice(5..98);
2884
        for i in 0..(s1.len_chars() - 1) {
2885
            let s2 = s1.slice(i..(i + 1));
2886
            assert!(s2.is_light());
2887
        }
2888
    }
2889
2890
    #[test]
2891
    #[should_panic]
2892
    fn slice_06() {
2893
        let r = Rope::from_str(TEXT);
2894
        let s = r.slice(5..43);
2895
2896
        #[allow(clippy::reversed_empty_ranges)]
2897
        s.slice(21..20); // Wrong ordering on purpose.
2898
    }
2899
2900
    #[test]
2901
    #[should_panic]
2902
    fn slice_07() {
2903
        let r = Rope::from_str(TEXT);
2904
        let s = r.slice(5..43);
2905
2906
        s.slice(37..39);
2907
    }
2908
2909
    #[test]
2910
    fn byte_slice_01() {
2911
        let r = Rope::from_str(TEXT);
2912
        let s1 = r.byte_slice(..);
2913
2914
        let s2 = s1.byte_slice(..);
2915
2916
        assert_eq!(TEXT, s2);
2917
    }
2918
2919
    #[test]
2920
    fn byte_slice_02() {
2921
        let r = Rope::from_str(TEXT);
2922
        let s1 = r.byte_slice(50..118);
2923
2924
        let s2 = s1.byte_slice(3..25);
2925
2926
        assert_eq!(&TEXT[53..75], s2);
2927
    }
2928
2929
    #[test]
2930
    fn byte_slice_03() {
2931
        let r = Rope::from_str(TEXT);
2932
        let s1 = r.byte_slice(50..118);
2933
2934
        let s2 = s1.byte_slice(7..65);
2935
2936
        assert_eq!(&TEXT[57..115], s2);
2937
    }
2938
2939
    #[test]
2940
    fn byte_slice_04() {
2941
        let r = Rope::from_str(TEXT);
2942
        let s1 = r.byte_slice(50..118);
2943
2944
        let s2 = s1.byte_slice(21..21);
2945
2946
        assert!(s2.is_light());
2947
        assert_eq!("", s2);
2948
    }
2949
2950
    #[test]
2951
    fn byte_slice_05() {
2952
        let r = Rope::from_str(TEXT);
2953
        let s1 = r.byte_slice(4..86);
2954
        for i in 0..(s1.len_bytes() - 1) {
2955
            let s2 = s1.byte_slice(i..(i + 1));
2956
            assert!(s2.is_light());
2957
        }
2958
    }
2959
2960
    #[test]
2961
    #[should_panic]
2962
    fn byte_slice_06() {
2963
        let r = Rope::from_str(TEXT);
2964
        let s = r.byte_slice(50..118);
2965
2966
        #[allow(clippy::reversed_empty_ranges)]
2967
        s.byte_slice(21..20); // Wrong ordering on purpose.
2968
    }
2969
2970
    #[test]
2971
    #[should_panic]
2972
    fn byte_slice_07() {
2973
        let r = Rope::from_str(TEXT);
2974
        let s = r.byte_slice(50..85);
2975
2976
        s.byte_slice(35..36);
2977
    }
2978
2979
    #[test]
2980
    #[should_panic]
2981
    fn byte_slice_08() {
2982
        let r = Rope::from_str(TEXT);
2983
        let s = r.byte_slice(50..118);
2984
2985
        // Not a char boundary.
2986
        s.byte_slice(..43);
2987
    }
2988
2989
    #[test]
2990
    #[should_panic]
2991
    fn byte_slice_09() {
2992
        let r = Rope::from_str(TEXT);
2993
        let s = r.byte_slice(50..118);
2994
2995
        // Not a char boundary.
2996
        s.byte_slice(43..);
2997
    }
2998
2999
    #[test]
3000
    fn eq_str_01() {
3001
        let r = Rope::from_str(TEXT);
3002
        let slice = r.slice(..);
3003
3004
        assert_eq!(slice, TEXT);
3005
        assert_eq!(TEXT, slice);
3006
    }
3007
3008
    #[test]
3009
    fn eq_str_02() {
3010
        let r = Rope::from_str(TEXT);
3011
        let slice = r.slice(0..20);
3012
3013
        assert_ne!(slice, TEXT);
3014
        assert_ne!(TEXT, slice);
3015
    }
3016
3017
    #[test]
3018
    fn eq_str_03() {
3019
        let mut r = Rope::from_str(TEXT);
3020
        r.remove(20..21);
3021
        r.insert(20, "z");
3022
        let slice = r.slice(..);
3023
3024
        assert_ne!(slice, TEXT);
3025
        assert_ne!(TEXT, slice);
3026
    }
3027
3028
    #[test]
3029
    fn eq_str_04() {
3030
        let r = Rope::from_str(TEXT);
3031
        let slice = r.slice(..);
3032
        let s: String = TEXT.into();
3033
3034
        assert_eq!(slice, s);
3035
        assert_eq!(s, slice);
3036
    }
3037
3038
    #[test]
3039
    fn eq_rope_slice_01() {
3040
        let r = Rope::from_str(TEXT);
3041
        let s = r.slice(43..43);
3042
3043
        assert_eq!(s, s);
3044
    }
3045
3046
    #[test]
3047
    fn eq_rope_slice_02() {
3048
        let r = Rope::from_str(TEXT);
3049
        let s1 = r.slice(43..97);
3050
        let s2 = r.slice(43..97);
3051
3052
        assert_eq!(s1, s2);
3053
    }
3054
3055
    #[test]
3056
    fn eq_rope_slice_03() {
3057
        let r = Rope::from_str(TEXT);
3058
        let s1 = r.slice(43..43);
3059
        let s2 = r.slice(43..45);
3060
3061
        assert_ne!(s1, s2);
3062
    }
3063
3064
    #[test]
3065
    fn eq_rope_slice_04() {
3066
        let r = Rope::from_str(TEXT);
3067
        let s1 = r.slice(43..45);
3068
        let s2 = r.slice(43..43);
3069
3070
        assert_ne!(s1, s2);
3071
    }
3072
3073
    #[test]
3074
    fn eq_rope_slice_05() {
3075
        let r = Rope::from_str("");
3076
        let s = r.slice(0..0);
3077
3078
        assert_eq!(s, s);
3079
    }
3080
3081
    #[test]
3082
    fn cmp_rope_slice_01() {
3083
        let r1 = Rope::from_str("abcdefghijklmnopqrstuvwxyz");
3084
        let r2 = Rope::from_str("abcdefghijklmnopqrstuvwxyz");
3085
        let s1 = r1.slice(..);
3086
        let s2 = r2.slice(..);
3087
3088
        assert_eq!(s1.cmp(&s2), std::cmp::Ordering::Equal);
3089
        assert_eq!(s1.slice(..24).cmp(&s2), std::cmp::Ordering::Less);
3090
        assert_eq!(s1.cmp(&s2.slice(..24)), std::cmp::Ordering::Greater);
3091
    }
3092
3093
    #[test]
3094
    fn cmp_rope_slice_02() {
3095
        let r1 = Rope::from_str("abcdefghijklmnzpqrstuvwxyz");
3096
        let r2 = Rope::from_str("abcdefghijklmnopqrstuvwxyz");
3097
        let s1 = r1.slice(..);
3098
        let s2 = r2.slice(..);
3099
3100
        assert_eq!(s1.cmp(&s2), std::cmp::Ordering::Greater);
3101
        assert_eq!(s2.cmp(&s1), std::cmp::Ordering::Less);
3102
    }
3103
3104
    #[test]
3105
    fn to_string_01() {
3106
        let r = Rope::from_str(TEXT);
3107
        let slc = r.slice(..);
3108
        let s: String = slc.into();
3109
3110
        assert_eq!(r, s);
3111
        assert_eq!(slc, s);
3112
    }
3113
3114
    #[test]
3115
    fn to_string_02() {
3116
        let r = Rope::from_str(TEXT);
3117
        let slc = r.slice(0..24);
3118
        let s: String = slc.into();
3119
3120
        assert_eq!(slc, s);
3121
    }
3122
3123
    #[test]
3124
    fn to_string_03() {
3125
        let r = Rope::from_str(TEXT);
3126
        let slc = r.slice(13..89);
3127
        let s: String = slc.into();
3128
3129
        assert_eq!(slc, s);
3130
    }
3131
3132
    #[test]
3133
    fn to_string_04() {
3134
        let r = Rope::from_str(TEXT);
3135
        let slc = r.slice(13..41);
3136
        let s: String = slc.into();
3137
3138
        assert_eq!(slc, s);
3139
    }
3140
3141
    #[test]
3142
    fn to_cow_01() {
3143
        use std::borrow::Cow;
3144
        let r = Rope::from_str(TEXT);
3145
        let s = r.slice(13..83);
3146
        let cow: Cow<str> = s.into();
3147
3148
        assert_eq!(s, cow);
3149
    }
3150
3151
    #[test]
3152
    fn to_cow_02() {
3153
        use std::borrow::Cow;
3154
        let r = Rope::from_str(TEXT);
3155
        let s = r.slice(13..14);
3156
        let cow: Cow<str> = r.slice(13..14).into();
3157
3158
        // Make sure it's borrowed.
3159
        if let Cow::Owned(_) = cow {
3160
            panic!("Small Cow conversions should result in a borrow.");
3161
        }
3162
3163
        assert_eq!(s, cow);
3164
    }
3165
3166
    #[test]
3167
    fn hash_01() {
3168
        let mut h1 = std::collections::hash_map::DefaultHasher::new();
3169
        let mut h2 = std::collections::hash_map::DefaultHasher::new();
3170
        let r = Rope::from_str("Hello there!");
3171
        let s = r.slice(..);
3172
3173
        r.hash(&mut h1);
3174
        s.hash(&mut h2);
3175
3176
        assert_eq!(h1.finish(), h2.finish());
3177
    }
3178
3179
    // Iterator tests are in the iter module
3180
}