This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

core / knot2 / third_party / gix-pack / src / data / delta.rs
4.7 kB 127 lines
1/// 2pub mod apply { 3 /// Returned when failing to apply deltas. 4 #[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 18/// Given the decompressed pack delta `d`, decode a size in bytes (either the base object size or the result object size) 19/// Equivalent to [this canonical git function](https://github.com/git/git/blob/311531c9de557d25ac087c1637818bd2aad6eb3a/delta.h#L89) 20pub(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; // 65536 80 } 81 let ofs = ofs as usize; 82 let end = ofs 83 .checked_add(size as usize) 84 .ok_or(apply::Error::Corrupt { 85 message: "delta copy range overflows", 86 })?; 87 std::io::Write::write( 88 &mut target, 89 base.get(ofs..end).ok_or(apply::Error::Corrupt { 90 message: "delta copy range exceeds base object size", 91 })?, 92 ) 93 .map_err(|_e| apply::Error::DeltaCopyBaseSliceMismatch)?; 94 } 95 0 => { 96 return Err(apply::Error::Corrupt { 97 message: "delta command 0 is reserved and invalid", 98 }); 99 } 100 size => { 101 let end = i.checked_add(*size as usize).ok_or(apply::Error::Corrupt { 102 message: "delta insert range overflows", 103 })?; 104 std::io::Write::write( 105 &mut target, 106 data.get(i..end).ok_or(apply::Error::Corrupt { 107 message: "delta insert data is truncated", 108 })?, 109 ) 110 .map_err(|_e| apply::Error::DeltaCopyDataSliceMismatch)?; 111 i = end; 112 } 113 } 114 } 115 debug_assert_eq!( 116 i, 117 data.len(), 118 "delta instructions were not consumed completely, should be impossible" 119 ); 120 if !target.is_empty() { 121 return Err(apply::Error::Corrupt { 122 message: "delta instructions produced fewer bytes than promised", 123 }); 124 } 125 126 Ok(()) 127}