1
2
3
4
5
6
7
8use std::fmt;
9
10const MAX_LEAF_BYTES: usize = 64;
11
12#[derive(Clone, Debug, Default)]
13pub struct Rope {
14 root: Option<Box<Node>>,
15}
16
17#[derive(Clone, Debug)]
18enum Node {
19 Leaf(String),
20 Internal {
21 left: Box<Node>,
22 right: Box<Node>,
23 weight: usize,
24 len: usize,
25 depth: usize,
26 },
27}
28
29impl Node {
30 fn len_chars(&self) -> usize {
31 match self {
32 Node::Leaf(s) => s.chars().count(),
33 Node::Internal { len, .. } => *len,
34 }
35 }
36
37 fn depth(&self) -> usize {
38 match self {
39 Node::Leaf(_) => 0,
40 Node::Internal { depth, .. } => *depth,
41 }
42 }
43
44 fn byte_len(&self) -> usize {
45 match self {
46 Node::Leaf(s) => s.len(),
47 Node::Internal { left, right, .. } => left.byte_len() + right.byte_len(),
48 }
49 }
50
51 fn new_internal(left: Box<Node>, right: Box<Node>) -> Box<Node> {
52 let weight = left.len_chars();
53 let len = weight + right.len_chars();
54 let depth = 1 + left.depth().max(right.depth());
55 Box::new(Node::Internal { left, right, weight, len, depth })
56 }
57}
58
59fn char_boundary(s: &str, char_idx: usize) -> usize {
60 s.char_indices()
61 .nth(char_idx)
62 .map(|(i, _)| i)
63 .unwrap_or(s.len())
64}
65
66fn build_from_str(s: &str) -> Option<Box<Node>> {
67 if s.is_empty() {
68 return None;
69 }
70 let chunks = split_into_chunks(s);
71 build_balanced(&chunks)
72}
73
74fn split_into_chunks(s: &str) -> Vec<String> {
75 let mut chunks: Vec<String> = Vec::new();
76 let mut current = String::new();
77 for c in s.chars() {
78 let clen = c.len_utf8();
79 if current.len() + clen > MAX_LEAF_BYTES && !current.is_empty() {
80 chunks.push(std::mem::take(&mut current));
81 }
82 current.push(c);
83 }
84 if !current.is_empty() {
85 chunks.push(current);
86 }
87 chunks
88}
89
90fn build_balanced(chunks: &[String]) -> Option<Box<Node>> {
91 match chunks.len() {
92 0 => None,
93 1 => Some(Box::new(Node::Leaf(chunks[0].clone()))),
94 n => {
95 let mid = n / 2;
96 let left = build_balanced(&chunks[..mid]).unwrap();
97 let right = build_balanced(&chunks[mid..]).unwrap();
98 Some(Node::new_internal(left, right))
99 }
100 }
101}
102
103fn char_at(node: &Node, idx: usize) -> Option<char> {
104 match node {
105 Node::Leaf(s) => s.chars().nth(idx),
106 Node::Internal { left, right, weight, .. } => {
107 if idx < *weight {
108 char_at(left, idx)
109 } else {
110 char_at(right, idx - *weight)
111 }
112 }
113 }
114}
115
116fn collect_substring(node: &Node, start: usize, end: usize, out: &mut String) {
117 if start >= end {
118 return;
119 }
120 match node {
121 Node::Leaf(s) => {
122 for (i, c) in s.chars().enumerate() {
123 if i >= end {
124 break;
125 }
126 if i >= start {
127 out.push(c);
128 }
129 }
130 }
131 Node::Internal { left, right, weight, .. } => {
132 if start < *weight {
133 collect_substring(left, start, end.min(*weight), out);
134 }
135 if end > *weight {
136 let rs = start.saturating_sub(*weight);
137 let re = end - *weight;
138 collect_substring(right, rs, re, out);
139 }
140 }
141 }
142}
143
144fn append_to_string(node: &Node, out: &mut String) {
145 match node {
146 Node::Leaf(s) => out.push_str(s),
147 Node::Internal { left, right, .. } => {
148 append_to_string(left, out);
149 append_to_string(right, out);
150 }
151 }
152}
153
154fn split_node(node: Box<Node>, idx: usize) -> (Option<Box<Node>>, Option<Box<Node>>) {
155 let len = node.len_chars();
156 if idx == 0 {
157 return (None, Some(node));
158 }
159 if idx >= len {
160 return (Some(node), None);
161 }
162 match *node {
163 Node::Leaf(s) => {
164 let byte_idx = char_boundary(&s, idx);
165 let (left, right) = s.split_at(byte_idx);
166 (
167 Some(Box::new(Node::Leaf(left.to_string()))),
168 Some(Box::new(Node::Leaf(right.to_string()))),
169 )
170 }
171 Node::Internal { left, right, weight, .. } => {
172 use std::cmp::Ordering;
173 match idx.cmp(&weight) {
174 Ordering::Less => {
175 let (ll, lr) = split_node(left, idx);
176 let right_combined = concat_nodes(lr, Some(right));
177 (ll, right_combined)
178 }
179 Ordering::Greater => {
180 let (rl, rr) = split_node(right, idx - weight);
181 let left_combined = concat_nodes(Some(left), rl);
182 (left_combined, rr)
183 }
184 Ordering::Equal => (Some(left), Some(right)),
185 }
186 }
187 }
188}
189
190fn concat_nodes(left: Option<Box<Node>>, right: Option<Box<Node>>) -> Option<Box<Node>> {
191 match (left, right) {
192 (None, r) => r,
193 (l, None) => l,
194 (Some(l), Some(r)) => {
195 if let (Node::Leaf(ls), Node::Leaf(rs)) = (l.as_ref(), r.as_ref()) {
196 if ls.len() + rs.len() <= MAX_LEAF_BYTES {
197 let mut s = String::with_capacity(ls.len() + rs.len());
198 s.push_str(ls);
199 s.push_str(rs);
200 return Some(Box::new(Node::Leaf(s)));
201 }
202 }
203 Some(Node::new_internal(l, r))
204 }
205 }
206}
207
208fn should_rebalance(node: &Node) -> bool {
209 let d = node.depth();
210 let len = node.len_chars();
211 if len == 0 {
212 return false;
213 }
214
215 let log = (usize::BITS - len.leading_zeros()) as usize;
216 d > 2 * log + 4
217}
218
219fn collect_leaves(node: Box<Node>, out: &mut Vec<String>) {
220 match *node {
221 Node::Leaf(s) => {
222 if !s.is_empty() {
223 out.push(s);
224 }
225 }
226 Node::Internal { left, right, .. } => {
227 collect_leaves(left, out);
228 collect_leaves(right, out);
229 }
230 }
231}
232
233fn coalesce_leaves(leaves: Vec<String>) -> Vec<String> {
234 let mut result: Vec<String> = Vec::new();
235 let mut current = String::new();
236 for leaf in leaves {
237 for c in leaf.chars() {
238 let clen = c.len_utf8();
239 if current.len() + clen > MAX_LEAF_BYTES && !current.is_empty() {
240 result.push(std::mem::take(&mut current));
241 }
242 current.push(c);
243 }
244 }
245 if !current.is_empty() {
246 result.push(current);
247 }
248 result
249}
250
251fn rebalance(node: Box<Node>) -> Box<Node> {
252 let mut leaves = Vec::new();
253 collect_leaves(node, &mut leaves);
254 let merged = coalesce_leaves(leaves);
255 build_balanced(&merged).expect("non-empty tree yields non-empty leaves")
256}
257
258fn maybe_rebalance(node: Box<Node>) -> Box<Node> {
259 if should_rebalance(&node) {
260 rebalance(node)
261 } else {
262 node
263 }
264}
265
266impl Rope {
267 pub fn new() -> Self {
268 Rope { root: None }
269 }
270
271 pub fn from_str(s: &str) -> Self {
272 Rope { root: build_from_str(s) }
273 }
274
275 pub fn len(&self) -> usize {
276 self.root.as_ref().map(|n| n.len_chars()).unwrap_or(0)
277 }
278
279 pub fn is_empty(&self) -> bool {
280 self.root.is_none()
281 }
282
283 pub fn byte_len(&self) -> usize {
284 self.root.as_ref().map(|n| n.byte_len()).unwrap_or(0)
285 }
286
287 pub fn char_at(&self, idx: usize) -> Option<char> {
288 self.root.as_ref().and_then(|n| char_at(n, idx))
289 }
290
291 pub fn substring(&self, start: usize, end: usize) -> String {
292 let len = self.len();
293 assert!(start <= end, "start > end");
294 assert!(end <= len, "end > len");
295 let mut out = String::new();
296 if let Some(ref r) = self.root {
297 collect_substring(r, start, end, &mut out);
298 }
299 out
300 }
301
302 pub fn insert(&mut self, idx: usize, s: &str) {
303 assert!(idx <= self.len(), "insert index out of bounds");
304 if s.is_empty() {
305 return;
306 }
307 let root = self.root.take();
308 let (left, right) = match root {
309 Some(r) => split_node(r, idx),
310 None => (None, None),
311 };
312 let middle = build_from_str(s);
313 let combined = concat_nodes(concat_nodes(left, middle), right);
314 self.root = combined.map(maybe_rebalance);
315 }
316
317 pub fn delete(&mut self, start: usize, end: usize) {
318 let len = self.len();
319 assert!(start <= end, "start > end");
320 assert!(end <= len, "end > len");
321 if start == end {
322 return;
323 }
324 let root = match self.root.take() {
325 Some(r) => r,
326 None => return,
327 };
328 let (left, rest) = split_node(root, start);
329 let (_middle, right) = match rest {
330 Some(r) => split_node(r, end - start),
331 None => (None, None),
332 };
333 let combined = concat_nodes(left, right);
334 self.root = combined.map(maybe_rebalance);
335 }
336
337
338 pub fn concat(self, other: Rope) -> Rope {
339 let combined = concat_nodes(self.root, other.root);
340 Rope { root: combined.map(maybe_rebalance) }
341 }
342
343
344 pub fn split(self, idx: usize) -> (Rope, Rope) {
345 let len = self.len();
346 assert!(idx <= len, "split index out of bounds");
347 match self.root {
348 None => (Rope::new(), Rope::new()),
349 Some(r) => {
350 let (l, rt) = split_node(r, idx);
351 (Rope { root: l }, Rope { root: rt })
352 }
353 }
354 }
355
356 pub fn to_string(&self) -> String {
357 let mut out = String::with_capacity(self.byte_len());
358 if let Some(ref r) = self.root {
359 append_to_string(r, &mut out);
360 }
361 out
362 }
363}
364
365impl fmt::Display for Rope {
366 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
367 if let Some(ref r) = self.root {
368 fmt_node(r, f)?;
369 }
370 Ok(())
371 }
372}
373
374fn fmt_node(node: &Node, f: &mut fmt::Formatter<'_>) -> fmt::Result {
375 match node {
376 Node::Leaf(s) => f.write_str(s),
377 Node::Internal { left, right, .. } => {
378 fmt_node(left, f)?;
379 fmt_node(right, f)
380 }
381 }
382}
383
384impl From<&str> for Rope {
385 fn from(s: &str) -> Self {
386 Rope::from_str(s)
387 }
388}
389
390impl From<String> for Rope {
391 fn from(s: String) -> Self {
392 Rope::from_str(&s)
393 }
394}
395
396
397
398
399
400#[cfg(test)]
401mod tests {
402 use super::*;
403
404 fn char_byte_index(s: &str, char_idx: usize) -> usize {
405 s.char_indices()
406 .nth(char_idx)
407 .map(|(i, _)| i)
408 .unwrap_or(s.len())
409 }
410
411 #[test]
412 fn empty_rope() {
413 let r = Rope::new();
414 assert_eq!(r.len(), 0);
415 assert_eq!(r.byte_len(), 0);
416 assert!(r.is_empty());
417 assert_eq!(r.to_string(), "");
418 assert_eq!(r.char_at(0), None);
419 assert_eq!(r.substring(0, 0), "");
420 }
421
422 #[test]
423 fn from_short_str() {
424 let r = Rope::from_str("Hello, world!");
425 assert_eq!(r.len(), 13);
426 assert_eq!(r.byte_len(), 13);
427 assert_eq!(r.to_string(), "Hello, world!");
428 assert_eq!(r.char_at(0), Some('H'));
429 assert_eq!(r.char_at(12), Some('!'));
430 assert_eq!(r.char_at(13), None);
431 }
432
433 #[test]
434 fn from_long_str_splits_into_leaves() {
435 let s: String = "abcdefghij".repeat(50);
436 let r = Rope::from_str(&s);
437 assert_eq!(r.len(), 500);
438 assert_eq!(r.byte_len(), 500);
439 assert_eq!(r.to_string(), s);
440
441 assert_eq!(r.char_at(250), Some('a'));
442 }
443
444 #[test]
445 fn multibyte_utf8_roundtrip() {
446 let s = "日本語テキスト🎉🎊 Hello — привет — مرحبا";
447 let r = Rope::from_str(s);
448 assert_eq!(r.len(), s.chars().count());
449 assert_eq!(r.byte_len(), s.len());
450 assert_eq!(r.to_string(), s);
451 for (i, c) in s.chars().enumerate() {
452 assert_eq!(r.char_at(i), Some(c), "mismatch at {}", i);
453 }
454 }
455
456 #[test]
457 fn insert_at_positions() {
458 let mut r = Rope::from_str("Hello world");
459 r.insert(5, ",");
460 assert_eq!(r.to_string(), "Hello, world");
461 r.insert(0, ">> ");
462 assert_eq!(r.to_string(), ">> Hello, world");
463 let end = r.len();
464 r.insert(end, "!");
465 assert_eq!(r.to_string(), ">> Hello, world!");
466 }
467
468 #[test]
469 fn insert_multibyte() {
470 let mut r = Rope::from_str("Hello!");
471 r.insert(5, " 世界");
472 assert_eq!(r.to_string(), "Hello 世界!");
473 assert_eq!(r.len(), "Hello 世界!".chars().count());
474 }
475
476 #[test]
477 fn delete_ranges() {
478 let mut r = Rope::from_str("Hello, world!");
479 r.delete(5, 7);
480 assert_eq!(r.to_string(), "Helloworld!");
481 r.delete(0, 5);
482 assert_eq!(r.to_string(), "world!");
483 let n = r.len();
484 r.delete(n, n);
485 assert_eq!(r.to_string(), "world!");
486 r.delete(0, r.len());
487 assert_eq!(r.to_string(), "");
488 assert!(r.is_empty());
489 }
490
491 #[test]
492 fn delete_across_leaves() {
493 let s: String = "0123456789".repeat(20);
494 let mut r = Rope::from_str(&s);
495 r.delete(30, 170);
496 let mut expected = s.clone();
497 expected.replace_range(30..170, "");
498 assert_eq!(r.to_string(), expected);
499 assert_eq!(r.len(), expected.chars().count());
500 }
501
502 #[test]
503 fn substring_across_leaves() {
504 let s: String = "0123456789".repeat(20);
505 let r = Rope::from_str(&s);
506 assert_eq!(r.substring(0, 0), "");
507 assert_eq!(r.substring(5, 15), &s[5..15]);
508 assert_eq!(r.substring(0, r.len()), s);
509 }
510
511 #[test]
512 fn substring_multibyte() {
513 let s = "αβγδε日本語🎉テスト";
514 let r = Rope::from_str(s);
515 let n = s.chars().count();
516 for i in 0..=n {
517 for j in i..=n {
518 let bi = char_byte_index(s, i);
519 let bj = char_byte_index(s, j);
520 assert_eq!(r.substring(i, j), &s[bi..bj], "substring({}, {})", i, j);
521 }
522 }
523 }
524
525 #[test]
526 fn concat_two_ropes() {
527 let a = Rope::from_str("Hello, ");
528 let b = Rope::from_str("world!");
529 let c = a.concat(b);
530 assert_eq!(c.to_string(), "Hello, world!");
531 assert_eq!(c.len(), 13);
532 }
533
534 #[test]
535 fn concat_with_empty() {
536 let a = Rope::from_str("foo");
537 let empty = Rope::new();
538 let c = a.concat(empty);
539 assert_eq!(c.to_string(), "foo");
540
541 let empty2 = Rope::new();
542 let b = Rope::from_str("bar");
543 let c2 = empty2.concat(b);
544 assert_eq!(c2.to_string(), "bar");
545 }
546
547 #[test]
548 fn split_two_ropes() {
549 let r = Rope::from_str("Hello, world!");
550 let (a, b) = r.split(7);
551 assert_eq!(a.to_string(), "Hello, ");
552 assert_eq!(b.to_string(), "world!");
553 }
554
555 #[test]
556 fn split_at_boundaries() {
557 let r = Rope::from_str("abcdef");
558 let (a, b) = r.clone().split(0);
559 assert_eq!(a.to_string(), "");
560 assert_eq!(b.to_string(), "abcdef");
561 let (a, b) = r.split(6);
562 assert_eq!(a.to_string(), "abcdef");
563 assert_eq!(b.to_string(), "");
564 }
565
566 #[test]
567 fn split_and_reconcat_multibyte() {
568 let s = "abc日本語🎉ghi";
569 let r = Rope::from_str(s);
570 let n = r.len();
571 for i in 0..=n {
572 let (l, r2) = r.clone().split(i);
573 let combined = l.concat(r2);
574 assert_eq!(combined.to_string(), s, "split at {}", i);
575 }
576 }
577
578 #[test]
579 fn insert_and_delete_grow_and_shrink() {
580 let mut r = Rope::new();
581 for i in 0..200 {
582 r.insert(r.len(), &format!("{} ", i));
583 }
584 let s = r.to_string();
585 assert!(s.starts_with("0 1 2 3"));
586 assert!(s.ends_with("198 199 "));
587 assert_eq!(r.len(), s.chars().count());
588 r.delete(0, r.len());
589 assert_eq!(r.to_string(), "");
590 }
591
592 #[test]
593 fn leaves_stay_within_cap() {
594
595 let s: String = "The quick brown fox jumps over the lazy dog. ".repeat(30);
596 let r = Rope::from_str(&s);
597 check_leaf_cap(r.root.as_deref().unwrap());
598 }
599
600 fn check_leaf_cap(node: &Node) {
601 match node {
602 Node::Leaf(s) => assert!(s.len() <= MAX_LEAF_BYTES, "leaf too big: {}", s.len()),
603 Node::Internal { left, right, .. } => {
604 check_leaf_cap(left);
605 check_leaf_cap(right);
606 }
607 }
608 }
609
610
611
612 struct Rng {
613 state: u64,
614 }
615 impl Rng {
616 fn new(seed: u64) -> Self {
617 Rng { state: seed | 1 }
618 }
619 fn next_u64(&mut self) -> u64 {
620
621 self.state = self.state.wrapping_add(0x9E3779B97F4A7C15);
622 let mut z = self.state;
623 z = (z ^ (z >> 30)).wrapping_mul(0xBF58476D1CE4E5B9);
624 z = (z ^ (z >> 27)).wrapping_mul(0x94D049BB133111EB);
625 z ^ (z >> 31)
626 }
627 fn range(&mut self, max_exclusive: usize) -> usize {
628 if max_exclusive == 0 {
629 return 0;
630 }
631 (self.next_u64() as usize) % max_exclusive
632 }
633 fn range_inclusive(&mut self, max_inclusive: usize) -> usize {
634 self.range(max_inclusive + 1)
635 }
636 }
637
638 #[test]
639 fn property_random_edits_match_string() {
640 let charset: &[char] = &[
641 'a', 'b', 'c', 'd', 'e', ' ', '!', '\n', '日', '本', '🎉', 'ñ', 'ß',
642 ];
643 let mut rng = Rng::new(0xC0FFEE_BABE_1234);
644 let mut rope = Rope::from_str("start");
645 let mut expected = String::from("start");
646
647 for iter in 0..10_000 {
648 let len = expected.chars().count();
649
650 let op_pick = rng.range(100);
651 let op = if len < 32 {
652
653 match op_pick {
654 0..=59 => 0,
655 60..=74 => 1,
656 75..=87 => 2,
657 _ => 3,
658 }
659 } else if len > 512 {
660
661 match op_pick {
662 0..=19 => 0,
663 20..=74 => 1,
664 75..=87 => 2,
665 _ => 3,
666 }
667 } else {
668 match op_pick {
669 0..=39 => 0,
670 40..=74 => 1,
671 75..=87 => 2,
672 _ => 3,
673 }
674 };
675
676 match op {
677 0 => {
678 let idx = rng.range_inclusive(len);
679 let n = 1 + rng.range(6);
680 let mut s = String::new();
681 for _ in 0..n {
682 s.push(charset[rng.range(charset.len())]);
683 }
684 rope.insert(idx, &s);
685 let bi = char_byte_index(&expected, idx);
686 expected.insert_str(bi, &s);
687 }
688 1 => {
689 if len == 0 {
690 continue;
691 }
692 let start = rng.range(len);
693 let end = start + rng.range_inclusive(len - start);
694 rope.delete(start, end);
695 let bs = char_byte_index(&expected, start);
696 let be = char_byte_index(&expected, end);
697 expected.replace_range(bs..be, "");
698 }
699 2 => {
700 if len == 0 {
701 assert_eq!(rope.char_at(0), None);
702 } else {
703 let idx = rng.range(len);
704 assert_eq!(
705 rope.char_at(idx),
706 expected.chars().nth(idx),
707 "char_at mismatch iter {} idx {}",
708 iter,
709 idx
710 );
711 }
712 }
713 3 => {
714 let start = rng.range_inclusive(len);
715 let end = start + rng.range_inclusive(len - start);
716 let got = rope.substring(start, end);
717 let bs = char_byte_index(&expected, start);
718 let be = char_byte_index(&expected, end);
719 let want = &expected[bs..be];
720 assert_eq!(got, want, "substring mismatch iter {} [{},{})", iter, start, end);
721 }
722 _ => unreachable!(),
723 }
724
725
726 let rope_str = rope.to_string();
727 assert_eq!(rope_str, expected, "roundtrip mismatch iter {}", iter);
728 assert_eq!(
729 rope.len(),
730 expected.chars().count(),
731 "len mismatch iter {}",
732 iter
733 );
734 assert_eq!(
735 rope.byte_len(),
736 expected.len(),
737 "byte_len mismatch iter {}",
738 iter
739 );
740 }
741 }
742}
743
Discussion
No comments yet. Start the discussion. Recorded by @patrick-toulme.