gix_pack/data/file/decode/
header.rs1use gix_features::zlib;
2
3use crate::{
4 data,
5 data::{delta, file::decode::Error, File},
6};
7
8#[derive(Debug, PartialEq, Eq, Hash, Ord, PartialOrd, Clone)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11pub enum ResolvedBase {
12 InPack(data::Entry),
14 OutOfPack {
16 kind: gix_object::Kind,
18 num_deltas: Option<u32>,
20 },
21}
22
23#[derive(Debug, PartialEq, Eq, Hash, Ord, PartialOrd, Clone, Copy)]
27#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
28pub struct Outcome {
29 pub kind: gix_object::Kind,
31 pub object_size: u64,
33 pub num_deltas: u32,
35}
36
37impl<T> File<T>
39where
40 T: crate::FileData,
41{
42 pub fn decode_header(
50 &self,
51 mut entry: data::Entry,
52 inflate: &mut zlib::Inflate,
53 resolve: &dyn Fn(&gix_hash::oid) -> Option<ResolvedBase>,
54 ) -> Result<Outcome, Error> {
55 use crate::data::entry::Header::*;
56 let mut num_deltas = 0;
57 let mut first_delta_decompressed_size = None::<u64>;
58 loop {
59 match entry.header {
60 Tree | Blob | Commit | Tag => {
61 return Ok(Outcome {
62 kind: entry.header.as_kind().expect("always valid for non-refs"),
63 object_size: first_delta_decompressed_size.unwrap_or(entry.decompressed_size),
64 num_deltas,
65 });
66 }
67 OfsDelta { base_distance } => {
68 num_deltas += 1;
69 if first_delta_decompressed_size.is_none() {
70 first_delta_decompressed_size = Some(self.decode_delta_object_size(inflate, &entry)?);
71 }
72 entry = self.entry(entry.checked_base_pack_offset(base_distance).ok_or(
73 crate::data::entry::decode::Error::Corrupt {
74 message: "an ofs-delta base distance pointing before pack start",
75 },
76 )?)?;
77 }
78 RefDelta { base_id } => {
79 num_deltas += 1;
80 if first_delta_decompressed_size.is_none() {
81 first_delta_decompressed_size = Some(self.decode_delta_object_size(inflate, &entry)?);
82 }
83 match resolve(base_id.as_ref()) {
84 Some(ResolvedBase::InPack(base_entry)) => entry = base_entry,
85 Some(ResolvedBase::OutOfPack {
86 kind,
87 num_deltas: origin_num_deltas,
88 }) => {
89 return Ok(Outcome {
90 kind,
91 object_size: first_delta_decompressed_size.unwrap_or(entry.decompressed_size),
92 num_deltas: origin_num_deltas.unwrap_or_default() + num_deltas,
93 })
94 }
95 None => return Err(Error::DeltaBaseUnresolved(base_id)),
96 }
97 }
98 }
99 }
100 }
101
102 #[inline]
103 fn decode_delta_object_size(&self, inflate: &mut zlib::Inflate, entry: &data::Entry) -> Result<u64, Error> {
104 let mut buf = [0_u8; 32];
105 let used = self
106 .decompress_entry_from_data_offset_2(entry.data_offset, inflate, &mut buf)?
107 .1;
108 let buf = &buf[..used];
109 let (_base_size, offset) = delta::decode_header_size(buf)?;
110 let (result_size, _offset) = delta::decode_header_size(&buf[offset..])?;
111 Ok(result_size)
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118
119 #[test]
120 fn size_of_decode_entry_outcome() {
121 assert_eq!(
122 std::mem::size_of::<Outcome>(),
123 16,
124 "this shouldn't change without use noticing as it's returned a lot"
125 );
126 }
127}