git_plumber/git/pack/
delta.rs

1use std::fmt;
2
3#[derive(Debug, Clone)]
4pub enum DeltaInstruction {
5    Copy { offset: usize, size: usize },
6    Insert { data: Vec<u8> },
7}
8
9impl fmt::Display for DeltaInstruction {
10    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
11        match self {
12            DeltaInstruction::Copy { offset, size } => {
13                write!(f, "Copy: offset={offset}, size={size}")
14            }
15            DeltaInstruction::Insert { data } => {
16                write!(f, "Insert: {} bytes", data.len())?;
17                if data.len() <= 100 {
18                    write!(f, " (Data: {:?})", String::from_utf8_lossy(data))
19                } else {
20                    write!(f, " (Data: {:?}...)", String::from_utf8_lossy(&data[..100]))
21                }
22            }
23        }
24    }
25}
26
27pub fn parse_delta_instructions(input: &[u8]) -> nom::IResult<&[u8], Vec<DeltaInstruction>> {
28    let mut instructions = Vec::new();
29    let mut i = 0;
30
31    while i < input.len() {
32        let first_byte = input[i];
33        i += 1;
34
35        if first_byte == 0 {
36            // Reserved instruction
37            continue;
38        }
39
40        if first_byte & 0x80 != 0 {
41            // Copy instruction
42            let mut offset = 0;
43            let mut size = 0;
44            let mut shift = 0;
45
46            // Parse offset bytes
47            for bit in 0..4 {
48                if first_byte & (1 << bit) != 0 {
49                    if i >= input.len() {
50                        return Err(nom::Err::Incomplete(nom::Needed::new(1)));
51                    }
52                    offset |= (input[i] as usize) << shift;
53                    i += 1;
54                }
55                shift += 8;
56            }
57
58            // Parse size bytes
59            shift = 0;
60            for bit in 4..7 {
61                if first_byte & (1 << bit) != 0 {
62                    if i >= input.len() {
63                        return Err(nom::Err::Incomplete(nom::Needed::new(1)));
64                    }
65                    size |= (input[i] as usize) << shift;
66                    i += 1;
67                }
68                shift += 8;
69            }
70
71            // Handle special case: size 0 means 0x10000
72            if size == 0 {
73                size = 0x10000;
74            }
75
76            instructions.push(DeltaInstruction::Copy { offset, size });
77        } else {
78            // Insert instruction
79            let size = first_byte as usize;
80            if size == 0 {
81                return Err(nom::Err::Error(nom::error::Error::new(
82                    &input[i..],
83                    nom::error::ErrorKind::Tag,
84                )));
85            }
86            if i + size > input.len() {
87                return Err(nom::Err::Incomplete(nom::Needed::new(size)));
88            }
89            let data = input[i..i + size].to_vec();
90            i += size;
91            instructions.push(DeltaInstruction::Insert { data });
92        }
93    }
94
95    Ok((&input[i..], instructions))
96}
97
98pub fn parse_delta_object(data: &[u8]) -> Vec<u8> {
99    let mut i = 0;
100    let mut _shift = 0;
101
102    // Parse base object size
103    loop {
104        if i >= data.len() {
105            return data.to_vec(); // fallback: return the data as is
106        }
107        let byte = data[i];
108        i += 1;
109        if byte & 0x80 == 0 {
110            break;
111        }
112        _shift += 7;
113    }
114
115    // Reset shift for target size
116    _shift = 0;
117
118    // Parse target object size
119    loop {
120        if i >= data.len() {
121            return data.to_vec(); // fallback: return the data as is
122        }
123        let byte = data[i];
124        i += 1;
125        if byte & 0x80 == 0 {
126            break;
127        }
128        _shift += 7;
129    }
130
131    // Return just the delta instructions portion
132    data[i..].to_vec()
133}