ropey/
slice.rs

1use std::ops::RangeBounds;
2use std::sync::Arc;
3
4use crate::iter::{Bytes, Chars, Chunks, Lines};
5use crate::rope::Rope;
6use 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};
11use crate::tree::{Count, Node, TextInfo};
12use 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)]
25pub struct RopeSlice<'a>(pub(crate) RSEnum<'a>);
26
27#[derive(Copy, Clone, Debug)]
28pub(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
42impl<'a> RopeSlice<'a> {
43    /// Used for tests and debugging purposes.
44    #[allow(dead_code)]
45    pub(crate) fn is_light(&self) -> bool {
46        matches!(&self.0, RSEnum::Light { .. })
47    }
48
49    pub(crate) fn new_with_range(node: &'a Arc<Node>, start: usize, end: usize) -> RopeSlice<'a> {
50        assert!(start <= end);
51        assert!(end <= node.text_info().chars as usize);
52
53        // Early-out shortcut for taking a slice of the full thing.
54        if start == 0 && end == node.char_count() {
55            if node.is_leaf() {
56                let text = node.leaf_text();
57                return RopeSlice(RSEnum::Light {
58                    text: text,
59                    char_count: (end - start) as Count,
60                    utf16_surrogate_count: count_utf16_surrogates(text) as Count,
61                    line_break_count: count_line_breaks(text) as Count,
62                });
63            } else {
64                return RopeSlice(RSEnum::Full {
65                    node: node,
66                    start_info: TextInfo {
67                        bytes: 0,
68                        chars: 0,
69                        utf16_surrogates: 0,
70                        line_breaks: 0,
71                    },
72                    end_info: TextInfo {
73                        bytes: node.byte_count() as Count,
74                        chars: node.char_count() as Count,
75                        utf16_surrogates: node.utf16_surrogate_count() as Count,
76                        line_breaks: node.line_break_count() as Count,
77                    },
78                });
79            }
80        }
81
82        // Find the deepest node that still contains the full range given.
83        let mut n_start = start;
84        let mut n_end = end;
85        let mut node = node;
86        'outer: loop {
87            match *(node as &Node) {
88                // Early out if we reach a leaf, because we can do the
89                // simpler lightweight slice then.
90                Node::Leaf(ref text) => {
91                    let start_byte = char_to_byte_idx(text, n_start);
92                    let end_byte =
93                        start_byte + char_to_byte_idx(&text[start_byte..], n_end - n_start);
94                    return RopeSlice(RSEnum::Light {
95                        text: &text[start_byte..end_byte],
96                        char_count: (n_end - n_start) as Count,
97                        utf16_surrogate_count: count_utf16_surrogates(&text[start_byte..end_byte])
98                            as Count,
99                        line_break_count: count_line_breaks(&text[start_byte..end_byte]) as Count,
100                    });
101                }
102
103                Node::Internal(ref children) => {
104                    let mut start_char = 0;
105                    for (i, inf) in children.info().iter().enumerate() {
106                        if n_start >= start_char && n_end <= (start_char + inf.chars as usize) {
107                            n_start -= start_char;
108                            n_end -= start_char;
109                            node = &children.nodes()[i];
110                            continue 'outer;
111                        }
112                        start_char += inf.chars as usize;
113                    }
114                    break;
115                }
116            }
117        }
118
119        // Create the slice
120        RopeSlice(RSEnum::Full {
121            node: node,
122            start_info: node.char_to_text_info(n_start),
123            end_info: {
124                #[cfg(any(feature = "cr_lines", feature = "unicode_lines"))]
125                {
126                    let mut info = node.char_to_text_info(n_end);
127                    if node.is_crlf_split(n_end) {
128                        info.line_breaks += 1;
129                    }
130                    info
131                }
132                #[cfg(not(any(feature = "cr_lines", feature = "unicode_lines")))]
133                node.char_to_text_info(n_end)
134            },
135        })
136    }
137
138    pub(crate) fn new_with_byte_range(
139        node: &'a Arc<Node>,
140        start: usize,
141        end: usize,
142    ) -> Result<RopeSlice<'a>> {
143        assert!(start <= end);
144        assert!(end <= node.text_info().bytes as usize);
145
146        // Early-out shortcut for taking a slice of the full thing.
147        if start == 0 && end == node.byte_count() {
148            if node.is_leaf() {
149                let text = node.leaf_text();
150                return Ok(RopeSlice(RSEnum::Light {
151                    text,
152                    char_count: count_chars(text) as Count,
153                    utf16_surrogate_count: count_utf16_surrogates(text) as Count,
154                    line_break_count: count_line_breaks(text) as Count,
155                }));
156            } else {
157                return Ok(RopeSlice(RSEnum::Full {
158                    node,
159                    start_info: TextInfo {
160                        bytes: 0,
161                        chars: 0,
162                        utf16_surrogates: 0,
163                        line_breaks: 0,
164                    },
165                    end_info: TextInfo {
166                        bytes: node.byte_count() as Count,
167                        chars: node.char_count() as Count,
168                        utf16_surrogates: node.utf16_surrogate_count() as Count,
169                        line_breaks: node.line_break_count() as Count,
170                    },
171                }));
172            }
173        }
174
175        // Find the deepest node that still contains the full range given.
176        let mut n_start = start;
177        let mut n_end = end;
178        let mut node = node;
179        'outer: loop {
180            match *(node as &Node) {
181                // Early out if we reach a leaf, because we can do the
182                // simpler lightweight slice then.
183                Node::Leaf(ref text) => {
184                    let start_byte = n_start;
185                    let end_byte = n_end;
186                    if !text.is_char_boundary(start_byte) || !text.is_char_boundary(end_byte) {
187                        return Err(Error::ByteRangeNotCharBoundary(Some(start), Some(end)));
188                    }
189                    return Ok(RopeSlice(RSEnum::Light {
190                        text: &text[start_byte..end_byte],
191                        char_count: count_chars(&text[start_byte..end_byte]) as Count,
192                        utf16_surrogate_count: count_utf16_surrogates(&text[start_byte..end_byte])
193                            as Count,
194                        line_break_count: count_line_breaks(&text[start_byte..end_byte]) as Count,
195                    }));
196                }
197
198                Node::Internal(ref children) => {
199                    let mut start_byte = 0;
200                    for (i, inf) in children.info().iter().enumerate() {
201                        if n_start >= start_byte && n_end <= (start_byte + inf.bytes as usize) {
202                            n_start -= start_byte;
203                            n_end -= start_byte;
204                            node = &children.nodes()[i];
205                            continue 'outer;
206                        }
207                        start_byte += inf.bytes as usize;
208                    }
209                    break;
210                }
211            }
212        }
213
214        // Make sure the bytes indices are valid char boundaries.
215        if !node.is_char_boundary(n_start) || !node.is_char_boundary(n_end) {
216            return Err(Error::ByteRangeNotCharBoundary(Some(start), Some(end)));
217        }
218
219        // Create the slice
220        Ok(RopeSlice(RSEnum::Full {
221            node,
222            start_info: node.byte_to_text_info(n_start),
223            end_info: node.byte_to_text_info(n_end),
224        }))
225    }
226
227    //-----------------------------------------------------------------------
228    // Informational methods
229
230    /// Total number of bytes in the `RopeSlice`.
231    ///
232    /// Runs in O(1) time.
233    #[inline]
234    pub fn len_bytes(&self) -> usize {
235        match *self {
236            RopeSlice(RSEnum::Full {
237                end_info,
238                start_info,
239                ..
240            }) => (end_info.bytes - start_info.bytes) as usize,
241            RopeSlice(RSEnum::Light { text, .. }) => text.len(),
242        }
243    }
244
245    /// Total number of chars in the `RopeSlice`.
246    ///
247    /// Runs in O(1) time.
248    #[inline]
249    pub fn len_chars(&self) -> usize {
250        match *self {
251            RopeSlice(RSEnum::Full {
252                end_info,
253                start_info,
254                ..
255            }) => (end_info.chars - start_info.chars) as usize,
256            RopeSlice(RSEnum::Light { char_count, .. }) => char_count as usize,
257        }
258    }
259
260    /// Total number of lines in the `RopeSlice`.
261    ///
262    /// Runs in O(1) time.
263    #[inline]
264    pub fn len_lines(&self) -> usize {
265        match *self {
266            RopeSlice(RSEnum::Full {
267                end_info,
268                start_info,
269                ..
270            }) => (end_info.line_breaks - start_info.line_breaks) as usize + 1,
271            RopeSlice(RSEnum::Light {
272                line_break_count, ..
273            }) => line_break_count as usize + 1,
274        }
275    }
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    pub fn len_utf16_cu(&self) -> usize {
288        match *self {
289            RopeSlice(RSEnum::Full {
290                end_info,
291                start_info,
292                ..
293            }) => {
294                ((end_info.chars + end_info.utf16_surrogates)
295                    - (start_info.chars + start_info.utf16_surrogates)) as usize
296            }
297            RopeSlice(RSEnum::Light {
298                char_count,
299                utf16_surrogate_count,
300                ..
301            }) => (char_count + utf16_surrogate_count) as usize,
302        }
303    }
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    pub fn byte_to_char(&self, byte_idx: usize) -> usize {
324        self.try_byte_to_char(byte_idx).unwrap()
325    }
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    pub fn byte_to_line(&self, byte_idx: usize) -> usize {
343        self.try_byte_to_line(byte_idx).unwrap()
344    }
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    pub fn char_to_byte(&self, char_idx: usize) -> usize {
360        self.try_char_to_byte(char_idx).unwrap()
361    }
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    pub fn char_to_line(&self, char_idx: usize) -> usize {
379        self.try_char_to_line(char_idx).unwrap()
380    }
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    pub fn char_to_utf16_cu(&self, char_idx: usize) -> usize {
396        self.try_char_to_utf16_cu(char_idx).unwrap()
397    }
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    pub fn utf16_cu_to_char(&self, utf16_cu_idx: usize) -> usize {
417        self.try_utf16_cu_to_char(utf16_cu_idx).unwrap()
418    }
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    pub fn line_to_byte(&self, line_idx: usize) -> usize {
435        self.try_line_to_byte(line_idx).unwrap()
436    }
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    pub fn line_to_char(&self, line_idx: usize) -> usize {
453        self.try_line_to_char(line_idx).unwrap()
454    }
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    pub fn byte(&self, byte_idx: usize) -> u8 {
468        // Bounds check
469        if let Some(out) = self.get_byte(byte_idx) {
470            out
471        } else {
472            panic!(
473                "Attempt to index past end of slice: byte index {}, slice byte length {}",
474                byte_idx,
475                self.len_bytes()
476            );
477        }
478    }
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    pub fn char(&self, char_idx: usize) -> char {
489        if let Some(out) = self.get_char(char_idx) {
490            out
491        } else {
492            panic!(
493                "Attempt to index past end of slice: char index {}, slice char length {}",
494                char_idx,
495                self.len_chars()
496            );
497        }
498    }
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    pub fn line(&self, line_idx: usize) -> RopeSlice<'a> {
511        if let Some(out) = self.get_line(line_idx) {
512            out
513        } else {
514            let len_lines = self.len_lines();
515            panic!(
516                "Attempt to index past end of slice: line index {}, slice line length {}",
517                line_idx, len_lines
518            );
519        }
520    }
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    pub fn chunk_at_byte(&self, byte_idx: usize) -> (&'a str, usize, usize, usize) {
539        self.try_chunk_at_byte(byte_idx).unwrap()
540    }
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    pub fn chunk_at_char(&self, char_idx: usize) -> (&'a str, usize, usize, usize) {
559        if let Some(out) = self.get_chunk_at_char(char_idx) {
560            out
561        } else {
562            panic!(
563                "Attempt to index past end of slice: char index {}, slice char length {}",
564                char_idx,
565                self.len_chars()
566            );
567        }
568    }
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    pub fn chunk_at_line_break(&self, line_break_idx: usize) -> (&'a str, usize, usize, usize) {
590        if let Some(out) = self.get_chunk_at_line_break(line_break_idx) {
591            out
592        } else {
593            panic!(
594                "Attempt to index past end of Rope: line break index {}, max index {}",
595                line_break_idx,
596                self.len_lines()
597            );
598        }
599    }
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    pub fn as_str(&self) -> Option<&'a str> {
616        match *self {
617            RopeSlice(RSEnum::Full { .. }) => None,
618            RopeSlice(RSEnum::Light { text, .. }) => Some(text),
619        }
620    }
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    pub fn slice<R>(&self, char_range: R) -> RopeSlice<'a>
636    where
637        R: RangeBounds<usize>,
638    {
639        let (start, end) = {
640            let start_range = start_bound_to_num(char_range.start_bound());
641            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            if start_range == None && end_range == None {
645                return *self;
646            }
647
648            (
649                start_range.unwrap_or(0),
650                end_range.unwrap_or_else(|| self.len_chars()),
651            )
652        };
653
654        // Bounds check
655        assert!(start <= end);
656        assert!(
657            end <= self.len_chars(),
658            "Attempt to slice past end of RopeSlice: slice end {}, RopeSlice length {}",
659            end,
660            self.len_chars()
661        );
662
663        match *self {
664            RopeSlice(RSEnum::Full {
665                node, start_info, ..
666            }) => RopeSlice::new_with_range(
667                node,
668                start_info.chars as usize + start,
669                start_info.chars as usize + end,
670            ),
671            RopeSlice(RSEnum::Light { text, .. }) => {
672                let start_byte = char_to_byte_idx(text, start);
673                let end_byte = char_to_byte_idx(text, end);
674                let new_text = &text[start_byte..end_byte];
675                RopeSlice(RSEnum::Light {
676                    text: new_text,
677                    char_count: (end - start) as Count,
678                    utf16_surrogate_count: count_utf16_surrogates(new_text) as Count,
679                    line_break_count: count_line_breaks(new_text) as Count,
680                })
681            }
682        }
683    }
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    pub fn byte_slice<R>(&self, byte_range: R) -> RopeSlice<'a>
698    where
699        R: RangeBounds<usize>,
700    {
701        match self.get_byte_slice_impl(byte_range) {
702            Ok(s) => return s,
703            Err(e) => panic!("byte_slice(): {}", e),
704        }
705    }
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    pub fn bytes(&self) -> Bytes<'a> {
715        match *self {
716            RopeSlice(RSEnum::Full {
717                node,
718                start_info,
719                end_info,
720            }) => Bytes::new_with_range(
721                node,
722                (start_info.bytes as usize, end_info.bytes as usize),
723                (start_info.chars as usize, end_info.chars as usize),
724                (
725                    start_info.line_breaks as usize,
726                    end_info.line_breaks as usize + 1,
727                ),
728            ),
729            RopeSlice(RSEnum::Light { text, .. }) => Bytes::from_str(text),
730        }
731    }
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    pub fn bytes_at(&self, byte_idx: usize) -> Bytes<'a> {
746        if let Some(out) = self.get_bytes_at(byte_idx) {
747            out
748        } else {
749            panic!(
750                "Attempt to index past end of RopeSlice: byte index {}, RopeSlice byte length {}",
751                byte_idx,
752                self.len_bytes()
753            );
754        }
755    }
756
757    /// Creates an iterator over the chars of the `RopeSlice`.
758    ///
759    /// Runs in O(log N) time.
760    #[inline]
761    pub fn chars(&self) -> Chars<'a> {
762        match *self {
763            RopeSlice(RSEnum::Full {
764                node,
765                start_info,
766                end_info,
767            }) => Chars::new_with_range(
768                node,
769                (start_info.bytes as usize, end_info.bytes as usize),
770                (start_info.chars as usize, end_info.chars as usize),
771                (
772                    start_info.line_breaks as usize,
773                    end_info.line_breaks as usize + 1,
774                ),
775            ),
776            RopeSlice(RSEnum::Light { text, .. }) => Chars::from_str(text),
777        }
778    }
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    pub fn chars_at(&self, char_idx: usize) -> Chars<'a> {
793        if let Some(out) = self.get_chars_at(char_idx) {
794            out
795        } else {
796            panic!(
797                "Attempt to index past end of RopeSlice: char index {}, RopeSlice char length {}",
798                char_idx,
799                self.len_chars()
800            );
801        }
802    }
803
804    /// Creates an iterator over the lines of the `RopeSlice`.
805    ///
806    /// Runs in O(log N) time.
807    #[inline]
808    pub fn lines(&self) -> Lines<'a> {
809        match *self {
810            RopeSlice(RSEnum::Full {
811                node,
812                start_info,
813                end_info,
814            }) => Lines::new_with_range(
815                node,
816                (start_info.bytes as usize, end_info.bytes as usize),
817                (
818                    start_info.line_breaks as usize,
819                    end_info.line_breaks as usize + 1,
820                ),
821            ),
822            RopeSlice(RSEnum::Light {
823                text,
824                line_break_count,
825                ..
826            }) => Lines::from_str(text, line_break_count as usize + 1),
827        }
828    }
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    pub fn lines_at(&self, line_idx: usize) -> Lines<'a> {
843        if let Some(out) = self.get_lines_at(line_idx) {
844            out
845        } else {
846            panic!(
847                "Attempt to index past end of RopeSlice: line index {}, RopeSlice line length {}",
848                line_idx,
849                self.len_lines()
850            );
851        }
852    }
853
854    /// Creates an iterator over the chunks of the `RopeSlice`.
855    ///
856    /// Runs in O(log N) time.
857    #[inline]
858    pub fn chunks(&self) -> Chunks<'a> {
859        match *self {
860            RopeSlice(RSEnum::Full {
861                node,
862                start_info,
863                end_info,
864            }) => Chunks::new_with_range(
865                node,
866                (start_info.bytes as usize, end_info.bytes as usize),
867                (start_info.chars as usize, end_info.chars as usize),
868                (
869                    start_info.line_breaks as usize,
870                    end_info.line_breaks as usize + 1,
871                ),
872            ),
873            RopeSlice(RSEnum::Light { text, .. }) => Chunks::from_str(text, false),
874        }
875    }
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    pub fn chunks_at_byte(&self, byte_idx: usize) -> (Chunks<'a>, usize, usize, usize) {
896        if let Some(out) = self.get_chunks_at_byte(byte_idx) {
897            out
898        } else {
899            panic!(
900                "Attempt to index past end of RopeSlice: byte index {}, RopeSlice byte length {}",
901                byte_idx,
902                self.len_bytes()
903            );
904        }
905    }
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    pub fn chunks_at_char(&self, char_idx: usize) -> (Chunks<'a>, usize, usize, usize) {
926        if let Some(out) = self.get_chunks_at_char(char_idx) {
927            out
928        } else {
929            panic!(
930                "Attempt to index past end of RopeSlice: char index {}, RopeSlice char length {}",
931                char_idx,
932                self.len_chars()
933            );
934        }
935    }
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    pub fn chunks_at_line_break(&self, line_break_idx: usize) -> (Chunks<'a>, usize, usize, usize) {
960        if let Some(out) = self.get_chunks_at_line_break(line_break_idx) {
961            out
962        } else {
963            panic!(
964                "Attempt to index past end of RopeSlice: line break index {}, RopeSlice line break max index {}",
965                line_break_idx,
966                self.len_lines()
967            );
968        }
969    }
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.
977impl<'a> RopeSlice<'a> {
978    /// Non-panicking version of [`byte_to_char()`](RopeSlice::byte_to_char).
979    #[inline]
980    pub fn try_byte_to_char(&self, byte_idx: usize) -> Result<usize> {
981        // Bounds check
982        if byte_idx <= self.len_bytes() {
983            let (chunk, b, c, _) = self.chunk_at_byte(byte_idx);
984            Ok(c + byte_to_char_idx(chunk, byte_idx - b))
985        } else {
986            Err(Error::ByteIndexOutOfBounds(byte_idx, self.len_bytes()))
987        }
988    }
989
990    /// Non-panicking version of [`byte_to_line()`](RopeSlice::byte_to_line).
991    #[inline]
992    pub fn try_byte_to_line(&self, byte_idx: usize) -> Result<usize> {
993        // Bounds check
994        if byte_idx <= self.len_bytes() {
995            let (chunk, b, _, l) = self.chunk_at_byte(byte_idx);
996            Ok(l + byte_to_line_idx(chunk, byte_idx - b))
997        } else {
998            Err(Error::ByteIndexOutOfBounds(byte_idx, self.len_bytes()))
999        }
1000    }
1001
1002    /// Non-panicking version of [`char_to_byte()`](RopeSlice::char_to_byte).
1003    #[inline]
1004    pub fn try_char_to_byte(&self, char_idx: usize) -> Result<usize> {
1005        // Bounds check
1006        if char_idx <= self.len_chars() {
1007            let (chunk, b, c, _) = self.chunk_at_char(char_idx);
1008            Ok(b + char_to_byte_idx(chunk, char_idx - c))
1009        } else {
1010            Err(Error::CharIndexOutOfBounds(char_idx, self.len_chars()))
1011        }
1012    }
1013
1014    /// Non-panicking version of [`char_to_line()`](RopeSlice::char_to_line).
1015    #[inline]
1016    pub fn try_char_to_line(&self, char_idx: usize) -> Result<usize> {
1017        // Bounds check
1018        if char_idx <= self.len_chars() {
1019            let (chunk, _, c, l) = self.chunk_at_char(char_idx);
1020            Ok(l + char_to_line_idx(chunk, char_idx - c))
1021        } else {
1022            Err(Error::CharIndexOutOfBounds(char_idx, self.len_chars()))
1023        }
1024    }
1025
1026    /// Non-panicking version of [`char_to_utf16_cu()`](RopeSlice::char_to_utf16_cu).
1027    #[inline]
1028    pub fn try_char_to_utf16_cu(&self, char_idx: usize) -> Result<usize> {
1029        // Bounds check
1030        if char_idx <= self.len_chars() {
1031            match *self {
1032                RopeSlice(RSEnum::Full {
1033                    node, start_info, ..
1034                }) => {
1035                    let char_idx = char_idx + start_info.chars as usize;
1036
1037                    let (chunk, chunk_start_info) = node.get_chunk_at_char(char_idx);
1038                    let chunk_byte_idx =
1039                        char_to_byte_idx(chunk, char_idx - chunk_start_info.chars as usize);
1040                    let surrogate_count = byte_to_utf16_surrogate_idx(chunk, chunk_byte_idx);
1041
1042                    Ok(
1043                        char_idx + chunk_start_info.utf16_surrogates as usize + surrogate_count
1044                            - start_info.chars as usize
1045                            - start_info.utf16_surrogates as usize,
1046                    )
1047                }
1048
1049                RopeSlice(RSEnum::Light { text, .. }) => {
1050                    let byte_idx = char_to_byte_idx(text, char_idx);
1051                    let surrogate_count = byte_to_utf16_surrogate_idx(text, byte_idx);
1052                    Ok(char_idx + surrogate_count)
1053                }
1054            }
1055        } else {
1056            Err(Error::CharIndexOutOfBounds(char_idx, self.len_chars()))
1057        }
1058    }
1059
1060    /// Non-panicking version of [`utf16_cu_to_char()`](RopeSlice::utf16_cu_to_char).
1061    #[inline]
1062    pub fn try_utf16_cu_to_char(&self, utf16_cu_idx: usize) -> Result<usize> {
1063        // Bounds check
1064        if utf16_cu_idx <= self.len_utf16_cu() {
1065            match *self {
1066                RopeSlice(RSEnum::Full {
1067                    node, start_info, ..
1068                }) => {
1069                    let utf16_cu_idx =
1070                        utf16_cu_idx + (start_info.chars + start_info.utf16_surrogates) as usize;
1071
1072                    let (chunk, chunk_start_info) = node.get_chunk_at_utf16_code_unit(utf16_cu_idx);
1073                    let chunk_utf16_cu_idx = utf16_cu_idx
1074                        - (chunk_start_info.chars + chunk_start_info.utf16_surrogates) as usize;
1075                    let chunk_char_idx = utf16_code_unit_to_char_idx(chunk, chunk_utf16_cu_idx);
1076
1077                    Ok(
1078                        chunk_start_info.chars as usize + chunk_char_idx
1079                            - start_info.chars as usize,
1080                    )
1081                }
1082
1083                RopeSlice(RSEnum::Light { text, .. }) => {
1084                    Ok(utf16_code_unit_to_char_idx(text, utf16_cu_idx))
1085                }
1086            }
1087        } else {
1088            Err(Error::Utf16IndexOutOfBounds(
1089                utf16_cu_idx,
1090                self.len_utf16_cu(),
1091            ))
1092        }
1093    }
1094
1095    /// Non-panicking version of [`line_to_byte()`](RopeSlice::line_to_byte).
1096    #[inline]
1097    pub fn try_line_to_byte(&self, line_idx: usize) -> Result<usize> {
1098        // Bounds check
1099        if line_idx <= self.len_lines() {
1100            if line_idx == self.len_lines() {
1101                Ok(self.len_bytes())
1102            } else {
1103                let (chunk, b, _, l) = self.chunk_at_line_break(line_idx);
1104                Ok(b + line_to_byte_idx(chunk, line_idx - l))
1105            }
1106        } else {
1107            Err(Error::LineIndexOutOfBounds(line_idx, self.len_lines()))
1108        }
1109    }
1110
1111    /// Non-panicking version of [`line_to_char()`](RopeSlice::line_to_char).
1112    #[inline]
1113    pub fn try_line_to_char(&self, line_idx: usize) -> Result<usize> {
1114        // Bounds check
1115        if line_idx <= self.len_lines() {
1116            if line_idx == self.len_lines() {
1117                Ok(self.len_chars())
1118            } else {
1119                let (chunk, _, c, l) = self.chunk_at_line_break(line_idx);
1120                Ok(c + line_to_char_idx(chunk, line_idx - l))
1121            }
1122        } else {
1123            Err(Error::LineIndexOutOfBounds(line_idx, self.len_lines()))
1124        }
1125    }
1126
1127    /// Non-panicking version of [`get_byte()`](RopeSlice::get_byte).
1128    #[inline]
1129    pub fn get_byte(&self, byte_idx: usize) -> Option<u8> {
1130        // Bounds check
1131        if byte_idx < self.len_bytes() {
1132            let (chunk, chunk_byte_idx, _, _) = self.chunk_at_byte(byte_idx);
1133            let chunk_rel_byte_idx = byte_idx - chunk_byte_idx;
1134            Some(chunk.as_bytes()[chunk_rel_byte_idx])
1135        } else {
1136            None
1137        }
1138    }
1139
1140    /// Non-panicking version of [`char()`](RopeSlice::char).
1141    #[inline]
1142    pub fn get_char(&self, char_idx: usize) -> Option<char> {
1143        // Bounds check
1144        if char_idx < self.len_chars() {
1145            let (chunk, _, chunk_char_idx, _) = self.chunk_at_char(char_idx);
1146            let byte_idx = char_to_byte_idx(chunk, char_idx - chunk_char_idx);
1147            Some(chunk[byte_idx..].chars().next().unwrap())
1148        } else {
1149            None
1150        }
1151    }
1152
1153    /// Non-panicking version of [`line()`](RopeSlice::line).
1154    #[inline]
1155    pub fn get_line(&self, line_idx: usize) -> Option<RopeSlice<'a>> {
1156        let len_lines = self.len_lines();
1157        // Bounds check
1158        if line_idx < len_lines {
1159            let (chunk_1, _, c1, l1) = self.chunk_at_line_break(line_idx);
1160            let (chunk_2, _, c2, l2) = self.chunk_at_line_break(line_idx + 1);
1161            if c1 == c2 {
1162                let text1 = &chunk_1[line_to_byte_idx(chunk_1, line_idx - l1)..];
1163                let text2 = &text1[..line_to_byte_idx(text1, 1)];
1164                Some(RopeSlice(RSEnum::Light {
1165                    text: text2,
1166                    char_count: count_chars(text2) as Count,
1167                    utf16_surrogate_count: count_utf16_surrogates(text2) as Count,
1168                    line_break_count: if line_idx == (len_lines - 1) { 0 } else { 1 },
1169                }))
1170            } else {
1171                let start = c1 + line_to_char_idx(chunk_1, line_idx - l1);
1172                let end = c2 + line_to_char_idx(chunk_2, line_idx + 1 - l2);
1173                Some(self.slice(start..end))
1174            }
1175        } else {
1176            None
1177        }
1178    }
1179
1180    /// Non-panicking version of [`chunk_at_byte()`](RopeSlice::chunk_at_byte).
1181    pub fn try_chunk_at_byte(&self, byte_idx: usize) -> Result<(&'a str, usize, usize, usize)> {
1182        // Bounds check
1183        if byte_idx <= self.len_bytes() {
1184            match *self {
1185                RopeSlice(RSEnum::Full {
1186                    node,
1187                    start_info,
1188                    end_info,
1189                }) => {
1190                    // Get the chunk.
1191                    let (chunk, chunk_start_info) =
1192                        node.get_chunk_at_byte(byte_idx + start_info.bytes as usize);
1193
1194                    // Calculate clipped start/end byte indices within the chunk.
1195                    let chunk_start_byte_idx =
1196                        start_info.bytes.saturating_sub(chunk_start_info.bytes);
1197                    let chunk_end_byte_idx =
1198                        (chunk.len() as Count).min(end_info.bytes - chunk_start_info.bytes);
1199
1200                    // Return the clipped chunk and byte offset.
1201                    Ok((
1202                        &chunk[chunk_start_byte_idx as usize..chunk_end_byte_idx as usize],
1203                        chunk_start_info.bytes.saturating_sub(start_info.bytes) as usize,
1204                        chunk_start_info.chars.saturating_sub(start_info.chars) as usize,
1205                        chunk_start_info
1206                            .line_breaks
1207                            .saturating_sub(start_info.line_breaks)
1208                            as usize,
1209                    ))
1210                }
1211                RopeSlice(RSEnum::Light { text, .. }) => Ok((text, 0, 0, 0)),
1212            }
1213        } else {
1214            Err(Error::ByteIndexOutOfBounds(byte_idx, self.len_bytes()))
1215        }
1216    }
1217
1218    /// Non-panicking version of [`chunk_at_char()`](RopeSlice::chunk_at_char).
1219    pub fn get_chunk_at_char(&self, char_idx: usize) -> Option<(&'a str, usize, usize, usize)> {
1220        // Bounds check
1221        if char_idx <= self.len_chars() {
1222            match *self {
1223                RopeSlice(RSEnum::Full {
1224                    node,
1225                    start_info,
1226                    end_info,
1227                }) => {
1228                    // Get the chunk.
1229                    let (chunk, chunk_start_info) =
1230                        node.get_chunk_at_char(char_idx + start_info.chars as usize);
1231
1232                    // Calculate clipped start/end byte indices within the chunk.
1233                    let chunk_start_byte_idx =
1234                        start_info.bytes.saturating_sub(chunk_start_info.bytes);
1235                    let chunk_end_byte_idx =
1236                        (chunk.len() as Count).min(end_info.bytes - chunk_start_info.bytes);
1237
1238                    // Return the clipped chunk and byte offset.
1239                    Some((
1240                        &chunk[chunk_start_byte_idx as usize..chunk_end_byte_idx as usize],
1241                        chunk_start_info.bytes.saturating_sub(start_info.bytes) as usize,
1242                        chunk_start_info.chars.saturating_sub(start_info.chars) as usize,
1243                        chunk_start_info
1244                            .line_breaks
1245                            .saturating_sub(start_info.line_breaks)
1246                            as usize,
1247                    ))
1248                }
1249                RopeSlice(RSEnum::Light { text, .. }) => Some((text, 0, 0, 0)),
1250            }
1251        } else {
1252            None
1253        }
1254    }
1255
1256    /// Non-panicking version of [`chunk_at_line_break()`](RopeSlice::chunk_at_line_break).
1257    pub fn get_chunk_at_line_break(
1258        &self,
1259        line_break_idx: usize,
1260    ) -> Option<(&'a str, usize, usize, usize)> {
1261        // Bounds check
1262        if line_break_idx <= self.len_lines() {
1263            match *self {
1264                RopeSlice(RSEnum::Full {
1265                    node,
1266                    start_info,
1267                    end_info,
1268                }) => {
1269                    // Get the chunk.
1270                    let (chunk, chunk_start_info) = if line_break_idx == 0 {
1271                        node.get_chunk_at_byte(start_info.bytes as usize)
1272                    } else if line_break_idx == self.len_lines() {
1273                        node.get_chunk_at_byte(end_info.bytes as usize)
1274                    } else {
1275                        node.get_chunk_at_line_break(
1276                            line_break_idx + start_info.line_breaks as usize,
1277                        )
1278                    };
1279
1280                    // Calculate clipped start/end byte indices within the chunk.
1281                    let chunk_start_byte_idx =
1282                        start_info.bytes.saturating_sub(chunk_start_info.bytes);
1283                    let chunk_end_byte_idx =
1284                        (chunk.len() as Count).min(end_info.bytes - chunk_start_info.bytes);
1285
1286                    // Return the clipped chunk and byte offset.
1287                    Some((
1288                        &chunk[chunk_start_byte_idx as usize..chunk_end_byte_idx as usize],
1289                        chunk_start_info.bytes.saturating_sub(start_info.bytes) as usize,
1290                        chunk_start_info.chars.saturating_sub(start_info.chars) as usize,
1291                        chunk_start_info
1292                            .line_breaks
1293                            .saturating_sub(start_info.line_breaks)
1294                            as usize,
1295                    ))
1296                }
1297                RopeSlice(RSEnum::Light { text, .. }) => Some((text, 0, 0, 0)),
1298            }
1299        } else {
1300            None
1301        }
1302    }
1303
1304    /// Non-panicking version of [`slice()`](RopeSlice::slice).
1305    pub fn get_slice<R>(&self, char_range: R) -> Option<RopeSlice<'a>>
1306    where
1307        R: RangeBounds<usize>,
1308    {
1309        let (start, end) = {
1310            let start_range = start_bound_to_num(char_range.start_bound());
1311            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            if start_range == None && end_range == None {
1315                return Some(*self);
1316            }
1317
1318            (
1319                start_range.unwrap_or(0),
1320                end_range.unwrap_or_else(|| self.len_chars()),
1321            )
1322        };
1323
1324        // Bounds check
1325        if start <= end && end <= self.len_chars() {
1326            match *self {
1327                RopeSlice(RSEnum::Full {
1328                    node, start_info, ..
1329                }) => Some(RopeSlice::new_with_range(
1330                    node,
1331                    start_info.chars as usize + start,
1332                    start_info.chars as usize + end,
1333                )),
1334                RopeSlice(RSEnum::Light { text, .. }) => {
1335                    let start_byte = char_to_byte_idx(text, start);
1336                    let end_byte = char_to_byte_idx(text, end);
1337                    let new_text = &text[start_byte..end_byte];
1338                    Some(RopeSlice(RSEnum::Light {
1339                        text: new_text,
1340                        char_count: (end - start) as Count,
1341                        utf16_surrogate_count: count_utf16_surrogates(new_text) as Count,
1342                        line_break_count: count_line_breaks(new_text) as Count,
1343                    }))
1344                }
1345            }
1346        } else {
1347            None
1348        }
1349    }
1350
1351    /// Non-panicking version of [`byte_slice()`](RopeSlice::byte_slice).
1352    pub fn get_byte_slice<R>(&self, byte_range: R) -> Option<RopeSlice<'a>>
1353    where
1354        R: RangeBounds<usize>,
1355    {
1356        self.get_byte_slice_impl(byte_range).ok()
1357    }
1358
1359    pub(crate) fn get_byte_slice_impl<R>(&self, byte_range: R) -> Result<RopeSlice<'a>>
1360    where
1361        R: RangeBounds<usize>,
1362    {
1363        let start_range = start_bound_to_num(byte_range.start_bound());
1364        let end_range = end_bound_to_num(byte_range.end_bound());
1365
1366        // Bounds checks.
1367        match (start_range, end_range) {
1368            (None, None) => {
1369                // Early-out shortcut for taking a slice of the full thing.
1370                return Ok(*self);
1371            }
1372            (Some(s), Some(e)) => {
1373                if s > e {
1374                    return Err(Error::ByteRangeInvalid(s, e));
1375                } else if e > self.len_bytes() {
1376                    return Err(Error::ByteRangeOutOfBounds(
1377                        start_range,
1378                        end_range,
1379                        self.len_bytes(),
1380                    ));
1381                }
1382            }
1383            (Some(s), None) => {
1384                if s > self.len_bytes() {
1385                    return Err(Error::ByteRangeOutOfBounds(
1386                        start_range,
1387                        end_range,
1388                        self.len_bytes(),
1389                    ));
1390                }
1391            }
1392            (None, Some(e)) => {
1393                if e > self.len_bytes() {
1394                    return Err(Error::ByteRangeOutOfBounds(
1395                        start_range,
1396                        end_range,
1397                        self.len_bytes(),
1398                    ));
1399                }
1400            }
1401        }
1402
1403        let (start, end) = (
1404            start_range.unwrap_or(0),
1405            end_range.unwrap_or_else(|| self.len_bytes()),
1406        );
1407
1408        match *self {
1409            RopeSlice(RSEnum::Full {
1410                node, start_info, ..
1411            }) => RopeSlice::new_with_byte_range(
1412                node,
1413                start_info.bytes as usize + start,
1414                start_info.bytes as usize + end,
1415            )
1416            .map_err(|e| {
1417                if let Error::ByteRangeNotCharBoundary(_, _) = e {
1418                    Error::ByteRangeNotCharBoundary(start_range, end_range)
1419                } else {
1420                    e
1421                }
1422            }),
1423            RopeSlice(RSEnum::Light { text, .. }) => {
1424                if !text.is_char_boundary(start) || !text.is_char_boundary(end) {
1425                    return Err(Error::ByteRangeNotCharBoundary(start_range, end_range));
1426                }
1427                let new_text = &text[start..end];
1428                Ok(RopeSlice(RSEnum::Light {
1429                    text: new_text,
1430                    char_count: count_chars(new_text) as Count,
1431                    utf16_surrogate_count: count_utf16_surrogates(new_text) as Count,
1432                    line_break_count: count_line_breaks(new_text) as Count,
1433                }))
1434            }
1435        }
1436    }
1437
1438    /// Non-panicking version of [`bytes_at()`](RopeSlice::bytes_at).
1439    #[inline]
1440    pub fn get_bytes_at(&self, byte_idx: usize) -> Option<Bytes<'a>> {
1441        // Bounds check
1442        if byte_idx <= self.len_bytes() {
1443            match *self {
1444                RopeSlice(RSEnum::Full {
1445                    node,
1446                    start_info,
1447                    end_info,
1448                }) => Some(Bytes::new_with_range_at(
1449                    node,
1450                    start_info.bytes as usize + byte_idx,
1451                    (start_info.bytes as usize, end_info.bytes as usize),
1452                    (start_info.chars as usize, end_info.chars as usize),
1453                    (
1454                        start_info.line_breaks as usize,
1455                        end_info.line_breaks as usize + 1,
1456                    ),
1457                )),
1458                RopeSlice(RSEnum::Light { text, .. }) => Some(Bytes::from_str_at(text, byte_idx)),
1459            }
1460        } else {
1461            None
1462        }
1463    }
1464
1465    /// Non-panicking version of [`chars_at()`](RopeSlice::chars_at).
1466    #[inline]
1467    pub fn get_chars_at(&self, char_idx: usize) -> Option<Chars<'a>> {
1468        // Bounds check
1469        if char_idx <= self.len_chars() {
1470            match *self {
1471                RopeSlice(RSEnum::Full {
1472                    node,
1473                    start_info,
1474                    end_info,
1475                }) => Some(Chars::new_with_range_at(
1476                    node,
1477                    start_info.chars as usize + char_idx,
1478                    (start_info.bytes as usize, end_info.bytes as usize),
1479                    (start_info.chars as usize, end_info.chars as usize),
1480                    (
1481                        start_info.line_breaks as usize,
1482                        end_info.line_breaks as usize + 1,
1483                    ),
1484                )),
1485                RopeSlice(RSEnum::Light { text, .. }) => Some(Chars::from_str_at(text, char_idx)),
1486            }
1487        } else {
1488            None
1489        }
1490    }
1491
1492    /// Non-panicking version of [`lines_at()`](RopeSlice::lines_at).
1493    #[inline]
1494    pub fn get_lines_at(&self, line_idx: usize) -> Option<Lines<'a>> {
1495        // Bounds check
1496        if line_idx <= self.len_lines() {
1497            match *self {
1498                RopeSlice(RSEnum::Full {
1499                    node,
1500                    start_info,
1501                    end_info,
1502                }) => Some(Lines::new_with_range_at(
1503                    node,
1504                    start_info.line_breaks as usize + line_idx,
1505                    (start_info.bytes as usize, end_info.bytes as usize),
1506                    (
1507                        start_info.line_breaks as usize,
1508                        end_info.line_breaks as usize + 1,
1509                    ),
1510                )),
1511                RopeSlice(RSEnum::Light {
1512                    text,
1513                    line_break_count,
1514                    ..
1515                }) => Some(Lines::from_str_at(
1516                    text,
1517                    line_idx,
1518                    line_break_count as usize + 1,
1519                )),
1520            }
1521        } else {
1522            None
1523        }
1524    }
1525
1526    /// Non-panicking version of [`chunks_at_byte()`](RopeSlice::chunks_at_byte).
1527    #[inline]
1528    pub fn get_chunks_at_byte(&self, byte_idx: usize) -> Option<(Chunks<'a>, usize, usize, usize)> {
1529        // Bounds check
1530        if byte_idx <= self.len_bytes() {
1531            match *self {
1532                RopeSlice(RSEnum::Full {
1533                    node,
1534                    start_info,
1535                    end_info,
1536                }) => {
1537                    let (chunks, chunk_byte_idx, chunk_char_idx, chunk_line_idx) =
1538                        Chunks::new_with_range_at_byte(
1539                            node,
1540                            byte_idx + start_info.bytes as usize,
1541                            (start_info.bytes as usize, end_info.bytes as usize),
1542                            (start_info.chars as usize, end_info.chars as usize),
1543                            (
1544                                start_info.line_breaks as usize,
1545                                end_info.line_breaks as usize + 1,
1546                            ),
1547                        );
1548
1549                    Some((
1550                        chunks,
1551                        chunk_byte_idx.saturating_sub(start_info.bytes as usize),
1552                        chunk_char_idx.saturating_sub(start_info.chars as usize),
1553                        chunk_line_idx.saturating_sub(start_info.line_breaks as usize),
1554                    ))
1555                }
1556                RopeSlice(RSEnum::Light {
1557                    text,
1558                    char_count,
1559                    line_break_count,
1560                    ..
1561                }) => {
1562                    let chunks = Chunks::from_str(text, byte_idx == text.len());
1563
1564                    if byte_idx == text.len() {
1565                        Some((
1566                            chunks,
1567                            text.len(),
1568                            char_count as usize,
1569                            line_break_count as usize,
1570                        ))
1571                    } else {
1572                        Some((chunks, 0, 0, 0))
1573                    }
1574                }
1575            }
1576        } else {
1577            None
1578        }
1579    }
1580
1581    /// Non-panicking version of [`chunks_at_char()`](RopeSlice::chunks_at_char).
1582    #[inline]
1583    pub fn get_chunks_at_char(&self, char_idx: usize) -> Option<(Chunks<'a>, usize, usize, usize)> {
1584        // Bounds check
1585        if char_idx <= self.len_chars() {
1586            match *self {
1587                RopeSlice(RSEnum::Full {
1588                    node,
1589                    start_info,
1590                    end_info,
1591                }) => {
1592                    let (chunks, chunk_byte_idx, chunk_char_idx, chunk_line_idx) =
1593                        Chunks::new_with_range_at_char(
1594                            node,
1595                            char_idx + start_info.chars as usize,
1596                            (start_info.bytes as usize, end_info.bytes as usize),
1597                            (start_info.chars as usize, end_info.chars as usize),
1598                            (
1599                                start_info.line_breaks as usize,
1600                                end_info.line_breaks as usize + 1,
1601                            ),
1602                        );
1603
1604                    Some((
1605                        chunks,
1606                        chunk_byte_idx.saturating_sub(start_info.bytes as usize),
1607                        chunk_char_idx.saturating_sub(start_info.chars as usize),
1608                        chunk_line_idx.saturating_sub(start_info.line_breaks as usize),
1609                    ))
1610                }
1611                RopeSlice(RSEnum::Light {
1612                    text,
1613                    char_count,
1614                    line_break_count,
1615                    ..
1616                }) => {
1617                    let chunks = Chunks::from_str(text, char_idx == char_count as usize);
1618
1619                    if char_idx == char_count as usize {
1620                        Some((
1621                            chunks,
1622                            text.len(),
1623                            char_count as usize,
1624                            line_break_count as usize,
1625                        ))
1626                    } else {
1627                        Some((chunks, 0, 0, 0))
1628                    }
1629                }
1630            }
1631        } else {
1632            None
1633        }
1634    }
1635
1636    /// Non-panicking version of [`chunks_at_line_break()`](RopeSlice::chunks_at_line_break).
1637    #[inline]
1638    pub fn get_chunks_at_line_break(
1639        &self,
1640        line_break_idx: usize,
1641    ) -> Option<(Chunks<'a>, usize, usize, usize)> {
1642        // Bounds check
1643        if line_break_idx <= self.len_lines() {
1644            match *self {
1645                RopeSlice(RSEnum::Full {
1646                    node,
1647                    start_info,
1648                    end_info,
1649                }) => {
1650                    // Get the chunk.
1651                    let (chunks, chunk_byte_idx, chunk_char_idx, chunk_line_idx) =
1652                        if line_break_idx == 0 {
1653                            Chunks::new_with_range_at_byte(
1654                                node,
1655                                start_info.bytes as usize,
1656                                (start_info.bytes as usize, end_info.bytes as usize),
1657                                (start_info.chars as usize, end_info.chars as usize),
1658                                (
1659                                    start_info.line_breaks as usize,
1660                                    end_info.line_breaks as usize + 1,
1661                                ),
1662                            )
1663                        } else if line_break_idx == self.len_lines() {
1664                            Chunks::new_with_range_at_byte(
1665                                node,
1666                                end_info.bytes as usize,
1667                                (start_info.bytes as usize, end_info.bytes as usize),
1668                                (start_info.chars as usize, end_info.chars as usize),
1669                                (
1670                                    start_info.line_breaks as usize,
1671                                    end_info.line_breaks as usize + 1,
1672                                ),
1673                            )
1674                        } else {
1675                            Chunks::new_with_range_at_line_break(
1676                                node,
1677                                line_break_idx + start_info.line_breaks as usize,
1678                                (start_info.bytes as usize, end_info.bytes as usize),
1679                                (start_info.chars as usize, end_info.chars as usize),
1680                                (
1681                                    start_info.line_breaks as usize,
1682                                    end_info.line_breaks as usize + 1,
1683                                ),
1684                            )
1685                        };
1686                    Some((
1687                        chunks,
1688                        chunk_byte_idx.saturating_sub(start_info.bytes as usize),
1689                        chunk_char_idx.saturating_sub(start_info.chars as usize),
1690                        chunk_line_idx.saturating_sub(start_info.line_breaks as usize),
1691                    ))
1692                }
1693                RopeSlice(RSEnum::Light {
1694                    text,
1695                    char_count,
1696                    line_break_count,
1697                    ..
1698                }) => {
1699                    let chunks =
1700                        Chunks::from_str(text, line_break_idx == line_break_count as usize);
1701
1702                    if line_break_idx == line_break_count as usize {
1703                        Some((
1704                            chunks,
1705                            text.len(),
1706                            char_count as usize,
1707                            line_break_count as usize,
1708                        ))
1709                    } else {
1710                        Some((chunks, 0, 0, 0))
1711                    }
1712                }
1713            }
1714        } else {
1715            None
1716        }
1717    }
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.
1737impl<'a> From<&'a str> for RopeSlice<'a> {
1738    #[inline]
1739    fn from(text: &'a str) -> Self {
1740        RopeSlice(RSEnum::Light {
1741            text: text,
1742            char_count: count_chars(text) as Count,
1743            utf16_surrogate_count: count_utf16_surrogates(text) as Count,
1744            line_break_count: count_line_breaks(text) as Count,
1745        })
1746    }
1747}
1748
1749impl<'a> From<RopeSlice<'a>> for String {
1750    #[inline]
1751    fn from(s: RopeSlice<'a>) -> Self {
1752        let mut text = String::with_capacity(s.len_bytes());
1753        text.extend(s.chunks());
1754        text
1755    }
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).
1762impl<'a> From<RopeSlice<'a>> for std::borrow::Cow<'a, str> {
1763    #[inline]
1764    fn from(s: RopeSlice<'a>) -> Self {
1765        if let Some(text) = s.as_str() {
1766            std::borrow::Cow::Borrowed(text)
1767        } else {
1768            std::borrow::Cow::Owned(String::from(s))
1769        }
1770    }
1771}
1772
1773//==============================================================
1774// Other impls
1775
1776impl<'a> std::fmt::Debug for RopeSlice<'a> {
1777    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1778        f.debug_list().entries(self.chunks()).finish()
1779    }
1780}
1781
1782impl<'a> std::fmt::Display for RopeSlice<'a> {
1783    #[inline]
1784    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
1785        for chunk in self.chunks() {
1786            write!(f, "{}", chunk)?
1787        }
1788        Ok(())
1789    }
1790}
1791
1792impl<'a> std::cmp::Eq for RopeSlice<'a> {}
1793
1794impl<'a, 'b> std::cmp::PartialEq<RopeSlice<'b>> for RopeSlice<'a> {
1795    fn eq(&self, other: &RopeSlice<'b>) -> bool {
1796        if self.len_bytes() != other.len_bytes() {
1797            return false;
1798        }
1799
1800        let mut chunk_itr_1 = self.chunks();
1801        let mut chunk_itr_2 = other.chunks();
1802        let mut chunk1 = chunk_itr_1.next().unwrap_or("").as_bytes();
1803        let mut chunk2 = chunk_itr_2.next().unwrap_or("").as_bytes();
1804
1805        loop {
1806            if chunk1.len() > chunk2.len() {
1807                if &chunk1[..chunk2.len()] != chunk2 {
1808                    return false;
1809                } else {
1810                    chunk1 = &chunk1[chunk2.len()..];
1811                    chunk2 = &[];
1812                }
1813            } else if &chunk2[..chunk1.len()] != chunk1 {
1814                return false;
1815            } else {
1816                chunk2 = &chunk2[chunk1.len()..];
1817                chunk1 = &[];
1818            }
1819
1820            if chunk1.is_empty() {
1821                if let Some(chunk) = chunk_itr_1.next() {
1822                    chunk1 = chunk.as_bytes();
1823                } else {
1824                    break;
1825                }
1826            }
1827
1828            if chunk2.is_empty() {
1829                if let Some(chunk) = chunk_itr_2.next() {
1830                    chunk2 = chunk.as_bytes();
1831                } else {
1832                    break;
1833                }
1834            }
1835        }
1836
1837        return true;
1838    }
1839}
1840
1841impl<'a, 'b> std::cmp::PartialEq<&'b str> for RopeSlice<'a> {
1842    #[inline]
1843    fn eq(&self, other: &&'b str) -> bool {
1844        match *self {
1845            RopeSlice(RSEnum::Full { .. }) => {
1846                if self.len_bytes() != other.len() {
1847                    return false;
1848                }
1849                let other = other.as_bytes();
1850
1851                let mut idx = 0;
1852                for chunk in self.chunks() {
1853                    let chunk = chunk.as_bytes();
1854                    if chunk != &other[idx..(idx + chunk.len())] {
1855                        return false;
1856                    }
1857                    idx += chunk.len();
1858                }
1859
1860                return true;
1861            }
1862            RopeSlice(RSEnum::Light { text, .. }) => {
1863                return text == *other;
1864            }
1865        }
1866    }
1867}
1868
1869impl<'a, 'b> std::cmp::PartialEq<RopeSlice<'a>> for &'b str {
1870    #[inline]
1871    fn eq(&self, other: &RopeSlice<'a>) -> bool {
1872        other == self
1873    }
1874}
1875
1876impl<'a> std::cmp::PartialEq<str> for RopeSlice<'a> {
1877    #[inline]
1878    fn eq(&self, other: &str) -> bool {
1879        std::cmp::PartialEq::<&str>::eq(self, &other)
1880    }
1881}
1882
1883impl<'a> std::cmp::PartialEq<RopeSlice<'a>> for str {
1884    #[inline]
1885    fn eq(&self, other: &RopeSlice<'a>) -> bool {
1886        std::cmp::PartialEq::<&str>::eq(other, &self)
1887    }
1888}
1889
1890impl<'a> std::cmp::PartialEq<String> for RopeSlice<'a> {
1891    #[inline]
1892    fn eq(&self, other: &String) -> bool {
1893        self == other.as_str()
1894    }
1895}
1896
1897impl<'a> std::cmp::PartialEq<RopeSlice<'a>> for String {
1898    #[inline]
1899    fn eq(&self, other: &RopeSlice<'a>) -> bool {
1900        self.as_str() == other
1901    }
1902}
1903
1904impl<'a, 'b> std::cmp::PartialEq<std::borrow::Cow<'b, str>> for RopeSlice<'a> {
1905    #[inline]
1906    fn eq(&self, other: &std::borrow::Cow<'b, str>) -> bool {
1907        *self == **other
1908    }
1909}
1910
1911impl<'a, 'b> std::cmp::PartialEq<RopeSlice<'a>> for std::borrow::Cow<'b, str> {
1912    #[inline]
1913    fn eq(&self, other: &RopeSlice<'a>) -> bool {
1914        **self == *other
1915    }
1916}
1917
1918impl<'a> std::cmp::PartialEq<Rope> for RopeSlice<'a> {
1919    #[inline]
1920    fn eq(&self, other: &Rope) -> bool {
1921        *self == other.slice(..)
1922    }
1923}
1924
1925impl<'a> std::cmp::PartialEq<RopeSlice<'a>> for Rope {
1926    #[inline]
1927    fn eq(&self, other: &RopeSlice<'a>) -> bool {
1928        self.slice(..) == *other
1929    }
1930}
1931
1932impl<'a> std::cmp::Ord for RopeSlice<'a> {
1933    #[allow(clippy::op_ref)] // Erroneously thinks with can directly use a slice.
1934    fn cmp(&self, other: &RopeSlice<'a>) -> std::cmp::Ordering {
1935        let mut chunk_itr_1 = self.chunks();
1936        let mut chunk_itr_2 = other.chunks();
1937        let mut chunk1 = chunk_itr_1.next().unwrap_or("").as_bytes();
1938        let mut chunk2 = chunk_itr_2.next().unwrap_or("").as_bytes();
1939
1940        loop {
1941            if chunk1.len() >= chunk2.len() {
1942                let compared = chunk1[..chunk2.len()].cmp(chunk2);
1943                if compared != std::cmp::Ordering::Equal {
1944                    return compared;
1945                }
1946
1947                chunk1 = &chunk1[chunk2.len()..];
1948                chunk2 = &[];
1949            } else {
1950                let compared = chunk1.cmp(&chunk2[..chunk1.len()]);
1951                if compared != std::cmp::Ordering::Equal {
1952                    return compared;
1953                }
1954
1955                chunk1 = &[];
1956                chunk2 = &chunk2[chunk1.len()..];
1957            }
1958
1959            if chunk1.is_empty() {
1960                if let Some(chunk) = chunk_itr_1.next() {
1961                    chunk1 = chunk.as_bytes();
1962                } else {
1963                    break;
1964                }
1965            }
1966
1967            if chunk2.is_empty() {
1968                if let Some(chunk) = chunk_itr_2.next() {
1969                    chunk2 = chunk.as_bytes();
1970                } else {
1971                    break;
1972                }
1973            }
1974        }
1975
1976        self.len_bytes().cmp(&other.len_bytes())
1977    }
1978}
1979
1980impl<'a, 'b> std::cmp::PartialOrd<RopeSlice<'b>> for RopeSlice<'a> {
1981    #[inline]
1982    fn partial_cmp(&self, other: &RopeSlice<'b>) -> Option<std::cmp::Ordering> {
1983        Some(self.cmp(other))
1984    }
1985}
1986
1987impl<'a> std::hash::Hash for RopeSlice<'a> {
1988    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        let mut buffer = [0u8; BLOCK_SIZE];
2005        let mut buffer_len = 0;
2006
2007        for chunk in self.chunks() {
2008            let mut data = chunk.as_bytes();
2009
2010            while !data.is_empty() {
2011                if buffer_len == 0 && data.len() >= BLOCK_SIZE {
2012                    // Process data directly, skipping the buffer.
2013                    let (head, tail) = data.split_at(BLOCK_SIZE);
2014                    state.write(head);
2015                    data = tail;
2016                } else if buffer_len == BLOCK_SIZE {
2017                    // Process the filled buffer.
2018                    state.write(&buffer[..]);
2019                    buffer_len = 0;
2020                } else {
2021                    // Append to the buffer.
2022                    let n = (BLOCK_SIZE - buffer_len).min(data.len());
2023                    let (head, tail) = data.split_at(n);
2024                    (&mut buffer[buffer_len..(buffer_len + n)]).copy_from_slice(head);
2025                    buffer_len += n;
2026                    data = tail;
2027                }
2028            }
2029        }
2030
2031        // Write any remaining unprocessed data in the buffer.
2032        if buffer_len > 0 {
2033            state.write(&buffer[..buffer_len]);
2034        }
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        state.write_u8(0xff)
2040    }
2041}
2042
2043//===========================================================
2044
2045#[cfg(test)]
2046mod 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}