1use std::ops::Range;
27use std::rc::Rc;
28
29use crate::error::Error;
30
31#[derive(Clone)]
33struct Span {
34 range: Range<usize>,
36 data: Rc<[u8]>,
38 committed: bool,
40}
41
42impl std::fmt::Debug for Span {
43 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
44 let state = if self.is_insert() {
45 "inserted"
46 } else {
47 "replaced"
48 };
49
50 let committed = if self.committed {
51 "committed"
52 } else {
53 "uncommitted"
54 };
55
56 write!(
57 f,
58 "({}, {}: {state}, {committed})",
59 self.range.start, self.range.end
60 )
61 }
62}
63
64impl Span {
65 fn new(range: Range<usize>, data: &[u8]) -> Self {
66 Self {
67 range,
68 data: data.into(),
69 committed: false,
70 }
71 }
72
73 fn is_insert(&self) -> bool {
78 self.range.start == self.range.end
79 }
80}
81
82impl PartialEq for Span {
83 fn eq(&self, other: &Self) -> bool {
86 self.range == other.range && self.data == other.data
87 }
88}
89
90#[derive(Debug, Clone, Default)]
92pub struct Data {
93 original: Vec<u8>,
95 parts: Vec<Span>,
99}
100
101impl Data {
102 pub fn new(data: &[u8]) -> Self {
104 Data {
105 original: data.into(),
106 parts: vec![],
107 }
108 }
109
110 pub fn commit(&mut self) {
112 self.parts.iter_mut().for_each(|span| span.committed = true);
113 }
114
115 pub fn restore(&mut self) {
117 self.parts.retain(|parts| parts.committed);
118 }
119
120 pub fn to_vec(&self) -> Vec<u8> {
124 let mut prev_end = 0;
125 let mut s = self.parts.iter().fold(Vec::new(), |mut acc, span| {
126 debug_assert!(
128 prev_end <= span.range.start,
129 "expected parts in sorted order"
130 );
131
132 acc.extend_from_slice(&self.original[prev_end..span.range.start]);
133 acc.extend_from_slice(&span.data);
134 prev_end = span.range.end;
135 acc
136 });
137
138 s.extend_from_slice(&self.original[prev_end..]);
140 s
141 }
142
143 pub fn replace_range(&mut self, range: Range<usize>, data: &[u8]) -> Result<(), Error> {
151 if range.start > range.end {
152 return Err(Error::InvalidRange(range));
153 }
154
155 if range.end > self.original.len() {
156 return Err(Error::DataLengthExceeded(range, self.original.len()));
157 }
158
159 let ins_point = self.parts.partition_point(|span| {
163 span.range.start < range.start
164 || (span.range.start == range.start && span.range.end < range.end)
165 });
166
167 let incoming = Span::new(range, data.as_ref());
168
169 if let Some(before) = ins_point.checked_sub(1).and_then(|i| self.parts.get(i)) {
171 if incoming.range.start < before.range.end {
172 return Err(Error::AlreadyReplaced {
173 is_identical: incoming == *before,
174 range: incoming.range,
175 });
176 }
177 }
178
179 if let Some(after) = self.parts.get(ins_point) {
182 if incoming.range.end > after.range.start || incoming.range == after.range {
183 return Err(Error::AlreadyReplaced {
184 is_identical: incoming == *after,
185 range: incoming.range,
186 });
187 }
188 }
189
190 self.parts.insert(ins_point, incoming);
191 Ok(())
192 }
193}
194
195#[cfg(test)]
196mod tests {
197 use super::*;
198 use proptest::prelude::*;
199
200 fn str(i: &[u8]) -> &str {
201 ::std::str::from_utf8(i).unwrap()
202 }
203
204 #[test]
205 fn insert_at_beginning() {
206 let mut d = Data::new(b"foo bar baz");
207 d.replace_range(0..0, b"oh no ").unwrap();
208 assert_eq!("oh no foo bar baz", str(&d.to_vec()));
209 }
210
211 #[test]
212 fn insert_at_end() {
213 let mut d = Data::new(b"foo bar baz");
214 d.replace_range(11..11, b" oh no").unwrap();
215 assert_eq!("foo bar baz oh no", str(&d.to_vec()));
216 }
217
218 #[test]
219 fn replace_some_stuff() {
220 let mut d = Data::new(b"foo bar baz");
221 d.replace_range(4..7, b"lol").unwrap();
222 assert_eq!("foo lol baz", str(&d.to_vec()));
223 }
224
225 #[test]
226 fn replace_a_single_char() {
227 let mut d = Data::new(b"let y = true;");
228 d.replace_range(4..5, b"mut y").unwrap();
229 assert_eq!("let mut y = true;", str(&d.to_vec()));
230 }
231
232 #[test]
233 fn replace_multiple_lines() {
234 let mut d = Data::new(b"lorem\nipsum\ndolor");
235
236 d.replace_range(6..11, b"lol").unwrap();
237 assert_eq!("lorem\nlol\ndolor", str(&d.to_vec()));
238
239 d.replace_range(12..17, b"lol").unwrap();
240 assert_eq!("lorem\nlol\nlol", str(&d.to_vec()));
241 }
242
243 #[test]
244 fn replace_multiple_lines_with_insert_only() {
245 let mut d = Data::new(b"foo!");
246
247 d.replace_range(3..3, b"bar").unwrap();
248 assert_eq!("foobar!", str(&d.to_vec()));
249
250 d.replace_range(0..3, b"baz").unwrap();
251 assert_eq!("bazbar!", str(&d.to_vec()));
252
253 d.replace_range(3..4, b"?").unwrap();
254 assert_eq!("bazbar?", str(&d.to_vec()));
255 }
256
257 #[test]
258 #[allow(clippy::reversed_empty_ranges)]
259 fn replace_invalid_range() {
260 let mut d = Data::new(b"foo!");
261
262 assert!(d.replace_range(2..1, b"bar").is_err());
263 assert!(d.replace_range(0..3, b"bar").is_ok());
264 }
265
266 #[test]
267 fn empty_to_vec_roundtrip() {
268 let s = "";
269 assert_eq!(s.as_bytes(), Data::new(s.as_bytes()).to_vec().as_slice());
270 }
271
272 #[test]
273 fn replace_same_range_diff_data() {
274 let mut d = Data::new(b"foo bar baz");
275
276 d.replace_range(4..7, b"lol").unwrap();
277 assert_eq!("foo lol baz", str(&d.to_vec()));
278
279 assert!(matches!(
280 d.replace_range(4..7, b"lol2").unwrap_err(),
281 Error::AlreadyReplaced {
282 is_identical: false,
283 ..
284 },
285 ));
286 }
287
288 #[test]
289 fn replace_same_range_same_data() {
290 let mut d = Data::new(b"foo bar baz");
291
292 d.replace_range(4..7, b"lol").unwrap();
293 assert_eq!("foo lol baz", str(&d.to_vec()));
294
295 assert!(matches!(
296 d.replace_range(4..7, b"lol").unwrap_err(),
297 Error::AlreadyReplaced {
298 is_identical: true,
299 ..
300 },
301 ));
302 }
303
304 #[test]
305 fn broken_replacements() {
306 let mut d = Data::new(b"foo");
307 assert!(matches!(
308 d.replace_range(4..8, b"lol").unwrap_err(),
309 Error::DataLengthExceeded(std::ops::Range { start: 4, end: 8 }, 3),
310 ));
311 }
312
313 #[test]
314 fn insert_same_twice() {
315 let mut d = Data::new(b"foo");
316 d.replace_range(1..1, b"b").unwrap();
317 assert_eq!("fboo", str(&d.to_vec()));
318 assert!(matches!(
319 d.replace_range(1..1, b"b").unwrap_err(),
320 Error::AlreadyReplaced {
321 is_identical: true,
322 ..
323 },
324 ));
325 assert_eq!("fboo", str(&d.to_vec()));
326 }
327
328 #[test]
329 fn commit_restore() {
330 let mut d = Data::new(b", ");
331 assert_eq!(", ", str(&d.to_vec()));
332
333 d.replace_range(2..2, b"world").unwrap();
334 d.replace_range(0..0, b"hello").unwrap();
335 assert_eq!("hello, world", str(&d.to_vec()));
336
337 d.restore();
338 assert_eq!(", ", str(&d.to_vec()));
339
340 d.commit();
341 assert_eq!(", ", str(&d.to_vec()));
342
343 d.replace_range(2..2, b"world").unwrap();
344 assert_eq!(", world", str(&d.to_vec()));
345 d.commit();
346 assert_eq!(", world", str(&d.to_vec()));
347 d.restore();
348 assert_eq!(", world", str(&d.to_vec()));
349
350 d.replace_range(0..0, b"hello").unwrap();
351 assert_eq!("hello, world", str(&d.to_vec()));
352 d.commit();
353 assert_eq!("hello, world", str(&d.to_vec()));
354 d.restore();
355 assert_eq!("hello, world", str(&d.to_vec()));
356 }
357
358 proptest! {
359 #[test]
360 fn new_to_vec_roundtrip(ref s in "\\PC*") {
361 assert_eq!(s.as_bytes(), Data::new(s.as_bytes()).to_vec().as_slice());
362 }
363
364 #[test]
365 fn replace_random_chunks(
366 ref data in "\\PC*",
367 ref replacements in prop::collection::vec(
368 (any::<::std::ops::Range<usize>>(), any::<Vec<u8>>()),
369 1..100,
370 )
371 ) {
372 let mut d = Data::new(data.as_bytes());
373 for &(ref range, ref bytes) in replacements {
374 let _ = d.replace_range(range.clone(), bytes);
375 }
376 }
377 }
378}