From 86a47754a75135a5a0f4e97bef393c3431eb2e3a Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Wed, 13 Aug 2025 12:58:45 +0200 Subject: [PATCH 01/35] Tweak `Add*` and `Sub*` requirements on `Summary` --- src/rope/metrics.rs | 53 ++++++++++------------------ src/tree/inode.rs | 18 +++++----- src/tree/traits.rs | 4 +-- src/tree/tree.rs | 28 +++++++-------- src/tree/tree_slice.rs | 26 +++++++------- src/tree/units.rs | 80 +++++++++++++++++++++++------------------- 6 files changed, 98 insertions(+), 111 deletions(-) diff --git a/src/rope/metrics.rs b/src/rope/metrics.rs index 67131d3..b2ad4b0 100644 --- a/src/rope/metrics.rs +++ b/src/rope/metrics.rs @@ -82,48 +82,35 @@ impl Add for ChunkSummary { } } -impl Sub for ChunkSummary { - type Output = Self; - +impl AddAssign for ChunkSummary { #[inline] - fn sub(mut self, rhs: Self) -> Self { - self -= rhs; - self + fn add_assign(&mut self, rhs: Self) { + self.bytes += rhs.bytes; + self.line_breaks += rhs.line_breaks; + #[cfg(feature = "utf16-metric")] + { + self.utf16_code_units += rhs.utf16_code_units; + } } } -impl Add<&Self> for ChunkSummary { - type Output = Self; - +impl AddAssign<&Self> for ChunkSummary { #[inline] - fn add(mut self, rhs: &Self) -> Self { - self += rhs; - self + fn add_assign(&mut self, rhs: &Self) { + *self += *rhs; } } -impl Sub<&Self> for ChunkSummary { +impl Sub for ChunkSummary { type Output = Self; #[inline] - fn sub(mut self, rhs: &Self) -> Self { + fn sub(mut self, rhs: Self) -> Self { self -= rhs; self } } -impl AddAssign for ChunkSummary { - #[inline] - fn add_assign(&mut self, rhs: Self) { - self.bytes += rhs.bytes; - self.line_breaks += rhs.line_breaks; - #[cfg(feature = "utf16-metric")] - { - self.utf16_code_units += rhs.utf16_code_units; - } - } -} - impl SubAssign for ChunkSummary { #[inline] fn sub_assign(&mut self, rhs: Self) { @@ -136,17 +123,13 @@ impl SubAssign for ChunkSummary { } } -impl AddAssign<&Self> for ChunkSummary { - #[inline] - fn add_assign(&mut self, rhs: &Self) { - *self += *rhs; - } -} +impl Sub<&Self> for ChunkSummary { + type Output = Self; -impl SubAssign<&Self> for ChunkSummary { #[inline] - fn sub_assign(&mut self, rhs: &Self) { - *self -= *rhs; + fn sub(mut self, rhs: &Self) -> Self { + self -= *rhs; + self } } diff --git a/src/tree/inode.rs b/src/tree/inode.rs index 1cedf71..1a2b588 100644 --- a/src/tree/inode.rs +++ b/src/tree/inode.rs @@ -491,7 +491,7 @@ impl Inode { debug_assert!(end <= self.len()); for child in &self.children[start..end] { - self.summary -= &child.summary(); + self.summary -= child.summary(); } self.children.drain(start..end) @@ -535,7 +535,7 @@ impl Inode { let mut summary = children[0].summary(); for child in &children[1..] { - summary += &child.summary(); + summary += child.summary(); } Self { children, depth, summary } @@ -608,7 +608,7 @@ impl Inode { debug_assert!(!self.is_full()); debug_assert_eq!(child.depth() + 1, self.depth()); - self.summary += &child.summary(); + self.summary += child.summary(); self.children.insert(child_offset, child); } @@ -849,7 +849,7 @@ impl Inode { debug_assert!(!self.is_full()); debug_assert_eq!(child.depth() + 1, self.depth()); - self.summary += &child.summary(); + self.summary += child.summary(); self.children.push(child); } @@ -862,7 +862,7 @@ impl Inode { pub(super) fn remove(&mut self, child_idx: usize) -> Arc> { debug_assert!(child_idx < self.len()); let child = self.children.remove(child_idx); - self.summary -= &child.summary(); + self.summary -= child.summary(); child } @@ -887,9 +887,9 @@ impl Inode { debug_assert_eq!(new_child.depth() + 1, self.depth()); let to_swap = &self.children[child_idx]; - self.summary -= &to_swap.summary(); + self.summary -= to_swap.summary(); - self.summary += &new_child.summary(); + self.summary += new_child.summary(); self.children[child_idx] = new_child; } @@ -929,11 +929,11 @@ impl Inode { { let child = &mut self.children[child_idx]; - self.summary -= &child.summary(); + self.summary -= child.summary(); let ret = fun(child); - self.summary += &child.summary(); + self.summary += child.summary(); ret } diff --git a/src/tree/traits.rs b/src/tree/traits.rs index 07eae20..89c94c2 100644 --- a/src/tree/traits.rs +++ b/src/tree/traits.rs @@ -6,11 +6,11 @@ pub trait Summary: + Default + Clone + Add - + for<'a> Add<&'a Self, Output = Self> + + AddAssign + for<'a> AddAssign<&'a Self> + Sub + + SubAssign + for<'a> Sub<&'a Self, Output = Self> - + for<'a> SubAssign<&'a Self> + PartialEq { /// The leaf type this summary is for. diff --git a/src/tree/tree.rs b/src/tree/tree.rs index 7aa3113..168cf33 100644 --- a/src/tree/tree.rs +++ b/src/tree/tree.rs @@ -1568,13 +1568,9 @@ mod tests { } } - impl Add<&Self> for UsizeSummary { - type Output = Self; - - #[inline] - fn add(mut self, rhs: &Self) -> Self { - self += rhs; - self + impl AddAssign for UsizeSummary { + fn add_assign(&mut self, rhs: Self) { + *self += &rhs; } } @@ -1590,28 +1586,28 @@ mod tests { #[inline] fn sub(mut self, rhs: Self) -> Self { - self -= &rhs; + self -= rhs; self } } + impl SubAssign for UsizeSummary { + fn sub_assign(&mut self, rhs: Self) { + self.count -= rhs.count; + self.leaves -= rhs.leaves; + } + } + impl Sub<&Self> for UsizeSummary { type Output = Self; #[inline] fn sub(mut self, rhs: &Self) -> Self { - self -= rhs; + self -= *rhs; self } } - impl SubAssign<&Self> for UsizeSummary { - fn sub_assign(&mut self, rhs: &Self) { - self.count -= rhs.count; - self.leaves -= rhs.leaves; - } - } - impl Summary for UsizeSummary { type Leaf = usize; } diff --git a/src/tree/tree_slice.rs b/src/tree/tree_slice.rs index 62b861b..038004f 100644 --- a/src/tree/tree_slice.rs +++ b/src/tree/tree_slice.rs @@ -63,7 +63,7 @@ impl<'a, const ARITY: usize, L: Leaf> TreeSlice<'a, ARITY, L> { if self.leaf_count() == 2 { assert_eq!( self.summary, - self.start_summary() + &self.end_summary() + self.start_summary() + self.end_summary() ); } @@ -341,7 +341,7 @@ where ); slice.root = root; - slice.offset -= &offset; + slice.offset -= offset; } slice @@ -425,7 +425,7 @@ where return (node, start, end); } } else { - measured += &child_summary; + measured += child_summary; } } @@ -478,13 +478,13 @@ where node = child; start -= L::BaseMetric::measure(&measured); end -= L::BaseMetric::measure(&measured); - offset += &measured; + offset += measured; continue 'outer; } else { return (node, offset); } } else { - measured += &child_summary; + measured += child_summary; } } @@ -558,7 +558,7 @@ fn build_slice<'a, const N: usize, L, S, E>( ); } else { // This child comes before the starting leaf. - slice.offset += &child_summary; + slice.offset += child_summary; } } else if E::measure(&slice.offset) + E::measure(&slice.summary) @@ -580,7 +580,7 @@ fn build_slice<'a, const N: usize, L, S, E>( } else { // This is a node fully contained between the starting and // the ending slices. - slice.summary += &child_summary; + slice.summary += child_summary; } } }, @@ -614,7 +614,7 @@ fn build_slice<'a, const N: usize, L, S, E>( let right_slice = S::slice_from(leaf.as_slice(), start); let left_summary = - leaf.summarize() - &right_slice.summarize(); + leaf.summarize() - right_slice.summarize(); let end = end - E::measure(&slice.offset) @@ -622,7 +622,7 @@ fn build_slice<'a, const N: usize, L, S, E>( let start_slice = E::slice_up_to(right_slice, end); - slice.offset += &left_summary; + slice.offset += left_summary; slice.summary = start_slice.summarize(); slice.start_slice = start_slice; slice.end_slice = start_slice; @@ -640,13 +640,13 @@ fn build_slice<'a, const N: usize, L, S, E>( let right_summary = leaf.summarize() - &start_summary; if start_slice.is_empty() { - slice.offset += &leaf.summarize(); + slice.offset += leaf.summarize(); *recompute_root = true; return; } - slice.offset += &right_summary; - slice.summary += &start_summary; + slice.offset += right_summary; + slice.summary += start_summary; slice.start_slice = start_slice; *found_start_slice = true; @@ -663,7 +663,7 @@ fn build_slice<'a, const N: usize, L, S, E>( debug_assert!(!end_slice.is_empty()); - slice.summary += &end_slice.summarize(); + slice.summary += end_slice.summarize(); slice.end_slice = end_slice; *done = true; diff --git a/src/tree/units.rs b/src/tree/units.rs index 9f1861b..954c52c 100644 --- a/src/tree/units.rs +++ b/src/tree/units.rs @@ -343,7 +343,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { match self.first_slice.take() { Some(slice) => { self.yielded_in_leaf = - leaf.summarize() - &slice.summarize(); + leaf.summarize() - slice.summarize(); self.start_slice = slice; }, @@ -479,7 +479,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { let inode = node.get_internal(); for child in &inode.children()[..child_idx] { - before += &child.summary(); + before += child.summary(); } for (idx, child) in @@ -489,7 +489,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { self.path.push((node, child_idx + 1 + idx)); break 'outer; } else { - summary += &child.summary(); + summary += child.summary(); } } } @@ -511,7 +511,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { node = child; continue 'outer; } else { - summary += &child.summary(); + summary += child.summary(); } } @@ -591,7 +591,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { L::BaseMetric::measure(&summary) == L::BaseMetric::zero(); offset += &self.yielded_in_leaf; - summary += &start_summary; + summary += start_summary; let slice = { let contains_last_slice = self.base_yielded @@ -614,7 +614,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { advance += &summary; if !end_slice.is_empty() { - summary += &end_slice.summarize(); + summary += end_slice.summarize(); } // This edge case can happen when the first unit of `slice` is empty. // @@ -641,7 +641,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { root = new_root; - offset -= &remove_offset; + offset -= remove_offset; // `previous_leaf` is guaranteed to be a leaf node by // `Self::previous_leaf()`. @@ -726,11 +726,11 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { let inode = node.get_internal(); for child in &inode.children()[..child_idx] { - before += &child.summary(); + before += child.summary(); } for child in &inode.children()[child_idx + 1..] { - summary += &child.summary(); + summary += child.summary(); } } @@ -744,7 +744,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { for child in &inode.children()[..child_idx] { let child_summary = child.summary(); offset += L::BaseMetric::measure(&child_summary); - before += &child_summary; + before += child_summary; } offset += inode.child(child_idx).base_measure(); @@ -761,7 +761,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { break; } else { offset += child_measure; - summary += &child.summary(); + summary += child.summary(); } } @@ -776,7 +776,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { continue 'outer; } else { offset += child_measure; - summary += &child.summary(); + summary += child.summary(); } } @@ -831,22 +831,31 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { let start_slice = self.start_slice; - let (last_leaf, root, before, mut summary) = self.last_leaf(); + let (last_leaf, root, mut before, mut summary) = self.last_leaf(); - summary += &start_slice.summarize(); + summary += start_slice.summarize(); let end_slice = match self.last_slice.take() { Some(slice) => slice, None => last_leaf.as_slice(), }; - summary += &end_slice.summarize(); + summary += end_slice.summarize(); - let offset = before + &self.yielded_in_leaf; + before += &self.yielded_in_leaf; let advance = summary.clone(); - (TreeSlice { root, offset, summary, start_slice, end_slice }, advance) + ( + TreeSlice { + root, + offset: before, + summary, + start_slice, + end_slice, + }, + advance, + ) } } @@ -993,7 +1002,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> match self.last_slice.take() { Some(slice) => { self.yielded_in_leaf = - leaf.summarize() - &slice.summarize(); + leaf.summarize() - slice.summarize(); self.end_slice = slice; }, @@ -1112,11 +1121,11 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> let inode = node.get_internal(); for child in &inode.children()[..child_idx] { - summary += &child.summary(); + summary += child.summary(); } for child in &inode.children()[child_idx + 1..] { - after += &child.summary(); + after += child.summary(); } } @@ -1126,7 +1135,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> let inode = root.get_internal(); for child in &inode.children()[child_idx + 1..] { - after += &child.summary(); + after += child.summary(); } // This will be the child of the root node that contains the first @@ -1142,7 +1151,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> if offset + child_measure > range.start { for child in children { - summary += &child.summary(); + summary += child.summary(); } node = child; break; @@ -1161,7 +1170,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> if offset + child_measure > range.start { for child in children { - summary += &child.summary(); + summary += child.summary(); } node = child; continue 'outer; @@ -1234,7 +1243,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> self.base_remaining += L::BaseMetric::measure(&advance); - advance += &first_advance; + advance += first_advance; return (first, advance); }; @@ -1243,7 +1252,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> advance += &summary; - summary += &end_slice.summarize(); + summary += end_slice.summarize(); let start_slice = match self.first_slice.take() { Some(slice) => slice, @@ -1254,10 +1263,9 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> advance += &start_summary; - summary += &start_summary; + summary += start_summary; - let offset = - root.summary() - &after - &self.yielded_in_leaf - &advance; + let offset = root.summary() - after - &self.yielded_in_leaf - &advance; (TreeSlice { root, offset, start_slice, end_slice, summary }, advance) } @@ -1335,7 +1343,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> let inode = node.get_internal(); for child in &inode.children()[child_idx + 1..] { - after += &child.summary(); + after += child.summary(); } for (idx, child) in @@ -1345,7 +1353,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> self.path.push((node, idx)); break 'outer; } else { - summary += &child.summary(); + summary += child.summary(); } } } @@ -1369,7 +1377,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> node = child; continue 'outer; } else { - summary += &child.summary(); + summary += child.summary(); } } @@ -1482,7 +1490,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> self.base_remaining += L::BaseMetric::measure(&advance); - advance += &slice_advance; + advance += slice_advance; return (slice, advance); } @@ -1495,7 +1503,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> advance += &summary; - summary += &end_slice.summarize(); + summary += end_slice.summarize(); let slice = { let contains_first_slice = L::BaseMetric::measure(&advance) @@ -1516,13 +1524,13 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> advance += &start_summary; let mut offset = - root.summary() - &after - &self.yielded_in_leaf - &advance; + root.summary() - after - &self.yielded_in_leaf - &advance; self.yielded_in_leaf = start_summary.clone(); self.end_slice = rest; if !start_slice.is_empty() { - summary += &start_summary; + summary += start_summary; } // This edge case can happen when the remainder of `slice` is empty. // @@ -1548,7 +1556,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> root = new_root; - offset -= &remove_offset; + offset -= remove_offset; // `next_leaf` is guaranteed to be a leaf node by // `Self::next_leaf()`. From 581cdfb6163aed8f9db8316cead6c287dceb60f4 Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Wed, 13 Aug 2025 13:08:52 +0200 Subject: [PATCH 02/35] Tweak --- src/tree/tree_slice.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tree/tree_slice.rs b/src/tree/tree_slice.rs index 038004f..4b7581b 100644 --- a/src/tree/tree_slice.rs +++ b/src/tree/tree_slice.rs @@ -635,16 +635,16 @@ fn build_slice<'a, const N: usize, L, S, E>( start - S::measure(&slice.offset), ); - let start_summary = start_slice.summarize(); - - let right_summary = leaf.summarize() - &start_summary; - if start_slice.is_empty() { slice.offset += leaf.summarize(); *recompute_root = true; return; } + let start_summary = start_slice.summarize(); + + let right_summary = leaf.summarize() - &start_summary; + slice.offset += right_summary; slice.summary += start_summary; slice.start_slice = start_slice; From 8bb3e78d4d0ab41518ba954d37fd5751cf4fe66d Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Wed, 13 Aug 2025 13:34:58 +0200 Subject: [PATCH 03/35] WIP --- src/rope/metrics.rs | 20 ++++++------- src/tree/traits.rs | 9 ++++-- src/tree/units.rs | 70 +++++++++++++++++++++------------------------ 3 files changed, 49 insertions(+), 50 deletions(-) diff --git a/src/rope/metrics.rs b/src/rope/metrics.rs index b2ad4b0..492b5c2 100644 --- a/src/rope/metrics.rs +++ b/src/rope/metrics.rs @@ -411,12 +411,12 @@ impl UnitMetric for RawLineMetric { #[inline] fn first_unit<'a>( chunk: GapSlice<'a>, - ) -> (GapSlice<'a>, GapSlice<'a>, ChunkSummary) + ) -> (GapSlice<'a>, GapSlice<'a>, ByteMetric) where 'a: 'a, { let (first, rest) = chunk.split_at_offset(RawLineMetric(1)); - (first, rest, first.summarize()) + (first, rest, first.measure()) } } @@ -424,7 +424,7 @@ impl DoubleEndedUnitMetric for RawLineMetric { #[inline] fn last_unit<'a>( slice: GapSlice<'a>, - ) -> (GapSlice<'a>, GapSlice<'a>, ChunkSummary) + ) -> (GapSlice<'a>, GapSlice<'a>, ByteMetric) where 'a: 'a, { @@ -433,7 +433,7 @@ impl DoubleEndedUnitMetric for RawLineMetric { let (rest, last) = slice.split_at_offset(RawLineMetric(split_offset)); - (rest, last, last.summarize()) + (rest, last, last.measure()) } #[inline] @@ -518,16 +518,16 @@ impl UnitMetric for LineMetric { #[inline] fn first_unit<'a>( chunk: GapSlice<'a>, - ) -> (GapSlice<'a>, GapSlice<'a>, ChunkSummary) + ) -> (GapSlice<'a>, GapSlice<'a>, ByteMetric) where 'a: 'a, { - let (mut first, rest, first_summary) = + let (mut first, rest, first_byte_len) = >::first_unit(chunk); first.truncate_trailing_line_break(); - (first, rest, first_summary) + (first, rest, first_byte_len) } } @@ -535,18 +535,18 @@ impl DoubleEndedUnitMetric for LineMetric { #[inline] fn last_unit<'a>( chunk: GapSlice<'a>, - ) -> (GapSlice<'a>, GapSlice<'a>, ChunkSummary) + ) -> (GapSlice<'a>, GapSlice<'a>, ByteMetric) where 'a: 'a, { - let (rest, mut last, last_summary) = + let (rest, mut last, last_byte_len) = >::last_unit( chunk, ); last.truncate_trailing_line_break(); - (rest, last, last_summary) + (rest, last, last_byte_len) } #[inline] diff --git a/src/tree/traits.rs b/src/tree/traits.rs index 89c94c2..b0d7bbc 100644 --- a/src/tree/traits.rs +++ b/src/tree/traits.rs @@ -15,6 +15,11 @@ pub trait Summary: { /// The leaf type this summary is for. type Leaf: Leaf; + + #[inline] + fn measure>(&self) -> M { + M::measure(self) + } } pub trait Leaf: Debug + Sized { @@ -162,7 +167,7 @@ pub trait UnitMetric: Metric { /// `first_slice`'s summary. fn first_unit<'a>( slice: L::Slice<'a>, - ) -> (L::Slice<'a>, L::Slice<'a>, L::Summary); + ) -> (L::Slice<'a>, L::Slice<'a>, L::BaseMetric); } /// Allows iterating backward over the units of this metric. @@ -176,7 +181,7 @@ pub trait DoubleEndedUnitMetric: UnitMetric { /// summary. fn last_unit<'a>( slice: L::Slice<'a>, - ) -> (L::Slice<'a>, L::Slice<'a>, L::Summary); + ) -> (L::Slice<'a>, L::Slice<'a>, L::BaseMetric); /// It's possible for a leaf slice to contain some content that extends /// past the end of its last `M`-unit. This is referred to as "the diff --git a/src/tree/units.rs b/src/tree/units.rs index 954c52c..cef34e3 100644 --- a/src/tree/units.rs +++ b/src/tree/units.rs @@ -5,6 +5,7 @@ use super::traits::{ Leaf, LeafSlice, Metric, + Summary, UnitMetric, }; use super::tree_slice; @@ -113,7 +114,7 @@ impl<'a, const ARITY: usize, L: Leaf, M: UnitMetric> Iterator let (tree_slice, advance) = if iter.start_slice.measure::() > M::zero() { iter.next_unit_in_leaf() - } else if iter.units_total > iter.units_yielded { + } else if iter.units_yielded < iter.units_total { iter.next_unit_in_range() } else if iter.base_total > iter.base_yielded { let (remainder, advance) = iter.remainder(); @@ -132,12 +133,10 @@ impl<'a, const ARITY: usize, L: Leaf, M: UnitMetric> Iterator return None; }; - debug_assert_eq!(M::measure(&advance), M::one()); - - iter.base_yielded += L::BaseMetric::measure(&advance); + iter.base_yielded += advance; iter.units_yielded += M::one(); - Some((tree_slice, L::BaseMetric::measure(&advance))) + Some((tree_slice, advance)) } } @@ -157,14 +156,8 @@ impl> if iter.base_remaining > L::BaseMetric::zero() { if let Some((remainder, advance)) = iter.remainder() { - debug_assert_eq!(M::measure(&advance), M::zero()); - - iter.base_remaining -= L::BaseMetric::measure(&advance); - - return Some(( - remainder, - L::BaseMetric::measure(&advance), - )); + iter.base_remaining -= advance; + return Some((remainder, advance)); } } } @@ -181,12 +174,10 @@ impl> return None; }; - debug_assert_eq!(M::measure(&advance), M::one()); - - iter.base_remaining -= L::BaseMetric::measure(&advance); + iter.base_remaining -= advance; iter.units_remaining -= M::one(); - Some((tree_slice, L::BaseMetric::measure(&advance))) + Some((tree_slice, advance)) } } @@ -410,7 +401,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { /// should only be called when `self.start_slice` has an `M`-measure of at /// least `M::one()`. #[inline] - fn next_unit_in_leaf(&mut self) -> (TreeSlice<'a, N, L>, L::Summary) { + fn next_unit_in_leaf(&mut self) -> (TreeSlice<'a, N, L>, L::BaseMetric) { debug_assert_ne!(self.start_slice.measure::(), M::zero()); debug_assert!(self.units_total > self.units_yielded); @@ -565,7 +556,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { /// when the `TreeSlice` is not totally contained in `self.leaf_node` and /// it's not the remainder. #[inline] - fn next_unit_in_range(&mut self) -> (TreeSlice<'a, N, L>, L::Summary) { + fn next_unit_in_range(&mut self) -> (TreeSlice<'a, N, L>, L::BaseMetric) { debug_assert_eq!(self.start_slice.measure::(), M::zero()); debug_assert!(self.units_total > self.units_yielded); @@ -611,7 +602,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { self.yielded_in_leaf = advance.clone(); self.start_slice = rest; - advance += &summary; + advance += L::BaseMetric::measure(&summary); if !end_slice.is_empty() { summary += end_slice.summarize(); @@ -1196,14 +1187,14 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> /// /// - by [`Self::previous()`] when there's one final unit to yield. #[inline] - fn first(&mut self) -> (TreeSlice<'a, N, L>, L::Summary) { + fn first(&mut self) -> (TreeSlice<'a, N, L>, L::BaseMetric) { debug_assert!(self.base_remaining > L::BaseMetric::zero()); let (_, end_slice, mut advance) = M::last_unit(self.end_slice); // First, check if the current leaf node is the root. If it is we're // done. - if self.base_remaining == L::BaseMetric::measure(&advance) { + if self.base_remaining == advance { return ( TreeSlice { root: self.leaf_node, @@ -1226,7 +1217,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> if end_slice.is_empty() { let previous_leaf = self.previous_leaf(); - self.base_remaining -= L::BaseMetric::measure(&advance); + self.base_remaining -= advance; let contains_first_slice = previous_leaf.base_measure() > self.base_remaining; @@ -1241,7 +1232,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> let (first, first_advance) = self.first(); - self.base_remaining += L::BaseMetric::measure(&advance); + self.base_remaining += advance; advance += first_advance; @@ -1250,7 +1241,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> let (first_leaf, root, after, mut summary) = self.first_leaf(); - advance += &summary; + advance += L::BaseMetric::measure(&summary); summary += end_slice.summarize(); @@ -1261,7 +1252,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> let start_summary = start_slice.summarize(); - advance += &start_summary; + advance += L::BaseMetric::measure(&start_summary); summary += start_summary; @@ -1274,7 +1265,9 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> /// correctly `self.end_slice` cannot have any `M`-remainder and it needs /// to contain at least 2 `M`-units. #[inline] - fn previous_unit_in_leaf(&mut self) -> (TreeSlice<'a, N, L>, L::Summary) { + fn previous_unit_in_leaf( + &mut self, + ) -> (TreeSlice<'a, N, L>, L::BaseMetric) { debug_assert!(self.end_slice.measure::() > M::one()); debug_assert!(self.units_remaining > M::zero()); @@ -1440,7 +1433,9 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> /// /// [1]: UnitsBackward::previous_leaf_with_measure() #[inline] - fn previous_unit_in_range(&mut self) -> (TreeSlice<'a, N, L>, L::Summary) { + fn previous_unit_in_range( + &mut self, + ) -> (TreeSlice<'a, N, L>, L::BaseMetric) { debug_assert!(self.units_remaining > M::zero()); let (_, end_slice, mut advance) = M::last_unit(self.end_slice); @@ -1455,7 +1450,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> if end_slice.is_empty() { let previous_leaf = self.previous_leaf(); - self.base_remaining -= L::BaseMetric::measure(&advance); + self.base_remaining -= advance; let contains_first_slice = previous_leaf.base_measure() > self.base_remaining; @@ -1483,12 +1478,12 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> end_slice: empty, summary: L::Summary::default(), }, - L::Summary::default(), + L::BaseMetric::zero(), ) }, }; - self.base_remaining += L::BaseMetric::measure(&advance); + self.base_remaining += advance; advance += slice_advance; @@ -1501,14 +1496,13 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> let is_immediately_before = L::BaseMetric::measure(&summary) == L::BaseMetric::zero(); - advance += &summary; + advance += summary.measure::(); summary += end_slice.summarize(); let slice = { - let contains_first_slice = L::BaseMetric::measure(&advance) - + leaf.base_measure() - > self.base_remaining; + let contains_first_slice = + advance + leaf.base_measure() > self.base_remaining; if contains_first_slice { self.first_slice.take().unwrap() @@ -1521,7 +1515,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> let start_summary = start_slice.summarize(); - advance += &start_summary; + advance += L::BaseMetric::measure(&start_summary); let mut offset = root.summary() - after - &self.yielded_in_leaf - &advance; @@ -1588,7 +1582,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> /// It also follows that if `M` is the `BaseMetric` this function will /// always return `None`. #[inline] - fn remainder(&mut self) -> Option<(TreeSlice<'a, N, L>, L::Summary)> { + fn remainder(&mut self) -> Option<(TreeSlice<'a, N, L>, L::BaseMetric)> { debug_assert!(self.base_remaining > L::BaseMetric::zero()); if self.end_slice.measure::() > M::zero() { @@ -1608,7 +1602,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> end_slice: slice, summary: summary.clone(), }, - summary, + slice.base_measure(), )) } else { None From e8571421890cd95c8ff8d133940d85feac7aedc6 Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Wed, 13 Aug 2025 14:08:48 +0200 Subject: [PATCH 04/35] WIP --- src/rope/rope_slice.rs | 4 ++-- src/tree/tree_slice.rs | 17 ++++------------- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/rope/rope_slice.rs b/src/rope/rope_slice.rs index fc8c7d2..9df0300 100644 --- a/src/rope/rope_slice.rs +++ b/src/rope/rope_slice.rs @@ -7,7 +7,7 @@ use super::metrics::{ByteMetric, RawLineMetric}; use super::rope::RopeChunk; use super::utils::{panic_messages as panic, *}; use crate::range_bounds_to_start_end; -use crate::tree::TreeSlice; +use crate::tree::{LeafSlice, TreeSlice}; /// An immutable slice of a [`Rope`](crate::Rope). #[derive(Copy, Clone)] @@ -588,7 +588,7 @@ impl<'a> RopeSlice<'a> { let slice = &mut self.tree_slice; // The last slice only contains one byte so we have to re-slice. - if slice.end_summary().bytes() == 1 { + if slice.end_slice.base_measure() == ByteMetric(1) { *self = self.byte_slice(..self.byte_len() - 1); } // The last slice contains more than 2 bytes so we can just mutate diff --git a/src/tree/tree_slice.rs b/src/tree/tree_slice.rs index 4b7581b..bd4dd44 100644 --- a/src/tree/tree_slice.rs +++ b/src/tree/tree_slice.rs @@ -63,7 +63,8 @@ impl<'a, const ARITY: usize, L: Leaf> TreeSlice<'a, ARITY, L> { if self.leaf_count() == 2 { assert_eq!( self.summary, - self.start_summary() + self.end_summary() + self.start_slice.summarize() + + self.end_slice.summarize() ); } @@ -127,11 +128,6 @@ impl<'a, const ARITY: usize, L: Leaf> TreeSlice<'a, ARITY, L> { self.end_slice } - #[inline] - pub fn end_summary(&self) -> L::Summary { - self.end_slice.summarize() - } - /// Returns the leaf containing the `measure`-th unit of the `M`-metric, /// plus the `M`-measure of all the leaves before it. #[inline] @@ -141,11 +137,11 @@ impl<'a, const ARITY: usize, L: Leaf> TreeSlice<'a, ARITY, L> { { debug_assert!(measure <= self.measure::() + M::one()); - if M::measure(&self.start_summary()) >= measure { + if self.start_slice.measure::() >= measure { (self.start_slice, M::zero()) } else { let all_minus_last = - M::measure(&self.summary) - M::measure(&self.end_summary()); + M::measure(&self.summary) - self.end_slice.measure::(); if all_minus_last >= measure { let (leaf, mut offset) = self @@ -196,11 +192,6 @@ impl<'a, const ARITY: usize, L: Leaf> TreeSlice<'a, ARITY, L> { self.start_slice } - #[inline] - pub fn start_summary(&self) -> L::Summary { - self.start_slice.summarize() - } - #[inline] pub fn summary(&self) -> &L::Summary { &self.summary From 0d515c37e9ec289beb8241661d4f0766bde04827 Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Wed, 13 Aug 2025 16:46:26 +0200 Subject: [PATCH 05/35] WIP --- src/tree/tree.rs | 2 +- src/tree/tree_slice.rs | 98 +++++++++++++++--------------------------- 2 files changed, 35 insertions(+), 65 deletions(-) diff --git a/src/tree/tree.rs b/src/tree/tree.rs index 168cf33..e523cef 100644 --- a/src/tree/tree.rs +++ b/src/tree/tree.rs @@ -200,7 +200,7 @@ impl Tree { debug_assert!(range.start <= range.end); debug_assert!(range.end <= self.measure::() + M::one()); - TreeSlice::from_range_in_root(&self.root, range) + TreeSlice::slice_node(&self.root, range.start, range.end) } #[inline] diff --git a/src/tree/tree_slice.rs b/src/tree/tree_slice.rs index bd4dd44..3ebc26c 100644 --- a/src/tree/tree_slice.rs +++ b/src/tree/tree_slice.rs @@ -204,75 +204,38 @@ where { #[track_caller] #[inline] - pub(super) fn from_range_in_root( - root: &'a Arc>, - range: Range, - ) -> Self + pub fn slice(self, range: Range) -> Self where M: SlicingMetric, L::BaseMetric: SlicingMetric, { debug_assert!(M::zero() <= range.start); debug_assert!(range.start <= range.end); - debug_assert!(range.end <= root.measure::() + M::one()); + debug_assert!(range.end <= self.measure::() + M::one()); - if range.end <= root.measure::() { - Self::slice_impl(root, range.start, range.end) - } else if range.start <= root.measure::() { - Self::slice_impl(root, range.start, root.base_measure()) + if range.end < self.measure::() + M::one() { + Self::slice_node_at_offset( + self.root, + self.offset.measure(), + range.start, + range.end, + ) } else { - Self::slice_impl(root, root.base_measure(), root.base_measure()) + Self::slice_node_at_offset( + self.root, + self.offset.measure(), + range.start, + self.base_measure(), + ) } } - #[track_caller] #[inline] - pub fn slice(self, mut range: Range) -> Self + pub fn units(&self) -> Units<'a, ARITY, L, M> where - M: SlicingMetric, - L::BaseMetric: SlicingMetric, + M: Metric, { - debug_assert!(M::zero() <= range.start); - debug_assert!(range.start <= range.end); - debug_assert!(range.end <= self.measure::() + M::one()); - - match ( - range.start > M::zero(), - range.end < self.measure::() + M::one(), - ) { - (true, true) => { - range.start += M::measure(&self.offset); - range.end += M::measure(&self.offset); - Self::from_range_in_root(self.root, range) - }, - - (true, false) if range.start < self.measure::() + M::one() => { - let start = M::measure(&self.offset) + range.start; - let end = - L::BaseMetric::measure(&self.offset) + self.base_measure(); - Self::slice_impl(self.root, start, end) - }, - - (true, false) => { - let start = - L::BaseMetric::measure(&self.offset) + self.base_measure(); - let end = start; - Self::slice_impl(self.root, start, end) - }, - - (false, true) if range.end > M::zero() => { - let start = L::BaseMetric::measure(&self.offset); - let end = M::measure(&self.offset) + range.end; - Self::slice_impl(self.root, start, end) - }, - - (false, true) => { - let start = L::BaseMetric::measure(&self.offset); - Self::slice_impl(self.root, start, start) - }, - - (false, false) => self, - } + Units::from(self) } /// Returns the `TreeSlice` obtained by slicing `root` between `start` and @@ -287,8 +250,23 @@ where /// condition is not met. #[track_caller] #[inline] - fn slice_impl( + pub(super) fn slice_node( + root: &'a Arc>, + start: S, + end: E, + ) -> Self + where + S: SlicingMetric, + E: SlicingMetric, + { + Self::slice_node_at_offset(root, L::BaseMetric::zero(), start, end) + } + + #[track_caller] + #[inline] + fn slice_node_at_offset( root: &'a Arc>, + _offset: L::BaseMetric, start: S, end: E, ) -> Self @@ -337,14 +315,6 @@ where slice } - - #[inline] - pub fn units(&self) -> Units<'a, ARITY, L, M> - where - M: Metric, - { - Units::from(self) - } } impl PartialEq for SliceLeafCount { From 4545bbb9a7f466201882046ae01b615e4e072646 Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Wed, 13 Aug 2025 16:51:08 +0200 Subject: [PATCH 06/35] WIP --- src/tree/node.rs | 26 ++++++++++++++++++++++++++ src/tree/tree_slice.rs | 13 +++++-------- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/src/tree/node.rs b/src/tree/node.rs index 2d8722a..462f6c2 100644 --- a/src/tree/node.rs +++ b/src/tree/node.rs @@ -116,6 +116,20 @@ impl Node { } } + #[track_caller] + #[inline] + pub(super) fn convert_measure_from_offset( + &self, + _start_from: L::BaseMetric, + _up_to: M1, + ) -> M2 + where + M1: SlicingMetric, + M2: Metric, + { + todo!(); + } + #[inline] pub(super) fn depth(&self) -> usize { match self { @@ -215,6 +229,18 @@ impl Node { } } + #[inline] + pub(super) fn leaf_at_measure_from_offset( + &self, + _start_from: L::BaseMetric, + _measure: M, + ) -> (L::Slice<'_>, M) + where + M: Metric, + { + todo!(); + } + #[inline] pub(super) fn measure(&self) -> M where diff --git a/src/tree/tree_slice.rs b/src/tree/tree_slice.rs index 3ebc26c..5f3609c 100644 --- a/src/tree/tree_slice.rs +++ b/src/tree/tree_slice.rs @@ -117,9 +117,7 @@ impl<'a, const ARITY: usize, L: Leaf> TreeSlice<'a, ARITY, L> { if up_to == M1::zero() { M2::zero() } else { - self.root - .convert_measure::(M1::measure(&self.offset) + up_to) - - M2::measure(&self.offset) + self.root.convert_measure_from_offset(self.offset.measure(), up_to) } } @@ -144,11 +142,10 @@ impl<'a, const ARITY: usize, L: Leaf> TreeSlice<'a, ARITY, L> { M::measure(&self.summary) - self.end_slice.measure::(); if all_minus_last >= measure { - let (leaf, mut offset) = self - .root - .leaf_at_measure(M::measure(&self.offset) + measure); - offset -= M::measure(&self.offset); - (leaf, offset) + self.root.leaf_at_measure_from_offset( + self.offset.measure(), + measure, + ) } else { (self.end_slice, all_minus_last) } From 96ca83b6848912672c25491078f9067085015347 Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Wed, 13 Aug 2025 17:09:36 +0200 Subject: [PATCH 07/35] WIP --- src/tree/leaves.rs | 7 ++- src/tree/traits.rs | 5 ++ src/tree/tree.rs | 2 +- src/tree/tree_slice.rs | 54 ++++++++------------- src/tree/units.rs | 106 ++++++++++++++++++++--------------------- 5 files changed, 83 insertions(+), 91 deletions(-) diff --git a/src/tree/leaves.rs b/src/tree/leaves.rs index daa6e4b..5f1a4c6 100644 --- a/src/tree/leaves.rs +++ b/src/tree/leaves.rs @@ -164,7 +164,7 @@ impl<'a, const ARITY: usize, L: Leaf> From<&TreeSlice<'a, ARITY, L>> #[inline] fn from(slice: &TreeSlice<'a, ARITY, L>) -> LeavesForward<'a, ARITY, L> { Self { - base_offset: L::BaseMetric::measure(&slice.offset), + base_offset: slice.offset, first_slice: Some(slice.start_slice), last_slice: Some(slice.end_slice), root: &**slice.root(), @@ -381,9 +381,8 @@ impl<'a, const ARITY: usize, L: Leaf> From<&TreeSlice<'a, ARITY, L>> { #[inline] fn from(slice: &TreeSlice<'a, ARITY, L>) -> LeavesBackward<'a, ARITY, L> { - let base_offset = slice.root().base_measure() - - L::BaseMetric::measure(&slice.offset) - - slice.base_measure(); + let base_offset = + slice.root().base_measure() - slice.offset - slice.base_measure(); Self { base_offset, diff --git a/src/tree/traits.rs b/src/tree/traits.rs index b0d7bbc..88211f8 100644 --- a/src/tree/traits.rs +++ b/src/tree/traits.rs @@ -16,6 +16,11 @@ pub trait Summary: /// The leaf type this summary is for. type Leaf: Leaf; + #[inline] + fn base_measure(&self) -> ::BaseMetric { + self.measure() + } + #[inline] fn measure>(&self) -> M { M::measure(self) diff --git a/src/tree/tree.rs b/src/tree/tree.rs index e523cef..9365418 100644 --- a/src/tree/tree.rs +++ b/src/tree/tree.rs @@ -303,7 +303,7 @@ mod from_treeslice { // node. let mut children = slice.root().get_internal().children().iter(); - let start = L::BaseMetric::measure(&slice.offset); + let start = slice.offset; for child in children.by_ref() { let this = child.base_measure(); diff --git a/src/tree/tree_slice.rs b/src/tree/tree_slice.rs index 5f3609c..5e35b81 100644 --- a/src/tree/tree_slice.rs +++ b/src/tree/tree_slice.rs @@ -9,9 +9,9 @@ pub struct TreeSlice<'a, const ARITY: usize, L: Leaf> { /// [`start_slice`](Self::start_slice) and [`end_slice`](Self::end_slice). pub(super) root: &'a Arc>, - /// The summary of the subtree under [`root`](Self::root) up to the start - /// of the [`start_slice`](Self::start_slice). - pub(super) offset: L::Summary, + /// The base length of the subtree under [`root`](Self::root) up to the + /// start of the [`start_slice`](Self::start_slice). + pub(super) offset: L::BaseMetric, /// The total summary of this slice. pub(crate) summary: L::Summary, @@ -73,7 +73,7 @@ impl<'a, const ARITY: usize, L: Leaf> TreeSlice<'a, ARITY, L> { // deepest node that contains both. let (root, remove_offset) = { - let start = L::BaseMetric::measure(&self.offset); + let start = self.offset; deepest_node_containing_base_range( self.root, start, @@ -86,10 +86,7 @@ impl<'a, const ARITY: usize, L: Leaf> TreeSlice<'a, ARITY, L> { assert!(Arc::ptr_eq(self.root, root)); assert_eq!(self.root.depth(), root.depth()); - assert_eq!( - L::BaseMetric::measure(&remove_offset), - L::BaseMetric::zero() - ); + assert!(remove_offset.is_zero()); }, Node::Leaf(leaf) => { @@ -117,7 +114,7 @@ impl<'a, const ARITY: usize, L: Leaf> TreeSlice<'a, ARITY, L> { if up_to == M1::zero() { M2::zero() } else { - self.root.convert_measure_from_offset(self.offset.measure(), up_to) + self.root.convert_measure_from_offset(self.offset, up_to) } } @@ -142,10 +139,7 @@ impl<'a, const ARITY: usize, L: Leaf> TreeSlice<'a, ARITY, L> { M::measure(&self.summary) - self.end_slice.measure::(); if all_minus_last >= measure { - self.root.leaf_at_measure_from_offset( - self.offset.measure(), - measure, - ) + self.root.leaf_at_measure_from_offset(self.offset, measure) } else { (self.end_slice, all_minus_last) } @@ -213,14 +207,14 @@ where if range.end < self.measure::() + M::one() { Self::slice_node_at_offset( self.root, - self.offset.measure(), + self.offset, range.start, range.end, ) } else { Self::slice_node_at_offset( self.root, - self.offset.measure(), + self.offset, range.start, self.base_measure(), ) @@ -279,7 +273,7 @@ where let mut slice = Self { root, - offset: L::Summary::default(), + offset: L::BaseMetric::zero(), summary: L::Summary::default(), start_slice: Default::default(), end_slice: Default::default(), @@ -298,7 +292,7 @@ where ); if recompute_root { - let start = L::BaseMetric::measure(&slice.offset); + let start = slice.offset; let (root, offset) = deepest_node_containing_base_range( slice.root, @@ -401,48 +395,42 @@ where /// should always be zero), resulting in a potentially deeper node than the one /// returned by [`deepest_node_containing_range`]. /// -/// Also returns the summary between the input `node` and the returned node. +/// Also returns the base length between the input `node` and the returned node. #[inline] pub(super) fn deepest_node_containing_base_range( mut node: &Arc>, mut start: L::BaseMetric, mut end: L::BaseMetric, -) -> (&Arc>, L::Summary) +) -> (&Arc>, L::BaseMetric) where L: Leaf, { - let mut offset = L::Summary::default(); + let mut offset = L::BaseMetric::zero(); 'outer: loop { match &**node { Node::Internal(inode) => { - let mut measured = L::Summary::default(); + let mut measured = L::BaseMetric::zero(); for child in inode.children() { - let child_summary = child.summary(); + let child_len = child.base_measure(); - let contains_start_slice = - L::BaseMetric::measure(&measured) - + L::BaseMetric::measure(&child_summary) - > start; + let contains_start_slice = measured + child_len > start; if contains_start_slice { - let contains_end_slice = - L::BaseMetric::measure(&measured) - + L::BaseMetric::measure(&child_summary) - >= end; + let contains_end_slice = measured + child_len >= end; if contains_end_slice { node = child; - start -= L::BaseMetric::measure(&measured); - end -= L::BaseMetric::measure(&measured); + start -= measured; + end -= measured; offset += measured; continue 'outer; } else { return (node, offset); } } else { - measured += child_summary; + measured += child_len; } } diff --git a/src/tree/units.rs b/src/tree/units.rs index cef34e3..a10ecb6 100644 --- a/src/tree/units.rs +++ b/src/tree/units.rs @@ -200,8 +200,8 @@ struct UnitsForward<'a, const N: usize, L: Leaf, M: Metric> { /// The current leaf node. leaf_node: &'a Arc>, - /// How much of `leaf_node`'s summary has already been yielded. - yielded_in_leaf: L::Summary, + /// How much of `leaf_node`'s base length has already been yielded. + yielded_in_leaf: L::BaseMetric, /// The `start_slice` field of the next `TreeSlice` that'll be returned by /// [`next`](Self::next()). @@ -255,7 +255,7 @@ where is_initialized: false, path: Vec::with_capacity(tree.root().depth()), leaf_node: tree.root(), - yielded_in_leaf: L::Summary::default(), + yielded_in_leaf: L::BaseMetric::zero(), start_slice: L::Slice::default(), first_slice: None, last_slice: None, @@ -281,11 +281,11 @@ where is_initialized: false, path: Vec::with_capacity(tree_slice.root().depth()), leaf_node: tree_slice.root(), - yielded_in_leaf: L::Summary::default(), + yielded_in_leaf: L::BaseMetric::zero(), start_slice: L::Slice::default(), first_slice: Some(tree_slice.start_slice), last_slice: Some(tree_slice.end_slice), - base_start: L::BaseMetric::measure(&tree_slice.offset), + base_start: tree_slice.offset, base_yielded: L::BaseMetric::zero(), base_total: tree_slice.base_measure(), units_yielded: M::zero(), @@ -334,7 +334,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { match self.first_slice.take() { Some(slice) => { self.yielded_in_leaf = - leaf.summarize() - slice.summarize(); + leaf.base_measure() - slice.base_measure(); self.start_slice = slice; }, @@ -407,9 +407,9 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { let (slice, rest, advance) = M::first_unit(self.start_slice); - let offset = self.yielded_in_leaf.clone(); + let offset = self.yielded_in_leaf; - self.yielded_in_leaf += &advance; + self.yielded_in_leaf += advance; self.start_slice = rest; ( @@ -454,10 +454,10 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { #[inline] fn next_leaf_with_measure( &mut self, - ) -> (&'a L, &'a Arc>, L::Summary, L::Summary) { + ) -> (&'a L, &'a Arc>, L::BaseMetric, L::Summary) { debug_assert!(self.units_total > self.units_yielded); - let mut before = L::Summary::default(); + let mut before = L::BaseMetric::zero(); let mut summary = L::Summary::default(); // Step 1: pop nodes off the path until we find a node with some @@ -470,7 +470,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { let inode = node.get_internal(); for child in &inode.children()[..child_idx] { - before += child.summary(); + before += child.base_measure(); } for (idx, child) in @@ -564,7 +564,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { // slice empty. If it is we move to the next leaf before continuing. if self.start_slice.is_empty() { let leaf_slice = self.next_leaf(); - self.yielded_in_leaf = L::Summary::default(); + self.yielded_in_leaf = L::BaseMetric::zero(); self.start_slice = leaf_slice; if self.start_slice.measure::() > M::zero() { @@ -579,9 +579,9 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { self.next_leaf_with_measure(); let is_immediately_after = - L::BaseMetric::measure(&summary) == L::BaseMetric::zero(); + summary.measure::().is_zero(); - offset += &self.yielded_in_leaf; + offset += self.yielded_in_leaf; summary += start_summary; let slice = { @@ -618,10 +618,10 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { if is_immediately_after { root = previous_leaf; - offset = root.summary() - &summary; + offset = root.base_measure() - summary.base_measure(); end_slice = start_slice; } else { - let start = L::BaseMetric::measure(&offset); + let start = offset; let (new_root, remove_offset) = tree_slice::deepest_node_containing_base_range( @@ -660,7 +660,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { #[inline] fn last_leaf( &self, - ) -> (&'a L, &'a Arc>, L::Summary, L::Summary) { + ) -> (&'a L, &'a Arc>, L::BaseMetric, L::Summary) { // Step 1: find the index of deepest node in the path that fully // contains `range`. @@ -709,7 +709,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { // Step 2: traverse down the path starting from the node after the // root, increasing `before` and `summary` as you go. - let mut before = L::Summary::default(); + let mut before = L::BaseMetric::zero(); let mut summary = L::Summary::default(); for &(node, child_idx) in &self.path[root_idx + 1..] { @@ -717,7 +717,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { let inode = node.get_internal(); for child in &inode.children()[..child_idx] { - before += child.summary(); + before += child.base_measure(); } for child in &inode.children()[child_idx + 1..] { @@ -733,9 +733,8 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { let mut offset = L::BaseMetric::zero(); for child in &inode.children()[..child_idx] { - let child_summary = child.summary(); - offset += L::BaseMetric::measure(&child_summary); - before += child_summary; + offset += child.base_measure(); + before += child.base_measure(); } offset += inode.child(child_idx).base_measure(); @@ -796,7 +795,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { if self.start_slice.is_empty() { let next_slice = self.next_leaf(); - self.yielded_in_leaf = L::Summary::default(); + self.yielded_in_leaf = L::BaseMetric::zero(); self.start_slice = next_slice; } @@ -833,7 +832,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { summary += end_slice.summarize(); - before += &self.yielded_in_leaf; + before += self.yielded_in_leaf; let advance = summary.clone(); @@ -864,8 +863,8 @@ struct UnitsBackward<'a, const N: usize, L: Leaf, M: Metric> { /// The current leaf node. leaf_node: &'a Arc>, - /// How much of `leaf_node`'s base measure has already been yielded. - yielded_in_leaf: L::Summary, + /// How much of `leaf_node`'s base length has already been yielded. + yielded_in_leaf: L::BaseMetric, /// The `end_slice` of the next `TreeSlice` that'll be returned by /// [`previous`](Self::previous()). @@ -913,7 +912,7 @@ where is_initialized: false, path: Vec::with_capacity(tree.root().depth()), leaf_node: tree.root(), - yielded_in_leaf: L::Summary::default(), + yielded_in_leaf: L::BaseMetric::zero(), end_slice: L::Slice::default(), first_slice: None, last_slice: None, @@ -937,11 +936,11 @@ where is_initialized: false, path: Vec::with_capacity(tree_slice.root().depth()), leaf_node: tree_slice.root(), - yielded_in_leaf: L::Summary::default(), + yielded_in_leaf: L::BaseMetric::zero(), end_slice: L::Slice::default(), first_slice: Some(tree_slice.start_slice), last_slice: Some(tree_slice.end_slice), - base_start: L::BaseMetric::measure(&tree_slice.offset), + base_start: tree_slice.offset, base_remaining: tree_slice.base_measure(), units_remaining: tree_slice.measure::(), } @@ -993,7 +992,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> match self.last_slice.take() { Some(slice) => { self.yielded_in_leaf = - leaf.summarize() - slice.summarize(); + leaf.base_measure() - slice.base_measure(); self.end_slice = slice; }, @@ -1059,7 +1058,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> #[inline] fn first_leaf( &self, - ) -> (&'a L, &'a Arc>, L::Summary, L::Summary) { + ) -> (&'a L, &'a Arc>, L::BaseMetric, L::Summary) { // Step 1: find the index of deepest node in the path that fully // contains `range`. @@ -1104,7 +1103,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> // Step 2: traverse down the path starting from the node after the // root, increasing `after` and `summary` as you go. - let mut after = L::Summary::default(); + let mut after = L::BaseMetric::zero(); let mut summary = L::Summary::default(); for &(node, child_idx) in &self.path[root_idx + 1..] { @@ -1116,7 +1115,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> } for child in &inode.children()[child_idx + 1..] { - after += child.summary(); + after += child.base_measure(); } } @@ -1126,7 +1125,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> let inode = root.get_internal(); for child in &inode.children()[child_idx + 1..] { - after += child.summary(); + after += child.base_measure(); } // This will be the child of the root node that contains the first @@ -1198,7 +1197,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> return ( TreeSlice { root: self.leaf_node, - offset: L::Summary::default(), + offset: L::BaseMetric::zero(), summary: end_slice.summarize(), start_slice: end_slice, end_slice, @@ -1228,7 +1227,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> self.end_slice = previous_leaf.as_slice(); }; - self.yielded_in_leaf = L::Summary::default(); + self.yielded_in_leaf = L::BaseMetric::zero(); let (first, first_advance) = self.first(); @@ -1256,7 +1255,8 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> summary += start_summary; - let offset = root.summary() - after - &self.yielded_in_leaf - &advance; + let offset = + root.base_measure() - after - self.yielded_in_leaf - advance; (TreeSlice { root, offset, start_slice, end_slice, summary }, advance) } @@ -1275,13 +1275,13 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> debug_assert!(!rest.is_empty()); - self.yielded_in_leaf += &advance; + self.yielded_in_leaf += advance; self.end_slice = rest; ( TreeSlice { root: self.leaf_node, - offset: rest.summarize(), + offset: rest.base_measure(), summary: slice.summarize(), end_slice: slice, start_slice: slice, @@ -1320,10 +1320,10 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> #[inline] fn previous_leaf_with_measure( &mut self, - ) -> (&'a L, &'a Arc>, L::Summary, L::Summary) { + ) -> (&'a L, &'a Arc>, L::BaseMetric, L::Summary) { debug_assert!(self.units_remaining > M::zero()); - let mut after = L::Summary::default(); + let mut after = L::BaseMetric::zero(); let mut summary = L::Summary::default(); // Step 1: pop nodes off the path until we find a node with some @@ -1336,7 +1336,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> let inode = node.get_internal(); for child in &inode.children()[child_idx + 1..] { - after += child.summary(); + after += child.base_measure(); } for (idx, child) in @@ -1461,7 +1461,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> self.end_slice = previous_leaf.as_slice(); }; - self.yielded_in_leaf = L::Summary::default(); + self.yielded_in_leaf = L::BaseMetric::zero(); let (slice, slice_advance) = match self.remainder() { Some(remainder) => remainder, @@ -1473,7 +1473,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> ( TreeSlice { root: self.leaf_node, - offset: self.leaf_node.summary(), + offset: self.leaf_node.base_measure(), start_slice: empty, end_slice: empty, summary: L::Summary::default(), @@ -1515,12 +1515,12 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> let start_summary = start_slice.summarize(); - advance += L::BaseMetric::measure(&start_summary); + advance += start_summary.base_measure(); let mut offset = - root.summary() - after - &self.yielded_in_leaf - &advance; + root.base_measure() - after - self.yielded_in_leaf - advance; - self.yielded_in_leaf = start_summary.clone(); + self.yielded_in_leaf = start_summary.base_measure(); self.end_slice = rest; if !start_slice.is_empty() { @@ -1536,16 +1536,16 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> if is_immediately_before { root = next_leaf; - offset = L::Summary::default(); + offset = L::BaseMetric::zero(); start_slice = end_slice; } else { - let start = L::BaseMetric::measure(&offset); + let start = offset; let (new_root, remove_offset) = tree_slice::deepest_node_containing_base_range( root, start, - start + L::BaseMetric::measure(&summary), + start + summary.base_measure(), ); root = new_root; @@ -1591,16 +1591,16 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> let summary = slice.summarize(); if !slice.is_empty() { - self.yielded_in_leaf += &summary; + self.yielded_in_leaf += summary.base_measure(); self.end_slice = rest; Some(( TreeSlice { root: self.leaf_node, - offset: rest.summarize(), + offset: rest.base_measure(), start_slice: slice, end_slice: slice, - summary: summary.clone(), + summary, }, slice.base_measure(), )) From 9d20a3aad003d937505362ebe0421adc2c81df9e Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Wed, 13 Aug 2025 17:22:43 +0200 Subject: [PATCH 08/35] Make `TreeSlice::offset` a `BaseMetric` instead of a full `Summary` --- src/tree/tree_slice.rs | 65 +++++++++++++++++++++--------------------- src/tree/units.rs | 16 +++-------- 2 files changed, 36 insertions(+), 45 deletions(-) diff --git a/src/tree/tree_slice.rs b/src/tree/tree_slice.rs index 5e35b81..68544ef 100644 --- a/src/tree/tree_slice.rs +++ b/src/tree/tree_slice.rs @@ -34,11 +34,7 @@ pub enum SliceLeafCount { impl Clone for TreeSlice<'_, ARITY, L> { #[inline] fn clone(&self) -> Self { - TreeSlice { - offset: self.offset.clone(), - summary: self.summary.clone(), - ..*self - } + TreeSlice { summary: self.summary.clone(), ..*self } } } @@ -286,6 +282,8 @@ where root, start, end, + &mut S::zero(), + &mut E::zero(), &mut recompute_root, &mut false, &mut false, @@ -462,11 +460,14 @@ where /// the other fields of the slice are valid. #[track_caller] #[inline] +#[allow(clippy::too_many_arguments)] fn build_slice<'a, const N: usize, L, S, E>( slice: &mut TreeSlice<'a, N, L>, node: &'a Arc>, start: S, end: E, + start_offset: &mut S, + end_offset: &mut E, recompute_root: &mut bool, found_start_slice: &mut bool, done: &mut bool, @@ -487,9 +488,7 @@ fn build_slice<'a, const N: usize, L, S, E>( let child_summary = child.summary(); if !*found_start_slice { - if S::measure(&slice.offset) + S::measure(&child_summary) - >= start - { + if *start_offset + child_summary.measure::() >= start { // This child contains the starting slice somewhere in // its subtree. Run this function again with this child // as the node. @@ -498,17 +497,21 @@ fn build_slice<'a, const N: usize, L, S, E>( child, start, end, + start_offset, + end_offset, recompute_root, found_start_slice, done, ); } else { // This child comes before the starting leaf. - slice.offset += child_summary; + slice.offset += child_summary.base_measure(); + *start_offset += child_summary.measure::(); + *end_offset += child_summary.measure::(); } - } else if E::measure(&slice.offset) - + E::measure(&slice.summary) - + E::measure(&child_summary) + } else if *end_offset + + slice.summary.measure::() + + child_summary.measure::() >= end { // This child contains the ending leaf somewhere in its @@ -519,6 +522,8 @@ fn build_slice<'a, const N: usize, L, S, E>( child, start, end, + start_offset, + end_offset, recompute_root, found_start_slice, done, @@ -537,9 +542,9 @@ fn build_slice<'a, const N: usize, L, S, E>( // This leaf must contain either the first slice, the last slice or // both. - let contains_end_slice = E::measure(&slice.offset) - + E::measure(&slice.summary) - + E::measure(&leaf_summary) + let contains_end_slice = *end_offset + + slice.summary.measure::() + + leaf_summary.measure::() >= end; if !*found_start_slice { @@ -548,27 +553,24 @@ fn build_slice<'a, const N: usize, L, S, E>( debug_assert!({ // If we haven't yet found the first slice this leaf must // contain it. - S::measure(&slice.offset) + S::measure(&leaf_summary) - >= start + *start_offset + leaf_summary.measure::() >= start }); if contains_end_slice { // The end of the range is also contained in this leaf // so the final slice only spans this single leaf. - let start = start - S::measure(&slice.offset); + let start = start - *start_offset; let right_slice = S::slice_from(leaf.as_slice(), start); let left_summary = leaf.summarize() - right_slice.summarize(); - let end = end - - E::measure(&slice.offset) - - E::measure(&left_summary); + let end = end - *end_offset - left_summary.measure::(); let start_slice = E::slice_up_to(right_slice, end); - slice.offset += left_summary; + slice.offset += left_summary.base_measure(); slice.summary = start_slice.summarize(); slice.start_slice = start_slice; slice.end_slice = start_slice; @@ -576,22 +578,21 @@ fn build_slice<'a, const N: usize, L, S, E>( *done = true; } else { // This leaf contains the first slice but not the last. - let start_slice = S::slice_from( - leaf.as_slice(), - start - S::measure(&slice.offset), - ); + let start_slice = + S::slice_from(leaf.as_slice(), start - *start_offset); if start_slice.is_empty() { - slice.offset += leaf.summarize(); + slice.offset += leaf.base_measure(); + *start_offset += leaf.measure::(); + *end_offset += leaf.measure::(); *recompute_root = true; return; } let start_summary = start_slice.summarize(); - let right_summary = leaf.summarize() - &start_summary; - - slice.offset += right_summary; + slice.offset += + leaf.base_measure() - start_summary.base_measure(); slice.summary += start_summary; slice.start_slice = start_slice; @@ -600,9 +601,7 @@ fn build_slice<'a, const N: usize, L, S, E>( } else { debug_assert!(contains_end_slice); - let end = end - - E::measure(&slice.offset) - - E::measure(&slice.summary); + let end = end - *end_offset - slice.summary.measure::(); // This leaf contains the last slice. let end_slice = E::slice_up_to(leaf.as_slice(), end); diff --git a/src/tree/units.rs b/src/tree/units.rs index a10ecb6..bcdc60f 100644 --- a/src/tree/units.rs +++ b/src/tree/units.rs @@ -236,11 +236,7 @@ impl> Clone { #[inline] fn clone(&self) -> Self { - Self { - path: self.path.clone(), - yielded_in_leaf: self.yielded_in_leaf.clone(), - ..*self - } + Self { path: self.path.clone(), ..*self } } } @@ -599,7 +595,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { let (mut end_slice, rest, mut advance) = M::first_unit(slice); - self.yielded_in_leaf = advance.clone(); + self.yielded_in_leaf = advance; self.start_slice = rest; advance += L::BaseMetric::measure(&summary); @@ -810,7 +806,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { return ( TreeSlice { root: self.leaf_node, - offset: self.yielded_in_leaf.clone(), + offset: self.yielded_in_leaf, start_slice: self.start_slice, end_slice: self.start_slice, summary, @@ -893,11 +889,7 @@ impl> Clone { #[inline] fn clone(&self) -> Self { - Self { - path: self.path.clone(), - yielded_in_leaf: self.yielded_in_leaf.clone(), - ..*self - } + Self { path: self.path.clone(), ..*self } } } From 4761777770cfe3727db8d4339f582839ec014079 Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Wed, 13 Aug 2025 17:30:50 +0200 Subject: [PATCH 09/35] Use `summary.measure::()` instead of `M::measure(&summary)` --- src/rope/gap_slice.rs | 4 ++-- src/tree/tree.rs | 2 +- src/tree/tree_slice.rs | 18 +++++++++--------- src/tree/units.rs | 34 +++++++++++++--------------------- 4 files changed, 25 insertions(+), 33 deletions(-) diff --git a/src/rope/gap_slice.rs b/src/rope/gap_slice.rs index 69baf4c..8923ccd 100644 --- a/src/rope/gap_slice.rs +++ b/src/rope/gap_slice.rs @@ -1,7 +1,7 @@ use super::gap_buffer::GapBuffer; use super::metrics::{ChunkSummary, SummaryUpTo, ToByteOffset}; use super::utils::{debug_no_quotes, panic_messages as panic}; -use crate::tree::{LeafSlice, Metric}; +use crate::tree::{LeafSlice, Metric, Summary}; /// A slice of a [`GapBuffer`](super::gap_buffer::GapBuffer). #[derive(Copy, Clone, Default)] @@ -86,7 +86,7 @@ impl<'a> GapSlice<'a> { where M: Metric, { - M::measure(&self.left_summary) + self.left_summary.measure::() } #[inline] diff --git a/src/tree/tree.rs b/src/tree/tree.rs index 9365418..77103cc 100644 --- a/src/tree/tree.rs +++ b/src/tree/tree.rs @@ -155,7 +155,7 @@ impl Tree { where M: Metric, { - M::measure(&self.summary()) + self.root.measure::() } /// Replaces a range of the `Tree` with the given replacement. diff --git a/src/tree/tree_slice.rs b/src/tree/tree_slice.rs index 68544ef..1f425a5 100644 --- a/src/tree/tree_slice.rs +++ b/src/tree/tree_slice.rs @@ -73,7 +73,7 @@ impl<'a, const ARITY: usize, L: Leaf> TreeSlice<'a, ARITY, L> { deepest_node_containing_base_range( self.root, start, - start + L::BaseMetric::measure(&self.summary), + start + self.summary.base_measure(), ) }; @@ -132,7 +132,7 @@ impl<'a, const ARITY: usize, L: Leaf> TreeSlice<'a, ARITY, L> { (self.start_slice, M::zero()) } else { let all_minus_last = - M::measure(&self.summary) - self.end_slice.measure::(); + self.summary.measure::() - self.end_slice.measure::(); if all_minus_last >= measure { self.root.leaf_at_measure_from_offset(self.offset, measure) @@ -295,7 +295,7 @@ where let (root, offset) = deepest_node_containing_base_range( slice.root, start, - start + L::BaseMetric::measure(&slice.summary), + start + slice.summary.base_measure(), ); slice.root = root; @@ -357,19 +357,19 @@ where for child in inode.children() { let child_summary = child.summary(); - let contains_start_slice = S::measure(&measured) - + S::measure(&child_summary) + let contains_start_slice = measured.measure::() + + child_summary.measure::() >= start; if contains_start_slice { - let contains_end_slice = E::measure(&measured) - + E::measure(&child_summary) + let contains_end_slice = measured.measure::() + + child_summary.measure::() >= end; if contains_end_slice { node = child; - start -= S::measure(&measured); - end -= E::measure(&measured); + start -= measured.measure::(); + end -= measured.measure::(); continue 'outer; } else { return (node, start, end); diff --git a/src/tree/units.rs b/src/tree/units.rs index bcdc60f..4988415 100644 --- a/src/tree/units.rs +++ b/src/tree/units.rs @@ -119,16 +119,13 @@ impl<'a, const ARITY: usize, L: Leaf, M: UnitMetric> Iterator } else if iter.base_total > iter.base_yielded { let (remainder, advance) = iter.remainder(); - debug_assert_eq!(M::measure(&advance), M::zero()); + debug_assert!(advance.is_zero()); - debug_assert_eq!( - L::BaseMetric::measure(&advance), - iter.base_total - iter.base_yielded - ); + debug_assert_eq!(advance, iter.base_total - iter.base_yielded); iter.base_yielded = iter.base_total; - return Some((remainder, L::BaseMetric::measure(&advance))); + return Some((remainder, advance)); } else { return None; }; @@ -582,7 +579,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { let slice = { let contains_last_slice = self.base_yielded - + L::BaseMetric::measure(&summary) + + summary.base_measure() + leaf.base_measure() > self.base_total; @@ -598,7 +595,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { self.yielded_in_leaf = advance; self.start_slice = rest; - advance += L::BaseMetric::measure(&summary); + advance += summary.base_measure(); if !end_slice.is_empty() { summary += end_slice.summarize(); @@ -623,7 +620,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { tree_slice::deepest_node_containing_base_range( root, start, - start + L::BaseMetric::measure(&summary), + start + summary.base_measure(), ); root = new_root; @@ -785,7 +782,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { /// iterate forward this only gets called when we are sure there's a /// remainder to yield. #[inline] - fn remainder(&mut self) -> (TreeSlice<'a, N, L>, L::Summary) { + fn remainder(&mut self) -> (TreeSlice<'a, N, L>, L::BaseMetric) { debug_assert_eq!(self.units_total, self.units_yielded); debug_assert!(self.base_total > self.base_yielded); @@ -799,19 +796,15 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { if self.base_total - self.base_yielded == self.start_slice.base_measure() { - let summary = self.start_slice.summarize(); - - let advance = summary.clone(); - return ( TreeSlice { root: self.leaf_node, offset: self.yielded_in_leaf, start_slice: self.start_slice, end_slice: self.start_slice, - summary, + summary: self.start_slice.summarize(), }, - advance, + self.start_slice.base_measure(), ); } @@ -830,7 +823,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { before += self.yielded_in_leaf; - let advance = summary.clone(); + let advance = summary.base_measure(); ( TreeSlice { @@ -1232,7 +1225,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> let (first_leaf, root, after, mut summary) = self.first_leaf(); - advance += L::BaseMetric::measure(&summary); + advance += summary.base_measure(); summary += end_slice.summarize(); @@ -1243,7 +1236,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> let start_summary = start_slice.summarize(); - advance += L::BaseMetric::measure(&start_summary); + advance += start_summary.base_measure(); summary += start_summary; @@ -1485,8 +1478,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> let (leaf, mut root, after, mut summary) = self.previous_leaf_with_measure(); - let is_immediately_before = - L::BaseMetric::measure(&summary) == L::BaseMetric::zero(); + let is_immediately_before = summary.base_measure().is_zero(); advance += summary.measure::(); From c2bf99407b0c0af288d13a2f2b58a42fe4285460 Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Wed, 13 Aug 2025 17:40:10 +0200 Subject: [PATCH 10/35] Reduce `Summary`'s requirements to `{Add,Sub}Assign` --- src/rope/metrics.rs | 17 ----------------- src/tree/inode.rs | 4 ++-- src/tree/traits.rs | 11 +---------- src/tree/tree.rs | 38 +------------------------------------- src/tree/tree_slice.rs | 17 ++++++++++------- 5 files changed, 14 insertions(+), 73 deletions(-) diff --git a/src/rope/metrics.rs b/src/rope/metrics.rs index 492b5c2..34a7565 100644 --- a/src/rope/metrics.rs +++ b/src/rope/metrics.rs @@ -94,13 +94,6 @@ impl AddAssign for ChunkSummary { } } -impl AddAssign<&Self> for ChunkSummary { - #[inline] - fn add_assign(&mut self, rhs: &Self) { - *self += *rhs; - } -} - impl Sub for ChunkSummary { type Output = Self; @@ -123,16 +116,6 @@ impl SubAssign for ChunkSummary { } } -impl Sub<&Self> for ChunkSummary { - type Output = Self; - - #[inline] - fn sub(mut self, rhs: &Self) -> Self { - self -= *rhs; - self - } -} - /// Conversion trait from the metric implement this trait to the corresponding /// byte offset. pub trait ToByteOffset: Metric { diff --git a/src/tree/inode.rs b/src/tree/inode.rs index 1a2b588..fc82a28 100644 --- a/src/tree/inode.rs +++ b/src/tree/inode.rs @@ -281,7 +281,7 @@ impl Inode { // remove the second child. if first.len() + second.len() <= Self::max_children() { first.children.append(&mut second.children); - first.summary += second.summary(); + first.summary += second.summary.clone(); self.children.remove(1); } // Move the minimum number of children from the second child @@ -346,7 +346,7 @@ impl Inode { // then remove the last child. if penultimate.len() + last.len() <= Self::max_children() { penultimate.children.append(&mut last.children); - penultimate.summary += last.summary(); + penultimate.summary += last.summary.clone(); self.children.remove(last_idx); } // Move the minimum number of children from the penultimate diff --git a/src/tree/traits.rs b/src/tree/traits.rs index 88211f8..c68bc28 100644 --- a/src/tree/traits.rs +++ b/src/tree/traits.rs @@ -2,16 +2,7 @@ use core::fmt::Debug; use core::ops::{Add, AddAssign, RangeBounds, Sub, SubAssign}; pub trait Summary: - Debug - + Default - + Clone - + Add - + AddAssign - + for<'a> AddAssign<&'a Self> - + Sub - + SubAssign - + for<'a> Sub<&'a Self, Output = Self> - + PartialEq + Debug + Default + Clone + AddAssign + SubAssign + PartialEq { /// The leaf type this summary is for. type Leaf: Leaf; diff --git a/src/tree/tree.rs b/src/tree/tree.rs index 77103cc..3d646bb 100644 --- a/src/tree/tree.rs +++ b/src/tree/tree.rs @@ -1548,7 +1548,7 @@ mod tree_replace { #[cfg(test)] mod tests { - use core::ops::{Add, AddAssign, Sub, SubAssign}; + use core::ops::{AddAssign, SubAssign}; use super::*; @@ -1558,39 +1558,13 @@ mod tests { leaves: usize, } - impl Add for UsizeSummary { - type Output = Self; - - #[inline] - fn add(mut self, rhs: Self) -> Self { - self += &rhs; - self - } - } - impl AddAssign for UsizeSummary { fn add_assign(&mut self, rhs: Self) { - *self += &rhs; - } - } - - impl AddAssign<&Self> for UsizeSummary { - fn add_assign(&mut self, rhs: &Self) { self.count += rhs.count; self.leaves += rhs.leaves; } } - impl Sub for UsizeSummary { - type Output = Self; - - #[inline] - fn sub(mut self, rhs: Self) -> Self { - self -= rhs; - self - } - } - impl SubAssign for UsizeSummary { fn sub_assign(&mut self, rhs: Self) { self.count -= rhs.count; @@ -1598,16 +1572,6 @@ mod tests { } } - impl Sub<&Self> for UsizeSummary { - type Output = Self; - - #[inline] - fn sub(mut self, rhs: &Self) -> Self { - self -= *rhs; - self - } - } - impl Summary for UsizeSummary { type Leaf = usize; } diff --git a/src/tree/tree_slice.rs b/src/tree/tree_slice.rs index 1f425a5..51c4452 100644 --- a/src/tree/tree_slice.rs +++ b/src/tree/tree_slice.rs @@ -58,9 +58,9 @@ impl<'a, const ARITY: usize, L: Leaf> TreeSlice<'a, ARITY, L> { if self.leaf_count() == 2 { assert_eq!( - self.summary, - self.start_slice.summarize() - + self.end_slice.summarize() + self.summary.base_measure(), + self.start_slice.base_measure() + + self.end_slice.base_measure() ); } @@ -563,14 +563,17 @@ fn build_slice<'a, const N: usize, L, S, E>( let right_slice = S::slice_from(leaf.as_slice(), start); - let left_summary = - leaf.summarize() - right_slice.summarize(); + let left_slice_end_measure = + leaf.measure::() - right_slice.measure::(); - let end = end - *end_offset - left_summary.measure::(); + let left_slice_base_measure = + leaf.base_measure() - right_slice.base_measure(); + + let end = end - *end_offset - left_slice_end_measure; let start_slice = E::slice_up_to(right_slice, end); - slice.offset += left_summary.base_measure(); + slice.offset += left_slice_base_measure; slice.summary = start_slice.summarize(); slice.start_slice = start_slice; slice.end_slice = start_slice; From 0501b7efc00ec82cbf36235f9d099dc673a08ec5 Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Wed, 13 Aug 2025 17:48:21 +0200 Subject: [PATCH 11/35] Relax `PartialEq`'s requirement for `Summary` --- src/tree/traits.rs | 7 ++++++- src/tree/tree_slice.rs | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/tree/traits.rs b/src/tree/traits.rs index c68bc28..a7ee572 100644 --- a/src/tree/traits.rs +++ b/src/tree/traits.rs @@ -2,7 +2,7 @@ use core::fmt::Debug; use core::ops::{Add, AddAssign, RangeBounds, Sub, SubAssign}; pub trait Summary: - Debug + Default + Clone + AddAssign + SubAssign + PartialEq + Debug + Default + Clone + AddAssign + SubAssign { /// The leaf type this summary is for. type Leaf: Leaf; @@ -12,6 +12,11 @@ pub trait Summary: self.measure() } + #[inline] + fn is_empty(&self) -> bool { + self.base_measure().is_zero() + } + #[inline] fn measure>(&self) -> M { M::measure(self) diff --git a/src/tree/tree_slice.rs b/src/tree/tree_slice.rs index 51c4452..38070d6 100644 --- a/src/tree/tree_slice.rs +++ b/src/tree/tree_slice.rs @@ -548,7 +548,7 @@ fn build_slice<'a, const N: usize, L, S, E>( >= end; if !*found_start_slice { - debug_assert_eq!(L::Summary::default(), slice.summary); + debug_assert!(slice.summary.is_empty()); debug_assert!({ // If we haven't yet found the first slice this leaf must From 42b50ee7d6e3b50986f0dcb444666d8569b6dbac Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Wed, 13 Aug 2025 17:50:52 +0200 Subject: [PATCH 12/35] Add `Summary::empty()` instead of bounding it to `Default` It's more explicit --- src/rope/metrics.rs | 5 +++++ src/tree/inode.rs | 2 +- src/tree/traits.rs | 6 +++--- src/tree/tree.rs | 5 +++++ src/tree/tree_slice.rs | 4 ++-- src/tree/units.rs | 10 +++++----- 6 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/rope/metrics.rs b/src/rope/metrics.rs index 34a7565..4f55e23 100644 --- a/src/rope/metrics.rs +++ b/src/rope/metrics.rs @@ -70,6 +70,11 @@ impl ChunkSummary { impl Summary for ChunkSummary { type Leaf = GapBuffer; + + #[inline] + fn empty() -> Self { + Self::default() + } } impl Add for ChunkSummary { diff --git a/src/tree/inode.rs b/src/tree/inode.rs index fc82a28..c66a942 100644 --- a/src/tree/inode.rs +++ b/src/tree/inode.rs @@ -502,7 +502,7 @@ impl Inode { Self { children: Vec::with_capacity(N), depth: 1, - summary: Default::default(), + summary: L::Summary::empty(), } } diff --git a/src/tree/traits.rs b/src/tree/traits.rs index a7ee572..7c6bafb 100644 --- a/src/tree/traits.rs +++ b/src/tree/traits.rs @@ -1,12 +1,12 @@ use core::fmt::Debug; use core::ops::{Add, AddAssign, RangeBounds, Sub, SubAssign}; -pub trait Summary: - Debug + Default + Clone + AddAssign + SubAssign -{ +pub trait Summary: Debug + Clone + AddAssign + SubAssign { /// The leaf type this summary is for. type Leaf: Leaf; + fn empty() -> Self; + #[inline] fn base_measure(&self) -> ::BaseMetric { self.measure() diff --git a/src/tree/tree.rs b/src/tree/tree.rs index 3d646bb..9c9f3d8 100644 --- a/src/tree/tree.rs +++ b/src/tree/tree.rs @@ -1574,6 +1574,11 @@ mod tests { impl Summary for UsizeSummary { type Leaf = usize; + + #[inline] + fn empty() -> Self { + UsizeSummary { count: 0, leaves: 0 } + } } impl Leaf for usize { diff --git a/src/tree/tree_slice.rs b/src/tree/tree_slice.rs index 38070d6..afce674 100644 --- a/src/tree/tree_slice.rs +++ b/src/tree/tree_slice.rs @@ -270,7 +270,7 @@ where let mut slice = Self { root, offset: L::BaseMetric::zero(), - summary: L::Summary::default(), + summary: L::Summary::empty(), start_slice: Default::default(), end_slice: Default::default(), }; @@ -352,7 +352,7 @@ where 'outer: loop { match &**node { Node::Internal(inode) => { - let mut measured = L::Summary::default(); + let mut measured = L::Summary::empty(); for child in inode.children() { let child_summary = child.summary(); diff --git a/src/tree/units.rs b/src/tree/units.rs index 4988415..1174636 100644 --- a/src/tree/units.rs +++ b/src/tree/units.rs @@ -451,7 +451,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { debug_assert!(self.units_total > self.units_yielded); let mut before = L::BaseMetric::zero(); - let mut summary = L::Summary::default(); + let mut summary = L::Summary::empty(); // Step 1: pop nodes off the path until we find a node with some // `M`-units that we haven't yielded yet. @@ -703,7 +703,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { // root, increasing `before` and `summary` as you go. let mut before = L::BaseMetric::zero(); - let mut summary = L::Summary::default(); + let mut summary = L::Summary::empty(); for &(node, child_idx) in &self.path[root_idx + 1..] { // Every node in the path is an internal node. @@ -1089,7 +1089,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> // root, increasing `after` and `summary` as you go. let mut after = L::BaseMetric::zero(); - let mut summary = L::Summary::default(); + let mut summary = L::Summary::empty(); for &(node, child_idx) in &self.path[root_idx + 1..] { // Every node in the path is an internal node. @@ -1309,7 +1309,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> debug_assert!(self.units_remaining > M::zero()); let mut after = L::BaseMetric::zero(); - let mut summary = L::Summary::default(); + let mut summary = L::Summary::empty(); // Step 1: pop nodes off the path until we find a node with some // `M`-units that we haven't yielded yet. @@ -1461,7 +1461,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> offset: self.leaf_node.base_measure(), start_slice: empty, end_slice: empty, - summary: L::Summary::default(), + summary: L::Summary::empty(), }, L::BaseMetric::zero(), ) From ad49c585db0ce661c4b2378897bc164c35fdaca3 Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Wed, 13 Aug 2025 17:52:52 +0200 Subject: [PATCH 13/35] Tweaks --- src/tree/traits.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tree/traits.rs b/src/tree/traits.rs index 7c6bafb..d3caf6a 100644 --- a/src/tree/traits.rs +++ b/src/tree/traits.rs @@ -115,10 +115,10 @@ pub trait Metric: Debug + Copy + Ord - + Add - + Sub - + AddAssign - + SubAssign + + Add + + AddAssign + + Sub + + SubAssign { /// The identity element of this metric with respect to addition. /// From 01cd50dacf5a680c2e022e6dff0da72eec176674 Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Wed, 13 Aug 2025 17:54:41 +0200 Subject: [PATCH 14/35] Update docs --- src/tree/traits.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tree/traits.rs b/src/tree/traits.rs index d3caf6a..8e9bb9b 100644 --- a/src/tree/traits.rs +++ b/src/tree/traits.rs @@ -159,13 +159,13 @@ pub trait SlicingMetric: Metric { /// Allows iterating forward over the units of this metric. pub trait UnitMetric: Metric { /// Returns a `(first_slice, rest_slice, advance)` tuple, where `advance` - /// is equal to `first_slice`'s summary **plus** the summary of any + /// is equal to `first_slice`'s base length **plus** the length of any /// content between the end of `first_slice` and the start of `rest_slice` /// that's not included in either of them. /// /// It follows that if `slice == first_slice ++ rest_slice` (where `++` /// denotes concatenation), the `advance` should be equal to - /// `first_slice`'s summary. + /// `first_slice`'s base length. fn first_unit<'a>( slice: L::Slice<'a>, ) -> (L::Slice<'a>, L::Slice<'a>, L::BaseMetric); @@ -174,12 +174,12 @@ pub trait UnitMetric: Metric { /// Allows iterating backward over the units of this metric. pub trait DoubleEndedUnitMetric: UnitMetric { /// Returns a `(rest_slice, last_slice, advance)` tuple, where `advance` is - /// equal to `last_slice`'s summary **plus** the summary of any content + /// equal to `last_slice`'s base length **plus** the length of any content /// between the end of `last_slice` and the end of the original `slice`. /// /// It follows that if `slice == rest_slice ++ last_slice` (where `++` /// denotes concatenation) the `advance` should be equal to `last_slice`'s - /// summary. + /// base length. fn last_unit<'a>( slice: L::Slice<'a>, ) -> (L::Slice<'a>, L::Slice<'a>, L::BaseMetric); From 69d4d1fc7722156afc1665707ada4b50ec99d512 Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Wed, 13 Aug 2025 17:59:57 +0200 Subject: [PATCH 15/35] Tweak wording --- src/rope/gap_buffer.rs | 40 ++++++++++++++++++++-------------------- src/rope/gap_slice.rs | 2 +- src/rope/metrics.rs | 4 ++-- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/src/rope/gap_buffer.rs b/src/rope/gap_buffer.rs index 44c88c5..01bbd04 100644 --- a/src/rope/gap_buffer.rs +++ b/src/rope/gap_buffer.rs @@ -358,45 +358,45 @@ impl GapBuffer { left_summary += ChunkSummary::from(chunk); } else { - let (to_first, to_second) = split_adjusted::( + let (to_left, to_right) = split_adjusted::( chunk, to_left - left_summary.bytes(), ); let range = { let start = left_summary.bytes(); - let end = start + to_first.len(); + let end = start + to_left.len(); start..end }; - bytes[range].copy_from_slice(to_first.as_bytes()); + bytes[range].copy_from_slice(to_left.as_bytes()); - left_summary += ChunkSummary::from(to_first); + left_summary += ChunkSummary::from(to_left); let mut start = MAX_BYTES - (total_len - left_summary.bytes()); let range = { - let end = start + to_second.len(); + let end = start + to_right.len(); start..end }; - bytes[range].copy_from_slice(to_second.as_bytes()); + bytes[range].copy_from_slice(to_right.as_bytes()); - start += to_second.len(); + start += to_right.len(); - let mut right_summary = ChunkSummary::from(to_second); + let mut right_summary = ChunkSummary::from(to_right); - for &segment in chunks { + for &chunk in chunks { let range = { - let end = start + segment.len(); + let end = start + chunk.len(); start..end }; - bytes[range].copy_from_slice(segment.as_bytes()); + bytes[range].copy_from_slice(chunk.as_bytes()); - start += segment.len(); + start += chunk.len(); - right_summary += ChunkSummary::from(segment); + right_summary += ChunkSummary::from(chunk); } return Self { bytes, left_summary, right_summary }; @@ -540,8 +540,8 @@ impl GapBuffer { let offset = byte_offset; #[allow(clippy::comparison_chain)] - // The offset splits the first segment => move all the text after the - // offset to the start of the second segment. + // The offset splits the left chunk => move all the text after the + // offset to the start of the right chunk. // // aa|bb~~~ccc => aa~~~bbccc if offset < self.len_left() { @@ -555,8 +555,8 @@ impl GapBuffer { MAX_BYTES - len_right, ); } - // The offset splits the second segment => move all the text before the - // offset to the end of the first segment. + // The offset splits the right chunk => move all the text before the + // offset to the end of the left chunk. // // aaa~~~bb|cc => aaabb~~~cc else if offset > self.len_left() { @@ -714,9 +714,9 @@ impl GapBuffer { ChunkSummary::from(a) + ChunkSummary::from(b) ); - // Shift the first segment to the right. - let len_first = self.len_left(); - self.bytes.copy_within(..len_first, a.len() + b.len()); + // Shift the left chunk to the right. + let len_left = self.len_left(); + self.bytes.copy_within(..len_left, a.len() + b.len()); // Prepend the first string. self.bytes[..a.len()].copy_from_slice(a.as_bytes()); diff --git a/src/rope/gap_slice.rs b/src/rope/gap_slice.rs index 8923ccd..4d1519d 100644 --- a/src/rope/gap_slice.rs +++ b/src/rope/gap_slice.rs @@ -170,7 +170,7 @@ impl<'a> GapSlice<'a> { } } - /// The second segment if it's not empty, or the first one otherwise. + /// The right chunk if it's not empty, or the left one otherwise. #[inline] pub(super) fn last_chunk(&self) -> &'a str { if self.len_right() == 0 { diff --git a/src/rope/metrics.rs b/src/rope/metrics.rs index 4f55e23..0fa2aec 100644 --- a/src/rope/metrics.rs +++ b/src/rope/metrics.rs @@ -247,12 +247,12 @@ impl Metric for ByteMetric { #[inline] fn measure_leaf(gap_buffer: &GapBuffer) -> Self { - Self(gap_buffer.left_summary.bytes + gap_buffer.right_summary.bytes) + Self(gap_buffer.len()) } #[inline] fn measure_slice(gap_slice: &GapSlice) -> Self { - Self(gap_slice.left_summary.bytes + gap_slice.right_summary.bytes) + Self(gap_slice.len()) } } From dc87c68533b42c2753753fff44606cc061b368f6 Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Wed, 13 Aug 2025 18:07:10 +0200 Subject: [PATCH 16/35] Remove wrong assertion --- src/tree/units.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/tree/units.rs b/src/tree/units.rs index 1174636..a772233 100644 --- a/src/tree/units.rs +++ b/src/tree/units.rs @@ -119,8 +119,6 @@ impl<'a, const ARITY: usize, L: Leaf, M: UnitMetric> Iterator } else if iter.base_total > iter.base_yielded { let (remainder, advance) = iter.remainder(); - debug_assert!(advance.is_zero()); - debug_assert_eq!(advance, iter.base_total - iter.base_yielded); iter.base_yielded = iter.base_total; From 33f94cb62c65a2266a6fe39b58bd1bcc4d60b330 Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Wed, 13 Aug 2025 18:20:58 +0200 Subject: [PATCH 17/35] Use seedable RNG in randomized tests --- Cargo.toml | 1 + tests/common/mod.rs | 9 +++++++++ tests/iterators.rs | 10 +++++----- tests/rope_replace.rs | 8 ++++---- tests/slice_indexing.rs | 8 ++++---- tests/slicing.rs | 6 +++--- 6 files changed, 26 insertions(+), 16 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index cbb4531..aa515ca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,6 +45,7 @@ unicode-segmentation = { version = "1.10.0", optional = true } [dev-dependencies] criterion = "0.7" rand = "0.9" +rand_chacha = "0.9" ropey = "1.6" serde_json = "1" serde_test = "1.0.177" diff --git a/tests/common/mod.rs b/tests/common/mod.rs index 6c35fbb..cb1fc77 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -1,10 +1,19 @@ #![allow(dead_code)] +use rand::SeedableRng; + pub const TINY: &str = include_str!("tiny.txt"); pub const SMALL: &str = include_str!("small.txt"); pub const MEDIUM: &str = include_str!("medium.txt"); pub const LARGE: &str = include_str!("large.txt"); +pub fn rng() -> impl rand::Rng { + let rng = rand_chacha::ChaChaRng::from_os_rng(); + let seed = rng.get_seed(); + println!("Seed: {seed:?}"); + rng +} + /// A cursed version of a lorem ipsum paragraph taken from [this online /// tool][1] with mixed line breaks (LF and CRLF). /// diff --git a/tests/iterators.rs b/tests/iterators.rs index 03ace0e..c8627a9 100644 --- a/tests/iterators.rs +++ b/tests/iterators.rs @@ -1,5 +1,5 @@ use crop::Rope; -use rand::{Rng, rng}; +use rand::Rng; mod common; @@ -41,7 +41,7 @@ fn iter_bytes_both_ways() { let s = if cfg!(miri) { "Hello, world!" } else { LARGE }; let rope = Rope::from(s); - let i = rng().random_range(0..=s.len()); + let i = common::rng().random_range(0..=s.len()); println!("i: {i}"); @@ -209,7 +209,7 @@ fn iter_chars_both_ways() { let rope = Rope::from(LARGE); let total_chars = LARGE.chars().count(); - let i = rng().random_range(0..=total_chars); + let i = common::rng().random_range(0..=total_chars); println!("i: {i}"); @@ -429,7 +429,7 @@ fn iter_lines_forward_backward() { #[cfg_attr(miri, ignore)] #[test] fn iter_lines_over_random_slices() { - let mut rng = rand::rng(); + let mut rng = common::rng(); for s in [TINY, SMALL, MEDIUM, LARGE] { let rope = Rope::from(s); @@ -541,7 +541,7 @@ fn iter_raw_lines_over_test_vectors() { #[cfg_attr(miri, ignore)] #[test] fn iter_raw_lines_over_random_slices() { - let mut rng = rand::rng(); + let mut rng = common::rng(); for s in [TINY, SMALL, MEDIUM, LARGE] { let rope = Rope::from(s); diff --git a/tests/rope_replace.rs b/tests/rope_replace.rs index 94febd9..209650d 100644 --- a/tests/rope_replace.rs +++ b/tests/rope_replace.rs @@ -122,7 +122,7 @@ fn insert_8() { #[cfg_attr(miri, ignore)] #[test] fn insert_small_random() { - let mut rng = rand::rng(); + let mut rng = common::rng(); let mut rope = Rope::new(); let mut string = String::new(); @@ -159,7 +159,7 @@ fn insert_small_random() { #[cfg_attr(miri, ignore)] #[test] fn insert_random() { - let mut rng = rand::rng(); + let mut rng = common::rng(); for s in [TINY, SMALL, MEDIUM, LARGE] { let mut r = Rope::from(s); @@ -272,7 +272,7 @@ fn delete_9() { #[cfg_attr(miri, ignore)] #[test] fn delete_random() { - let mut rng = rand::rng(); + let mut rng = common::rng(); for s in [TINY, SMALL, MEDIUM, LARGE] { let mut r = Rope::from(s); @@ -324,7 +324,7 @@ fn replace_1() { #[cfg_attr(miri, ignore)] #[test] fn replace_random() { - let mut rng = rand::rng(); + let mut rng = common::rng(); for s in [TINY, SMALL, MEDIUM, LARGE] { let mut r = Rope::from(s); diff --git a/tests/slice_indexing.rs b/tests/slice_indexing.rs index 196f3bc..f4024b9 100644 --- a/tests/slice_indexing.rs +++ b/tests/slice_indexing.rs @@ -10,7 +10,7 @@ use common::{CURSED_LIPSUM, LARGE, MEDIUM, SMALL, TINY}; #[cfg_attr(miri, ignore)] #[test] fn byte_random() { - let mut rng = rand::rng(); + let mut rng = common::rng(); for s in [TINY, SMALL, MEDIUM, LARGE] { let r = Rope::from(s); @@ -40,7 +40,7 @@ fn byte_random() { #[cfg_attr(miri, ignore)] #[test] fn is_char_boundary_random() { - let mut rng = rand::rng(); + let mut rng = common::rng(); for s in [CURSED_LIPSUM, TINY, SMALL, MEDIUM, LARGE] { let r = Rope::from(s); @@ -76,7 +76,7 @@ fn is_char_boundary_random() { #[cfg_attr(miri, ignore)] #[test] fn line_of_byte_random() { - let mut rng = rand::rng(); + let mut rng = common::rng(); for s in [TINY, SMALL, MEDIUM, LARGE] { let crop = Rope::from(s); @@ -110,7 +110,7 @@ fn line_of_byte_random() { #[cfg_attr(miri, ignore)] #[test] fn byte_of_line_random() { - let mut rng = rand::rng(); + let mut rng = common::rng(); for s in [TINY, SMALL, MEDIUM, LARGE] { let crop = Rope::from(s); diff --git a/tests/slicing.rs b/tests/slicing.rs index 8797dd4..c1a0505 100644 --- a/tests/slicing.rs +++ b/tests/slicing.rs @@ -54,7 +54,7 @@ fn byte_slice_0() { #[cfg_attr(miri, ignore)] #[test] fn byte_slice_random() { - let mut rng = rand::rng(); + let mut rng = common::rng(); for s in [TINY, SMALL, MEDIUM, LARGE] { let r = Rope::from(s); @@ -139,7 +139,7 @@ fn byte_slice_then_line() { #[cfg_attr(miri, ignore)] #[test] fn line_slices_random() { - let mut rng = rand::rng(); + let mut rng = common::rng(); for s in [TINY, SMALL, MEDIUM, LARGE] { let r = Rope::from(s); @@ -189,7 +189,7 @@ fn line_slices_random() { /// content while also satisying its invariants. #[test] fn rope_from_slice() { - let mut rng = rand::rng(); + let mut rng = common::rng(); let slices = if cfg!(miri) { ["Hello world", "ƒoo", "bär", "baz", "🗻∈🌏"] From 1977524a6108b93640651d5e4d3fffc4ff447e70 Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Wed, 13 Aug 2025 18:54:11 +0200 Subject: [PATCH 18/35] Fix bug --- src/tree/traits.rs | 2 +- src/tree/tree_slice.rs | 38 ++++++++++++++++++++------------------ 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/tree/traits.rs b/src/tree/traits.rs index 8e9bb9b..e980c25 100644 --- a/src/tree/traits.rs +++ b/src/tree/traits.rs @@ -52,7 +52,7 @@ pub trait Leaf: Debug + Sized { } } -pub trait LeafSlice<'a>: Copy +pub trait LeafSlice<'a>: Copy + Debug where Self: 'a, { diff --git a/src/tree/tree_slice.rs b/src/tree/tree_slice.rs index afce674..d9a0e27 100644 --- a/src/tree/tree_slice.rs +++ b/src/tree/tree_slice.rs @@ -242,21 +242,6 @@ where start: S, end: E, ) -> Self - where - S: SlicingMetric, - E: SlicingMetric, - { - Self::slice_node_at_offset(root, L::BaseMetric::zero(), start, end) - } - - #[track_caller] - #[inline] - fn slice_node_at_offset( - root: &'a Arc>, - _offset: L::BaseMetric, - start: S, - end: E, - ) -> Self where S: SlicingMetric, E: SlicingMetric, @@ -304,6 +289,21 @@ where slice } + + #[track_caller] + #[inline] + fn slice_node_at_offset( + _root: &'a Arc>, + _offset: L::BaseMetric, + _start: S, + _end: E, + ) -> Self + where + S: SlicingMetric, + E: SlicingMetric, + { + todo!(); + } } impl PartialEq for SliceLeafCount { @@ -514,7 +514,7 @@ fn build_slice<'a, const N: usize, L, S, E>( + child_summary.measure::() >= end { - // This child contains the ending leaf somewhere in its + // This child contains the ending slice somewhere in its // subtree. Run this function again with this child as the // node. build_slice( @@ -529,7 +529,7 @@ fn build_slice<'a, const N: usize, L, S, E>( done, ); } else { - // This is a node fully contained between the starting and + // This node is fully contained between the starting and // the ending slices. slice.summary += child_summary; } @@ -596,9 +596,11 @@ fn build_slice<'a, const N: usize, L, S, E>( slice.offset += leaf.base_measure() - start_summary.base_measure(); + *end_offset += + leaf.measure::() - start_summary.measure::(); + slice.summary += start_summary; slice.start_slice = start_slice; - *found_start_slice = true; } } else { From fb3eab8519f6abdb16f1735883a1164878277e72 Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Wed, 13 Aug 2025 18:58:18 +0200 Subject: [PATCH 19/35] Fix assertion --- src/tree/tree_slice.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tree/tree_slice.rs b/src/tree/tree_slice.rs index d9a0e27..520a95a 100644 --- a/src/tree/tree_slice.rs +++ b/src/tree/tree_slice.rs @@ -247,7 +247,7 @@ where E: SlicingMetric, { debug_assert!(S::zero() <= start); - debug_assert!(end <= root.measure::()); + debug_assert!(end <= root.measure::() + E::one()); let (root, start, end) = deepest_node_containing_range(root, start, end); From 56cb1fa23455da919a2779910a28d79eeac8f8d7 Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Wed, 13 Aug 2025 19:03:15 +0200 Subject: [PATCH 20/35] Tweaks --- src/tree/node.rs | 2 -- src/tree/tree_slice.rs | 24 +++++++++++++----------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/tree/node.rs b/src/tree/node.rs index 462f6c2..b37108e 100644 --- a/src/tree/node.rs +++ b/src/tree/node.rs @@ -76,7 +76,6 @@ impl Node { self.measure::() } - #[track_caller] #[inline] pub(super) fn convert_measure(&self, up_to: M1) -> M2 where @@ -116,7 +115,6 @@ impl Node { } } - #[track_caller] #[inline] pub(super) fn convert_measure_from_offset( &self, diff --git a/src/tree/tree_slice.rs b/src/tree/tree_slice.rs index 520a95a..e919ff4 100644 --- a/src/tree/tree_slice.rs +++ b/src/tree/tree_slice.rs @@ -128,18 +128,20 @@ impl<'a, const ARITY: usize, L: Leaf> TreeSlice<'a, ARITY, L> { { debug_assert!(measure <= self.measure::() + M::one()); - if self.start_slice.measure::() >= measure { - (self.start_slice, M::zero()) - } else { - let all_minus_last = - self.summary.measure::() - self.end_slice.measure::(); + let len_start_slice = self.start_slice.measure::(); - if all_minus_last >= measure { - self.root.leaf_at_measure_from_offset(self.offset, measure) - } else { - (self.end_slice, all_minus_last) - } + if measure <= len_start_slice { + return (self.start_slice, M::zero()); } + + let len_total_minus_end = + self.measure::() - self.end_slice.measure::(); + + if len_total_minus_end <= measure { + return (self.end_slice, len_total_minus_end); + } + + self.root.leaf_at_measure_from_offset(self.offset, measure) } #[inline] @@ -166,7 +168,7 @@ impl<'a, const ARITY: usize, L: Leaf> TreeSlice<'a, ARITY, L> { where M: Metric, { - M::measure(self.summary()) + self.summary.measure::() } #[inline] From 9fc7d94e32bf360af383cea9ec6648e790b35cf5 Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Thu, 14 Aug 2025 12:08:10 +0200 Subject: [PATCH 21/35] WIP --- src/rope/gap_buffer.rs | 19 ++++- src/tree/node.rs | 44 +++++----- src/tree/tree.rs | 3 +- src/tree/tree_slice.rs | 177 ++++++++++++++++++++++++++++------------- 4 files changed, 164 insertions(+), 79 deletions(-) diff --git a/src/rope/gap_buffer.rs b/src/rope/gap_buffer.rs index 01bbd04..9d65144 100644 --- a/src/rope/gap_buffer.rs +++ b/src/rope/gap_buffer.rs @@ -306,6 +306,23 @@ impl GapBuffer { } } + /// Returns the byte at the given index. + /// + /// # Panics + /// + /// Panics if the index is out of bounds, i.e. greater than or equal to + /// [`len()`](Self::len()). + #[inline] + pub(super) fn byte(&self, byte_index: usize) -> u8 { + debug_assert!(byte_index < self.len()); + + if byte_index < self.len_left() { + self.left_chunk().as_bytes()[byte_index] + } else { + self.right_chunk().as_bytes()[byte_index - self.len_left()] + } + } + /// The number of bytes `RopeChunk`s must always stay over. pub(super) const fn chunk_min() -> usize { // The buffer can be underfilled by 3 bytes at most, which can happen @@ -443,7 +460,7 @@ impl GapBuffer { } #[inline] - fn is_char_boundary(&self, byte_offset: usize) -> bool { + pub(super) fn is_char_boundary(&self, byte_offset: usize) -> bool { debug_assert!(byte_offset <= self.len()); if byte_offset <= self.len_left() { diff --git a/src/tree/node.rs b/src/tree/node.rs index b37108e..4f072be 100644 --- a/src/tree/node.rs +++ b/src/tree/node.rs @@ -1,3 +1,5 @@ +use core::cmp::Ordering; + use super::traits::{BalancedLeaf, Leaf, LeafSlice, Metric, SlicingMetric}; use super::{Arc, Inode}; @@ -95,12 +97,19 @@ impl Node { for child in inode.children() { let child_m1 = child.measure::(); - if m1 + child_m1 >= up_to { - node = &**child; - continue 'outer; - } else { - m1 += child_m1; - m2 += child.measure::(); + match (m1 + child_m1).cmp(&up_to) { + Ordering::Less => { + m1 += child_m1; + m2 += child.measure::(); + }, + + // The child is exactly the right size. + Ordering::Equal => return m2 + child.measure(), + + Ordering::Greater => { + node = &**child; + continue 'outer; + }, } } @@ -198,8 +207,13 @@ impl Node { } } + /// Returns the leaf at the given measure, together with the leaf's + /// `M`-offset in the tree. + /// + /// If the measure falls on a leaf boundary, the leaf to the left of the + /// measure is returned. #[inline] - pub(super) fn leaf_at_measure(&self, measure: M) -> (L::Slice<'_>, M) + pub(super) fn leaf_at_measure(&self, measure: M) -> (&L, M) where M: Metric, { @@ -220,25 +234,11 @@ impl Node { node = inode.child(child_idx); }, - Node::Leaf(leaf) => { - return (leaf.as_slice(), measured); - }, + Node::Leaf(leaf) => return (leaf, measured), } } } - #[inline] - pub(super) fn leaf_at_measure_from_offset( - &self, - _start_from: L::BaseMetric, - _measure: M, - ) -> (L::Slice<'_>, M) - where - M: Metric, - { - todo!(); - } - #[inline] pub(super) fn measure(&self) -> M where diff --git a/src/tree/tree.rs b/src/tree/tree.rs index 9c9f3d8..8c9aa59 100644 --- a/src/tree/tree.rs +++ b/src/tree/tree.rs @@ -133,12 +133,11 @@ impl Tree { /// Returns the leaf containing the `measure`-th unit of the `M`-metric, /// plus the `M`-measure of all the leaves before it. #[inline] - pub fn leaf_at_measure(&self, measure: M) -> (L::Slice<'_>, M) + pub fn leaf_at_measure(&self, measure: M) -> (&L, M) where M: Metric, { debug_assert!(measure <= self.measure::() + M::one()); - self.root.leaf_at_measure(measure) } diff --git a/src/tree/tree_slice.rs b/src/tree/tree_slice.rs index e919ff4..a8afe1c 100644 --- a/src/tree/tree_slice.rs +++ b/src/tree/tree_slice.rs @@ -44,54 +44,6 @@ impl Copy for TreeSlice<'_, ARITY, L> where } impl<'a, const ARITY: usize, L: Leaf> TreeSlice<'a, ARITY, L> { - /* - Public methods - */ - - #[doc(hidden)] - pub fn assert_invariants(&self) { - match &**self.root { - Node::Internal(_) => { - assert!(self.leaf_count() > 1); - assert!(!self.start_slice.is_empty()); - assert!(!self.end_slice.is_empty()); - - if self.leaf_count() == 2 { - assert_eq!( - self.summary.base_measure(), - self.start_slice.base_measure() - + self.end_slice.base_measure() - ); - } - - // This last part checks that the first and last slices are - // under different children of the root, making the latter the - // deepest node that contains both. - - let (root, remove_offset) = { - let start = self.offset; - deepest_node_containing_base_range( - self.root, - start, - start + self.summary.base_measure(), - ) - }; - - // These asserts should be equivalent but we use them all for - // redundancy. - - assert!(Arc::ptr_eq(self.root, root)); - assert_eq!(self.root.depth(), root.depth()); - assert!(remove_offset.is_zero()); - }, - - Node::Leaf(leaf) => { - assert_eq!(self.leaf_count(), 1); - assert!(leaf.base_measure() >= self.base_measure()); - }, - } - } - #[inline] pub fn base_measure(&self) -> L::BaseMetric { self.measure::() @@ -141,7 +93,9 @@ impl<'a, const ARITY: usize, L: Leaf> TreeSlice<'a, ARITY, L> { return (self.end_slice, len_total_minus_end); } - self.root.leaf_at_measure_from_offset(self.offset, measure) + let offset = self.measure_offset::(); + let (leaf, leaf_offset) = self.root.leaf_at_measure(offset + measure); + (leaf.as_slice(), leaf_offset - offset) } #[inline] @@ -171,11 +125,6 @@ impl<'a, const ARITY: usize, L: Leaf> TreeSlice<'a, ARITY, L> { self.summary.measure::() } - #[inline] - pub(super) fn root(&self) -> &'a Arc> { - self.root - } - #[inline] pub fn start_slice(&self) -> L::Slice<'a> { self.start_slice @@ -185,6 +134,126 @@ impl<'a, const ARITY: usize, L: Leaf> TreeSlice<'a, ARITY, L> { pub fn summary(&self) -> &L::Summary { &self.summary } + + #[inline] + pub(super) fn root(&self) -> &'a Arc> { + self.root + } + + #[doc(hidden)] + pub fn assert_invariants(&self) { + match &**self.root { + Node::Internal(_) => { + assert!(self.leaf_count() > 1); + assert!(!self.start_slice.is_empty()); + assert!(!self.end_slice.is_empty()); + + if self.leaf_count() == 2 { + assert_eq!( + self.summary.base_measure(), + self.start_slice.base_measure() + + self.end_slice.base_measure() + ); + } + + // This last part checks that the first and last slices are + // under different children of the root, making the latter the + // deepest node that contains both. + + let (root, remove_offset) = { + let start = self.offset; + deepest_node_containing_base_range( + self.root, + start, + start + self.summary.base_measure(), + ) + }; + + // These asserts should be equivalent but we use them all for + // redundancy. + + assert!(Arc::ptr_eq(self.root, root)); + assert_eq!(self.root.depth(), root.depth()); + assert!(remove_offset.is_zero()); + }, + + Node::Leaf(leaf) => { + assert_eq!(self.leaf_count(), 1); + assert!(leaf.base_measure() >= self.base_measure()); + }, + } + } + + /// Returns the `M`-length of the subtree under [`root`](Self::root) up to + /// the start of the [`start_slice`](Self::start_slice). + /// + /// Note that it's never necessary to call this with `L::BaseMetric`, as + /// that's already known to be [`Self::offset`]. + #[inline] + fn measure_offset(&self) -> M + where + M: Metric, + { + use core::ops::{Add, AddAssign, Sub, SubAssign}; + + /// A `Metric`-wrapper with `unreachable!` `SlicingMetric` impls + /// to be given to [`Node::convert_measure()`] which lets us avoid + /// adding a `L::BaseMetric: SlicingMetric` bound to this function. + #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] + struct OnBoundary(T); + + impl> Add for OnBoundary { + type Output = Self; + #[inline] + fn add(self, rhs: Self) -> Self { + Self(self.0 + rhs.0) + } + } + impl AddAssign for OnBoundary { + #[inline] + fn add_assign(&mut self, rhs: Self) { + self.0 += rhs.0; + } + } + impl> Sub for OnBoundary { + type Output = Self; + fn sub(self, _: Self) -> Self { + unreachable!() + } + } + impl SubAssign for OnBoundary { + fn sub_assign(&mut self, _: Self) { + unreachable!() + } + } + impl Metric for OnBoundary<::BaseMetric> { + #[inline] + fn zero() -> Self { + Self(::BaseMetric::zero()) + } + #[inline] + fn measure(summary: &S) -> Self { + Self(summary.base_measure()) + } + #[inline] + fn one() -> Self { + unreachable!() + } + } + impl SlicingMetric for OnBoundary { + fn slice_up_to<'a>(_: L::Slice<'a>, _: Self) -> L::Slice<'a> { + unreachable!() + } + fn slice_from<'a>(_: L::Slice<'a>, _: Self) -> L::Slice<'a> { + unreachable!() + } + } + + // Make the offset lie on a leaf boundary to avoid slicing. + let base_offset = self.offset + self.start_slice.base_measure(); + let m_offset: M = self.root.convert_measure(OnBoundary(base_offset)); + m_offset - self.start_slice.measure::() + } } impl<'a, const ARITY: usize, L: Leaf> TreeSlice<'a, ARITY, L> From 3af5311eaf5301f83ba040246cf969b2d7a8abfd Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Thu, 14 Aug 2025 13:36:35 +0200 Subject: [PATCH 22/35] Add a `FromMetric` trait for converting metrics without slicing --- src/rope/gap_buffer.rs | 65 +++++++++++++++++++++++++++++++++++++-- src/rope/metrics.rs | 69 ++++++++++++++++++++++++++++++++++-------- src/tree/node.rs | 35 +++++++-------------- src/tree/traits.rs | 12 ++++++++ src/tree/tree.rs | 4 +-- src/tree/tree_slice.rs | 64 ++------------------------------------- 6 files changed, 147 insertions(+), 102 deletions(-) diff --git a/src/rope/gap_buffer.rs b/src/rope/gap_buffer.rs index 9d65144..a79ff17 100644 --- a/src/rope/gap_buffer.rs +++ b/src/rope/gap_buffer.rs @@ -5,13 +5,14 @@ use alloc::boxed::Box; use alloc::vec::Vec; +use core::cmp::Ordering; use core::ops::{Range, RangeBounds}; use super::gap_slice::GapSlice; -use super::metrics::{ByteMetric, ChunkSummary}; +use super::metrics::{ByteMetric, ChunkSummary, ToByteOffset}; use super::utils::{panic_messages as panic, *}; use crate::range_bounds_to_start_end; -use crate::tree::{BalancedLeaf, Leaf, ReplaceableLeaf}; +use crate::tree::{BalancedLeaf, Leaf, Metric, ReplaceableLeaf, Summary}; #[cfg(any(test, feature = "small_chunks"))] const MAX_BYTES: usize = 4; @@ -288,7 +289,7 @@ impl GapBuffer { /// is not a character boundary. #[track_caller] #[inline] - fn assert_char_boundary(&self, byte_offset: usize) { + pub(super) fn assert_char_boundary(&self, byte_offset: usize) { debug_assert!(byte_offset <= self.len()); if !self.is_char_boundary(byte_offset) { @@ -330,6 +331,62 @@ impl GapBuffer { Self::min_bytes().saturating_sub(3) } + #[inline] + pub(super) fn convert_measure_from_byte(&self, byte_offset: usize) -> M + where + M: Metric, + { + debug_assert!(self.is_char_boundary(byte_offset)); + + #[inline] + fn measure_up_to>( + chunk: &str, + _chunk_len: M, + byte_offset: usize, + ) -> M { + // debug_assert_eq!(chunk.measure::(), chunk_len); + if byte_offset <= chunk.len() / 2 { + todo!(); + // M::measure_leaf(&chunk[..byte_offset]) + } else { + todo!(); + // chunk_len - M::measure_leaf(&chunk[byte_offset..]) + } + } + + match byte_offset.cmp(&self.len_left()) { + Ordering::Less => measure_up_to( + self.left_chunk(), + self.left_summary.measure::(), + byte_offset, + ), + Ordering::Equal => self.left_summary.measure::(), + Ordering::Greater => { + self.left_summary.measure::() + + measure_up_to( + self.right_chunk(), + self.right_summary.measure::(), + byte_offset - self.len_left(), + ) + }, + } + } + + #[inline] + pub(super) fn convert_measure_to_byte(&self, offset: M) -> usize + where + M: Metric + ToByteOffset, + { + let len_left = self.left_summary.measure::(); + + if offset <= len_left { + offset.to_byte_offset(self.left_chunk()) + } else { + self.len_left() + + (offset - len_left).to_byte_offset(self.right_chunk()) + } + } + /// Creates a new `GapBuffer` from a slice of `&str`s. /// /// # Panics @@ -517,10 +574,12 @@ impl GapBuffer { self.right_summary.bytes() } + #[inline] pub(super) const fn max_bytes() -> usize { MAX_BYTES } + #[inline] /// The minimum number of bytes this buffer should have to not be /// considered underfilled. pub(super) const fn min_bytes() -> usize { diff --git a/src/rope/metrics.rs b/src/rope/metrics.rs index 0fa2aec..51891b1 100644 --- a/src/rope/metrics.rs +++ b/src/rope/metrics.rs @@ -4,6 +4,7 @@ use super::gap_buffer::GapBuffer; use super::gap_slice::GapSlice; use crate::tree::{ DoubleEndedUnitMetric, + FromMetric, LeafSlice, Metric, SlicingMetric, @@ -77,7 +78,7 @@ impl Summary for ChunkSummary { } } -impl Add for ChunkSummary { +impl Add for ChunkSummary { type Output = Self; #[inline] @@ -87,7 +88,7 @@ impl Add for ChunkSummary { } } -impl AddAssign for ChunkSummary { +impl AddAssign for ChunkSummary { #[inline] fn add_assign(&mut self, rhs: Self) { self.bytes += rhs.bytes; @@ -99,7 +100,7 @@ impl AddAssign for ChunkSummary { } } -impl Sub for ChunkSummary { +impl Sub for ChunkSummary { type Output = Self; #[inline] @@ -109,7 +110,7 @@ impl Sub for ChunkSummary { } } -impl SubAssign for ChunkSummary { +impl SubAssign for ChunkSummary { #[inline] fn sub_assign(&mut self, rhs: Self) { self.bytes -= rhs.bytes; @@ -256,6 +257,27 @@ impl Metric for ByteMetric { } } +impl FromMetric for ByteMetric { + #[inline] + fn measure_up_to( + gap_buffer: &GapBuffer, + line_offset: RawLineMetric, + ) -> Self { + Self(gap_buffer.convert_measure_to_byte(line_offset)) + } +} + +#[cfg(feature = "utf16-metric")] +impl FromMetric for ByteMetric { + #[inline] + fn measure_up_to( + gap_buffer: &GapBuffer, + utf16_offset: utf16_metric::Utf16Metric, + ) -> Self { + Self(gap_buffer.convert_measure_to_byte(utf16_offset)) + } +} + impl SlicingMetric for ByteMetric { #[track_caller] #[inline] @@ -313,6 +335,17 @@ impl SubAssign for RawLineMetric { } } +impl FromMetric for RawLineMetric { + #[inline] + fn measure_up_to( + gap_buffer: &GapBuffer, + ByteMetric(byte_offset): ByteMetric, + ) -> Self { + gap_buffer.assert_char_boundary(byte_offset); + gap_buffer.convert_measure_from_byte::(byte_offset) + } +} + impl ToByteOffset for RawLineMetric { #[inline] fn to_byte_offset(&self, s: &str) -> usize { @@ -556,7 +589,7 @@ mod utf16_metric { #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct Utf16Metric(pub usize); - impl Add for Utf16Metric { + impl Add for Utf16Metric { type Output = Self; #[inline] @@ -565,6 +598,13 @@ mod utf16_metric { } } + impl AddAssign for Utf16Metric { + #[inline] + fn add_assign(&mut self, other: Self) { + self.0 += other.0 + } + } + impl Sub for Utf16Metric { type Output = Self; @@ -574,13 +614,6 @@ mod utf16_metric { } } - impl AddAssign for Utf16Metric { - #[inline] - fn add_assign(&mut self, other: Self) { - self.0 += other.0 - } - } - impl SubAssign for Utf16Metric { #[inline] fn sub_assign(&mut self, other: Self) { @@ -654,6 +687,18 @@ mod utf16_metric { } } + impl FromMetric for Utf16Metric { + #[track_caller] + #[inline] + fn measure_up_to( + gap_buffer: &GapBuffer, + ByteMetric(byte_offset): ByteMetric, + ) -> Self { + gap_buffer.assert_char_boundary(byte_offset); + gap_buffer.convert_measure_from_byte::(byte_offset) + } + } + impl SlicingMetric for Utf16Metric { #[track_caller] #[inline] diff --git a/src/tree/node.rs b/src/tree/node.rs index 4f072be..f0d6f9b 100644 --- a/src/tree/node.rs +++ b/src/tree/node.rs @@ -1,6 +1,4 @@ -use core::cmp::Ordering; - -use super::traits::{BalancedLeaf, Leaf, LeafSlice, Metric, SlicingMetric}; +use super::traits::{BalancedLeaf, FromMetric, Leaf, Metric}; use super::{Arc, Inode}; #[derive(Clone)] @@ -81,8 +79,8 @@ impl Node { #[inline] pub(super) fn convert_measure(&self, up_to: M1) -> M2 where - M1: SlicingMetric, - M2: Metric, + M1: Metric, + M2: FromMetric, { debug_assert!(up_to <= self.measure::()); @@ -97,19 +95,12 @@ impl Node { for child in inode.children() { let child_m1 = child.measure::(); - match (m1 + child_m1).cmp(&up_to) { - Ordering::Less => { - m1 += child_m1; - m2 += child.measure::(); - }, - - // The child is exactly the right size. - Ordering::Equal => return m2 + child.measure(), - - Ordering::Greater => { - node = &**child; - continue 'outer; - }, + if m1 + child_m1 >= up_to { + node = &**child; + continue 'outer; + } else { + m1 += child_m1; + m2 += child.measure::(); } } @@ -117,8 +108,7 @@ impl Node { }, Node::Leaf(leaf) => { - let slice = M1::slice_up_to(leaf.as_slice(), up_to - m1); - return m2 + slice.measure::(); + return m2 + M2::measure_up_to(leaf, up_to - m1); }, } } @@ -130,10 +120,7 @@ impl Node { _start_from: L::BaseMetric, _up_to: M1, ) -> M2 - where - M1: SlicingMetric, - M2: Metric, - { +where { todo!(); } diff --git a/src/tree/traits.rs b/src/tree/traits.rs index e980c25..eaf9e10 100644 --- a/src/tree/traits.rs +++ b/src/tree/traits.rs @@ -149,6 +149,18 @@ pub trait Metric: } } +/// Trait for metrics that can be converted from another metric. +pub trait FromMetric, S: Summary>: Metric { + fn measure_up_to(leaf: &S::Leaf, up_to: M) -> Self; +} + +impl> FromMetric for M { + #[inline] + fn measure_up_to(_: &S::Leaf, up_to: Self) -> Self { + up_to + } +} + /// Metrics that can be used to slice `Tree`s and `TreeSlice`s. pub trait SlicingMetric: Metric { fn slice_up_to<'a>(slice: L::Slice<'a>, up_to: Self) -> L::Slice<'a>; diff --git a/src/tree/tree.rs b/src/tree/tree.rs index 8c9aa59..4a00b08 100644 --- a/src/tree/tree.rs +++ b/src/tree/tree.rs @@ -93,8 +93,8 @@ impl Tree { #[inline] pub fn convert_measure(&self, up_to: M1) -> M2 where - M1: SlicingMetric, - M2: Metric, + M1: Metric, + M2: FromMetric, { debug_assert!(up_to <= self.measure::()); self.root.convert_measure(up_to) diff --git a/src/tree/tree_slice.rs b/src/tree/tree_slice.rs index a8afe1c..3049d03 100644 --- a/src/tree/tree_slice.rs +++ b/src/tree/tree_slice.rs @@ -76,7 +76,7 @@ impl<'a, const ARITY: usize, L: Leaf> TreeSlice<'a, ARITY, L> { #[inline] pub fn leaf_at_measure(&self, measure: M) -> (L::Slice<'a>, M) where - M: Metric, + M: Metric + FromMetric, { debug_assert!(measure <= self.measure::() + M::one()); @@ -192,67 +192,9 @@ impl<'a, const ARITY: usize, L: Leaf> TreeSlice<'a, ARITY, L> { #[inline] fn measure_offset(&self) -> M where - M: Metric, + M: FromMetric, { - use core::ops::{Add, AddAssign, Sub, SubAssign}; - - /// A `Metric`-wrapper with `unreachable!` `SlicingMetric` impls - /// to be given to [`Node::convert_measure()`] which lets us avoid - /// adding a `L::BaseMetric: SlicingMetric` bound to this function. - #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] - struct OnBoundary(T); - - impl> Add for OnBoundary { - type Output = Self; - #[inline] - fn add(self, rhs: Self) -> Self { - Self(self.0 + rhs.0) - } - } - impl AddAssign for OnBoundary { - #[inline] - fn add_assign(&mut self, rhs: Self) { - self.0 += rhs.0; - } - } - impl> Sub for OnBoundary { - type Output = Self; - fn sub(self, _: Self) -> Self { - unreachable!() - } - } - impl SubAssign for OnBoundary { - fn sub_assign(&mut self, _: Self) { - unreachable!() - } - } - impl Metric for OnBoundary<::BaseMetric> { - #[inline] - fn zero() -> Self { - Self(::BaseMetric::zero()) - } - #[inline] - fn measure(summary: &S) -> Self { - Self(summary.base_measure()) - } - #[inline] - fn one() -> Self { - unreachable!() - } - } - impl SlicingMetric for OnBoundary { - fn slice_up_to<'a>(_: L::Slice<'a>, _: Self) -> L::Slice<'a> { - unreachable!() - } - fn slice_from<'a>(_: L::Slice<'a>, _: Self) -> L::Slice<'a> { - unreachable!() - } - } - - // Make the offset lie on a leaf boundary to avoid slicing. - let base_offset = self.offset + self.start_slice.base_measure(); - let m_offset: M = self.root.convert_measure(OnBoundary(base_offset)); - m_offset - self.start_slice.measure::() + self.root.convert_measure(self.offset) } } From cec5452c332cb0eaa762aad44f263d4d3279c88a Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Thu, 14 Aug 2025 14:11:23 +0200 Subject: [PATCH 23/35] Add `GapBufferSummary`, make `ChunkSummary` a `Summary` of `str`s --- src/rope/gap_buffer.rs | 105 +++++++++++++++++++++++++++++++++++++---- src/rope/gap_slice.rs | 8 ++-- src/rope/metrics.rs | 84 +++++++++++++++++++-------------- src/rope/rope_slice.rs | 9 ++-- src/tree/traits.rs | 6 +-- 5 files changed, 157 insertions(+), 55 deletions(-) diff --git a/src/rope/gap_buffer.rs b/src/rope/gap_buffer.rs index a79ff17..f50ba98 100644 --- a/src/rope/gap_buffer.rs +++ b/src/rope/gap_buffer.rs @@ -6,7 +6,7 @@ use alloc::boxed::Box; use alloc::vec::Vec; use core::cmp::Ordering; -use core::ops::{Range, RangeBounds}; +use core::ops::{Add, AddAssign, Deref, Range, RangeBounds, Sub, SubAssign}; use super::gap_slice::GapSlice; use super::metrics::{ByteMetric, ChunkSummary, ToByteOffset}; @@ -47,6 +47,12 @@ pub struct GapBuffer { pub(super) right_summary: ChunkSummary, } +#[derive(Copy, Clone, Default, Debug, PartialEq)] +pub struct GapBufferSummary { + /// The sum of the [`ChunkSummary`]s of the left and right chunks. + pub(super) chunks_summary: ChunkSummary, +} + impl core::fmt::Debug for GapBuffer { #[inline] fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { @@ -341,16 +347,14 @@ impl GapBuffer { #[inline] fn measure_up_to>( chunk: &str, - _chunk_len: M, + chunk_len: M, byte_offset: usize, ) -> M { - // debug_assert_eq!(chunk.measure::(), chunk_len); + debug_assert_eq!(chunk.measure::(), chunk_len); if byte_offset <= chunk.len() / 2 { - todo!(); - // M::measure_leaf(&chunk[..byte_offset]) + M::measure_leaf(&chunk[..byte_offset]) } else { - todo!(); - // chunk_len - M::measure_leaf(&chunk[byte_offset..]) + chunk_len - M::measure_leaf(&chunk[byte_offset..]) } } @@ -1206,7 +1210,7 @@ impl GapBuffer { } impl Leaf for GapBuffer { - type Summary = ChunkSummary; + type Summary = GapBufferSummary; type BaseMetric = ByteMetric; type Slice<'a> = GapSlice<'a>; @@ -1228,7 +1232,9 @@ impl Leaf for GapBuffer { #[inline] fn summarize(&self) -> Self::Summary { - self.left_summary + self.right_summary + GapBufferSummary { + chunks_summary: self.left_summary + self.right_summary, + } } } @@ -1333,6 +1339,87 @@ impl ReplaceableLeaf for GapBuffer { } } +impl Summary for GapBufferSummary { + type Leaf = GapBuffer; + + #[inline] + fn empty() -> Self { + Self { chunks_summary: ChunkSummary::empty() } + } +} + +impl Add for GapBufferSummary { + type Output = Self; + + #[inline] + fn add(mut self, other: Self) -> Self::Output { + self += other; + self + } +} + +impl AddAssign for GapBufferSummary { + #[inline] + fn add_assign(&mut self, rhs: Self) { + self.chunks_summary += rhs.chunks_summary; + } +} + +impl Sub for GapBufferSummary { + type Output = Self; + + #[inline] + fn sub(mut self, other: Self) -> Self::Output { + self -= other; + self + } +} + +impl SubAssign for GapBufferSummary { + #[inline] + fn sub_assign(&mut self, rhs: Self) { + self.chunks_summary -= rhs.chunks_summary; + } +} + +impl Deref for GapBufferSummary { + type Target = ChunkSummary; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.chunks_summary + } +} + +impl> Metric for M { + #[inline(always)] + fn zero() -> Self { + >::zero() + } + + #[inline(always)] + fn one() -> Self { + >::one() + } + + #[inline(always)] + fn measure(summary: &GapBufferSummary) -> Self { + M::measure(&summary.chunks_summary) + } + + #[inline(always)] + fn measure_leaf(gap_buffer: &GapBuffer) -> Self { + M::measure(&gap_buffer.left_summary) + + M::measure(&gap_buffer.right_summary) + } + + #[inline(always)] + fn measure_slice(gap_slice: &GapSlice) -> Self { + M::measure(&gap_slice.left_summary) + + M::measure(&gap_slice.right_summary) + } +} + /// Segments a string into [`GapBuffer`]s with at least /// [`GapBuffer::chunk_min()`] bytes. /// diff --git a/src/rope/gap_slice.rs b/src/rope/gap_slice.rs index 4d1519d..66cdff8 100644 --- a/src/rope/gap_slice.rs +++ b/src/rope/gap_slice.rs @@ -1,4 +1,4 @@ -use super::gap_buffer::GapBuffer; +use super::gap_buffer::{GapBuffer, GapBufferSummary}; use super::metrics::{ChunkSummary, SummaryUpTo, ToByteOffset}; use super::utils::{debug_no_quotes, panic_messages as panic}; use crate::tree::{LeafSlice, Metric, Summary}; @@ -328,8 +328,10 @@ impl<'a> LeafSlice<'a> for GapSlice<'a> { type Leaf = GapBuffer; #[inline] - fn summarize(&self) -> ChunkSummary { - self.right_summary + self.left_summary + fn summarize(&self) -> GapBufferSummary { + GapBufferSummary { + chunks_summary: self.right_summary + self.left_summary, + } } } diff --git a/src/rope/metrics.rs b/src/rope/metrics.rs index 51891b1..9800d1a 100644 --- a/src/rope/metrics.rs +++ b/src/rope/metrics.rs @@ -1,10 +1,11 @@ use core::ops::{Add, AddAssign, Sub, SubAssign}; -use super::gap_buffer::GapBuffer; +use super::gap_buffer::{GapBuffer, GapBufferSummary}; use super::gap_slice::GapSlice; use crate::tree::{ DoubleEndedUnitMetric, FromMetric, + Leaf, LeafSlice, Metric, SlicingMetric, @@ -70,7 +71,7 @@ impl ChunkSummary { } impl Summary for ChunkSummary { - type Leaf = GapBuffer; + type Leaf = str; #[inline] fn empty() -> Self { @@ -122,6 +123,31 @@ impl SubAssign for ChunkSummary { } } +impl Leaf for str { + type BaseMetric = ByteMetric; + type Slice<'a> = &'a Self; + type Summary = ChunkSummary; + + #[inline] + fn as_slice(&self) -> Self::Slice<'_> { + self + } + + #[inline] + fn summarize(&self) -> Self::Summary { + ChunkSummary::from(self) + } +} + +impl<'a> LeafSlice<'a> for &'a str { + type Leaf = str; + + #[inline] + fn summarize(&self) -> ::Summary { + ChunkSummary::from(*self) + } +} + /// Conversion trait from the metric implement this trait to the corresponding /// byte offset. pub trait ToByteOffset: Metric { @@ -247,17 +273,17 @@ impl Metric for ByteMetric { } #[inline] - fn measure_leaf(gap_buffer: &GapBuffer) -> Self { - Self(gap_buffer.len()) + fn measure_leaf(str: &str) -> Self { + Self(str.len()) } #[inline] - fn measure_slice(gap_slice: &GapSlice) -> Self { - Self(gap_slice.len()) + fn measure_slice(str: &&str) -> Self { + Self(str.len()) } } -impl FromMetric for ByteMetric { +impl FromMetric for ByteMetric { #[inline] fn measure_up_to( gap_buffer: &GapBuffer, @@ -268,7 +294,7 @@ impl FromMetric for ByteMetric { } #[cfg(feature = "utf16-metric")] -impl FromMetric for ByteMetric { +impl FromMetric for ByteMetric { #[inline] fn measure_up_to( gap_buffer: &GapBuffer, @@ -335,7 +361,7 @@ impl SubAssign for RawLineMetric { } } -impl FromMetric for RawLineMetric { +impl FromMetric for RawLineMetric { #[inline] fn measure_up_to( gap_buffer: &GapBuffer, @@ -394,19 +420,13 @@ impl Metric for RawLineMetric { } #[inline] - fn measure_leaf(gap_buffer: &GapBuffer) -> Self { - Self( - gap_buffer.left_summary.line_breaks - + gap_buffer.right_summary.line_breaks, - ) + fn measure_leaf(str: &str) -> Self { + Self(count::line_breaks(str)) } #[inline] - fn measure_slice(gap_slice: &GapSlice) -> Self { - Self( - gap_slice.left_summary.line_breaks - + gap_slice.right_summary.line_breaks, - ) + fn measure_slice(str: &&str) -> Self { + Self(count::line_breaks(str)) } } @@ -449,7 +469,7 @@ impl DoubleEndedUnitMetric for RawLineMetric { where 'a: 'a, { - let split_offset = slice.summarize().line_breaks + let split_offset = slice.measure::().0 - (slice.has_trailing_newline() as usize); let (rest, last) = slice.split_at_offset(RawLineMetric(split_offset)); @@ -525,13 +545,13 @@ impl Metric for LineMetric { } #[inline] - fn measure_leaf(gap_buffer: &GapBuffer) -> Self { - Self(RawLineMetric::measure_leaf(gap_buffer).0) + fn measure_leaf(str: &str) -> Self { + Self(count::line_breaks(str)) } #[inline] - fn measure_slice(gap_slice: &GapSlice) -> Self { - Self(RawLineMetric::measure_slice(gap_slice).0) + fn measure_slice(str: &&str) -> Self { + Self(count::line_breaks(str)) } } @@ -671,23 +691,17 @@ mod utf16_metric { } #[inline] - fn measure_leaf(gap_buffer: &GapBuffer) -> Self { - Self( - gap_buffer.left_summary.utf16_code_units - + gap_buffer.right_summary.utf16_code_units, - ) + fn measure_leaf(str: &str) -> Self { + Self(count::utf16_code_units(str)) } #[inline] - fn measure_slice(gap_slice: &GapSlice) -> Self { - Self( - gap_slice.left_summary.utf16_code_units - + gap_slice.right_summary.utf16_code_units, - ) + fn measure_slice(str: &&str) -> Self { + Self(count::utf16_code_units(str)) } } - impl FromMetric for Utf16Metric { + impl FromMetric for Utf16Metric { #[track_caller] #[inline] fn measure_up_to( diff --git a/src/rope/rope_slice.rs b/src/rope/rope_slice.rs index 9df0300..1bfff64 100644 --- a/src/rope/rope_slice.rs +++ b/src/rope/rope_slice.rs @@ -591,12 +591,11 @@ impl<'a> RopeSlice<'a> { if slice.end_slice.base_measure() == ByteMetric(1) { *self = self.byte_slice(..self.byte_len() - 1); } - // The last slice contains more than 2 bytes so we can just mutate - // in place. + // The last slice contains at least 2 bytes, so we can just mutate in + // place. else { - let last = &mut slice.end_slice; - - slice.summary -= last.truncate_last_char(); + slice.summary.chunks_summary -= + slice.end_slice.truncate_last_char(); if slice.leaf_count() == 1 { slice.start_slice = slice.end_slice; diff --git a/src/tree/traits.rs b/src/tree/traits.rs index eaf9e10..ea604c1 100644 --- a/src/tree/traits.rs +++ b/src/tree/traits.rs @@ -3,7 +3,7 @@ use core::ops::{Add, AddAssign, RangeBounds, Sub, SubAssign}; pub trait Summary: Debug + Clone + AddAssign + SubAssign { /// The leaf type this summary is for. - type Leaf: Leaf; + type Leaf: Leaf + ?Sized; fn empty() -> Self; @@ -23,7 +23,7 @@ pub trait Summary: Debug + Clone + AddAssign + SubAssign { } } -pub trait Leaf: Debug + Sized { +pub trait Leaf: Debug { type BaseMetric: Metric; type Slice<'a>: LeafSlice<'a, Leaf = Self> @@ -56,7 +56,7 @@ pub trait LeafSlice<'a>: Copy + Debug where Self: 'a, { - type Leaf: Leaf = Self>; + type Leaf: Leaf = Self> + ?Sized; fn summarize(&self) -> ::Summary; From ebb0b48bfa1bbe667db860ed37cd1d6015a692f4 Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Thu, 14 Aug 2025 14:12:51 +0200 Subject: [PATCH 24/35] Rename `ChunkSummary` to `StrSummary` --- src/lib.rs | 2 +- src/rope/gap_buffer.rs | 125 +++++++++++++++++++-------------------- src/rope/gap_slice.rs | 26 ++++---- src/rope/metrics.rs | 66 ++++++++++----------- src/rope/rope_builder.rs | 6 +- 5 files changed, 112 insertions(+), 113 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 952809e..52350ad 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -169,7 +169,7 @@ pub use rope::{Rope, RopeBuilder, RopeSlice}; pub use rope::{ gap_buffer::GapBuffer, gap_slice::GapSlice, - metrics::ChunkSummary, + metrics::StrSummary, }; #[inline] diff --git a/src/rope/gap_buffer.rs b/src/rope/gap_buffer.rs index f50ba98..f5135fc 100644 --- a/src/rope/gap_buffer.rs +++ b/src/rope/gap_buffer.rs @@ -9,7 +9,7 @@ use core::cmp::Ordering; use core::ops::{Add, AddAssign, Deref, Range, RangeBounds, Sub, SubAssign}; use super::gap_slice::GapSlice; -use super::metrics::{ByteMetric, ChunkSummary, ToByteOffset}; +use super::metrics::{ByteMetric, StrSummary, ToByteOffset}; use super::utils::{panic_messages as panic, *}; use crate::range_bounds_to_start_end; use crate::tree::{BalancedLeaf, Leaf, Metric, ReplaceableLeaf, Summary}; @@ -43,14 +43,14 @@ const MAX_BYTES: usize = 2048; #[derive(Clone)] pub struct GapBuffer { pub(super) bytes: Box<[u8; MAX_BYTES]>, - pub(super) left_summary: ChunkSummary, - pub(super) right_summary: ChunkSummary, + pub(super) left_summary: StrSummary, + pub(super) right_summary: StrSummary, } #[derive(Copy, Clone, Default, Debug, PartialEq)] pub struct GapBufferSummary { - /// The sum of the [`ChunkSummary`]s of the left and right chunks. - pub(super) chunks_summary: ChunkSummary, + /// The sum of the [`StrSummary`]s of the left and right chunks. + pub(super) chunks_summary: StrSummary, } impl core::fmt::Debug for GapBuffer { @@ -69,8 +69,8 @@ impl Default for GapBuffer { fn default() -> Self { Self { bytes: Box::new([0u8; MAX_BYTES]), - left_summary: ChunkSummary::default(), - right_summary: ChunkSummary::default(), + left_summary: StrSummary::default(), + right_summary: StrSummary::default(), } } } @@ -135,7 +135,7 @@ impl GapBuffer { &mut self, bytes_to_add: usize, right: &mut Self, - ) -> ChunkSummary { + ) -> StrSummary { debug_assert!(right.len() >= bytes_to_add); debug_assert!(self.len() + bytes_to_add <= MAX_BYTES); @@ -156,7 +156,7 @@ impl GapBuffer { bytes_to_add - right.len_left(), ); - let summary = right.left_summary + ChunkSummary::from(move_left); + let summary = right.left_summary + StrSummary::from(move_left); self.append_two(right.left_chunk(), move_left, summary); @@ -208,8 +208,8 @@ impl GapBuffer { self.left_summary += self.right_summary; self.right_summary = other.left_summary + other.right_summary; - other.left_summary = ChunkSummary::new(); - other.right_summary = ChunkSummary::new(); + other.left_summary = StrSummary::new(); + other.right_summary = StrSummary::new(); } /// Appends the given string to `self`, shifting the bytes currently in the @@ -224,20 +224,20 @@ impl GapBuffer { /// # Examples /// /// ``` - /// # use crop::{GapBuffer, ChunkSummary}; + /// # use crop::{GapBuffer, StrSummary}; /// /// let mut buffer = GapBuffer::from("ab"); /// assert_eq!(buffer.left_chunk(), "a"); /// assert_eq!(buffer.right_chunk(), "b"); /// - /// buffer.append_str("c", ChunkSummary::from("c")); + /// buffer.append_str("c", StrSummary::from("c")); /// assert_eq!(buffer.left_chunk(), "a"); /// assert_eq!(buffer.right_chunk(), "bc"); /// ``` #[inline] - pub fn append_str(&mut self, str: &str, str_summary: ChunkSummary) { + pub fn append_str(&mut self, str: &str, str_summary: StrSummary) { debug_assert!(str.len() <= self.len_gap()); - debug_assert_eq!(str_summary, ChunkSummary::from(str)); + debug_assert_eq!(str_summary, StrSummary::from(str)); let start = MAX_BYTES - self.len_right(); @@ -261,19 +261,19 @@ impl GapBuffer { /// # Examples /// /// ``` - /// # use crop::{GapBuffer, ChunkSummary}; + /// # use crop::{GapBuffer, StrSummary}; /// let mut buffer = GapBuffer::from("ab"); /// - /// buffer.append_two("c", "d", ChunkSummary::from("cd")); + /// buffer.append_two("c", "d", StrSummary::from("cd")); /// assert_eq!(buffer.left_chunk(), "a"); /// assert_eq!(buffer.right_chunk(), "bcd"); /// ``` #[inline] - pub fn append_two(&mut self, a: &str, b: &str, a_b_summary: ChunkSummary) { + pub fn append_two(&mut self, a: &str, b: &str, a_b_summary: StrSummary) { debug_assert!(a.len() + b.len() <= self.len_gap()); debug_assert_eq!( a_b_summary, - ChunkSummary::from(a) + ChunkSummary::from(b) + StrSummary::from(a) + StrSummary::from(b) ); // Shift the right chunk to the left. @@ -340,12 +340,12 @@ impl GapBuffer { #[inline] pub(super) fn convert_measure_from_byte(&self, byte_offset: usize) -> M where - M: Metric, + M: Metric, { debug_assert!(self.is_char_boundary(byte_offset)); #[inline] - fn measure_up_to>( + fn measure_up_to>( chunk: &str, chunk_len: M, byte_offset: usize, @@ -379,7 +379,7 @@ impl GapBuffer { #[inline] pub(super) fn convert_measure_to_byte(&self, offset: M) -> usize where - M: Metric + ToByteOffset, + M: Metric + ToByteOffset, { let len_left = self.left_summary.measure::(); @@ -420,7 +420,7 @@ impl GapBuffer { let mut bytes = Box::new([0u8; MAX_BYTES]); - let mut left_summary = ChunkSummary::new(); + let mut left_summary = StrSummary::new(); let mut chunks = chunks.iter(); @@ -434,7 +434,7 @@ impl GapBuffer { bytes[range].copy_from_slice(chunk.as_bytes()); - left_summary += ChunkSummary::from(chunk); + left_summary += StrSummary::from(chunk); } else { let (to_left, to_right) = split_adjusted::( chunk, @@ -449,7 +449,7 @@ impl GapBuffer { bytes[range].copy_from_slice(to_left.as_bytes()); - left_summary += ChunkSummary::from(to_left); + left_summary += StrSummary::from(to_left); let mut start = MAX_BYTES - (total_len - left_summary.bytes()); @@ -462,7 +462,7 @@ impl GapBuffer { start += to_right.len(); - let mut right_summary = ChunkSummary::from(to_right); + let mut right_summary = StrSummary::from(to_right); for &chunk in chunks { let range = { @@ -474,7 +474,7 @@ impl GapBuffer { start += chunk.len(); - right_summary += ChunkSummary::from(chunk); + right_summary += StrSummary::from(chunk); } return Self { bytes, left_summary, right_summary }; @@ -515,7 +515,7 @@ impl GapBuffer { self.bytes[insert_range].copy_from_slice(s.as_bytes()); - let inserted_summary = ChunkSummary::from(s); + let inserted_summary = StrSummary::from(s); self.left_summary += inserted_summary; } @@ -697,7 +697,7 @@ impl GapBuffer { self.len_right() - bytes_to_move, ); - let moved_summary = ChunkSummary::from(move_right); + let moved_summary = StrSummary::from(move_right); right.prepend(move_right, moved_summary); @@ -708,7 +708,7 @@ impl GapBuffer { self.len_left() - (bytes_to_move - self.len_right()), ); - let move_right_summary = ChunkSummary::from(move_right); + let move_right_summary = StrSummary::from(move_right); let moved_summary = move_right_summary + self.right_summary; @@ -724,26 +724,26 @@ impl GapBuffer { /// /// Panics if the resulting buffer would have a length greater than /// `MAX_BYTES`, or if the given summary differs from the string's - /// `ChunkSummary`. + /// `StrSummary`. /// /// # Examples /// /// ``` - /// # use crop::{ChunkSummary, GapBuffer}; + /// # use crop::{StrSummary, GapBuffer}; /// let mut buf = GapBuffer::from("at"); /// /// let prepend = "c"; /// - /// let prepended_summary = ChunkSummary::from(prepend); + /// let prepended_summary = StrSummary::from(prepend); /// /// buf.prepend(prepend, prepended_summary); /// /// assert_eq!(buf, "cat"); /// ``` #[inline] - pub fn prepend(&mut self, str: &str, str_summary: ChunkSummary) { + pub fn prepend(&mut self, str: &str, str_summary: StrSummary) { debug_assert!(str.len() <= self.len_gap()); - debug_assert_eq!(str_summary, ChunkSummary::from(str)); + debug_assert_eq!(str_summary, StrSummary::from(str)); // Shift the left chunk over. let len_left = self.len_left(); @@ -766,15 +766,14 @@ impl GapBuffer { /// # Examples /// /// ``` - /// # use crop::{ChunkSummary, GapBuffer}; + /// # use crop::{StrSummary, GapBuffer}; /// let mut buf = GapBuffer::from("!"); /// /// let first = "c"; /// /// let second = "at"; /// - /// let prepended_summary = - /// ChunkSummary::from(first) + ChunkSummary::from(second); + /// let prepended_summary = StrSummary::from(first) + StrSummary::from(second); /// /// buf.prepend_two(first, second, prepended_summary); /// @@ -785,13 +784,13 @@ impl GapBuffer { &mut self, a: &str, b: &str, - prepended_summary: ChunkSummary, + prepended_summary: StrSummary, ) { debug_assert!(a.len() + b.len() <= self.len_gap()); debug_assert_eq!( prepended_summary, - ChunkSummary::from(a) + ChunkSummary::from(b) + StrSummary::from(a) + StrSummary::from(b) ); // Shift the left chunk to the right. @@ -818,10 +817,10 @@ impl GapBuffer { /// # Examples /// /// ``` - /// # use crop::{ChunkSummary, GapBuffer}; + /// # use crop::{StrSummary, GapBuffer}; /// let mut buffer = GapBuffer::from("a\nbc"); /// - /// let removed_summary = ChunkSummary::from("a\n"); + /// let removed_summary = StrSummary::from("a\n"); /// /// buffer.remove_up_to(2, removed_summary); /// @@ -831,7 +830,7 @@ impl GapBuffer { pub fn remove_up_to( &mut self, byte_offset: usize, - removed_summary: ChunkSummary, + removed_summary: StrSummary, ) { debug_assert!(byte_offset <= self.len()); debug_assert!(self.is_char_boundary(byte_offset)); @@ -853,7 +852,7 @@ impl GapBuffer { self.left_summary -= removed_summary; } else { self.right_summary -= removed_summary - self.left_summary; - self.left_summary = ChunkSummary::new(); + self.left_summary = StrSummary::new(); } } @@ -893,7 +892,7 @@ impl GapBuffer { let removed_summary = self.summarize_range(start..end); - let added_summary = ChunkSummary::from(s); + let added_summary = StrSummary::from(s); self.bytes[start..start + s.len()].copy_from_slice(s.as_bytes()); @@ -1075,15 +1074,15 @@ impl GapBuffer { /// Returns the summary of the left chunk up to the given byte offset. #[inline] - fn summarize_left_chunk_up_to(&self, byte_offset: usize) -> ChunkSummary { + fn summarize_left_chunk_up_to(&self, byte_offset: usize) -> StrSummary { debug_assert!(byte_offset <= self.len_left()); debug_assert!(self.left_chunk().is_char_boundary(byte_offset)); if byte_offset <= self.len_left() / 2 { - ChunkSummary::from(&self.left_chunk()[..byte_offset]) + StrSummary::from(&self.left_chunk()[..byte_offset]) } else { self.left_summary - - ChunkSummary::from(&self.left_chunk()[byte_offset..]) + - StrSummary::from(&self.left_chunk()[byte_offset..]) } } @@ -1115,7 +1114,7 @@ impl GapBuffer { pub fn summarize_range( &self, Range { start, end }: Range, - ) -> ChunkSummary { + ) -> StrSummary { debug_assert!(start <= end); debug_assert!(end <= self.len()); debug_assert!(self.is_char_boundary(start)); @@ -1126,17 +1125,17 @@ impl GapBuffer { buffer: &GapBuffer, mut start: usize, mut end: usize, - ) -> ChunkSummary { + ) -> StrSummary { // The whole range is inside the left chunk. if end <= buffer.len_left() { let chunk = &buffer.left_chunk()[start..end]; - ChunkSummary::from(chunk) + StrSummary::from(chunk) } // The start is on the left chunk and the end is on the right. else if start <= buffer.len_left() { let left_chunk = &buffer.left_chunk()[start..]; - ChunkSummary::from(left_chunk) + StrSummary::from(left_chunk) + buffer .summarize_right_chunk_up_to(end - buffer.len_left()) } @@ -1145,7 +1144,7 @@ impl GapBuffer { start -= buffer.len_left(); end -= buffer.len_left(); let chunk = &buffer.right_chunk()[start..end]; - ChunkSummary::from(chunk) + StrSummary::from(chunk) } } @@ -1167,15 +1166,15 @@ impl GapBuffer { /// Note that the offset is only relative to the right chunk, not to the /// whole gap buffer. #[inline] - fn summarize_right_chunk_up_to(&self, byte_offset: usize) -> ChunkSummary { + fn summarize_right_chunk_up_to(&self, byte_offset: usize) -> StrSummary { debug_assert!(byte_offset <= self.len_right()); debug_assert!(self.right_chunk().is_char_boundary(byte_offset)); if byte_offset <= self.len_right() / 2 { - ChunkSummary::from(&self.right_chunk()[..byte_offset]) + StrSummary::from(&self.right_chunk()[..byte_offset]) } else { self.right_summary - - ChunkSummary::from(&self.right_chunk()[byte_offset..]) + - StrSummary::from(&self.right_chunk()[byte_offset..]) } } @@ -1190,7 +1189,7 @@ impl GapBuffer { if byte_offset <= self.len_left() { self.left_summary = self.summarize_left_chunk_up_to(byte_offset); - self.right_summary = ChunkSummary::new(); + self.right_summary = StrSummary::new(); } else { let offset = byte_offset - self.len_left(); @@ -1344,7 +1343,7 @@ impl Summary for GapBufferSummary { #[inline] fn empty() -> Self { - Self { chunks_summary: ChunkSummary::empty() } + Self { chunks_summary: StrSummary::empty() } } } @@ -1383,7 +1382,7 @@ impl SubAssign for GapBufferSummary { } impl Deref for GapBufferSummary { - type Target = ChunkSummary; + type Target = StrSummary; #[inline] fn deref(&self) -> &Self::Target { @@ -1391,15 +1390,15 @@ impl Deref for GapBufferSummary { } } -impl> Metric for M { +impl> Metric for M { #[inline(always)] fn zero() -> Self { - >::zero() + >::zero() } #[inline(always)] fn one() -> Self { - >::one() + >::one() } #[inline(always)] @@ -1595,7 +1594,7 @@ mod tests { buffer.move_gap(2); let offset = 1; - buffer.remove_up_to(offset, ChunkSummary::from(&s[..offset])); + buffer.remove_up_to(offset, StrSummary::from(&s[..offset])); assert_eq!("bb", buffer); } diff --git a/src/rope/gap_slice.rs b/src/rope/gap_slice.rs index 66cdff8..989e4ec 100644 --- a/src/rope/gap_slice.rs +++ b/src/rope/gap_slice.rs @@ -1,5 +1,5 @@ use super::gap_buffer::{GapBuffer, GapBufferSummary}; -use super::metrics::{ChunkSummary, SummaryUpTo, ToByteOffset}; +use super::metrics::{StrSummary, SummaryUpTo, ToByteOffset}; use super::utils::{debug_no_quotes, panic_messages as panic}; use crate::tree::{LeafSlice, Metric, Summary}; @@ -7,8 +7,8 @@ use crate::tree::{LeafSlice, Metric, Summary}; #[derive(Copy, Clone, Default)] pub struct GapSlice<'a> { pub(super) bytes: &'a [u8], - pub(super) left_summary: ChunkSummary, - pub(super) right_summary: ChunkSummary, + pub(super) left_summary: StrSummary, + pub(super) right_summary: StrSummary, } impl core::fmt::Debug for GapSlice<'_> { @@ -55,7 +55,7 @@ impl<'a> GapSlice<'a> { } pub(super) fn assert_invariants(&self) { - assert_eq!(self.left_summary, ChunkSummary::from(self.left_chunk())); + assert_eq!(self.left_summary, StrSummary::from(self.left_chunk())); if self.len_right() == 0 { assert_eq!(self.len_left(), self.bytes.len()); @@ -84,13 +84,13 @@ impl<'a> GapSlice<'a> { #[inline] fn left_measure(&self) -> M where - M: Metric, + M: Metric, { self.left_summary.measure::() } #[inline] - pub(super) fn truncate_last_char(&mut self) -> ChunkSummary { + pub(super) fn truncate_last_char(&mut self) -> StrSummary { debug_assert!(self.len() > 0); use core::cmp::Ordering; @@ -101,7 +101,7 @@ impl<'a> GapSlice<'a> { .next_back() .expect("this slice isn't empty"); - let removed_summary = ChunkSummary::from(last_char); + let removed_summary = StrSummary::from(last_char); let len_utf8 = removed_summary.bytes(); @@ -123,7 +123,7 @@ impl<'a> GapSlice<'a> { // The right chunk has exactly 1 character, so we can keep just the // left chunk. Ordering::Equal => { - self.right_summary = ChunkSummary::new(); + self.right_summary = StrSummary::new(); self.bytes = &self.bytes[..self.len_left()]; }, } @@ -134,9 +134,9 @@ impl<'a> GapSlice<'a> { /// Removes the trailing line break (if it has one), returning the summary /// of what was removed. #[inline] - pub(super) fn truncate_trailing_line_break(&mut self) -> ChunkSummary { + pub(super) fn truncate_trailing_line_break(&mut self) -> StrSummary { if !self.has_trailing_newline() { - return ChunkSummary::new(); + return StrSummary::new(); } let mut removed_summary = self.truncate_last_char(); @@ -241,7 +241,7 @@ impl<'a> GapSlice<'a> { #[inline] pub fn split_at_offset(&self, mut offset: M) -> (Self, Self) where - M: Metric + ToByteOffset + SummaryUpTo, + M: Metric + ToByteOffset + SummaryUpTo, { debug_assert!(offset <= self.measure::()); @@ -260,7 +260,7 @@ impl<'a> GapSlice<'a> { let left = Self { bytes: bytes_left, left_summary: left_left_summary, - right_summary: ChunkSummary::new(), + right_summary: StrSummary::new(), }; let right = Self { @@ -294,7 +294,7 @@ impl<'a> GapSlice<'a> { let right = Self { bytes: bytes_right, left_summary: self.right_summary - right_left_summary, - right_summary: ChunkSummary::new(), + right_summary: StrSummary::new(), }; (left, right) diff --git a/src/rope/metrics.rs b/src/rope/metrics.rs index 9800d1a..1c7a8b9 100644 --- a/src/rope/metrics.rs +++ b/src/rope/metrics.rs @@ -15,14 +15,14 @@ use crate::tree::{ #[derive(Copy, Clone, Default, Debug, PartialEq)] #[doc(hidden)] -pub struct ChunkSummary { +pub struct StrSummary { bytes: usize, line_breaks: usize, #[cfg(feature = "utf16-metric")] utf16_code_units: usize, } -impl From<&str> for ChunkSummary { +impl From<&str> for StrSummary { #[inline] fn from(s: &str) -> Self { Self { @@ -34,7 +34,7 @@ impl From<&str> for ChunkSummary { } } -impl From for ChunkSummary { +impl From for StrSummary { #[inline] fn from(ch: char) -> Self { Self { @@ -46,7 +46,7 @@ impl From for ChunkSummary { } } -impl ChunkSummary { +impl StrSummary { #[inline] pub fn bytes(&self) -> usize { self.bytes @@ -70,7 +70,7 @@ impl ChunkSummary { } } -impl Summary for ChunkSummary { +impl Summary for StrSummary { type Leaf = str; #[inline] @@ -79,7 +79,7 @@ impl Summary for ChunkSummary { } } -impl Add for ChunkSummary { +impl Add for StrSummary { type Output = Self; #[inline] @@ -89,7 +89,7 @@ impl Add for ChunkSummary { } } -impl AddAssign for ChunkSummary { +impl AddAssign for StrSummary { #[inline] fn add_assign(&mut self, rhs: Self) { self.bytes += rhs.bytes; @@ -101,7 +101,7 @@ impl AddAssign for ChunkSummary { } } -impl Sub for ChunkSummary { +impl Sub for StrSummary { type Output = Self; #[inline] @@ -111,7 +111,7 @@ impl Sub for ChunkSummary { } } -impl SubAssign for ChunkSummary { +impl SubAssign for StrSummary { #[inline] fn sub_assign(&mut self, rhs: Self) { self.bytes -= rhs.bytes; @@ -126,7 +126,7 @@ impl SubAssign for ChunkSummary { impl Leaf for str { type BaseMetric = ByteMetric; type Slice<'a> = &'a Self; - type Summary = ChunkSummary; + type Summary = StrSummary; #[inline] fn as_slice(&self) -> Self::Slice<'_> { @@ -135,7 +135,7 @@ impl Leaf for str { #[inline] fn summarize(&self) -> Self::Summary { - ChunkSummary::from(self) + StrSummary::from(self) } } @@ -144,29 +144,29 @@ impl<'a> LeafSlice<'a> for &'a str { #[inline] fn summarize(&self) -> ::Summary { - ChunkSummary::from(*self) + StrSummary::from(*self) } } /// Conversion trait from the metric implement this trait to the corresponding /// byte offset. -pub trait ToByteOffset: Metric { +pub trait ToByteOffset: Metric { /// Should return the byte offset of `self` in the given string. fn to_byte_offset(&self, in_str: &str) -> usize; } /// Trait to get the summary of a string up to a given offset. -pub trait SummaryUpTo: Metric { +pub trait SummaryUpTo: Metric { /// Return the summary of the given string up to `offset`, where /// /// * `str_summary` is the string's summary, /// * `byte_offset` is byte offset of `offset`. fn up_to( in_str: &str, - str_summary: ChunkSummary, + str_summary: StrSummary, offset: Self, byte_offset: usize, - ) -> ChunkSummary; + ) -> StrSummary; } #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] @@ -231,13 +231,13 @@ impl SummaryUpTo for ByteMetric { #[inline] fn up_to( in_str: &str, - str_summary: ChunkSummary, + str_summary: StrSummary, offset: Self, byte_offset: usize, - ) -> ChunkSummary { + ) -> StrSummary { debug_assert_eq!(offset.0, byte_offset); - ChunkSummary { + StrSummary { bytes: byte_offset, line_breaks: count::line_breaks_up_to( @@ -256,7 +256,7 @@ impl SummaryUpTo for ByteMetric { } } -impl Metric for ByteMetric { +impl Metric for ByteMetric { #[inline] fn zero() -> Self { Self(0) @@ -268,7 +268,7 @@ impl Metric for ByteMetric { } #[inline] - fn measure(summary: &ChunkSummary) -> Self { + fn measure(summary: &StrSummary) -> Self { Self(summary.bytes) } @@ -384,11 +384,11 @@ impl SummaryUpTo for RawLineMetric { #[inline] fn up_to( in_str: &str, - str_summary: ChunkSummary, + str_summary: StrSummary, Self(line_offset): Self, byte_offset: usize, - ) -> ChunkSummary { - ChunkSummary { + ) -> StrSummary { + StrSummary { bytes: byte_offset, line_breaks: line_offset, @@ -403,7 +403,7 @@ impl SummaryUpTo for RawLineMetric { } } -impl Metric for RawLineMetric { +impl Metric for RawLineMetric { #[inline] fn zero() -> Self { Self(0) @@ -415,7 +415,7 @@ impl Metric for RawLineMetric { } #[inline] - fn measure(summary: &ChunkSummary) -> Self { + fn measure(summary: &StrSummary) -> Self { Self(summary.line_breaks) } @@ -528,7 +528,7 @@ impl SubAssign for LineMetric { } } -impl Metric for LineMetric { +impl Metric for LineMetric { #[inline] fn zero() -> Self { Self(0) @@ -540,7 +540,7 @@ impl Metric for LineMetric { } #[inline] - fn measure(summary: &ChunkSummary) -> Self { + fn measure(summary: &StrSummary) -> Self { Self(summary.line_breaks) } @@ -656,11 +656,11 @@ mod utf16_metric { #[inline] fn up_to( in_str: &str, - str_summary: ChunkSummary, + str_summary: StrSummary, Self(utf16_code_unit_offset): Self, byte_offset: usize, - ) -> ChunkSummary { - ChunkSummary { + ) -> StrSummary { + StrSummary { bytes: byte_offset, line_breaks: count::line_breaks_up_to( @@ -674,7 +674,7 @@ mod utf16_metric { } } - impl Metric for Utf16Metric { + impl Metric for Utf16Metric { #[inline] fn zero() -> Self { Self(0) @@ -686,7 +686,7 @@ mod utf16_metric { } #[inline] - fn measure(summary: &ChunkSummary) -> Self { + fn measure(summary: &StrSummary) -> Self { Self(summary.utf16_code_units) } diff --git a/src/rope/rope_builder.rs b/src/rope/rope_builder.rs index 54bfa08..e51abf5 100644 --- a/src/rope/rope_builder.rs +++ b/src/rope/rope_builder.rs @@ -1,6 +1,6 @@ use super::Rope; use super::gap_buffer::GapBuffer; -use super::metrics::ChunkSummary; +use super::metrics::StrSummary; use super::rope::RopeChunk; use super::utils::split_adjusted; use crate::tree::TreeBuilder; @@ -59,7 +59,7 @@ impl RopeBuilder { text, ) { self.buffer.left_summary = - ChunkSummary::from(self.buffer_left_chunk()); + StrSummary::from(self.buffer_left_chunk()); self.tree_builder.append(core::mem::take(&mut self.buffer)); @@ -104,7 +104,7 @@ impl RopeBuilder { pub fn build(mut self) -> Rope { if self.buffer_len_left > 0 { self.buffer.left_summary = - ChunkSummary::from(self.buffer_left_chunk()); + StrSummary::from(self.buffer_left_chunk()); self.rope_has_trailing_newline = self.buffer.has_trailing_newline(); From f5c7ead4ac583df297ed943c3f369ee2fbb8f6e0 Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Thu, 14 Aug 2025 14:20:19 +0200 Subject: [PATCH 25/35] Prefer `str.summarize()` over `StrSummary::from(str)` --- src/rope/gap_buffer.rs | 83 +++++++++++++++++----------------------- src/rope/gap_slice.rs | 2 +- src/rope/metrics.rs | 45 +++++++++------------- src/rope/rope_builder.rs | 9 ++--- 4 files changed, 59 insertions(+), 80 deletions(-) diff --git a/src/rope/gap_buffer.rs b/src/rope/gap_buffer.rs index f5135fc..27f7ff8 100644 --- a/src/rope/gap_buffer.rs +++ b/src/rope/gap_buffer.rs @@ -156,7 +156,7 @@ impl GapBuffer { bytes_to_add - right.len_left(), ); - let summary = right.left_summary + StrSummary::from(move_left); + let summary = right.left_summary + move_left.summarize(); self.append_two(right.left_chunk(), move_left, summary); @@ -224,20 +224,21 @@ impl GapBuffer { /// # Examples /// /// ``` - /// # use crop::{GapBuffer, StrSummary}; + /// # use crop::GapBuffer; + /// # use crop::tree::Leaf; /// /// let mut buffer = GapBuffer::from("ab"); /// assert_eq!(buffer.left_chunk(), "a"); /// assert_eq!(buffer.right_chunk(), "b"); /// - /// buffer.append_str("c", StrSummary::from("c")); + /// buffer.append_str("c", "c".summarize()); /// assert_eq!(buffer.left_chunk(), "a"); /// assert_eq!(buffer.right_chunk(), "bc"); /// ``` #[inline] pub fn append_str(&mut self, str: &str, str_summary: StrSummary) { debug_assert!(str.len() <= self.len_gap()); - debug_assert_eq!(str_summary, StrSummary::from(str)); + debug_assert_eq!(str_summary, str.summarize()); let start = MAX_BYTES - self.len_right(); @@ -261,20 +262,18 @@ impl GapBuffer { /// # Examples /// /// ``` - /// # use crop::{GapBuffer, StrSummary}; + /// # use crop::GapBuffer; + /// # use crop::tree::Leaf; /// let mut buffer = GapBuffer::from("ab"); /// - /// buffer.append_two("c", "d", StrSummary::from("cd")); + /// buffer.append_two("c", "d", "cd".summarize()); /// assert_eq!(buffer.left_chunk(), "a"); /// assert_eq!(buffer.right_chunk(), "bcd"); /// ``` #[inline] pub fn append_two(&mut self, a: &str, b: &str, a_b_summary: StrSummary) { debug_assert!(a.len() + b.len() <= self.len_gap()); - debug_assert_eq!( - a_b_summary, - StrSummary::from(a) + StrSummary::from(b) - ); + debug_assert_eq!(a_b_summary, a.summarize() + b.summarize()); // Shift the right chunk to the left. let start = MAX_BYTES - self.len_right(); @@ -434,7 +433,7 @@ impl GapBuffer { bytes[range].copy_from_slice(chunk.as_bytes()); - left_summary += StrSummary::from(chunk); + left_summary += chunk.summarize(); } else { let (to_left, to_right) = split_adjusted::( chunk, @@ -449,7 +448,7 @@ impl GapBuffer { bytes[range].copy_from_slice(to_left.as_bytes()); - left_summary += StrSummary::from(to_left); + left_summary += to_left.summarize(); let mut start = MAX_BYTES - (total_len - left_summary.bytes()); @@ -462,7 +461,7 @@ impl GapBuffer { start += to_right.len(); - let mut right_summary = StrSummary::from(to_right); + let mut right_summary = to_right.summarize(); for &chunk in chunks { let range = { @@ -474,7 +473,7 @@ impl GapBuffer { start += chunk.len(); - right_summary += StrSummary::from(chunk); + right_summary += chunk.summarize(); } return Self { bytes, left_summary, right_summary }; @@ -515,7 +514,7 @@ impl GapBuffer { self.bytes[insert_range].copy_from_slice(s.as_bytes()); - let inserted_summary = StrSummary::from(s); + let inserted_summary = s.summarize(); self.left_summary += inserted_summary; } @@ -697,7 +696,7 @@ impl GapBuffer { self.len_right() - bytes_to_move, ); - let moved_summary = StrSummary::from(move_right); + let moved_summary = move_right.summarize(); right.prepend(move_right, moved_summary); @@ -708,7 +707,7 @@ impl GapBuffer { self.len_left() - (bytes_to_move - self.len_right()), ); - let move_right_summary = StrSummary::from(move_right); + let move_right_summary = move_right.summarize(); let moved_summary = move_right_summary + self.right_summary; @@ -729,21 +728,20 @@ impl GapBuffer { /// # Examples /// /// ``` - /// # use crop::{StrSummary, GapBuffer}; + /// # use crop::GapBuffer; + /// # use crop::tree::Leaf; /// let mut buf = GapBuffer::from("at"); /// /// let prepend = "c"; /// - /// let prepended_summary = StrSummary::from(prepend); - /// - /// buf.prepend(prepend, prepended_summary); + /// buf.prepend(prepend, prepend.summarize()); /// /// assert_eq!(buf, "cat"); /// ``` #[inline] pub fn prepend(&mut self, str: &str, str_summary: StrSummary) { debug_assert!(str.len() <= self.len_gap()); - debug_assert_eq!(str_summary, StrSummary::from(str)); + debug_assert_eq!(str_summary, str.summarize()); // Shift the left chunk over. let len_left = self.len_left(); @@ -766,16 +764,15 @@ impl GapBuffer { /// # Examples /// /// ``` - /// # use crop::{StrSummary, GapBuffer}; + /// # use crop::GapBuffer; + /// # use crop::tree::Leaf; /// let mut buf = GapBuffer::from("!"); /// /// let first = "c"; /// /// let second = "at"; /// - /// let prepended_summary = StrSummary::from(first) + StrSummary::from(second); - /// - /// buf.prepend_two(first, second, prepended_summary); + /// buf.prepend_two(first, second, "cat".summarize()); /// /// assert_eq!(buf, "cat!"); /// ``` @@ -787,11 +784,7 @@ impl GapBuffer { prepended_summary: StrSummary, ) { debug_assert!(a.len() + b.len() <= self.len_gap()); - - debug_assert_eq!( - prepended_summary, - StrSummary::from(a) + StrSummary::from(b) - ); + debug_assert_eq!(prepended_summary, a.summarize() + b.summarize()); // Shift the left chunk to the right. let len_left = self.len_left(); @@ -817,12 +810,11 @@ impl GapBuffer { /// # Examples /// /// ``` - /// # use crop::{StrSummary, GapBuffer}; + /// # use crop::GapBuffer; + /// # use crop::tree::Leaf; /// let mut buffer = GapBuffer::from("a\nbc"); /// - /// let removed_summary = StrSummary::from("a\n"); - /// - /// buffer.remove_up_to(2, removed_summary); + /// buffer.remove_up_to(2, "a\n".summarize()); /// /// assert_eq!(buffer, "bc"); /// ``` @@ -892,7 +884,7 @@ impl GapBuffer { let removed_summary = self.summarize_range(start..end); - let added_summary = StrSummary::from(s); + let added_summary = s.summarize(); self.bytes[start..start + s.len()].copy_from_slice(s.as_bytes()); @@ -1079,10 +1071,9 @@ impl GapBuffer { debug_assert!(self.left_chunk().is_char_boundary(byte_offset)); if byte_offset <= self.len_left() / 2 { - StrSummary::from(&self.left_chunk()[..byte_offset]) + self.left_chunk()[..byte_offset].summarize() } else { - self.left_summary - - StrSummary::from(&self.left_chunk()[byte_offset..]) + self.left_summary - self.left_chunk()[byte_offset..].summarize() } } @@ -1128,14 +1119,13 @@ impl GapBuffer { ) -> StrSummary { // The whole range is inside the left chunk. if end <= buffer.len_left() { - let chunk = &buffer.left_chunk()[start..end]; - StrSummary::from(chunk) + buffer.left_chunk()[start..end].summarize() } // The start is on the left chunk and the end is on the right. else if start <= buffer.len_left() { let left_chunk = &buffer.left_chunk()[start..]; - StrSummary::from(left_chunk) + left_chunk.summarize() + buffer .summarize_right_chunk_up_to(end - buffer.len_left()) } @@ -1143,8 +1133,7 @@ impl GapBuffer { else { start -= buffer.len_left(); end -= buffer.len_left(); - let chunk = &buffer.right_chunk()[start..end]; - StrSummary::from(chunk) + buffer.right_chunk()[start..end].summarize() } } @@ -1171,10 +1160,10 @@ impl GapBuffer { debug_assert!(self.right_chunk().is_char_boundary(byte_offset)); if byte_offset <= self.len_right() / 2 { - StrSummary::from(&self.right_chunk()[..byte_offset]) + self.right_chunk()[..byte_offset].summarize() } else { self.right_summary - - StrSummary::from(&self.right_chunk()[byte_offset..]) + - self.right_chunk()[byte_offset..].summarize() } } @@ -1594,7 +1583,7 @@ mod tests { buffer.move_gap(2); let offset = 1; - buffer.remove_up_to(offset, StrSummary::from(&s[..offset])); + buffer.remove_up_to(offset, s[..offset].summarize()); assert_eq!("bb", buffer); } diff --git a/src/rope/gap_slice.rs b/src/rope/gap_slice.rs index 989e4ec..1d81a2a 100644 --- a/src/rope/gap_slice.rs +++ b/src/rope/gap_slice.rs @@ -55,7 +55,7 @@ impl<'a> GapSlice<'a> { } pub(super) fn assert_invariants(&self) { - assert_eq!(self.left_summary, StrSummary::from(self.left_chunk())); + assert_eq!(self.left_summary, self.left_chunk().summarize()); if self.len_right() == 0 { assert_eq!(self.len_left(), self.bytes.len()); diff --git a/src/rope/metrics.rs b/src/rope/metrics.rs index 1c7a8b9..2181690 100644 --- a/src/rope/metrics.rs +++ b/src/rope/metrics.rs @@ -22,30 +22,6 @@ pub struct StrSummary { utf16_code_units: usize, } -impl From<&str> for StrSummary { - #[inline] - fn from(s: &str) -> Self { - Self { - bytes: s.len(), - line_breaks: count::line_breaks(s), - #[cfg(feature = "utf16-metric")] - utf16_code_units: count::utf16_code_units(s), - } - } -} - -impl From for StrSummary { - #[inline] - fn from(ch: char) -> Self { - Self { - bytes: ch.len_utf8(), - line_breaks: (ch == '\n') as usize, - #[cfg(feature = "utf16-metric")] - utf16_code_units: ch.len_utf16(), - } - } -} - impl StrSummary { #[inline] pub fn bytes(&self) -> usize { @@ -79,6 +55,18 @@ impl Summary for StrSummary { } } +impl From for StrSummary { + #[inline] + fn from(ch: char) -> Self { + Self { + bytes: ch.len_utf8(), + line_breaks: (ch == '\n') as usize, + #[cfg(feature = "utf16-metric")] + utf16_code_units: ch.len_utf16(), + } + } +} + impl Add for StrSummary { type Output = Self; @@ -135,7 +123,12 @@ impl Leaf for str { #[inline] fn summarize(&self) -> Self::Summary { - StrSummary::from(self) + StrSummary { + bytes: self.len(), + line_breaks: count::line_breaks(self), + #[cfg(feature = "utf16-metric")] + utf16_code_units: count::utf16_code_units(self), + } } } @@ -144,7 +137,7 @@ impl<'a> LeafSlice<'a> for &'a str { #[inline] fn summarize(&self) -> ::Summary { - StrSummary::from(*self) + (*self).summarize() } } diff --git a/src/rope/rope_builder.rs b/src/rope/rope_builder.rs index e51abf5..cb84ab2 100644 --- a/src/rope/rope_builder.rs +++ b/src/rope/rope_builder.rs @@ -1,9 +1,8 @@ use super::Rope; use super::gap_buffer::GapBuffer; -use super::metrics::StrSummary; use super::rope::RopeChunk; use super::utils::split_adjusted; -use crate::tree::TreeBuilder; +use crate::tree::{Leaf, TreeBuilder}; /// An incremental [`Rope`](crate::Rope) builder. #[derive(Clone, Default)] @@ -58,8 +57,7 @@ impl RopeBuilder { &mut self.buffer_len_left, text, ) { - self.buffer.left_summary = - StrSummary::from(self.buffer_left_chunk()); + self.buffer.left_summary = self.buffer_left_chunk().summarize(); self.tree_builder.append(core::mem::take(&mut self.buffer)); @@ -103,8 +101,7 @@ impl RopeBuilder { #[inline] pub fn build(mut self) -> Rope { if self.buffer_len_left > 0 { - self.buffer.left_summary = - StrSummary::from(self.buffer_left_chunk()); + self.buffer.left_summary = self.buffer_left_chunk().summarize(); self.rope_has_trailing_newline = self.buffer.has_trailing_newline(); From 0a4000dc6dfab708b1622cfe52b155dc19839d49 Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Thu, 14 Aug 2025 14:24:22 +0200 Subject: [PATCH 26/35] Prefer `StrSummary::empty()` over `StrSummary::new()` --- src/rope/gap_buffer.rs | 13 ++++++------- src/rope/gap_slice.rs | 8 ++++---- src/rope/metrics.rs | 6 ------ 3 files changed, 10 insertions(+), 17 deletions(-) diff --git a/src/rope/gap_buffer.rs b/src/rope/gap_buffer.rs index 27f7ff8..d6a4d50 100644 --- a/src/rope/gap_buffer.rs +++ b/src/rope/gap_buffer.rs @@ -208,8 +208,8 @@ impl GapBuffer { self.left_summary += self.right_summary; self.right_summary = other.left_summary + other.right_summary; - other.left_summary = StrSummary::new(); - other.right_summary = StrSummary::new(); + other.left_summary = StrSummary::empty(); + other.right_summary = StrSummary::empty(); } /// Appends the given string to `self`, shifting the bytes currently in the @@ -419,7 +419,7 @@ impl GapBuffer { let mut bytes = Box::new([0u8; MAX_BYTES]); - let mut left_summary = StrSummary::new(); + let mut left_summary = StrSummary::empty(); let mut chunks = chunks.iter(); @@ -844,7 +844,7 @@ impl GapBuffer { self.left_summary -= removed_summary; } else { self.right_summary -= removed_summary - self.left_summary; - self.left_summary = StrSummary::new(); + self.left_summary = StrSummary::empty(); } } @@ -1162,8 +1162,7 @@ impl GapBuffer { if byte_offset <= self.len_right() / 2 { self.right_chunk()[..byte_offset].summarize() } else { - self.right_summary - - self.right_chunk()[byte_offset..].summarize() + self.right_summary - self.right_chunk()[byte_offset..].summarize() } } @@ -1178,7 +1177,7 @@ impl GapBuffer { if byte_offset <= self.len_left() { self.left_summary = self.summarize_left_chunk_up_to(byte_offset); - self.right_summary = StrSummary::new(); + self.right_summary = StrSummary::empty(); } else { let offset = byte_offset - self.len_left(); diff --git a/src/rope/gap_slice.rs b/src/rope/gap_slice.rs index 1d81a2a..138dc83 100644 --- a/src/rope/gap_slice.rs +++ b/src/rope/gap_slice.rs @@ -123,7 +123,7 @@ impl<'a> GapSlice<'a> { // The right chunk has exactly 1 character, so we can keep just the // left chunk. Ordering::Equal => { - self.right_summary = StrSummary::new(); + self.right_summary = StrSummary::empty(); self.bytes = &self.bytes[..self.len_left()]; }, } @@ -136,7 +136,7 @@ impl<'a> GapSlice<'a> { #[inline] pub(super) fn truncate_trailing_line_break(&mut self) -> StrSummary { if !self.has_trailing_newline() { - return StrSummary::new(); + return StrSummary::empty(); } let mut removed_summary = self.truncate_last_char(); @@ -260,7 +260,7 @@ impl<'a> GapSlice<'a> { let left = Self { bytes: bytes_left, left_summary: left_left_summary, - right_summary: StrSummary::new(), + right_summary: StrSummary::empty(), }; let right = Self { @@ -294,7 +294,7 @@ impl<'a> GapSlice<'a> { let right = Self { bytes: bytes_right, left_summary: self.right_summary - right_left_summary, - right_summary: StrSummary::new(), + right_summary: StrSummary::empty(), }; (left, right) diff --git a/src/rope/metrics.rs b/src/rope/metrics.rs index 2181690..0f023dd 100644 --- a/src/rope/metrics.rs +++ b/src/rope/metrics.rs @@ -33,12 +33,6 @@ impl StrSummary { self.line_breaks } - #[doc(hidden)] - #[inline] - pub fn new() -> Self { - Self::default() - } - #[cfg(feature = "utf16-metric")] #[inline] pub fn utf16_code_units(&self) -> usize { From 2e8fa5f61e703ca9b8b7d3074aa08fe5a2fba36e Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Thu, 14 Aug 2025 14:43:25 +0200 Subject: [PATCH 27/35] Avoid the need for `Node::convert_measure_from_offset()` --- src/rope/rope_slice.rs | 15 ++++++++------- src/tree/node.rs | 10 ---------- src/tree/tree_slice.rs | 38 +++++++++++++++++++++++++++++--------- 3 files changed, 37 insertions(+), 26 deletions(-) diff --git a/src/rope/rope_slice.rs b/src/rope/rope_slice.rs index 1bfff64..8a478eb 100644 --- a/src/rope/rope_slice.rs +++ b/src/rope/rope_slice.rs @@ -115,8 +115,9 @@ impl<'a> RopeSlice<'a> { return self.byte_len(); } - let ByteMetric(byte_offset) = - self.tree_slice.convert_measure(RawLineMetric(line_offset)); + let ByteMetric(byte_offset) = self + .tree_slice + .convert_measure_to_base(RawLineMetric(line_offset)); byte_offset } @@ -150,9 +151,9 @@ impl<'a> RopeSlice<'a> { panic::utf16_offset_out_of_bounds(utf16_offset, self.utf16_len()) } - let ByteMetric(byte_offset) = self - .tree_slice - .convert_measure(super::metrics::Utf16Metric(utf16_offset)); + let ByteMetric(byte_offset) = self.tree_slice.convert_measure_to_base( + super::metrics::Utf16Metric(utf16_offset), + ); byte_offset } @@ -463,7 +464,7 @@ impl<'a> RopeSlice<'a> { } let RawLineMetric(line_offset) = - self.tree_slice.convert_measure(ByteMetric(byte_offset)); + self.tree_slice.convert_measure_from_base(ByteMetric(byte_offset)); line_offset } @@ -671,7 +672,7 @@ impl<'a> RopeSlice<'a> { } let super::metrics::Utf16Metric(utf16_offset) = - self.tree_slice.convert_measure(ByteMetric(byte_offset)); + self.tree_slice.convert_measure_from_base(ByteMetric(byte_offset)); utf16_offset } diff --git a/src/tree/node.rs b/src/tree/node.rs index f0d6f9b..8732844 100644 --- a/src/tree/node.rs +++ b/src/tree/node.rs @@ -114,16 +114,6 @@ impl Node { } } - #[inline] - pub(super) fn convert_measure_from_offset( - &self, - _start_from: L::BaseMetric, - _up_to: M1, - ) -> M2 -where { - todo!(); - } - #[inline] pub(super) fn depth(&self) -> usize { match self { diff --git a/src/tree/tree_slice.rs b/src/tree/tree_slice.rs index 3049d03..14f4425 100644 --- a/src/tree/tree_slice.rs +++ b/src/tree/tree_slice.rs @@ -49,20 +49,40 @@ impl<'a, const ARITY: usize, L: Leaf> TreeSlice<'a, ARITY, L> { self.measure::() } - /// Returns the `M2`-measure of all the leaves before `up_to` plus the - /// `M2`-measure of the left sub-slice of the leaf at `up_to`. + /// Returns the `M`-measure of all the leaves before the given offset, plus + /// the `M`-measure of the left sub-slice of the leaf at the given offset. #[inline] - pub fn convert_measure(&self, up_to: M1) -> M2 + pub fn convert_measure_from_base(&self, base_offset: L::BaseMetric) -> M where - M1: SlicingMetric, - M2: Metric, + M: FromMetric, + { + debug_assert!(base_offset <= self.base_measure()); + + if base_offset.is_zero() { + M::zero() + } else { + self.root.convert_measure::<_, M>(self.offset + base_offset) + - self.measure_offset::() + } + } + + /// Returns the base measure of all the leaves before the given offset, + /// plus the base measure of the left sub-slice of the leaf at the given + /// offset. + #[inline] + pub fn convert_measure_to_base(&self, offset: M) -> L::BaseMetric + where + M: FromMetric, + L::BaseMetric: FromMetric, { - debug_assert!(up_to <= self.measure::()); + debug_assert!(offset <= self.measure::()); - if up_to == M1::zero() { - M2::zero() + if offset.is_zero() { + L::BaseMetric::zero() } else { - self.root.convert_measure_from_offset(self.offset, up_to) + let m_offset = self.measure_offset::(); + self.root.convert_measure::<_, L::BaseMetric>(m_offset + offset) + - self.offset } } From 786da19525407abec48a5275d7f8497d962fb4ff Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Thu, 14 Aug 2025 15:05:03 +0200 Subject: [PATCH 28/35] Prefer "length" over "measure" --- src/rope/gap_buffer.rs | 4 +- src/rope/gap_slice.rs | 2 +- src/rope/metrics.rs | 8 +- src/rope/rope.rs | 15 ++-- src/rope/rope_slice.rs | 21 +++-- src/tree/inode.rs | 30 +++---- src/tree/leaves.rs | 93 ++++++++++----------- src/tree/node.rs | 22 ++--- src/tree/traits.rs | 12 +-- src/tree/tree.rs | 60 ++++++------- src/tree/tree_slice.rs | 90 ++++++++++---------- src/tree/units.rs | 185 ++++++++++++++++++++--------------------- 12 files changed, 265 insertions(+), 277 deletions(-) diff --git a/src/rope/gap_buffer.rs b/src/rope/gap_buffer.rs index d6a4d50..3f20032 100644 --- a/src/rope/gap_buffer.rs +++ b/src/rope/gap_buffer.rs @@ -337,7 +337,7 @@ impl GapBuffer { } #[inline] - pub(super) fn convert_measure_from_byte(&self, byte_offset: usize) -> M + pub(super) fn convert_len_from_byte(&self, byte_offset: usize) -> M where M: Metric, { @@ -376,7 +376,7 @@ impl GapBuffer { } #[inline] - pub(super) fn convert_measure_to_byte(&self, offset: M) -> usize + pub(super) fn convert_len_to_byte(&self, offset: M) -> usize where M: Metric + ToByteOffset, { diff --git a/src/rope/gap_slice.rs b/src/rope/gap_slice.rs index 138dc83..71a5c97 100644 --- a/src/rope/gap_slice.rs +++ b/src/rope/gap_slice.rs @@ -223,7 +223,7 @@ impl<'a> GapSlice<'a> { /// /// # Panics /// - /// Panics if the offset is greater than the M-measure of the slice. + /// Panics if the offset is greater than the M-length of the slice. /// /// # Examples /// diff --git a/src/rope/metrics.rs b/src/rope/metrics.rs index 0f023dd..e0866dd 100644 --- a/src/rope/metrics.rs +++ b/src/rope/metrics.rs @@ -276,7 +276,7 @@ impl FromMetric for ByteMetric { gap_buffer: &GapBuffer, line_offset: RawLineMetric, ) -> Self { - Self(gap_buffer.convert_measure_to_byte(line_offset)) + Self(gap_buffer.convert_len_to_byte(line_offset)) } } @@ -287,7 +287,7 @@ impl FromMetric for ByteMetric { gap_buffer: &GapBuffer, utf16_offset: utf16_metric::Utf16Metric, ) -> Self { - Self(gap_buffer.convert_measure_to_byte(utf16_offset)) + Self(gap_buffer.convert_len_to_byte(utf16_offset)) } } @@ -355,7 +355,7 @@ impl FromMetric for RawLineMetric { ByteMetric(byte_offset): ByteMetric, ) -> Self { gap_buffer.assert_char_boundary(byte_offset); - gap_buffer.convert_measure_from_byte::(byte_offset) + gap_buffer.convert_len_from_byte::(byte_offset) } } @@ -696,7 +696,7 @@ mod utf16_metric { ByteMetric(byte_offset): ByteMetric, ) -> Self { gap_buffer.assert_char_boundary(byte_offset); - gap_buffer.convert_measure_from_byte::(byte_offset) + gap_buffer.convert_len_from_byte::(byte_offset) } } diff --git a/src/rope/rope.rs b/src/rope/rope.rs index 6da2669..fefe2ae 100644 --- a/src/rope/rope.rs +++ b/src/rope/rope.rs @@ -81,7 +81,7 @@ impl Rope { } let (chunk, ByteMetric(chunk_byte_offset)) = - self.tree.leaf_at_measure(ByteMetric(byte_index + 1)); + self.tree.leaf_at_offset(ByteMetric(byte_index + 1)); chunk.byte(byte_index - chunk_byte_offset) } @@ -133,7 +133,7 @@ impl Rope { } let ByteMetric(byte_offset) = - self.tree.convert_measure(RawLineMetric(line_offset)); + self.tree.convert_len(RawLineMetric(line_offset)); byte_offset } @@ -166,9 +166,8 @@ impl Rope { panic::utf16_offset_out_of_bounds(utf16_offset, self.utf16_len()) } - let ByteMetric(byte_offset) = self - .tree - .convert_measure(super::metrics::Utf16Metric(utf16_offset)); + let ByteMetric(byte_offset) = + self.tree.convert_len(super::metrics::Utf16Metric(utf16_offset)); byte_offset } @@ -375,7 +374,7 @@ impl Rope { } let (chunk, ByteMetric(chunk_byte_offset)) = - self.tree.leaf_at_measure(ByteMetric(byte_offset)); + self.tree.leaf_at_offset(ByteMetric(byte_offset)); chunk.is_char_boundary(byte_offset - chunk_byte_offset) } @@ -534,7 +533,7 @@ impl Rope { } let RawLineMetric(line_offset) = - self.tree.convert_measure(ByteMetric(byte_offset)); + self.tree.convert_len(ByteMetric(byte_offset)); line_offset } @@ -757,7 +756,7 @@ impl Rope { } let super::metrics::Utf16Metric(utf16_offset) = - self.tree.convert_measure(ByteMetric(byte_offset)); + self.tree.convert_len(ByteMetric(byte_offset)); utf16_offset } diff --git a/src/rope/rope_slice.rs b/src/rope/rope_slice.rs index 8a478eb..f833940 100644 --- a/src/rope/rope_slice.rs +++ b/src/rope/rope_slice.rs @@ -57,7 +57,7 @@ impl<'a> RopeSlice<'a> { } let (chunk, ByteMetric(chunk_byte_offset)) = - self.tree_slice.leaf_at_measure(ByteMetric(byte_index + 1)); + self.tree_slice.leaf_at_offset(ByteMetric(byte_index + 1)); chunk.byte(byte_index - chunk_byte_offset) } @@ -115,9 +115,8 @@ impl<'a> RopeSlice<'a> { return self.byte_len(); } - let ByteMetric(byte_offset) = self - .tree_slice - .convert_measure_to_base(RawLineMetric(line_offset)); + let ByteMetric(byte_offset) = + self.tree_slice.convert_len_to_base(RawLineMetric(line_offset)); byte_offset } @@ -151,9 +150,9 @@ impl<'a> RopeSlice<'a> { panic::utf16_offset_out_of_bounds(utf16_offset, self.utf16_len()) } - let ByteMetric(byte_offset) = self.tree_slice.convert_measure_to_base( - super::metrics::Utf16Metric(utf16_offset), - ); + let ByteMetric(byte_offset) = self + .tree_slice + .convert_len_to_base(super::metrics::Utf16Metric(utf16_offset)); byte_offset } @@ -302,7 +301,7 @@ impl<'a> RopeSlice<'a> { } let (chunk, ByteMetric(chunk_byte_offset)) = - self.tree_slice.leaf_at_measure(ByteMetric(byte_offset)); + self.tree_slice.leaf_at_offset(ByteMetric(byte_offset)); chunk.is_char_boundary(byte_offset - chunk_byte_offset) } @@ -464,7 +463,7 @@ impl<'a> RopeSlice<'a> { } let RawLineMetric(line_offset) = - self.tree_slice.convert_measure_from_base(ByteMetric(byte_offset)); + self.tree_slice.convert_len_from_base(ByteMetric(byte_offset)); line_offset } @@ -589,7 +588,7 @@ impl<'a> RopeSlice<'a> { let slice = &mut self.tree_slice; // The last slice only contains one byte so we have to re-slice. - if slice.end_slice.base_measure() == ByteMetric(1) { + if slice.end_slice.base_len() == ByteMetric(1) { *self = self.byte_slice(..self.byte_len() - 1); } // The last slice contains at least 2 bytes, so we can just mutate in @@ -672,7 +671,7 @@ impl<'a> RopeSlice<'a> { } let super::metrics::Utf16Metric(utf16_offset) = - self.tree_slice.convert_measure_from_base(ByteMetric(byte_offset)); + self.tree_slice.convert_len_from_base(ByteMetric(byte_offset)); utf16_offset } diff --git a/src/tree/inode.rs b/src/tree/inode.rs index c66a942..ce7a06c 100644 --- a/src/tree/inode.rs +++ b/src/tree/inode.rs @@ -379,7 +379,7 @@ impl Inode { } #[inline] - pub fn base_measure(&self) -> L::BaseMetric { + pub fn base_len(&self) -> L::BaseMetric { self.measure::() } @@ -393,18 +393,18 @@ impl Inode { &self.children } - /// Returns the index of the child at the given measure together - /// with the combined `M`-offset of the other children up to but not - /// including that child. + /// Returns the index of the child at the given offset together with the + /// combined M-length of the other children up to but not including that + /// child. #[inline] - pub(super) fn child_at_measure(&self, measure: M) -> (usize, M) + pub(super) fn child_at_offset(&self, offset: M) -> (usize, M) where M: Metric, { - debug_assert!(measure <= self.measure::()); + debug_assert!(offset <= self.measure::()); let mut idx = 0; - let mut offset = M::zero(); + let mut measured = M::zero(); let children = self.children(); loop { @@ -415,12 +415,12 @@ impl Inode { // goes out of bounds. let child = unsafe { children.get_unchecked(idx) }; - let child_measure = child.measure::(); + let child_len = child.measure::(); - offset += child_measure; + measured += child_len; - if offset >= measure { - return (idx, offset - child_measure); + if offset <= measured { + return (idx, measured - child_len); } idx += 1; @@ -454,13 +454,13 @@ impl Inode { // before the index goes out of bounds. let child = unsafe { children.get_unchecked(idx) }; - let measure = child.measure::(); + let child_len = child.measure::(); - offset += measure; + offset += child_len; if offset >= range.start { if offset >= range.end { - return Some((idx, offset - measure)); + return Some((idx, offset - child_len)); } else { return None; } @@ -768,7 +768,7 @@ impl Inode { #[inline] pub fn measure>(&self) -> M { - M::measure(self.summary()) + self.summary().measure() } #[inline] diff --git a/src/tree/leaves.rs b/src/tree/leaves.rs index 5f1a4c6..d2a9d5e 100644 --- a/src/tree/leaves.rs +++ b/src/tree/leaves.rs @@ -50,7 +50,7 @@ impl<'a, const ARITY: usize, L: Leaf> Iterator for Leaves<'a, ARITY, L> { #[inline] fn next(&mut self) -> Option { - if self.measure_yielded() == self.measure_total() { + if self.len_yielded() == self.len_total() { None } else { self.forward.next() @@ -63,7 +63,7 @@ impl DoubleEndedIterator { #[inline] fn next_back(&mut self) -> Option { - if self.measure_yielded() == self.measure_total() { + if self.len_yielded() == self.len_total() { None } else { self.backward.previous() @@ -77,21 +77,18 @@ impl core::iter::FusedIterator } impl Leaves<'_, ARITY, L> { - /// Returns the total base measure of all the leaves in the tree or slice + /// Returns the total base length of all the leaves in the tree or slice /// being iterated over. #[inline] - fn measure_total(&self) -> L::BaseMetric { - debug_assert_eq!( - self.forward.measure_total, - self.backward.measure_total - ); - self.forward.measure_total + fn len_total(&self) -> L::BaseMetric { + debug_assert_eq!(self.forward.len_total, self.backward.len_total); + self.forward.len_total } - /// Returns the base measure of all the leaf slices yielded so far. + /// Returns the base length of all the leaf slices yielded so far. #[inline] - fn measure_yielded(&self) -> L::BaseMetric { - self.forward.measure_yielded + self.backward.measure_yielded + fn len_yielded(&self) -> L::BaseMetric { + self.forward.len_yielded + self.backward.len_yielded } } @@ -124,12 +121,12 @@ struct LeavesForward<'a, const N: usize, L: Leaf> { /// iterating over a `Tree`. base_offset: L::BaseMetric, - /// The total base measure of all the leaves in the tree or slice being + /// The total base length of all the leaves in the tree or slice being /// iterated over. - measure_total: L::BaseMetric, + len_total: L::BaseMetric, - /// The base measure of all the leaf slices yielded so far. - measure_yielded: L::BaseMetric, + /// The base length of all the leaf slices yielded so far. + len_yielded: L::BaseMetric, } impl Clone for LeavesForward<'_, N, L> { @@ -152,8 +149,8 @@ impl<'a, const N: usize, L: Leaf> From<&'a Tree> path: Vec::with_capacity(tree.root().depth().saturating_sub(1)), leaves: &[], next_leaf_idx: 0, - measure_yielded: L::BaseMetric::zero(), - measure_total: tree.base_measure(), + len_yielded: L::BaseMetric::zero(), + len_total: tree.base_len(), } } } @@ -171,8 +168,8 @@ impl<'a, const ARITY: usize, L: Leaf> From<&TreeSlice<'a, ARITY, L>> path: Vec::with_capacity(slice.root().depth().saturating_sub(1)), leaves: &[], next_leaf_idx: 0, - measure_yielded: L::BaseMetric::zero(), - measure_total: slice.base_measure(), + len_yielded: L::BaseMetric::zero(), + len_total: slice.base_len(), } } } @@ -215,7 +212,7 @@ impl<'a, const N: usize, L: Leaf> LeavesForward<'a, N, L> { #[inline] fn initialize(&mut self) -> (L::Slice<'a>, &'a [Arc>]) { - debug_assert!(self.measure_yielded.is_zero()); + debug_assert!(self.len_yielded.is_zero()); let mut inode = match self.root { Node::Internal(inode) => inode, @@ -241,7 +238,7 @@ impl<'a, const N: usize, L: Leaf> LeavesForward<'a, N, L> { }) .enumerate() { - let this = i.base_measure(); + let this = i.base_len(); if offset + this > self.base_offset { self.path.push((inode, idx)); @@ -263,7 +260,7 @@ impl<'a, const N: usize, L: Leaf> LeavesForward<'a, N, L> { .children() .iter() .take_while(|child| { - offset += child.base_measure(); + offset += child.base_len(); offset <= self.base_offset }) .count(); @@ -282,13 +279,13 @@ impl<'a, const N: usize, L: Leaf> LeavesForward<'a, N, L> { #[inline] fn next(&mut self) -> Option> { - if self.measure_yielded == self.measure_total { + if self.len_yielded == self.len_total { return None; } - if self.measure_yielded.is_zero() { + if self.len_yielded.is_zero() { let (first, rest) = self.initialize(); - self.measure_yielded = first.base_measure(); + self.len_yielded = first.base_len(); self.leaves = rest; return Some(first); } @@ -299,12 +296,12 @@ impl<'a, const N: usize, L: Leaf> LeavesForward<'a, N, L> { let next_leaf = &self.leaves[self.next_leaf_idx].get_leaf(); self.next_leaf_idx += 1; - self.measure_yielded += next_leaf.base_measure(); + self.len_yielded += next_leaf.base_len(); // If we're iterating over a `TreeSlice`, make sure we're not yielding // past its end_slice. - if self.measure_yielded > self.measure_total { - self.measure_yielded = self.measure_total; + if self.len_yielded > self.len_total { + self.len_yielded = self.len_total; self.last_slice.take() } else { Some(next_leaf.as_slice()) @@ -337,17 +334,17 @@ struct LeavesBackward<'a, const N: usize, L: Leaf> { /// we're iterating over a `TreeSlice`. last_slice: Option>, - /// The base measure between the end of [`last_slice`](Self::last_slice) + /// The base length between the end of [`last_slice`](Self::last_slice) /// and the end of the subtree under [`root`](Self::root), or zero if we're /// iterating over a `Tree`. base_offset: L::BaseMetric, - /// The total base measure of all the leaves in the tree or slice being + /// The total base length of all the leaves in the tree or slice being /// iterated over. - measure_total: L::BaseMetric, + len_total: L::BaseMetric, - /// The base measure of all the leaf slices yielded so far. - measure_yielded: L::BaseMetric, + /// The base length of all the leaf slices yielded so far. + len_yielded: L::BaseMetric, } impl Clone for LeavesBackward<'_, N, L> { @@ -370,8 +367,8 @@ impl<'a, const N: usize, L: Leaf> From<&'a Tree> path: Vec::with_capacity(tree.root().depth().saturating_sub(1)), leaves: &[], last_leaf_idx: 0, - measure_yielded: L::BaseMetric::zero(), - measure_total: tree.base_measure(), + len_yielded: L::BaseMetric::zero(), + len_total: tree.base_len(), } } } @@ -382,7 +379,7 @@ impl<'a, const ARITY: usize, L: Leaf> From<&TreeSlice<'a, ARITY, L>> #[inline] fn from(slice: &TreeSlice<'a, ARITY, L>) -> LeavesBackward<'a, ARITY, L> { let base_offset = - slice.root().base_measure() - slice.offset - slice.base_measure(); + slice.root().base_len() - slice.offset - slice.base_len(); Self { base_offset, @@ -392,8 +389,8 @@ impl<'a, const ARITY: usize, L: Leaf> From<&TreeSlice<'a, ARITY, L>> path: Vec::with_capacity(slice.root().depth().saturating_sub(1)), leaves: &[], last_leaf_idx: 0, - measure_yielded: L::BaseMetric::zero(), - measure_total: slice.base_measure(), + len_yielded: L::BaseMetric::zero(), + len_total: slice.base_len(), } } } @@ -436,7 +433,7 @@ impl<'a, const N: usize, L: Leaf> LeavesBackward<'a, N, L> { #[inline] fn initialize(&mut self) -> (&'a [Arc>], L::Slice<'a>) { - debug_assert!(self.measure_yielded.is_zero()); + debug_assert!(self.len_yielded.is_zero()); let mut inode = match self.root { Node::Internal(inode) => inode, @@ -463,7 +460,7 @@ impl<'a, const N: usize, L: Leaf> LeavesBackward<'a, N, L> { .enumerate() .rev() { - let this = i.base_measure(); + let this = i.base_len(); if offset + this > self.base_offset { self.path.push((inode, idx)); @@ -488,7 +485,7 @@ impl<'a, const N: usize, L: Leaf> LeavesBackward<'a, N, L> { .iter() .rev() .take_while(|child| { - offset += child.base_measure(); + offset += child.base_len(); offset <= self.base_offset }) .count(); @@ -507,13 +504,13 @@ impl<'a, const N: usize, L: Leaf> LeavesBackward<'a, N, L> { #[inline] fn previous(&mut self) -> Option> { - if self.measure_yielded == self.measure_total { + if self.len_yielded == self.len_total { return None; } - if self.measure_yielded.is_zero() { + if self.len_yielded.is_zero() { let (rest, last) = self.initialize(); - self.measure_yielded = last.base_measure(); + self.len_yielded = last.base_len(); self.leaves = rest; self.last_leaf_idx = rest.len(); return Some(last); @@ -525,12 +522,12 @@ impl<'a, const N: usize, L: Leaf> LeavesBackward<'a, N, L> { self.last_leaf_idx -= 1; let next_leaf = &self.leaves[self.last_leaf_idx].get_leaf(); - self.measure_yielded += next_leaf.base_measure(); + self.len_yielded += next_leaf.base_len(); // If we're iterating over a `TreeSlice`, make sure we're not yielding // past its start_slice. - if self.measure_yielded > self.measure_total { - self.measure_yielded = self.measure_total; + if self.len_yielded > self.len_total { + self.len_yielded = self.len_total; self.first_slice.take() } else { Some(next_leaf.as_slice()) diff --git a/src/tree/node.rs b/src/tree/node.rs index 8732844..88ed258 100644 --- a/src/tree/node.rs +++ b/src/tree/node.rs @@ -72,12 +72,12 @@ impl Node { } #[inline] - pub(super) fn base_measure(&self) -> L::BaseMetric { + pub(super) fn base_len(&self) -> L::BaseMetric { self.measure::() } #[inline] - pub(super) fn convert_measure(&self, up_to: M1) -> M2 + pub(super) fn convert_len(&self, up_to: M1) -> M2 where M1: Metric, M2: FromMetric, @@ -184,17 +184,17 @@ impl Node { } } - /// Returns the leaf at the given measure, together with the leaf's - /// `M`-offset in the tree. + /// Returns the leaf at the given offset, together with the leaf's + /// M-offset in the tree. /// - /// If the measure falls on a leaf boundary, the leaf to the left of the - /// measure is returned. + /// If the offset falls on a leaf boundary, the leaf to its left is + /// returned. #[inline] - pub(super) fn leaf_at_measure(&self, measure: M) -> (&L, M) + pub(super) fn leaf_at_offset(&self, offset: M) -> (&L, M) where M: Metric, { - debug_assert!(measure <= self.measure::()); + debug_assert!(offset <= self.measure::()); let mut measured = M::zero(); @@ -203,10 +203,10 @@ impl Node { loop { match node { Node::Internal(inode) => { - let (child_idx, offset) = - inode.child_at_measure(measure - measured); + let (child_idx, child_offset) = + inode.child_at_offset(offset - measured); - measured += offset; + measured += child_offset; node = inode.child(child_idx); }, diff --git a/src/tree/traits.rs b/src/tree/traits.rs index ea604c1..9d4531d 100644 --- a/src/tree/traits.rs +++ b/src/tree/traits.rs @@ -8,13 +8,13 @@ pub trait Summary: Debug + Clone + AddAssign + SubAssign { fn empty() -> Self; #[inline] - fn base_measure(&self) -> ::BaseMetric { + fn base_len(&self) -> ::BaseMetric { self.measure() } #[inline] fn is_empty(&self) -> bool { - self.base_measure().is_zero() + self.base_len().is_zero() } #[inline] @@ -37,13 +37,13 @@ pub trait Leaf: Debug { fn summarize(&self) -> Self::Summary; #[inline] - fn base_measure(&self) -> Self::BaseMetric { + fn base_len(&self) -> Self::BaseMetric { self.measure::() } #[inline] fn is_empty(&self) -> bool { - self.base_measure().is_zero() + self.base_len().is_zero() } #[inline] @@ -61,13 +61,13 @@ where fn summarize(&self) -> ::Summary; #[inline] - fn base_measure(&self) -> ::BaseMetric { + fn base_len(&self) -> ::BaseMetric { self.measure() } #[inline] fn is_empty(&self) -> bool { - self.base_measure().is_zero() + self.base_len().is_zero() } #[inline] diff --git a/src/tree/tree.rs b/src/tree/tree.rs index 4a00b08..aace0ee 100644 --- a/src/tree/tree.rs +++ b/src/tree/tree.rs @@ -32,8 +32,8 @@ impl From> { #[inline] fn from(slice: TreeSlice<'_, ARITY, L>) -> Tree { - let root = if slice.base_measure() == slice.root().base_measure() { - // If the TreeSlice and its root have the same base measure it + let root = if slice.base_len() == slice.root().base_len() { + // If the TreeSlice and its root have the same base length it // means the TreeSlice spanned the whole Tree from which it was // created and we can simply clone the root. Arc::clone(slice.root()) @@ -83,21 +83,21 @@ impl Tree { } #[inline] - pub fn base_measure(&self) -> L::BaseMetric { + pub fn base_len(&self) -> L::BaseMetric { self.measure::() } - /// Returns the `M2`-measure of all the leaves before `up_to` plus the - /// `M2`-measure of the left sub-slice of the leaf at `up_to`. + /// Returns the M2-length of all the leaves before `up_to` plus the + /// M2-length of the left sub-slice of the leaf at `up_to`. #[track_caller] #[inline] - pub fn convert_measure(&self, up_to: M1) -> M2 + pub fn convert_len(&self, up_to: M1) -> M2 where M1: Metric, M2: FromMetric, { debug_assert!(up_to <= self.measure::()); - self.root.convert_measure(up_to) + self.root.convert_len(up_to) } /// Creates a new `Tree` from a sequence of leaves. @@ -130,15 +130,15 @@ impl Tree { Self { root: Arc::new(Node::Internal(root)) } } - /// Returns the leaf containing the `measure`-th unit of the `M`-metric, - /// plus the `M`-measure of all the leaves before it. + /// Returns the leaf at the offset, plus the M-length of all the leaves + /// before it. #[inline] - pub fn leaf_at_measure(&self, measure: M) -> (&L, M) + pub fn leaf_at_offset(&self, offset: M) -> (&L, M) where M: Metric, { - debug_assert!(measure <= self.measure::() + M::one()); - self.root.leaf_at_measure(measure) + debug_assert!(offset <= self.measure::() + M::one()); + self.root.leaf_at_offset(offset) } /// Returns an iterator over the leaves of this `Tree`. @@ -147,8 +147,8 @@ impl Tree { Leaves::from(self) } - /// Returns the `M`-measure of this `Tree` obtaining by summing up the - /// `M`-measures of all its leaves. + /// Returns the M-length of this `Tree` obtaining by summing up the + /// M-lengths of all its leaves. #[inline] pub fn measure(&self) -> M where @@ -273,7 +273,7 @@ mod from_treeslice { /// Returns a `(root, invalid_first, invalid_last)` tuple where: /// /// - `root` is the internal node obtained by removing all the nodes before - /// `slice.before` and after `slice.before + slice.base_measure`, + /// `slice.before` and after `slice.before + slice.base_measure()`, /// /// - `invalid_{first,last}` are the number of invalid nodes contained in /// the subtrees of the first and last child, respectively. @@ -305,7 +305,7 @@ mod from_treeslice { let start = slice.offset; for child in children.by_ref() { - let this = child.base_measure(); + let this = child.base_len(); if offset + this > start { if start == L::BaseMetric::zero() { @@ -328,13 +328,13 @@ mod from_treeslice { } } - let end = start + slice.base_measure(); + let end = start + slice.base_len(); for child in children { - let this = child.base_measure(); + let this = child.base_len(); if offset + this >= end { - if end == slice.root().base_measure() { + if end == slice.root().base_len() { root.push(Arc::clone(child)); } else { let last = cut_end_rec( @@ -375,7 +375,7 @@ mod from_treeslice { let mut children = i.children().iter(); for child in children.by_ref() { - let this = child.base_measure(); + let this = child.base_len(); if offset + this > take_from { let first = cut_start_rec( @@ -439,7 +439,7 @@ mod from_treeslice { let mut offset = L::BaseMetric::zero(); for child in i.children() { - let this = child.base_measure(); + let this = child.base_len(); if offset + this >= take_up_to { let last = cut_end_rec( @@ -714,9 +714,9 @@ mod tree_replace { for (idx, child) in indexes.by_ref().map(|idx| (idx, inode.child(idx))) { - let child_measure = child.measure::(); + let child_len = child.measure::(); - offset += child_measure; + offset += child_len; if offset >= range.start { start_idx = idx; @@ -724,7 +724,7 @@ mod tree_replace { extra_leaves = inode.with_child_mut(start_idx, |child| { replace_nodes_in_start_subtree( Arc::make_mut(child), - range.start + child_measure - offset, + range.start + child_len - offset, replace_with, &mut start_should_rebalance, ) @@ -742,9 +742,9 @@ mod tree_replace { }); for (idx, child) in indexes.map(|idx| (idx, inode.child(idx))) { - let child_measure = child.measure::(); + let child_len = child.measure::(); - offset += child_measure; + offset += child_len; if offset >= range.end { end_idx = idx; @@ -752,7 +752,7 @@ mod tree_replace { inode.with_child_mut(end_idx, |child| { replace_nodes_in_end_subtree( Arc::make_mut(child), - range.end + child_measure - offset, + range.end + child_len - offset, &mut extra_leaves, &mut end_should_rebalance, ) @@ -815,7 +815,7 @@ mod tree_replace { }, }; - let (start_idx, offset) = inode.child_at_measure(replace_from); + let (start_idx, offset) = inode.child_at_offset(replace_from); let extra_leaves = inode.with_child_mut(start_idx, |child| { replace_nodes_in_start_subtree( @@ -895,9 +895,9 @@ mod tree_replace { let mut offset = inode.measure::(); for (idx, child) in inode.children().iter().enumerate().rev() { - let child_measure = child.measure::(); + let child_len = child.measure::(); - offset -= child_measure; + offset -= child_len; if offset < replace_up_to { end_idx = idx; diff --git a/src/tree/tree_slice.rs b/src/tree/tree_slice.rs index 14f4425..fad604a 100644 --- a/src/tree/tree_slice.rs +++ b/src/tree/tree_slice.rs @@ -45,32 +45,32 @@ impl Copy for TreeSlice<'_, ARITY, L> where impl<'a, const ARITY: usize, L: Leaf> TreeSlice<'a, ARITY, L> { #[inline] - pub fn base_measure(&self) -> L::BaseMetric { + pub fn base_len(&self) -> L::BaseMetric { self.measure::() } - /// Returns the `M`-measure of all the leaves before the given offset, plus - /// the `M`-measure of the left sub-slice of the leaf at the given offset. + /// Returns the M-length of all the leaves before the given offset, plus + /// the M-length of the left sub-slice of the leaf at the given offset. #[inline] - pub fn convert_measure_from_base(&self, base_offset: L::BaseMetric) -> M + pub fn convert_len_from_base(&self, base_offset: L::BaseMetric) -> M where M: FromMetric, { - debug_assert!(base_offset <= self.base_measure()); + debug_assert!(base_offset <= self.base_len()); if base_offset.is_zero() { M::zero() } else { - self.root.convert_measure::<_, M>(self.offset + base_offset) - - self.measure_offset::() + self.root.convert_len::<_, M>(self.offset + base_offset) + - self.offset_len::() } } - /// Returns the base measure of all the leaves before the given offset, - /// plus the base measure of the left sub-slice of the leaf at the given + /// Returns the base length of all the leaves before the given offset, + /// plus the base length of the left sub-slice of the leaf at the given /// offset. #[inline] - pub fn convert_measure_to_base(&self, offset: M) -> L::BaseMetric + pub fn convert_len_to_base(&self, offset: M) -> L::BaseMetric where M: FromMetric, L::BaseMetric: FromMetric, @@ -80,8 +80,8 @@ impl<'a, const ARITY: usize, L: Leaf> TreeSlice<'a, ARITY, L> { if offset.is_zero() { L::BaseMetric::zero() } else { - let m_offset = self.measure_offset::(); - self.root.convert_measure::<_, L::BaseMetric>(m_offset + offset) + let m_offset = self.offset_len::(); + self.root.convert_len::<_, L::BaseMetric>(m_offset + offset) - self.offset } } @@ -91,40 +91,39 @@ impl<'a, const ARITY: usize, L: Leaf> TreeSlice<'a, ARITY, L> { self.end_slice } - /// Returns the leaf containing the `measure`-th unit of the `M`-metric, - /// plus the `M`-measure of all the leaves before it. + /// Returns the leaf at the given M-offset, plus the M-length of all the + /// leaves before it. #[inline] - pub fn leaf_at_measure(&self, measure: M) -> (L::Slice<'a>, M) + pub fn leaf_at_offset(&self, offset: M) -> (L::Slice<'a>, M) where M: Metric + FromMetric, { - debug_assert!(measure <= self.measure::() + M::one()); + debug_assert!(offset <= self.measure::() + M::one()); let len_start_slice = self.start_slice.measure::(); - if measure <= len_start_slice { + if offset <= len_start_slice { return (self.start_slice, M::zero()); } let len_total_minus_end = self.measure::() - self.end_slice.measure::(); - if len_total_minus_end <= measure { + if len_total_minus_end <= offset { return (self.end_slice, len_total_minus_end); } - let offset = self.measure_offset::(); - let (leaf, leaf_offset) = self.root.leaf_at_measure(offset + measure); - (leaf.as_slice(), leaf_offset - offset) + let m_offset = self.offset_len::(); + let (leaf, leaf_offset) = self.root.leaf_at_offset(m_offset + offset); + (leaf.as_slice(), leaf_offset - m_offset) } #[inline] pub fn leaf_count(&self) -> SliceLeafCount { if self.root.is_leaf() { SliceLeafCount::One - } else if self.start_slice.base_measure() - + self.end_slice.base_measure() - == self.base_measure() + } else if self.start_slice.base_len() + self.end_slice.base_len() + == self.base_len() { SliceLeafCount::Two } else { @@ -170,9 +169,9 @@ impl<'a, const ARITY: usize, L: Leaf> TreeSlice<'a, ARITY, L> { if self.leaf_count() == 2 { assert_eq!( - self.summary.base_measure(), - self.start_slice.base_measure() - + self.end_slice.base_measure() + self.summary.base_len(), + self.start_slice.base_len() + + self.end_slice.base_len() ); } @@ -185,7 +184,7 @@ impl<'a, const ARITY: usize, L: Leaf> TreeSlice<'a, ARITY, L> { deepest_node_containing_base_range( self.root, start, - start + self.summary.base_measure(), + start + self.summary.base_len(), ) }; @@ -199,7 +198,7 @@ impl<'a, const ARITY: usize, L: Leaf> TreeSlice<'a, ARITY, L> { Node::Leaf(leaf) => { assert_eq!(self.leaf_count(), 1); - assert!(leaf.base_measure() >= self.base_measure()); + assert!(leaf.base_len() >= self.base_len()); }, } } @@ -210,11 +209,11 @@ impl<'a, const ARITY: usize, L: Leaf> TreeSlice<'a, ARITY, L> { /// Note that it's never necessary to call this with `L::BaseMetric`, as /// that's already known to be [`Self::offset`]. #[inline] - fn measure_offset(&self) -> M + fn offset_len(&self) -> M where M: FromMetric, { - self.root.convert_measure(self.offset) + self.root.convert_len(self.offset) } } @@ -245,7 +244,7 @@ where self.root, self.offset, range.start, - self.base_measure(), + self.base_len(), ) } } @@ -313,7 +312,7 @@ where let (root, offset) = deepest_node_containing_base_range( slice.root, start, - start + slice.summary.base_measure(), + start + slice.summary.base_len(), ); slice.root = root; @@ -385,30 +384,30 @@ where 'outer: loop { match &**node { Node::Internal(inode) => { - let mut measured = L::Summary::empty(); + let mut offset = L::Summary::empty(); for child in inode.children() { let child_summary = child.summary(); - let contains_start_slice = measured.measure::() + let contains_start_slice = offset.measure::() + child_summary.measure::() >= start; if contains_start_slice { - let contains_end_slice = measured.measure::() + let contains_end_slice = offset.measure::() + child_summary.measure::() >= end; if contains_end_slice { node = child; - start -= measured.measure::(); - end -= measured.measure::(); + start -= offset.measure::(); + end -= offset.measure::(); continue 'outer; } else { return (node, start, end); } } else { - measured += child_summary; + offset += child_summary; } } @@ -421,7 +420,7 @@ where } /// Same as [`deepest_node_containing_range`] except it only accepts base -/// measures and thus can check whether a node contains `start` using `>` +/// lengths and thus can check whether a node contains `start` using `>` /// instead of `>=` (because the remainder of a slice divided by the BaseMetric /// should always be zero), resulting in a potentially deeper node than the one /// returned by [`deepest_node_containing_range`]. @@ -444,7 +443,7 @@ where let mut measured = L::BaseMetric::zero(); for child in inode.children() { - let child_len = child.base_measure(); + let child_len = child.base_len(); let contains_start_slice = measured + child_len > start; @@ -538,7 +537,7 @@ fn build_slice<'a, const N: usize, L, S, E>( ); } else { // This child comes before the starting leaf. - slice.offset += child_summary.base_measure(); + slice.offset += child_summary.base_len(); *start_offset += child_summary.measure::(); *end_offset += child_summary.measure::(); } @@ -600,7 +599,7 @@ fn build_slice<'a, const N: usize, L, S, E>( leaf.measure::() - right_slice.measure::(); let left_slice_base_measure = - leaf.base_measure() - right_slice.base_measure(); + leaf.base_len() - right_slice.base_len(); let end = end - *end_offset - left_slice_end_measure; @@ -618,7 +617,7 @@ fn build_slice<'a, const N: usize, L, S, E>( S::slice_from(leaf.as_slice(), start - *start_offset); if start_slice.is_empty() { - slice.offset += leaf.base_measure(); + slice.offset += leaf.base_len(); *start_offset += leaf.measure::(); *end_offset += leaf.measure::(); *recompute_root = true; @@ -627,8 +626,7 @@ fn build_slice<'a, const N: usize, L, S, E>( let start_summary = start_slice.summarize(); - slice.offset += - leaf.base_measure() - start_summary.base_measure(); + slice.offset += leaf.base_len() - start_summary.base_len(); *end_offset += leaf.measure::() - start_summary.measure::(); diff --git a/src/tree/units.rs b/src/tree/units.rs index a772233..69fe594 100644 --- a/src/tree/units.rs +++ b/src/tree/units.rs @@ -22,7 +22,7 @@ use super::{Arc, Node, Tree, TreeSlice}; // other, which could cause them to overlap if alternating between calling // `Units::next()` and `Units::next_back()`. // -// To prevent this we also store the base measure of the unyielded iterating +// To prevent this we also store the base length of the unyielded iterating // range, which is decreased as new `TreeSliece`s are yielded (both forward and // backward). Once that reaches zero this iterator will stop yielding any more // items. @@ -34,7 +34,7 @@ pub struct Units<'a, const ARITY: usize, L: Leaf, M: Metric> { /// Iterates over the `M`-units from back to front. backward: UnitsBackward<'a, ARITY, L, M>, - /// The base measure of all the `TreeSlice`s which are yet to be yielded. + /// The base length of all the `TreeSlice`s which are yet to be yielded. remaining: L::BaseMetric, } @@ -48,7 +48,7 @@ where Self { forward: UnitsForward::from(tree), backward: UnitsBackward::from(tree), - remaining: tree.base_measure(), + remaining: tree.base_len(), } } } @@ -63,7 +63,7 @@ where Self { forward: UnitsForward::from(tree_slice), backward: UnitsBackward::from(tree_slice), - remaining: tree_slice.base_measure(), + remaining: tree_slice.base_len(), } } } @@ -78,18 +78,18 @@ impl<'a, const ARITY: usize, L: Leaf, M: UnitMetric> Iterator /// /// The advance describes how much of the iterating range has been yielded /// by returning this `TreeSlice`, and it's always bigger than or equal to - /// the base measure of the slice. + /// the base length of the slice. /// /// To give an example let's consider the string "foo\nbar\nbaz". /// /// The `RawLineMetric` would first yield "foo\n", then "bar\n", and /// finally "baz". In this case the advance is always equal to the base - /// measure of the slice. + /// length of the slice. /// /// On the other hand, the `LineMetric` would first yield "foo" without /// including the trailing newline, then "bar" and lastly "baz". In the /// first and second calls the advance is 1 byte longer than the byte - /// measure of the slice to account for the newlines, which are not part of + /// length of the slice to account for the newlines, which are not part of /// the returned slices. /// /// The name is taken from [typography][1], where the advance of a glyph is @@ -213,16 +213,16 @@ struct UnitsForward<'a, const N: usize, L: Leaf, M: Metric> { /// The start of the yielding range as an offset into the root. base_start: L::BaseMetric, - /// The base measure of all the advances yielded so far. + /// The base length of all the advances yielded so far. base_yielded: L::BaseMetric, - /// The total base measure of the iterating range (doesn't change). + /// The total base length of the iterating range (doesn't change). base_total: L::BaseMetric, /// The `M`-units yielded so far. units_yielded: M, - /// The total `M`-measure of the iterating range (doesn't change). + /// The total `M`-length of the iterating range (doesn't change). units_total: M, } @@ -252,7 +252,7 @@ where last_slice: None, base_start: L::BaseMetric::zero(), base_yielded: L::BaseMetric::zero(), - base_total: tree.base_measure(), + base_total: tree.base_len(), units_yielded: M::zero(), units_total: tree.measure::(), } @@ -278,7 +278,7 @@ where last_slice: Some(tree_slice.end_slice), base_start: tree_slice.offset, base_yielded: L::BaseMetric::zero(), - base_total: tree_slice.base_measure(), + base_total: tree_slice.base_len(), units_yielded: M::zero(), units_total: tree_slice.measure::(), } @@ -305,14 +305,14 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { match &**node { Node::Internal(inode) => { for (idx, child) in inode.children().iter().enumerate() { - let child_measure = child.base_measure(); + let child_len = child.base_len(); - if offset + child_measure > self.base_start { + if offset + child_len > self.base_start { self.path.push((node, idx)); node = child; continue 'outer; } else { - offset += child_measure; + offset += child_len; } } @@ -325,7 +325,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { match self.first_slice.take() { Some(slice) => { self.yielded_in_leaf = - leaf.base_measure() - slice.base_measure(); + leaf.base_len() - slice.base_len(); self.start_slice = slice; }, @@ -375,8 +375,8 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { Node::Leaf(leaf) => { self.leaf_node = node; - let contains_last_slice = leaf.base_measure() - > self.base_total - self.base_yielded; + let contains_last_slice = + leaf.base_len() > self.base_total - self.base_yielded; return if contains_last_slice { self.last_slice.take().unwrap() @@ -389,7 +389,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { } /// Yields the next unit in the current `self.leaf_node`. This function - /// should only be called when `self.start_slice` has an `M`-measure of at + /// should only be called when `self.start_slice` has an `M`-length of at /// least `M::one()`. #[inline] fn next_unit_in_leaf(&mut self) -> (TreeSlice<'a, N, L>, L::BaseMetric) { @@ -416,7 +416,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { } /// Traverses the path to reach the next leaf node with a non-zero - /// `M`-measure. + /// `M`-length. /// /// Returns a `(leaf, root, before, summary)` tuple where: /// @@ -425,10 +425,10 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { /// - `root` is the deepest internal node containing both the current /// `self.leaf_node` and `leaf` in its subtree; /// - /// - `before` is the total base measure of all the nodes from the first + /// - `before` is the total base length of all the nodes from the first /// leaf in `root`'s subtree to the leaf preceding the current /// `self.leaf_node`. If `self.leaf_node` is the first leaf in `root`'s - /// subtree this measure will be zero; + /// subtree this will be zero; /// /// - `summary` is the total summary of all the nodes between (but not /// including) `self.leaf_node` and `leaf`. If `leaf` is the leaf node @@ -461,7 +461,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { let inode = node.get_internal(); for child in &inode.children()[..child_idx] { - before += child.base_measure(); + before += child.base_len(); } for (idx, child) in @@ -479,7 +479,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { let &(inode, child_idx) = self.path.last().unwrap(); // Step 2: push nodes on the path until we get to the first leaf node - // with a positive `M`-measure. Once we get there we're done. + // with a positive `M`-length. Once we get there we're done. // Every node in the path is an internal node. let mut node = inode.get_internal().child(child_idx); @@ -576,10 +576,9 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { summary += start_summary; let slice = { - let contains_last_slice = self.base_yielded - + summary.base_measure() - + leaf.base_measure() - > self.base_total; + let contains_last_slice = + self.base_yielded + summary.base_len() + leaf.base_len() + > self.base_total; if contains_last_slice { self.last_slice.take().unwrap() @@ -593,7 +592,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { self.yielded_in_leaf = advance; self.start_slice = rest; - advance += summary.base_measure(); + advance += summary.base_len(); if !end_slice.is_empty() { summary += end_slice.summarize(); @@ -609,7 +608,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { if is_immediately_after { root = previous_leaf; - offset = root.base_measure() - summary.base_measure(); + offset = root.base_len() - summary.base_len(); end_slice = start_slice; } else { let start = offset; @@ -618,7 +617,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { tree_slice::deepest_node_containing_base_range( root, start, - start + summary.base_measure(), + start + summary.base_len(), ); root = new_root; @@ -638,7 +637,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { /// Very similar to [`next_leaf_with_measure`](1), except it doesn't /// mutate any state and instead of returning the next leaf node with a - /// non-zero `M`-measure it returns the leaf node containing + /// non-zero `M`-length it returns the leaf node containing /// [`last_slice`](2), or the last leaf node in the root if that's not set. /// /// NOTE: it assumes that that leaf node is different from the current @@ -670,20 +669,20 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { let mut measured = L::BaseMetric::zero(); for child in &inode.children()[..child_idx] { - measured += child.base_measure(); + measured += child.base_len(); } for child in &inode.children()[child_idx..] { - let child_measure = child.base_measure(); + let child_len = child.base_len(); if measured <= range.start - && measured + child_measure >= range.end + && measured + child_len >= range.end { range.start -= measured; range.end -= measured; continue 'outer; } else { - measured += child_measure; + measured += child_len; } } @@ -708,7 +707,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { let inode = node.get_internal(); for child in &inode.children()[..child_idx] { - before += child.base_measure(); + before += child.base_len(); } for child in &inode.children()[child_idx + 1..] { @@ -724,24 +723,24 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { let mut offset = L::BaseMetric::zero(); for child in &inode.children()[..child_idx] { - offset += child.base_measure(); - before += child.base_measure(); + offset += child.base_len(); + before += child.base_len(); } - offset += inode.child(child_idx).base_measure(); + offset += inode.child(child_idx).base_len(); // This will be the child of the root node that contains the last // slice. let mut node = inode.first(); for child in &inode.children()[child_idx + 1..] { - let child_measure = child.base_measure(); + let child_len = child.base_len(); - if offset + child_measure >= range.end { + if offset + child_len >= range.end { node = child; break; } else { - offset += child_measure; + offset += child_len; summary += child.summary(); } } @@ -750,13 +749,13 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { match &**node { Node::Internal(inode) => { for child in inode.children() { - let child_measure = child.base_measure(); + let child_len = child.base_len(); - if offset + child_measure >= range.end { + if offset + child_len >= range.end { node = child; continue 'outer; } else { - offset += child_measure; + offset += child_len; summary += child.summary(); } } @@ -791,9 +790,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { } // First, check if the leaf node is the root. If it is we're done. - if self.base_total - self.base_yielded - == self.start_slice.base_measure() - { + if self.base_total - self.base_yielded == self.start_slice.base_len() { return ( TreeSlice { root: self.leaf_node, @@ -802,7 +799,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { end_slice: self.start_slice, summary: self.start_slice.summarize(), }, - self.start_slice.base_measure(), + self.start_slice.base_len(), ); } @@ -821,7 +818,7 @@ impl<'a, const N: usize, L: Leaf, M: UnitMetric> UnitsForward<'a, N, L, M> { before += self.yielded_in_leaf; - let advance = summary.base_measure(); + let advance = summary.base_len(); ( TreeSlice { @@ -868,7 +865,7 @@ struct UnitsBackward<'a, const N: usize, L: Leaf, M: Metric> { /// The start of the yielding range as an offset into the root. base_start: L::BaseMetric, - /// The base measure of all the advances which are yet to be yielded. + /// The base length of all the advances which are yet to be yielded. base_remaining: L::BaseMetric, /// The `M`-units which are yet to be yielded. @@ -900,7 +897,7 @@ where first_slice: None, last_slice: None, base_start: L::BaseMetric::zero(), - base_remaining: tree.base_measure(), + base_remaining: tree.base_len(), units_remaining: tree.root().measure::(), } } @@ -924,7 +921,7 @@ where first_slice: Some(tree_slice.start_slice), last_slice: Some(tree_slice.end_slice), base_start: tree_slice.offset, - base_remaining: tree_slice.base_measure(), + base_remaining: tree_slice.base_len(), units_remaining: tree_slice.measure::(), } } @@ -955,14 +952,14 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> match &**node { Node::Internal(inode) => { for (idx, child) in inode.children().iter().enumerate() { - let child_measure = child.base_measure(); + let child_len = child.base_len(); - if offset + child_measure >= last_slice_offset { + if offset + child_len >= last_slice_offset { self.path.push((node, idx)); node = child; continue 'outer; } else { - offset += child_measure; + offset += child_len; } } @@ -975,7 +972,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> match self.last_slice.take() { Some(slice) => { self.yielded_in_leaf = - leaf.base_measure() - slice.base_measure(); + leaf.base_len() - slice.base_len(); self.end_slice = slice; }, @@ -1027,7 +1024,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> /// Very similar to [`previous_leaf_with_measure`](1), except it doesn't /// mutate any state and instead of returning the previous leaf node with a - /// non-zero `M`-measure it returns the leaf node containing + /// non-zero `M`-length it returns the leaf node containing /// [`first_slice`](2), or the first leaf node in the root if that's not /// set. /// @@ -1060,16 +1057,15 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> let mut offset = L::BaseMetric::zero(); for child in &inode.children()[..=child_idx] { - let child_measure = child.base_measure(); + let child_len = child.base_len(); - if offset <= range.start - && offset + child_measure >= range.end + if offset <= range.start && offset + child_len >= range.end { range.start -= offset; range.end -= offset; continue 'outer; } else { - offset += child_measure; + offset += child_len; } } @@ -1098,7 +1094,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> } for child in &inode.children()[child_idx + 1..] { - after += child.base_measure(); + after += child.base_len(); } } @@ -1108,7 +1104,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> let inode = root.get_internal(); for child in &inode.children()[child_idx + 1..] { - after += child.base_measure(); + after += child.base_len(); } // This will be the child of the root node that contains the first @@ -1120,16 +1116,16 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> let mut children = inode.children()[..child_idx].iter(); while let Some(child) = children.next() { - let child_measure = child.base_measure(); + let child_len = child.base_len(); - if offset + child_measure > range.start { + if offset + child_len > range.start { for child in children { summary += child.summary(); } node = child; break; } else { - offset += child_measure; + offset += child_len; } } @@ -1139,16 +1135,16 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> let mut children = inode.children().iter(); while let Some(child) = children.next() { - let child_measure = child.base_measure(); + let child_len = child.base_len(); - if offset + child_measure > range.start { + if offset + child_len > range.start { for child in children { summary += child.summary(); } node = child; continue 'outer; } else { - offset += child_measure; + offset += child_len; } } @@ -1202,7 +1198,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> self.base_remaining -= advance; let contains_first_slice = - previous_leaf.base_measure() > self.base_remaining; + previous_leaf.base_len() > self.base_remaining; if contains_first_slice { self.end_slice = self.first_slice.take().unwrap(); @@ -1223,7 +1219,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> let (first_leaf, root, after, mut summary) = self.first_leaf(); - advance += summary.base_measure(); + advance += summary.base_len(); summary += end_slice.summarize(); @@ -1234,12 +1230,11 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> let start_summary = start_slice.summarize(); - advance += start_summary.base_measure(); + advance += start_summary.base_len(); summary += start_summary; - let offset = - root.base_measure() - after - self.yielded_in_leaf - advance; + let offset = root.base_len() - after - self.yielded_in_leaf - advance; (TreeSlice { root, offset, start_slice, end_slice, summary }, advance) } @@ -1264,7 +1259,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> ( TreeSlice { root: self.leaf_node, - offset: rest.base_measure(), + offset: rest.base_len(), summary: slice.summarize(), end_slice: slice, start_slice: slice, @@ -1274,7 +1269,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> } /// Traverses the path to reach the previous leaf node with a non-zero - /// `M`-measure. + /// `M`-length. /// /// Returns a `(leaf, root, after, summary)` tuple where: /// @@ -1283,10 +1278,10 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> /// - `root` is the deepest internal node containing both `leaf` and the /// current `self.leaf_node` in its subtree; /// - /// - `after` is the total base measure of all the nodes from the last leaf + /// - `after` is the total base length of all the nodes from the last leaf /// in `root`'s subtree to the leaf after the current `self.leaf_node`. - /// If `self.leaf_node` if the last leaf in `root`'s subtree this measure - /// will be zero; + /// If `self.leaf_node` if the last leaf in `root`'s subtree this will be + /// zero; /// /// - `summary` is the total summary of all the nodes between (but not /// including) `leaf` and `self.leaf_node`. If `leaf` is the leaf node @@ -1319,7 +1314,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> let inode = node.get_internal(); for child in &inode.children()[child_idx + 1..] { - after += child.base_measure(); + after += child.base_len(); } for (idx, child) in @@ -1337,7 +1332,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> let &(inode, child_idx) = self.path.last().unwrap(); // Step 2: push nodes on the path until we get to the first leaf node - // with a positive `M`-measure. Once we get there we're done. + // with a positive M-length. Once we get there we're done. // Every node in the path is an internal node. let mut node = &inode.get_internal().children()[child_idx]; @@ -1436,7 +1431,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> self.base_remaining -= advance; let contains_first_slice = - previous_leaf.base_measure() > self.base_remaining; + previous_leaf.base_len() > self.base_remaining; if contains_first_slice { self.end_slice = self.first_slice.take().unwrap(); @@ -1456,7 +1451,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> ( TreeSlice { root: self.leaf_node, - offset: self.leaf_node.base_measure(), + offset: self.leaf_node.base_len(), start_slice: empty, end_slice: empty, summary: L::Summary::empty(), @@ -1476,7 +1471,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> let (leaf, mut root, after, mut summary) = self.previous_leaf_with_measure(); - let is_immediately_before = summary.base_measure().is_zero(); + let is_immediately_before = summary.base_len().is_zero(); advance += summary.measure::(); @@ -1484,7 +1479,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> let slice = { let contains_first_slice = - advance + leaf.base_measure() > self.base_remaining; + advance + leaf.base_len() > self.base_remaining; if contains_first_slice { self.first_slice.take().unwrap() @@ -1497,12 +1492,12 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> let start_summary = start_slice.summarize(); - advance += start_summary.base_measure(); + advance += start_summary.base_len(); let mut offset = - root.base_measure() - after - self.yielded_in_leaf - advance; + root.base_len() - after - self.yielded_in_leaf - advance; - self.yielded_in_leaf = start_summary.base_measure(); + self.yielded_in_leaf = start_summary.base_len(); self.end_slice = rest; if !start_slice.is_empty() { @@ -1527,7 +1522,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> tree_slice::deepest_node_containing_base_range( root, start, - start + summary.base_measure(), + start + summary.base_len(), ); root = new_root; @@ -1555,7 +1550,7 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> /// /// This is because there's some text in the `LineMetric(2)..ByteMetric(5)` /// range. Calling this function will yield the `TreeSlice` in that range, - /// but only if its base measure is positive (hence the `Option`). + /// but only if its base length is positive (hence the `Option`). /// /// For example if the string was "a\n\b\n" the range would be /// `LineMetric(2)..ByteMetric(4)`, which doesn't contain any text. In that @@ -1573,18 +1568,18 @@ impl<'a, const N: usize, L: Leaf, M: DoubleEndedUnitMetric> let summary = slice.summarize(); if !slice.is_empty() { - self.yielded_in_leaf += summary.base_measure(); + self.yielded_in_leaf += summary.base_len(); self.end_slice = rest; Some(( TreeSlice { root: self.leaf_node, - offset: rest.base_measure(), + offset: rest.base_len(), start_slice: slice, end_slice: slice, summary, }, - slice.base_measure(), + slice.base_len(), )) } else { None From 5c56ec61940361d6db596b352e774598da9700e0 Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Thu, 14 Aug 2025 15:10:13 +0200 Subject: [PATCH 29/35] Seed random tests from `u64` --- src/rope/gap_buffer.rs | 2 +- src/tree/node.rs | 2 +- tests/common/mod.rs | 21 +++++++++++++++++---- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/rope/gap_buffer.rs b/src/rope/gap_buffer.rs index 3f20032..6992c78 100644 --- a/src/rope/gap_buffer.rs +++ b/src/rope/gap_buffer.rs @@ -1091,7 +1091,7 @@ impl GapBuffer { /// # use crop::GapBuffer; /// # use crop::tree::Leaf; /// let mut buffer = GapBuffer::from("f\n\r\n"); - /// assert_eq!(buffer.summarize_range(0..buffer.len()), buffer.summarize()); + /// assert_eq!(buffer.summarize_range(0..buffer.len()), *buffer.summarize()); /// /// let s = buffer.summarize_range(0..1); /// assert_eq!(s.bytes(), 1); diff --git a/src/tree/node.rs b/src/tree/node.rs index 88ed258..6abcccf 100644 --- a/src/tree/node.rs +++ b/src/tree/node.rs @@ -227,7 +227,7 @@ impl Node { } } - /// Continuously replaces the node its child qs long as it's an internal + /// Continuously replaces the node its child as long as it's an internal /// node with a single child. Note that an inode might become a leaf node /// after calling this. /// diff --git a/tests/common/mod.rs b/tests/common/mod.rs index cb1fc77..8be33b8 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -1,5 +1,7 @@ #![allow(dead_code)] +use std::env; + use rand::SeedableRng; pub const TINY: &str = include_str!("tiny.txt"); @@ -7,11 +9,22 @@ pub const SMALL: &str = include_str!("small.txt"); pub const MEDIUM: &str = include_str!("medium.txt"); pub const LARGE: &str = include_str!("large.txt"); +#[track_caller] pub fn rng() -> impl rand::Rng { - let rng = rand_chacha::ChaChaRng::from_os_rng(); - let seed = rng.get_seed(); - println!("Seed: {seed:?}"); - rng + let seed = seed(); + println!("SEED: {seed:?}"); + rand_chacha::ChaChaRng::seed_from_u64(seed) +} + +#[track_caller] +fn seed() -> u64 { + match env::var("SEED") { + Ok(seed) => seed.parse().expect("couldn't parse $SEED"), + Err(env::VarError::NotPresent) => rand::random(), + Err(env::VarError::NotUnicode(seed)) => { + panic!("$SEED contained invalid unicode: {seed:?}") + }, + } } /// A cursed version of a lorem ipsum paragraph taken from [this online From 6353e5ddabbbd5970dc1303a6ac97a3d2d754597 Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Thu, 14 Aug 2025 16:44:58 +0200 Subject: [PATCH 30/35] Fix bug --- src/tree/tree_slice.rs | 2 +- tests/slice_indexing.rs | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/tree/tree_slice.rs b/src/tree/tree_slice.rs index fad604a..5ed9641 100644 --- a/src/tree/tree_slice.rs +++ b/src/tree/tree_slice.rs @@ -109,7 +109,7 @@ impl<'a, const ARITY: usize, L: Leaf> TreeSlice<'a, ARITY, L> { let len_total_minus_end = self.measure::() - self.end_slice.measure::(); - if len_total_minus_end <= offset { + if len_total_minus_end < offset { return (self.end_slice, len_total_minus_end); } diff --git a/tests/slice_indexing.rs b/tests/slice_indexing.rs index f4024b9..af00e69 100644 --- a/tests/slice_indexing.rs +++ b/tests/slice_indexing.rs @@ -22,13 +22,13 @@ fn byte_random() { let str_slice = &s[start..end]; let rope_slice = r.byte_slice(start..end); - for (idx, byte) in str_slice.bytes().enumerate() { - if byte != rope_slice.byte(idx) { - println!( - "byte index: {idx}, byte range: {:?}", - start..end + for (idx, str_byte) in str_slice.bytes().enumerate() { + let rope_byte = rope_slice.byte(idx); + if str_byte != rope_byte { + panic!( + "string's byte is {str_byte}, but rope's byte is \ + {rope_byte}", ); - assert_eq!(byte, rope_slice.byte(idx)); } } } From 3981d917051c0c6214447371880aedbcabcb25e2 Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Thu, 14 Aug 2025 16:49:17 +0200 Subject: [PATCH 31/35] Avoid the need for a separate `TreeSlice::slice_node_at_offset()` function --- src/tree/tree_slice.rs | 41 +++++++++++++---------------------------- 1 file changed, 13 insertions(+), 28 deletions(-) diff --git a/src/tree/tree_slice.rs b/src/tree/tree_slice.rs index 5ed9641..8eefa20 100644 --- a/src/tree/tree_slice.rs +++ b/src/tree/tree_slice.rs @@ -62,7 +62,7 @@ impl<'a, const ARITY: usize, L: Leaf> TreeSlice<'a, ARITY, L> { M::zero() } else { self.root.convert_len::<_, M>(self.offset + base_offset) - - self.offset_len::() + - self.measure_offset::() } } @@ -80,7 +80,7 @@ impl<'a, const ARITY: usize, L: Leaf> TreeSlice<'a, ARITY, L> { if offset.is_zero() { L::BaseMetric::zero() } else { - let m_offset = self.offset_len::(); + let m_offset = self.measure_offset::(); self.root.convert_len::<_, L::BaseMetric>(m_offset + offset) - self.offset } @@ -113,7 +113,7 @@ impl<'a, const ARITY: usize, L: Leaf> TreeSlice<'a, ARITY, L> { return (self.end_slice, len_total_minus_end); } - let m_offset = self.offset_len::(); + let m_offset = self.measure_offset::(); let (leaf, leaf_offset) = self.root.leaf_at_offset(m_offset + offset); (leaf.as_slice(), leaf_offset - m_offset) } @@ -209,7 +209,7 @@ impl<'a, const ARITY: usize, L: Leaf> TreeSlice<'a, ARITY, L> { /// Note that it's never necessary to call this with `L::BaseMetric`, as /// that's already known to be [`Self::offset`]. #[inline] - fn offset_len(&self) -> M + fn measure_offset(&self) -> M where M: FromMetric, { @@ -225,26 +225,26 @@ where #[inline] pub fn slice(self, range: Range) -> Self where - M: SlicingMetric, + M: SlicingMetric + FromMetric, L::BaseMetric: SlicingMetric, { debug_assert!(M::zero() <= range.start); debug_assert!(range.start <= range.end); debug_assert!(range.end <= self.measure::() + M::one()); + let slice_offset = self.measure_offset::(); + if range.end < self.measure::() + M::one() { - Self::slice_node_at_offset( + Self::slice_node( self.root, - self.offset, - range.start, - range.end, + slice_offset + range.start, + slice_offset + range.end, ) } else { - Self::slice_node_at_offset( + Self::slice_node( self.root, - self.offset, - range.start, - self.base_len(), + slice_offset + range.start, + self.offset + self.base_len(), ) } } @@ -321,21 +321,6 @@ where slice } - - #[track_caller] - #[inline] - fn slice_node_at_offset( - _root: &'a Arc>, - _offset: L::BaseMetric, - _start: S, - _end: E, - ) -> Self - where - S: SlicingMetric, - E: SlicingMetric, - { - todo!(); - } } impl PartialEq for SliceLeafCount { From 610478c17be54092fbd00b99eb65a924f4a224bc Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Thu, 14 Aug 2025 16:58:07 +0200 Subject: [PATCH 32/35] Fix bug --- src/tree/tree_slice.rs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/tree/tree_slice.rs b/src/tree/tree_slice.rs index 8eefa20..20dbec9 100644 --- a/src/tree/tree_slice.rs +++ b/src/tree/tree_slice.rs @@ -226,26 +226,22 @@ where pub fn slice(self, range: Range) -> Self where M: SlicingMetric + FromMetric, - L::BaseMetric: SlicingMetric, + L::BaseMetric: SlicingMetric + FromMetric, { debug_assert!(M::zero() <= range.start); debug_assert!(range.start <= range.end); debug_assert!(range.end <= self.measure::() + M::one()); - let slice_offset = self.measure_offset::(); + let start = self.offset + self.convert_len_to_base(range.start); if range.end < self.measure::() + M::one() { Self::slice_node( self.root, - slice_offset + range.start, - slice_offset + range.end, + start, + self.measure_offset::() + range.end, ) } else { - Self::slice_node( - self.root, - slice_offset + range.start, - self.offset + self.base_len(), - ) + Self::slice_node(self.root, start, self.offset + self.base_len()) } } From a1cf465de2653bbc18d1e54e42aebaf257f0fea6 Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Thu, 14 Aug 2025 17:08:51 +0200 Subject: [PATCH 33/35] Tweaks --- src/rope/iterators.rs | 32 ++++++++++++++++---------------- src/tree/tree_slice.rs | 2 +- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/rope/iterators.rs b/src/rope/iterators.rs index c084898..da22a26 100644 --- a/src/rope/iterators.rs +++ b/src/rope/iterators.rs @@ -44,24 +44,24 @@ impl<'a> Iterator for Chunks<'a> { if let Some(extra) = self.forward_extra_right.take() { Some(extra) } else { - let Some(chunk) = self.leaves.next() else { + let Some(gap_slice) = self.leaves.next() else { return self.backward_extra_left.take(); }; - if chunk.left_chunk().is_empty() { + if gap_slice.left_chunk().is_empty() { #[cfg(feature = "small_chunks")] - if chunk.right_chunk().is_empty() { + if gap_slice.right_chunk().is_empty() { return self.next(); } - debug_assert!(!chunk.right_chunk().is_empty()); + debug_assert!(!gap_slice.right_chunk().is_empty()); - Some(chunk.right_chunk()) + Some(gap_slice.right_chunk()) } else { - if !chunk.right_chunk().is_empty() { - self.forward_extra_right = Some(chunk.right_chunk()); + if !gap_slice.right_chunk().is_empty() { + self.forward_extra_right = Some(gap_slice.right_chunk()); } - Some(chunk.left_chunk()) + Some(gap_slice.left_chunk()) } } } @@ -73,24 +73,24 @@ impl DoubleEndedIterator for Chunks<'_> { if let Some(extra) = self.backward_extra_left.take() { Some(extra) } else { - let Some(chunk) = self.leaves.next_back() else { + let Some(gap_slice) = self.leaves.next_back() else { return self.forward_extra_right.take(); }; - if chunk.right_chunk().is_empty() { + if gap_slice.right_chunk().is_empty() { #[cfg(feature = "small_chunks")] - if chunk.left_chunk().is_empty() { + if gap_slice.left_chunk().is_empty() { return self.next_back(); } - debug_assert!(!chunk.left_chunk().is_empty()); + debug_assert!(!gap_slice.left_chunk().is_empty()); - Some(chunk.left_chunk()) + Some(gap_slice.left_chunk()) } else { - if !chunk.left_chunk().is_empty() { - self.backward_extra_left = Some(chunk.left_chunk()); + if !gap_slice.left_chunk().is_empty() { + self.backward_extra_left = Some(gap_slice.left_chunk()); } - Some(chunk.right_chunk()) + Some(gap_slice.right_chunk()) } } } diff --git a/src/tree/tree_slice.rs b/src/tree/tree_slice.rs index 20dbec9..44f32ab 100644 --- a/src/tree/tree_slice.rs +++ b/src/tree/tree_slice.rs @@ -75,7 +75,7 @@ impl<'a, const ARITY: usize, L: Leaf> TreeSlice<'a, ARITY, L> { M: FromMetric, L::BaseMetric: FromMetric, { - debug_assert!(offset <= self.measure::()); + debug_assert!(offset <= self.measure::() + M::one()); if offset.is_zero() { L::BaseMetric::zero() From 55583c3b0d03bee3980a2ddf61c630907eb007bd Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Thu, 14 Aug 2025 17:27:53 +0200 Subject: [PATCH 34/35] Require `M::one()` only for `UnitMetric`s --- src/rope/gap_buffer.rs | 5 ----- src/rope/metrics.rs | 30 +++++++++----------------- src/rope/rope.rs | 14 ++++++++++++ src/rope/rope_slice.rs | 16 ++++++++++++++ src/tree/traits.rs | 8 +++---- src/tree/tree.rs | 23 ++++++++++++++------ src/tree/tree_slice.rs | 48 ++++++++++++++++++++++++++++++------------ 7 files changed, 96 insertions(+), 48 deletions(-) diff --git a/src/rope/gap_buffer.rs b/src/rope/gap_buffer.rs index 6992c78..7cb4184 100644 --- a/src/rope/gap_buffer.rs +++ b/src/rope/gap_buffer.rs @@ -1384,11 +1384,6 @@ impl> Metric for M { >::zero() } - #[inline(always)] - fn one() -> Self { - >::one() - } - #[inline(always)] fn measure(summary: &GapBufferSummary) -> Self { M::measure(&summary.chunks_summary) diff --git a/src/rope/metrics.rs b/src/rope/metrics.rs index e0866dd..c5e68e3 100644 --- a/src/rope/metrics.rs +++ b/src/rope/metrics.rs @@ -249,11 +249,6 @@ impl Metric for ByteMetric { Self(0) } - #[inline] - fn one() -> Self { - Self(1) - } - #[inline] fn measure(summary: &StrSummary) -> Self { Self(summary.bytes) @@ -396,11 +391,6 @@ impl Metric for RawLineMetric { Self(0) } - #[inline] - fn one() -> Self { - Self(1) - } - #[inline] fn measure(summary: &StrSummary) -> Self { Self(summary.line_breaks) @@ -436,6 +426,11 @@ impl SlicingMetric for RawLineMetric { } impl UnitMetric for RawLineMetric { + #[inline] + fn one() -> Self { + Self(1) + } + #[inline] fn first_unit<'a>( chunk: GapSlice<'a>, @@ -521,11 +516,6 @@ impl Metric for LineMetric { Self(0) } - #[inline] - fn one() -> Self { - Self(1) - } - #[inline] fn measure(summary: &StrSummary) -> Self { Self(summary.line_breaks) @@ -543,6 +533,11 @@ impl Metric for LineMetric { } impl UnitMetric for LineMetric { + #[inline] + fn one() -> Self { + Self(1) + } + #[inline] fn first_unit<'a>( chunk: GapSlice<'a>, @@ -667,11 +662,6 @@ mod utf16_metric { Self(0) } - #[inline] - fn one() -> Self { - Self(1) - } - #[inline] fn measure(summary: &StrSummary) -> Self { Self(summary.utf16_code_units) diff --git a/src/rope/rope.rs b/src/rope/rope.rs index fefe2ae..be6affc 100644 --- a/src/rope/rope.rs +++ b/src/rope/rope.rs @@ -459,6 +459,13 @@ impl Rope { panic::line_index_out_of_bounds(line_index, self.line_len()); } + if line_index == self.tree.summary().line_breaks() { + return RopeSlice { + tree_slice: self.tree.slice_from(RawLineMetric(line_index)), + has_trailing_newline: false, + }; + } + let tree_slice = self .tree .slice(RawLineMetric(line_index)..RawLineMetric(line_index + 1)); @@ -575,6 +582,13 @@ impl Rope { panic::line_offset_out_of_bounds(end, self.line_len()); } + if end == self.tree.summary().line_breaks() + 1 { + return RopeSlice { + tree_slice: self.tree.slice_from(RawLineMetric(start)), + has_trailing_newline: false, + }; + } + self.tree.slice(RawLineMetric(start)..RawLineMetric(end)).into() } diff --git a/src/rope/rope_slice.rs b/src/rope/rope_slice.rs index f833940..10b72e3 100644 --- a/src/rope/rope_slice.rs +++ b/src/rope/rope_slice.rs @@ -385,6 +385,15 @@ impl<'a> RopeSlice<'a> { panic::line_offset_out_of_bounds(line_index, self.line_len()); } + if line_index == self.tree_slice.summary().line_breaks() { + return Self { + tree_slice: self + .tree_slice + .slice_from(RawLineMetric(line_index)), + has_trailing_newline: false, + }; + } + let tree_slice = self .tree_slice .slice(RawLineMetric(line_index)..RawLineMetric(line_index + 1)); @@ -506,6 +515,13 @@ impl<'a> RopeSlice<'a> { panic::line_offset_out_of_bounds(end, self.line_len()); } + if end == self.tree_slice.summary().line_breaks() + 1 { + return Self { + tree_slice: self.tree_slice.slice_from(RawLineMetric(start)), + has_trailing_newline: false, + }; + } + self.tree_slice.slice(RawLineMetric(start)..RawLineMetric(end)).into() } diff --git a/src/tree/traits.rs b/src/tree/traits.rs index 9d4531d..c13fc6d 100644 --- a/src/tree/traits.rs +++ b/src/tree/traits.rs @@ -126,10 +126,6 @@ pub trait Metric: /// it should hold `m == m + M::zero()`. fn zero() -> Self; - /// The smallest value larger than [`zero`](Self::zero()) this metric can - /// measure. - fn one() -> Self; - /// Returns the measure of the leaf's summary according to this metric. fn measure(summary: &S) -> Self; @@ -170,6 +166,10 @@ pub trait SlicingMetric: Metric { /// Allows iterating forward over the units of this metric. pub trait UnitMetric: Metric { + /// The smallest value larger than [`zero`](Metric::zero()) this metric can + /// measure. + fn one() -> Self; + /// Returns a `(first_slice, rest_slice, advance)` tuple, where `advance` /// is equal to `first_slice`'s base length **plus** the length of any /// content between the end of `first_slice` and the start of `rest_slice` diff --git a/src/tree/tree.rs b/src/tree/tree.rs index aace0ee..d9e3cf8 100644 --- a/src/tree/tree.rs +++ b/src/tree/tree.rs @@ -137,7 +137,7 @@ impl Tree { where M: Metric, { - debug_assert!(offset <= self.measure::() + M::one()); + debug_assert!(offset <= self.measure::()); self.root.leaf_at_offset(offset) } @@ -197,11 +197,26 @@ impl Tree { { debug_assert!(M::zero() <= range.start); debug_assert!(range.start <= range.end); - debug_assert!(range.end <= self.measure::() + M::one()); + debug_assert!(range.end <= self.measure::()); TreeSlice::slice_node(&self.root, range.start, range.end) } + /// Returns a slice of the `Tree` from the given offset to the end of the + /// tree. + #[track_caller] + #[inline] + pub fn slice_from(&self, start: M) -> TreeSlice<'_, ARITY, L> + where + M: SlicingMetric, + L::BaseMetric: SlicingMetric, + for<'d> L::Slice<'d>: Default, + { + debug_assert!(start <= self.measure::()); + + TreeSlice::slice_node(&self.root, start, self.base_len()) + } + #[inline] pub fn summary(&self) -> L::Summary { self.root.summary() @@ -1615,10 +1630,6 @@ mod tests { 0 } - fn one() -> Self { - 1 - } - fn measure(summary: &UsizeSummary) -> Self { summary.leaves } diff --git a/src/tree/tree_slice.rs b/src/tree/tree_slice.rs index 44f32ab..411703e 100644 --- a/src/tree/tree_slice.rs +++ b/src/tree/tree_slice.rs @@ -75,7 +75,7 @@ impl<'a, const ARITY: usize, L: Leaf> TreeSlice<'a, ARITY, L> { M: FromMetric, L::BaseMetric: FromMetric, { - debug_assert!(offset <= self.measure::() + M::one()); + debug_assert!(offset <= self.measure::()); if offset.is_zero() { L::BaseMetric::zero() @@ -98,7 +98,7 @@ impl<'a, const ARITY: usize, L: Leaf> TreeSlice<'a, ARITY, L> { where M: Metric + FromMetric, { - debug_assert!(offset <= self.measure::() + M::one()); + debug_assert!(offset <= self.measure::()); let len_start_slice = self.start_slice.measure::(); @@ -230,19 +230,25 @@ where { debug_assert!(M::zero() <= range.start); debug_assert!(range.start <= range.end); - debug_assert!(range.end <= self.measure::() + M::one()); + debug_assert!(range.end <= self.measure::()); let start = self.offset + self.convert_len_to_base(range.start); + let end = self.measure_offset::() + range.end; + Self::slice_node(self.root, start, end) + } - if range.end < self.measure::() + M::one() { - Self::slice_node( - self.root, - start, - self.measure_offset::() + range.end, - ) - } else { - Self::slice_node(self.root, start, self.offset + self.base_len()) - } + #[track_caller] + #[inline] + pub fn slice_from(self, start: M) -> Self + where + M: SlicingMetric + FromMetric, + L::BaseMetric: SlicingMetric + FromMetric, + { + debug_assert!(start <= self.measure::()); + + let start = self.offset + self.convert_len_to_base(start); + let end = self.offset + self.base_len(); + Self::slice_node(self.root, start, end) } #[inline] @@ -275,7 +281,9 @@ where E: SlicingMetric, { debug_assert!(S::zero() <= start); - debug_assert!(end <= root.measure::() + E::one()); + debug_assert!(end <= root.measure::()); + + println!("Slicing between {start:?} and {end:?} in\n{root:#?}"); let (root, start, end) = deepest_node_containing_range(root, start, end); @@ -302,6 +310,8 @@ where &mut false, ); + println!("Need to recompute root: {recompute_root}"); + if recompute_root { let start = slice.offset; @@ -315,6 +325,12 @@ where slice.offset -= offset; } + println!("Slice's root: {:?}", slice.root); + println!("Slice's offset: {:?}", slice.offset); + println!("Slice's summary: {:?}", slice.summary); + println!("Slice's start_slice: {:?}", slice.start_slice); + println!("Slice's end_slice: {:?}", slice.end_slice); + slice } } @@ -489,6 +505,8 @@ fn build_slice<'a, const N: usize, L, S, E>( S: SlicingMetric, E: SlicingMetric, { + println!("Node is leaf? {}", node.is_leaf()); + match &**node { Node::Internal(inode) => { for child in inode.children() { @@ -591,6 +609,10 @@ fn build_slice<'a, const N: usize, L, S, E>( slice.start_slice = start_slice; slice.end_slice = start_slice; + println!( + "Set both start and end slices to {start_slice:?}" + ); + *done = true; } else { // This leaf contains the first slice but not the last. From 2f9c2ce06d28af93f974921eed4271099940745b Mon Sep 17 00:00:00 2001 From: Riccardo Mazzarini Date: Thu, 14 Aug 2025 17:58:30 +0200 Subject: [PATCH 35/35] Fix bug --- src/rope/rope.rs | 9 ++++++--- src/rope/rope_slice.rs | 9 ++++++--- src/tree/tree_slice.rs | 16 ---------------- 3 files changed, 12 insertions(+), 22 deletions(-) diff --git a/src/rope/rope.rs b/src/rope/rope.rs index be6affc..6bb778e 100644 --- a/src/rope/rope.rs +++ b/src/rope/rope.rs @@ -583,10 +583,13 @@ impl Rope { } if end == self.tree.summary().line_breaks() + 1 { - return RopeSlice { - tree_slice: self.tree.slice_from(RawLineMetric(start)), - has_trailing_newline: false, + let tree_slice = if start == end { + self.tree.slice_from(ByteMetric(self.byte_len())) + } else { + self.tree.slice_from(RawLineMetric(start)) }; + debug_assert!(!tree_slice.end_slice().has_trailing_newline()); + return RopeSlice { tree_slice, has_trailing_newline: false }; } self.tree.slice(RawLineMetric(start)..RawLineMetric(end)).into() diff --git a/src/rope/rope_slice.rs b/src/rope/rope_slice.rs index 10b72e3..c7c921c 100644 --- a/src/rope/rope_slice.rs +++ b/src/rope/rope_slice.rs @@ -516,10 +516,13 @@ impl<'a> RopeSlice<'a> { } if end == self.tree_slice.summary().line_breaks() + 1 { - return Self { - tree_slice: self.tree_slice.slice_from(RawLineMetric(start)), - has_trailing_newline: false, + let tree_slice = if start == end { + self.tree_slice.slice_from(ByteMetric(self.byte_len())) + } else { + self.tree_slice.slice_from(RawLineMetric(start)) }; + debug_assert!(!tree_slice.end_slice().has_trailing_newline()); + return Self { tree_slice, has_trailing_newline: false }; } self.tree_slice.slice(RawLineMetric(start)..RawLineMetric(end)).into() diff --git a/src/tree/tree_slice.rs b/src/tree/tree_slice.rs index 411703e..60254e0 100644 --- a/src/tree/tree_slice.rs +++ b/src/tree/tree_slice.rs @@ -283,8 +283,6 @@ where debug_assert!(S::zero() <= start); debug_assert!(end <= root.measure::()); - println!("Slicing between {start:?} and {end:?} in\n{root:#?}"); - let (root, start, end) = deepest_node_containing_range(root, start, end); @@ -310,8 +308,6 @@ where &mut false, ); - println!("Need to recompute root: {recompute_root}"); - if recompute_root { let start = slice.offset; @@ -325,12 +321,6 @@ where slice.offset -= offset; } - println!("Slice's root: {:?}", slice.root); - println!("Slice's offset: {:?}", slice.offset); - println!("Slice's summary: {:?}", slice.summary); - println!("Slice's start_slice: {:?}", slice.start_slice); - println!("Slice's end_slice: {:?}", slice.end_slice); - slice } } @@ -505,8 +495,6 @@ fn build_slice<'a, const N: usize, L, S, E>( S: SlicingMetric, E: SlicingMetric, { - println!("Node is leaf? {}", node.is_leaf()); - match &**node { Node::Internal(inode) => { for child in inode.children() { @@ -609,10 +597,6 @@ fn build_slice<'a, const N: usize, L, S, E>( slice.start_slice = start_slice; slice.end_slice = start_slice; - println!( - "Set both start and end slices to {start_slice:?}" - ); - *done = true; } else { // This leaf contains the first slice but not the last.