Skip to main content

git_zero_core/pack/
delta.rs

1//! Resumable Git delta instructions, independent of base/result storage.
2use super::{DeltaLimits, decode::variable_byte};
3use crate::{Error, Result};
4
5/// One instruction or boundary. Execute each copy/insert before resuming.
6#[derive(Clone, Copy, Debug, Eq, PartialEq)]
7pub enum DeltaStatus<'input> {
8    NeedInput,
9    Header { base_size: u64, result_size: u64 },
10    Copy { offset: u64, length: u64 },
11    Insert(&'input [u8]),
12    Finished,
13}
14
15#[derive(Clone, Copy, Debug, Eq, PartialEq)]
16pub struct DeltaProgress<'input> {
17    pub consumed: usize,
18    pub status: DeltaStatus<'input>,
19}
20
21#[derive(Clone, Copy)]
22enum State {
23    Base,
24    Size,
25    Opcode,
26    Copy(CopyFields),
27    Insert(u8),
28    Done,
29    Failed(Error),
30}
31
32#[derive(Clone, Copy)]
33struct CopyFields {
34    flags: u8,
35    index: u8,
36    offset: u32,
37    length: u32,
38}
39
40impl CopyFields {
41    fn read(&mut self, input: &[u8]) -> (usize, bool) {
42        let mut consumed = 0;
43        while self.index < 7 {
44            if self.flags & (1 << self.index) != 0 {
45                let Some(&byte) = input.get(consumed) else {
46                    return (consumed, false);
47                };
48                consumed += 1;
49                if self.index < 4 {
50                    self.offset |= u32::from(byte) << (self.index * 8);
51                } else {
52                    self.length |= u32::from(byte) << ((self.index - 4) * 8);
53                }
54            }
55            self.index += 1;
56        }
57        (consumed, true)
58    }
59}
60
61/// Parse fragmented, already inflated delta bytes with fixed inline state.
62/// The trusted base must be canonical and completely resolved at `base_depth`.
63/// Copies use 64-bit ranges so the host can await bounded storage reads between
64/// events. Inserts borrow the current input, including partial instructions.
65///
66/// Output is provisional: the caller must execute all events, hash the result
67/// with its inherited object kind, and validate the enclosing pack and graph
68/// before publishing refs. `position` counts emitted bytes, not successful I/O.
69pub struct DeltaDecoder {
70    state: State,
71    limits: DeltaLimits,
72    base_size: u64,
73    result_size: u64,
74    position: u64,
75    instructions: u64,
76    variable: u64,
77    shift: u32,
78    depth: u32,
79}
80
81impl DeltaDecoder {
82    pub fn new(base_size: u64, base_depth: u32, limits: DeltaLimits) -> Result<Self> {
83        if base_depth >= limits.max_depth {
84            return Err(Error::LimitExceeded);
85        }
86        Ok(Self {
87            state: State::Base,
88            limits,
89            base_size,
90            result_size: 0,
91            position: 0,
92            instructions: 0,
93            variable: 0,
94            shift: 0,
95            depth: base_depth + 1,
96        })
97    }
98
99    pub fn position(&self) -> u64 {
100        self.position
101    }
102    pub fn depth(&self) -> u32 {
103        self.depth
104    }
105
106    /// Return at most one event. Resubmit the unconsumed suffix, retaining its
107    /// EOF flag; `end_of_input` closes the delta, not merely this input chunk.
108    /// Failures are terminal. Exact EOF is required, including an empty call
109    /// after the last instruction if its event consumed the final input byte.
110    pub fn update<'input>(
111        &mut self,
112        input: &'input [u8],
113        end_of_input: bool,
114    ) -> Result<DeltaProgress<'input>> {
115        let result = self.advance(input, end_of_input);
116        if let Err(error) = result {
117            self.state = State::Failed(error);
118        }
119        result
120    }
121
122    pub fn finish(&self) -> Result<()> {
123        match self.state {
124            State::Done => Ok(()),
125            State::Failed(error) => Err(error),
126            _ => Err(Error::CorruptPack),
127        }
128    }
129
130    fn advance<'input>(&mut self, input: &'input [u8], eof: bool) -> Result<DeltaProgress<'input>> {
131        let mut consumed = 0;
132        loop {
133            let status = match self.state {
134                State::Failed(error) => return Err(error),
135                State::Done => {
136                    if !input.is_empty() {
137                        return Err(Error::CorruptPack);
138                    }
139                    Some(DeltaStatus::Finished)
140                }
141                State::Opcode if self.position == self.result_size => {
142                    if consumed != input.len() {
143                        return Err(Error::CorruptPack);
144                    }
145                    if eof {
146                        self.state = State::Done;
147                        Some(DeltaStatus::Finished)
148                    } else {
149                        Some(DeltaStatus::NeedInput)
150                    }
151                }
152                State::Copy(mut fields) => {
153                    let (read, complete) = fields.read(&input[consumed..]);
154                    consumed += read;
155                    if complete {
156                        Some(self.copy(fields.offset, fields.length)?)
157                    } else {
158                        self.state = State::Copy(fields);
159                        if eof {
160                            return Err(Error::CorruptPack);
161                        }
162                        Some(DeltaStatus::NeedInput)
163                    }
164                }
165                _ if consumed == input.len() => {
166                    if eof {
167                        return Err(Error::CorruptPack);
168                    }
169                    Some(DeltaStatus::NeedInput)
170                }
171                State::Base | State::Size => {
172                    let byte = input[consumed];
173                    consumed += 1;
174                    self.size_byte(byte)?
175                }
176                State::Opcode => {
177                    self.opcode(input[consumed])?;
178                    consumed += 1;
179                    None
180                }
181                State::Insert(remaining) => {
182                    let length = usize::from(remaining).min(input.len() - consumed);
183                    let bytes = &input[consumed..consumed + length];
184                    consumed += length;
185                    self.emit(length as u64)?;
186                    self.state = if length == usize::from(remaining) {
187                        State::Opcode
188                    } else {
189                        State::Insert(remaining - length as u8)
190                    };
191                    Some(DeltaStatus::Insert(bytes))
192                }
193            };
194            if let Some(status) = status {
195                return Ok(DeltaProgress { consumed, status });
196            }
197        }
198    }
199
200    fn size_byte(&mut self, byte: u8) -> Result<Option<DeltaStatus<'static>>> {
201        let Some(value) = variable_byte(&mut self.variable, &mut self.shift, byte)? else {
202            return Ok(None);
203        };
204        self.variable = 0;
205        self.shift = 0;
206        if matches!(self.state, State::Base) {
207            if value != self.base_size {
208                return Err(Error::CorruptPack);
209            }
210            self.state = State::Size;
211            return Ok(None);
212        }
213        if value > self.limits.max_output {
214            return Err(Error::LimitExceeded);
215        }
216        self.result_size = value;
217        self.state = State::Opcode;
218        Ok(Some(DeltaStatus::Header {
219            base_size: self.base_size,
220            result_size: value,
221        }))
222    }
223
224    fn opcode(&mut self, opcode: u8) -> Result<()> {
225        if self.instructions == self.limits.max_instructions {
226            return Err(Error::LimitExceeded);
227        }
228        self.instructions += 1;
229        self.state = match opcode {
230            0 => return Err(Error::CorruptPack),
231            1..=127 => {
232                if u64::from(opcode) > self.result_size - self.position {
233                    return Err(Error::CorruptPack);
234                }
235                State::Insert(opcode)
236            }
237            _ => State::Copy(CopyFields {
238                flags: opcode,
239                index: 0,
240                offset: 0,
241                length: 0,
242            }),
243        };
244        Ok(())
245    }
246
247    fn copy(&mut self, offset: u32, length: u32) -> Result<DeltaStatus<'static>> {
248        let length = if length == 0 {
249            65536
250        } else {
251            u64::from(length)
252        };
253        let offset = u64::from(offset);
254        if offset + length > self.base_size {
255            return Err(Error::CorruptPack);
256        }
257        self.emit(length)?;
258        self.state = State::Opcode;
259        Ok(DeltaStatus::Copy { offset, length })
260    }
261
262    fn emit(&mut self, length: u64) -> Result<()> {
263        if length > self.result_size - self.position {
264            return Err(Error::CorruptPack);
265        }
266        self.position += length;
267        Ok(())
268    }
269}