1pub mod apply {
3 #[derive(thiserror::Error, Debug)]
5 #[allow(missing_docs)]
6 pub enum Error {
7 #[error("Corrupt delta data: {message}")]
8 Corrupt { message: &'static str },
9 #[error("Encountered unsupported command code: 0")]
10 UnsupportedCommandCode,
11 #[error("Delta copy from base: byte slices must match")]
12 DeltaCopyBaseSliceMismatch,
13 #[error("Delta copy data: byte slices must match")]
14 DeltaCopyDataSliceMismatch,
15 }
16}
17
18pub(crate) fn decode_header_size(d: &[u8]) -> Result<(u64, usize), apply::Error> {
21 let mut shift = 0;
22 let mut size = 0u64;
23 let mut consumed = 0;
24 for cmd in d.iter() {
25 if shift >= u64::BITS {
26 return Err(apply::Error::Corrupt {
27 message: "delta header size uses more bits than fit into u64",
28 });
29 }
30 consumed += 1;
31 size |= (u64::from(*cmd) & 0x7f) << shift;
32 shift += 7;
33 if *cmd & 0x80 == 0 {
34 return Ok((size, consumed));
35 }
36 }
37 Err(apply::Error::Corrupt {
38 message: "delta header size is truncated",
39 })
40}
41
42pub(crate) fn apply(base: &[u8], mut target: &mut [u8], data: &[u8]) -> Result<(), apply::Error> {
43 fn next_byte(data: &[u8], i: &mut usize) -> Result<u8, apply::Error> {
44 let byte = *data.get(*i).ok_or(apply::Error::Corrupt {
45 message: "delta copy instruction is truncated",
46 })?;
47 *i += 1;
48 Ok(byte)
49 }
50
51 let mut i = 0;
52 while let Some(cmd) = data.get(i) {
53 i += 1;
54 match cmd {
55 cmd if cmd & 0b1000_0000 != 0 => {
56 let (mut ofs, mut size): (u32, u32) = (0, 0);
57 if cmd & 0b0000_0001 != 0 {
58 ofs = u32::from(next_byte(data, &mut i)?);
59 }
60 if cmd & 0b0000_0010 != 0 {
61 ofs |= u32::from(next_byte(data, &mut i)?) << 8;
62 }
63 if cmd & 0b0000_0100 != 0 {
64 ofs |= u32::from(next_byte(data, &mut i)?) << 16;
65 }
66 if cmd & 0b0000_1000 != 0 {
67 ofs |= u32::from(next_byte(data, &mut i)?) << 24;
68 }
69 if cmd & 0b0001_0000 != 0 {
70 size = u32::from(next_byte(data, &mut i)?);
71 }
72 if cmd & 0b0010_0000 != 0 {
73 size |= u32::from(next_byte(data, &mut i)?) << 8;
74 }
75 if cmd & 0b0100_0000 != 0 {
76 size |= u32::from(next_byte(data, &mut i)?) << 16;
77 }
78 if size == 0 {
79 size = 0x10000; }
81 let ofs = ofs as usize;
82 let end = ofs.checked_add(size as usize).ok_or(apply::Error::Corrupt {
83 message: "delta copy range overflows",
84 })?;
85 std::io::Write::write(
86 &mut target,
87 base.get(ofs..end).ok_or(apply::Error::Corrupt {
88 message: "delta copy range exceeds base object size",
89 })?,
90 )
91 .map_err(|_e| apply::Error::DeltaCopyBaseSliceMismatch)?;
92 }
93 0 => {
94 return Err(apply::Error::Corrupt {
95 message: "delta command 0 is reserved and invalid",
96 });
97 }
98 size => {
99 let end = i.checked_add(*size as usize).ok_or(apply::Error::Corrupt {
100 message: "delta insert range overflows",
101 })?;
102 std::io::Write::write(
103 &mut target,
104 data.get(i..end).ok_or(apply::Error::Corrupt {
105 message: "delta insert data is truncated",
106 })?,
107 )
108 .map_err(|_e| apply::Error::DeltaCopyDataSliceMismatch)?;
109 i = end;
110 }
111 }
112 }
113 debug_assert_eq!(
114 i,
115 data.len(),
116 "delta instructions were not consumed completely, should be impossible"
117 );
118 if !target.is_empty() {
119 return Err(apply::Error::Corrupt {
120 message: "delta instructions produced fewer bytes than promised",
121 });
122 }
123
124 Ok(())
125}