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 File {
39 pub fn decode_header(
47 &self,
48 mut entry: data::Entry,
49 inflate: &mut zlib::Inflate,
50 resolve: &dyn Fn(&gix_hash::oid) -> Option<ResolvedBase>,
51 ) -> Result<Outcome, Error> {
52 use crate::data::entry::Header::*;
53 let mut num_deltas = 0;
54 let mut first_delta_decompressed_size = None::<u64>;
55 loop {
56 match entry.header {
57 Tree | Blob | Commit | Tag => {
58 return Ok(Outcome {
59 kind: entry.header.as_kind().expect("always valid for non-refs"),
60 object_size: first_delta_decompressed_size.unwrap_or(entry.decompressed_size),
61 num_deltas,
62 });
63 }
64 OfsDelta { base_distance } => {
65 num_deltas += 1;
66 if first_delta_decompressed_size.is_none() {
67 first_delta_decompressed_size = Some(self.decode_delta_object_size(inflate, &entry)?);
68 }
69 entry = self.entry(entry.base_pack_offset(base_distance))?;
70 }
71 RefDelta { base_id } => {
72 num_deltas += 1;
73 if first_delta_decompressed_size.is_none() {
74 first_delta_decompressed_size = Some(self.decode_delta_object_size(inflate, &entry)?);
75 }
76 match resolve(base_id.as_ref()) {
77 Some(ResolvedBase::InPack(base_entry)) => entry = base_entry,
78 Some(ResolvedBase::OutOfPack {
79 kind,
80 num_deltas: origin_num_deltas,
81 }) => {
82 return Ok(Outcome {
83 kind,
84 object_size: first_delta_decompressed_size.unwrap_or(entry.decompressed_size),
85 num_deltas: origin_num_deltas.unwrap_or_default() + num_deltas,
86 })
87 }
88 None => return Err(Error::DeltaBaseUnresolved(base_id)),
89 }
90 }
91 };
92 }
93 }
94
95 #[inline]
96 fn decode_delta_object_size(&self, inflate: &mut zlib::Inflate, entry: &data::Entry) -> Result<u64, Error> {
97 let mut buf = [0_u8; 32];
98 let used = self
99 .decompress_entry_from_data_offset_2(entry.data_offset, inflate, &mut buf)?
100 .1;
101 let buf = &buf[..used];
102 let (_base_size, offset) = delta::decode_header_size(buf);
103 let (result_size, _offset) = delta::decode_header_size(&buf[offset..]);
104 Ok(result_size)
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
113 fn size_of_decode_entry_outcome() {
114 assert_eq!(
115 std::mem::size_of::<Outcome>(),
116 16,
117 "this shouldn't change without use noticing as it's returned a lot"
118 );
119 }
120}