git_pack/data/file/decode/
header.rs1use crate::{
2 data,
3 data::{delta, file::decode::Error, File},
4};
5
6#[derive(Debug, PartialEq, Eq, Hash, Ord, PartialOrd, Clone)]
8#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
9pub enum ResolvedBase {
10 InPack(data::Entry),
12 OutOfPack {
14 kind: git_object::Kind,
16 num_deltas: Option<u32>,
18 },
19}
20
21#[derive(Debug, PartialEq, Eq, Hash, Ord, PartialOrd, Clone, Copy)]
25#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
26pub struct Outcome {
27 pub kind: git_object::Kind,
29 pub object_size: u64,
31 pub num_deltas: u32,
33}
34
35impl File {
37 pub fn decode_header(
44 &self,
45 mut entry: data::Entry,
46 resolve: impl Fn(&git_hash::oid) -> Option<ResolvedBase>,
47 ) -> Result<Outcome, Error> {
48 use crate::data::entry::Header::*;
49 let mut num_deltas = 0;
50 let mut first_delta_decompressed_size = None::<u64>;
51 loop {
52 match entry.header {
53 Tree | Blob | Commit | Tag => {
54 return Ok(Outcome {
55 kind: entry.header.as_kind().expect("always valid for non-refs"),
56 object_size: first_delta_decompressed_size.unwrap_or(entry.decompressed_size),
57 num_deltas,
58 });
59 }
60 OfsDelta { base_distance } => {
61 num_deltas += 1;
62 if first_delta_decompressed_size.is_none() {
63 first_delta_decompressed_size = Some(self.decode_delta_object_size(&entry)?);
64 }
65 entry = self.entry(entry.base_pack_offset(base_distance))
66 }
67 RefDelta { base_id } => {
68 num_deltas += 1;
69 if first_delta_decompressed_size.is_none() {
70 first_delta_decompressed_size = Some(self.decode_delta_object_size(&entry)?);
71 }
72 match resolve(base_id.as_ref()) {
73 Some(ResolvedBase::InPack(base_entry)) => entry = base_entry,
74 Some(ResolvedBase::OutOfPack {
75 kind,
76 num_deltas: origin_num_deltas,
77 }) => {
78 return Ok(Outcome {
79 kind,
80 object_size: first_delta_decompressed_size.unwrap_or(entry.decompressed_size),
81 num_deltas: origin_num_deltas.unwrap_or_default() + num_deltas,
82 })
83 }
84 None => return Err(Error::DeltaBaseUnresolved(base_id)),
85 }
86 }
87 };
88 }
89 }
90
91 #[inline]
92 fn decode_delta_object_size(&self, entry: &data::Entry) -> Result<u64, Error> {
93 let mut buf = [0_u8; 32];
94 let used = self.decompress_entry_from_data_offset_2(entry.data_offset, &mut buf)?.1;
95 let buf = &buf[..used];
96 let (_base_size, offset) = delta::decode_header_size(buf);
97 let (result_size, _offset) = delta::decode_header_size(&buf[offset..]);
98 Ok(result_size)
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105
106 #[test]
107 fn size_of_decode_entry_outcome() {
108 assert_eq!(
109 std::mem::size_of::<Outcome>(),
110 16,
111 "this shouldn't change without use noticing as it's returned a lot"
112 );
113 }
114}