1use alloc::vec::Vec;
7
8use crate::err::{Error, Result};
9
10pub fn apply(base: &[u8], delta: &[u8]) -> Result<Vec<u8>> {
12 let mut pos = 0;
13 let base_size = read_varint(delta, &mut pos)?;
14 if base_size != base.len() as u64 {
15 return Err(Error::Corrupt("delta base size"));
16 }
17 let result_size = read_varint(delta, &mut pos)?;
18 let mut out = Vec::with_capacity(result_size as usize);
19
20 while pos < delta.len() {
21 let op = delta[pos];
22 pos += 1;
23 if op & 0x80 != 0 {
24 let mut offset: usize = 0;
26 for i in 0..4 {
27 if op & (1 << i) != 0 {
28 let b = *delta.get(pos).ok_or(Error::UnexpectedEof)?;
29 offset |= usize::from(b) << (8 * i);
30 pos += 1;
31 }
32 }
33 let mut size: usize = 0;
34 for i in 0..3 {
35 if op & (1 << (4 + i)) != 0 {
36 let b = *delta.get(pos).ok_or(Error::UnexpectedEof)?;
37 size |= usize::from(b) << (8 * i);
38 pos += 1;
39 }
40 }
41 if size == 0 {
43 size = 0x10000;
44 }
45 let src = base
46 .get(offset..offset.checked_add(size).ok_or(Error::UnexpectedEof)?)
47 .ok_or(Error::Corrupt("delta copy range"))?;
48 out.extend_from_slice(src);
49 } else if op != 0 {
50 let n = usize::from(op);
52 let src = delta.get(pos..pos + n).ok_or(Error::UnexpectedEof)?;
53 out.extend_from_slice(src);
54 pos += n;
55 } else {
56 return Err(Error::Corrupt("delta opcode 0"));
57 }
58 }
59
60 if out.len() as u64 != result_size {
61 return Err(Error::Corrupt("delta result size"));
62 }
63 Ok(out)
64}
65
66fn read_varint(data: &[u8], pos: &mut usize) -> Result<u64> {
68 let mut value: u64 = 0;
69 let mut shift = 0;
70 loop {
71 let b = *data.get(*pos).ok_or(Error::UnexpectedEof)?;
72 *pos += 1;
73 value |= u64::from(b & 0x7f) << shift;
74 if b & 0x80 == 0 {
75 return Ok(value);
76 }
77 shift += 7;
78 if shift >= 64 {
79 return Err(Error::Corrupt("varint overflow"));
80 }
81 }
82}
83
84#[cfg(test)]
85mod tests {
86 use super::*;
87
88 #[test]
89 fn insert_only() {
90 let delta = [0, 5, 5, b'h', b'e', b'l', b'l', b'o'];
92 assert_eq!(apply(b"", &delta).unwrap(), b"hello");
93 }
94
95 #[test]
96 fn copy_and_insert() {
97 let delta = [11, 6, 0x91, 6, 5, 1, b'!'];
99 assert_eq!(apply(b"hello world", &delta).unwrap(), b"world!");
100 }
101
102 #[test]
103 fn size_mismatch_rejected() {
104 let delta = [0, 4, 5, b'h', b'e', b'l', b'l', b'o'];
105 assert!(apply(b"", &delta).is_err());
106 }
107
108 #[test]
109 fn out_of_range_copy_rejected() {
110 let delta = [5, 5, 0x91, 4, 5];
111 assert!(apply(b"abcde", &delta).is_err());
112 }
113}