Skip to content

Commit 5506d50

Browse files
committed
perf: add size_hint for rope CharIndices
1 parent 739d6a7 commit 5506d50

File tree

1 file changed

+48
-3
lines changed

1 file changed

+48
-3
lines changed

src/rope.rs

+48-3
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,7 @@ impl<'a> Rope<'a> {
509509
Lines {
510510
iter: match &self.repr {
511511
Repr::Light(s) => LinesEnum::Light(s),
512-
Repr::Full(data) => LinesEnum::Complex {
512+
Repr::Full(data) => LinesEnum::Full {
513513
iter: data,
514514
in_chunk_byte_idx: 0,
515515
chunk_idx: 0,
@@ -554,7 +554,7 @@ impl Hash for Rope<'_> {
554554

555555
enum LinesEnum<'a, 'b> {
556556
Light(&'b str),
557-
Complex {
557+
Full {
558558
iter: &'a Vec<(&'b str, usize)>,
559559
in_chunk_byte_idx: usize,
560560
chunk_idx: usize,
@@ -605,7 +605,7 @@ impl<'a> Iterator for Lines<'_, 'a> {
605605
}
606606
Lines {
607607
iter:
608-
LinesEnum::Complex {
608+
LinesEnum::Full {
609609
iter: chunks,
610610
ref mut in_chunk_byte_idx,
611611
ref mut chunk_idx,
@@ -777,6 +777,38 @@ impl Iterator for CharIndices<'_, '_> {
777777
}
778778
}
779779
}
780+
781+
#[inline]
782+
fn size_hint(&self) -> (usize, Option<usize>) {
783+
match &self.iter {
784+
CharIndicesEnum::Light { iter } => (0, None),
785+
CharIndicesEnum::Full {
786+
chunks,
787+
char_indices,
788+
chunk_index,
789+
} => {
790+
if *chunk_index >= chunks.len() {
791+
if char_indices.is_empty() {
792+
return (0, Some(0));
793+
}
794+
return (char_indices.len(), Some(char_indices.len()));
795+
}
796+
797+
let Some(total) = chunks.last().map(|(s, l)| *l + s.len()) else {
798+
return (0, Some(0));
799+
};
800+
801+
let (_, prev) = chunks[*chunk_index];
802+
// SAFETY: The previous length is guaranteed be less than or equal to the latter one.
803+
let remaining = total - prev;
804+
805+
(
806+
(remaining + 3) / 4 + char_indices.len(),
807+
Some(remaining + char_indices.len()),
808+
)
809+
}
810+
}
811+
}
780812
}
781813

782814
impl Default for Rope<'_> {
@@ -1226,25 +1258,38 @@ mod tests {
12261258

12271259
#[test]
12281260
fn char_indices() {
1261+
// The algorithm is the same as the one used in `std::str::CharIndices::size_hint`.
1262+
macro_rules! lo {
1263+
($expr:expr) => {
1264+
($expr + 3) / 4
1265+
};
1266+
}
1267+
12291268
let mut a = Rope::new();
12301269
a.add("abc");
12311270
a.add("def");
12321271
assert_eq!(
12331272
a.char_indices().collect::<Vec<_>>(),
12341273
"abcdef".char_indices().collect::<Vec<_>>()
12351274
);
1275+
let len = "abcdef".char_indices().size_hint().1.unwrap();
1276+
assert_eq!(a.char_indices().size_hint(), (lo!(len), Some(len)));
12361277

12371278
let mut a = Rope::new();
12381279
a.add("こんにちは");
12391280
assert_eq!(
12401281
a.char_indices().collect::<Vec<_>>(),
12411282
"こんにちは".char_indices().collect::<Vec<_>>()
12421283
);
1284+
let len = "こんにちは".char_indices().size_hint().1.unwrap();
1285+
assert_eq!(a.char_indices().size_hint(), (lo!(len), Some(len)));
12431286
a.add("世界");
12441287
assert_eq!(
12451288
a.char_indices().collect::<Vec<_>>(),
12461289
"こんにちは世界".char_indices().collect::<Vec<_>>()
12471290
);
1291+
let len = "こんにちは世界".char_indices().size_hint().1.unwrap();
1292+
assert_eq!(a.char_indices().size_hint(), (lo!(len), Some(len)));
12481293
}
12491294

12501295
#[test]

0 commit comments

Comments
 (0)