oxihuman_core/
rope_string.rs1#![allow(dead_code)]
3#![allow(clippy::should_implement_trait)]
4#![allow(clippy::inherent_to_string)]
5
6const LEAF_MAX: usize = 64;
9
10#[allow(dead_code)]
12#[derive(Debug, Clone)]
13enum RopeNode {
14 Leaf(String),
15 Branch {
16 left: Box<RopeNode>,
17 right: Box<RopeNode>,
18 weight: usize,
19 },
20}
21
22impl RopeNode {
23 fn len(&self) -> usize {
24 match self {
25 RopeNode::Leaf(s) => s.len(),
26 RopeNode::Branch { weight, right, .. } => weight + right.len(),
27 }
28 }
29
30 fn to_string_buf(&self, buf: &mut String) {
31 match self {
32 RopeNode::Leaf(s) => buf.push_str(s),
33 RopeNode::Branch { left, right, .. } => {
34 left.to_string_buf(buf);
35 right.to_string_buf(buf);
36 }
37 }
38 }
39
40 fn char_at(&self, idx: usize) -> Option<u8> {
41 match self {
42 RopeNode::Leaf(s) => s.as_bytes().get(idx).copied(),
43 RopeNode::Branch {
44 left,
45 weight,
46 right,
47 } => {
48 if idx < *weight {
49 left.char_at(idx)
50 } else {
51 right.char_at(idx - weight)
52 }
53 }
54 }
55 }
56}
57
58#[allow(dead_code)]
60#[derive(Debug, Clone)]
61pub struct RopeString {
62 root: Option<RopeNode>,
63}
64
65#[allow(dead_code)]
66impl RopeString {
67 pub fn new() -> Self {
68 Self { root: None }
69 }
70
71 pub fn from_str(s: &str) -> Self {
72 if s.is_empty() {
73 return Self::new();
74 }
75 Self {
76 root: Some(Self::build_leaves(s)),
77 }
78 }
79
80 fn build_leaves(s: &str) -> RopeNode {
81 if s.len() <= LEAF_MAX {
82 return RopeNode::Leaf(s.to_string());
83 }
84 let mid = s.len() / 2;
85 let split = s.floor_char_boundary(mid);
87 let left = Self::build_leaves(&s[..split]);
88 let right = Self::build_leaves(&s[split..]);
89 let weight = left.len();
90 RopeNode::Branch {
91 left: Box::new(left),
92 right: Box::new(right),
93 weight,
94 }
95 }
96
97 pub fn len(&self) -> usize {
98 self.root.as_ref().map(|r| r.len()).unwrap_or(0)
99 }
100
101 pub fn is_empty(&self) -> bool {
102 self.len() == 0
103 }
104
105 pub fn to_string(&self) -> String {
106 let mut buf = String::with_capacity(self.len());
107 if let Some(ref r) = self.root {
108 r.to_string_buf(&mut buf);
109 }
110 buf
111 }
112
113 pub fn byte_at(&self, idx: usize) -> Option<u8> {
114 self.root.as_ref().and_then(|r| r.char_at(idx))
115 }
116
117 pub fn concat(&self, other: &RopeString) -> RopeString {
118 match (&self.root, &other.root) {
119 (None, _) => other.clone(),
120 (_, None) => self.clone(),
121 (Some(l), Some(r)) => {
122 let weight = l.len();
123 RopeString {
124 root: Some(RopeNode::Branch {
125 left: Box::new(l.clone()),
126 right: Box::new(r.clone()),
127 weight,
128 }),
129 }
130 }
131 }
132 }
133
134 pub fn append(&mut self, s: &str) {
135 let other = RopeString::from_str(s);
136 *self = self.concat(&other);
137 }
138
139 pub fn substring(&self, start: usize, end: usize) -> String {
141 let full = self.to_string();
142 if start >= full.len() || start >= end {
143 return String::new();
144 }
145 let e = end.min(full.len());
146 full[start..e].to_string()
147 }
148
149 pub fn depth(&self) -> usize {
150 fn d(node: &RopeNode) -> usize {
151 match node {
152 RopeNode::Leaf(_) => 1,
153 RopeNode::Branch { left, right, .. } => 1 + d(left).max(d(right)),
154 }
155 }
156 self.root.as_ref().map(d).unwrap_or(0)
157 }
158}
159
160impl Default for RopeString {
161 fn default() -> Self {
162 Self::new()
163 }
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169
170 #[test]
171 fn test_from_str() {
172 let r = RopeString::from_str("hello world");
173 assert_eq!(r.to_string(), "hello world");
174 assert_eq!(r.len(), 11);
175 }
176
177 #[test]
178 fn test_empty() {
179 let r = RopeString::new();
180 assert!(r.is_empty());
181 assert_eq!(r.to_string(), "");
182 }
183
184 #[test]
185 fn test_concat() {
186 let a = RopeString::from_str("hello ");
187 let b = RopeString::from_str("world");
188 let c = a.concat(&b);
189 assert_eq!(c.to_string(), "hello world");
190 }
191
192 #[test]
193 fn test_append() {
194 let mut r = RopeString::from_str("foo");
195 r.append("bar");
196 assert_eq!(r.to_string(), "foobar");
197 }
198
199 #[test]
200 fn test_byte_at() {
201 let r = RopeString::from_str("abc");
202 assert_eq!(r.byte_at(0), Some(b'a'));
203 assert_eq!(r.byte_at(2), Some(b'c'));
204 assert_eq!(r.byte_at(3), None);
205 }
206
207 #[test]
208 fn test_substring() {
209 let r = RopeString::from_str("hello world");
210 assert_eq!(r.substring(0, 5), "hello");
211 assert_eq!(r.substring(6, 11), "world");
212 }
213
214 #[test]
215 fn test_large_string() {
216 let s = "x".repeat(1000);
217 let r = RopeString::from_str(&s);
218 assert_eq!(r.len(), 1000);
219 assert_eq!(r.to_string(), s);
220 }
221
222 #[test]
223 fn test_depth() {
224 let r = RopeString::from_str("short");
225 assert!(r.depth() >= 1);
226 }
227
228 #[test]
229 fn test_concat_empty() {
230 let a = RopeString::new();
231 let b = RopeString::from_str("test");
232 assert_eq!(a.concat(&b).to_string(), "test");
233 assert_eq!(b.concat(&a).to_string(), "test");
234 }
235
236 #[test]
237 fn test_substring_out_of_range() {
238 let r = RopeString::from_str("abc");
239 assert_eq!(r.substring(10, 20), "");
240 }
241}