diff rust/hg-core/src/revlog/patch.rs @ 52160:e01e84e5e426

rust-revlog: add a Rust-only `InnerRevlog` This mirrors the Python `InnerRevlog` and will be used in a future patch to replace said Python implementation. This allows us to start doing more things in pure Rust, in particular reading and writing operations. A lot of changes have to be introduced all at once, it wouldn't be very useful to separate this patch IMO since all of them are either interlocked or only useful with the rest.
author Rapha?l Gom?s <rgomes@octobus.net>
date Thu, 10 Oct 2024 10:34:51 +0200
parents b68b19104d16
children bd8081e9fd62
line wrap: on
line diff
--- a/rust/hg-core/src/revlog/patch.rs	Thu Oct 10 10:38:35 2024 +0200
+++ b/rust/hg-core/src/revlog/patch.rs	Thu Oct 10 10:34:51 2024 +0200
@@ -1,5 +1,9 @@
 use byteorder::{BigEndian, ByteOrder};
 
+use crate::RevlogError;
+
+use super::inner_revlog::RevisionBuffer;
+
 /// A chunk of data to insert, delete or replace in a patch
 ///
 /// A chunk is:
@@ -61,14 +65,16 @@
 
 impl<'a> PatchList<'a> {
     /// Create a `PatchList` from bytes.
-    pub fn new(data: &'a [u8]) -> Self {
+    pub fn new(data: &'a [u8]) -> Result<Self, RevlogError> {
         let mut chunks = vec![];
         let mut data = data;
         while !data.is_empty() {
             let start = BigEndian::read_u32(&data[0..]);
             let end = BigEndian::read_u32(&data[4..]);
             let len = BigEndian::read_u32(&data[8..]);
-            assert!(start <= end);
+            if start > end {
+                return Err(RevlogError::corrupted("patch cannot be decoded"));
+            }
             chunks.push(Chunk {
                 start,
                 end,
@@ -76,29 +82,23 @@
             });
             data = &data[12 + (len as usize)..];
         }
-        PatchList { chunks }
-    }
-
-    /// Return the final length of data after patching
-    /// given its initial length .
-    fn size(&self, initial_size: i32) -> i32 {
-        self.chunks
-            .iter()
-            .fold(initial_size, |acc, chunk| acc + chunk.len_diff())
+        Ok(PatchList { chunks })
     }
 
     /// Apply the patch to some data.
-    pub fn apply(&self, initial: &[u8]) -> Vec<u8> {
+    pub fn apply<T>(
+        &self,
+        buffer: &mut dyn RevisionBuffer<Target = T>,
+        initial: &[u8],
+    ) {
         let mut last: usize = 0;
-        let mut vec =
-            Vec::with_capacity(self.size(initial.len() as i32) as usize);
         for Chunk { start, end, data } in self.chunks.iter() {
-            vec.extend(&initial[last..(*start as usize)]);
-            vec.extend(data.iter());
+            let slice = &initial[last..(*start as usize)];
+            buffer.extend_from_slice(slice);
+            buffer.extend_from_slice(data);
             last = *end as usize;
         }
-        vec.extend(&initial[last..]);
-        vec
+        buffer.extend_from_slice(&initial[last..]);
     }
 
     /// Combine two patch lists into a single patch list.
@@ -229,6 +229,8 @@
 
 #[cfg(test)]
 mod tests {
+    use crate::inner_revlog::CoreRevisionBuffer;
+
     use super::*;
 
     struct PatchDataBuilder {
@@ -264,17 +266,18 @@
         let data = vec![0u8, 0u8, 0u8];
         let mut patch1_data = PatchDataBuilder::new();
         patch1_data.replace(0, 1, &[1, 2]);
-        let mut patch1 = PatchList::new(patch1_data.get());
+        let mut patch1 = PatchList::new(patch1_data.get()).unwrap();
 
         let mut patch2_data = PatchDataBuilder::new();
         patch2_data.replace(2, 4, &[3, 4]);
-        let mut patch2 = PatchList::new(patch2_data.get());
+        let mut patch2 = PatchList::new(patch2_data.get()).unwrap();
 
         let patch = patch1.combine(&mut patch2);
 
-        let result = patch.apply(&data);
+        let mut buffer = CoreRevisionBuffer::new();
+        patch.apply(&mut buffer, &data);
 
-        assert_eq!(result, vec![1u8, 2, 3, 4]);
+        assert_eq!(buffer.finish(), vec![1u8, 2, 3, 4]);
     }
 
     #[test]
@@ -282,17 +285,18 @@
         let data = vec![0u8, 0u8, 0u8];
         let mut patch1_data = PatchDataBuilder::new();
         patch1_data.replace(2, 3, &[3]);
-        let mut patch1 = PatchList::new(patch1_data.get());
+        let mut patch1 = PatchList::new(patch1_data.get()).unwrap();
 
         let mut patch2_data = PatchDataBuilder::new();
         patch2_data.replace(1, 2, &[1, 2]);
-        let mut patch2 = PatchList::new(patch2_data.get());
+        let mut patch2 = PatchList::new(patch2_data.get()).unwrap();
 
         let patch = patch1.combine(&mut patch2);
 
-        let result = patch.apply(&data);
+        let mut buffer = CoreRevisionBuffer::new();
+        patch.apply(&mut buffer, &data);
 
-        assert_eq!(result, vec![0u8, 1, 2, 3]);
+        assert_eq!(buffer.finish(), vec![0u8, 1, 2, 3]);
     }
 
     #[test]
@@ -300,17 +304,18 @@
         let data = vec![0u8, 0, 0];
         let mut patch1_data = PatchDataBuilder::new();
         patch1_data.replace(1, 2, &[3, 4]);
-        let mut patch1 = PatchList::new(patch1_data.get());
+        let mut patch1 = PatchList::new(patch1_data.get()).unwrap();
 
         let mut patch2_data = PatchDataBuilder::new();
         patch2_data.replace(1, 4, &[1, 2, 3]);
-        let mut patch2 = PatchList::new(patch2_data.get());
+        let mut patch2 = PatchList::new(patch2_data.get()).unwrap();
 
         let patch = patch1.combine(&mut patch2);
 
-        let result = patch.apply(&data);
+        let mut buffer = CoreRevisionBuffer::new();
+        patch.apply(&mut buffer, &data);
 
-        assert_eq!(result, vec![0u8, 1, 2, 3]);
+        assert_eq!(buffer.finish(), vec![0u8, 1, 2, 3]);
     }
 
     #[test]
@@ -318,17 +323,18 @@
         let data = vec![0u8, 0, 0];
         let mut patch1_data = PatchDataBuilder::new();
         patch1_data.replace(0, 1, &[1, 3]);
-        let mut patch1 = PatchList::new(patch1_data.get());
+        let mut patch1 = PatchList::new(patch1_data.get()).unwrap();
 
         let mut patch2_data = PatchDataBuilder::new();
         patch2_data.replace(1, 4, &[2, 3, 4]);
-        let mut patch2 = PatchList::new(patch2_data.get());
+        let mut patch2 = PatchList::new(patch2_data.get()).unwrap();
 
         let patch = patch1.combine(&mut patch2);
 
-        let result = patch.apply(&data);
+        let mut buffer = CoreRevisionBuffer::new();
+        patch.apply(&mut buffer, &data);
 
-        assert_eq!(result, vec![1u8, 2, 3, 4]);
+        assert_eq!(buffer.finish(), vec![1u8, 2, 3, 4]);
     }
 
     #[test]
@@ -336,17 +342,18 @@
         let data = vec![0u8, 0, 0];
         let mut patch1_data = PatchDataBuilder::new();
         patch1_data.replace(1, 3, &[1, 3, 4]);
-        let mut patch1 = PatchList::new(patch1_data.get());
+        let mut patch1 = PatchList::new(patch1_data.get()).unwrap();
 
         let mut patch2_data = PatchDataBuilder::new();
         patch2_data.replace(0, 2, &[1, 2]);
-        let mut patch2 = PatchList::new(patch2_data.get());
+        let mut patch2 = PatchList::new(patch2_data.get()).unwrap();
 
         let patch = patch1.combine(&mut patch2);
 
-        let result = patch.apply(&data);
+        let mut buffer = CoreRevisionBuffer::new();
+        patch.apply(&mut buffer, &data);
 
-        assert_eq!(result, vec![1u8, 2, 3, 4]);
+        assert_eq!(buffer.finish(), vec![1u8, 2, 3, 4]);
     }
 
     #[test]
@@ -354,16 +361,17 @@
         let data = vec![0u8, 0, 0];
         let mut patch1_data = PatchDataBuilder::new();
         patch1_data.replace(0, 3, &[1, 3, 3, 4]);
-        let mut patch1 = PatchList::new(patch1_data.get());
+        let mut patch1 = PatchList::new(patch1_data.get()).unwrap();
 
         let mut patch2_data = PatchDataBuilder::new();
         patch2_data.replace(1, 3, &[2, 3]);
-        let mut patch2 = PatchList::new(patch2_data.get());
+        let mut patch2 = PatchList::new(patch2_data.get()).unwrap();
 
         let patch = patch1.combine(&mut patch2);
 
-        let result = patch.apply(&data);
+        let mut buffer = CoreRevisionBuffer::new();
+        patch.apply(&mut buffer, &data);
 
-        assert_eq!(result, vec![1u8, 2, 3, 4]);
+        assert_eq!(buffer.finish(), vec![1u8, 2, 3, 4]);
     }
 }