thin_fetch/
pack_decoder.rs1use std::{collections::HashMap, io::BufRead};
2
3use gix_object::Kind;
4use gix_pack::data::{entry::Header, input::BytesToEntriesIter};
5
6use crate::error::{Error, WithError as _, bail, error};
7use crate::object::{Blob, Commit, Object, Tag, Tree};
8use crate::utils::as_hex;
9use crate::{HashKind, delta};
10
11#[derive(Clone, Debug)]
12struct RawObject {
13 kind: Kind,
14 data: Vec<u8>,
15}
16
17#[derive(Clone)]
18struct ObjectStore {
19 objects: HashMap<String, RawObject>,
20}
21
22impl ObjectStore {
23 pub fn new() -> Self {
24 Self {
25 objects: HashMap::new(),
26 }
27 }
28
29 pub fn get(&self, hash: &str) -> Option<&RawObject> {
30 self.objects.get(hash)
31 }
32
33 pub fn insert(&mut self, hash: String, kind: Kind, data: Vec<u8>) {
34 let obj = RawObject { kind, data };
35
36 self.objects.entry(hash).or_insert(obj);
37 }
38}
39
40pub struct PackDecoder<R: BufRead> {
41 reader: BytesToEntriesIter<R>,
42 objects: ObjectStore,
43 hash_kind: HashKind,
44}
45
46fn new_object(kind: Kind, data: &[u8], hash_kind: HashKind) -> Result<Object, Error> {
47 let obj = match kind {
48 Kind::Commit => {
49 let commit = Commit::parse(data, hash_kind)?;
50 Object::Commit(Box::new(commit))
51 }
52 Kind::Tree => {
53 let tree = Tree::parse(data, hash_kind)?;
54 Object::Tree(tree)
55 }
56 Kind::Tag => {
57 let tag = Tag::parse(data, hash_kind)?;
58 Object::Tag(tag)
59 }
60 Kind::Blob => {
61 let blob = Blob::new(data.to_vec(), hash_kind);
62 Object::Blob(blob)
63 }
64 };
65
66 Ok(obj)
67}
68
69impl<R: BufRead> PackDecoder<R> {
73 pub fn new(reader: R, hash_kind: HashKind) -> Result<Self, Error> {
79 let file = BytesToEntriesIter::new_from_header(
80 reader,
81 gix_pack::data::input::Mode::AsIs,
82 gix_pack::data::input::EntryDataMode::KeepAndCrc32,
83 hash_kind.into(),
84 )
85 .map_err(|err| error!("could not read header: {err}"))?;
86
87 let store = ObjectStore::new();
88
89 Ok(Self {
90 reader: file,
91 objects: store,
92 hash_kind,
93 })
94 }
95
96 fn get_next(&mut self) -> Result<Option<Object>, Error> {
97 let Some(hdr) = self.reader.next() else {
98 return Ok(None);
99 };
100
101 let entry = hdr.map_err(|err| error!("could not read header: {err}"))?;
102 let data = entry.compressed.with_err_msg("compressed data not found")?;
103
104 let size = usize::try_from(entry.decompressed_size).with_err(|| {
105 error!(
106 "compressed data size exceeds max usize: {}",
107 entry.decompressed_size
108 )
109 })?;
110
111 let mut raw_data = vec![0; size];
112
113 let mut inf = zlib_rs::Inflate::new(true, 15);
114 inf.decompress(&data, &mut raw_data, zlib_rs::InflateFlush::Finish)
115 .map_err(|err| error!("could not decompress: {err:?}"))?;
116
117 let obj = match entry.header {
120 Header::Commit => new_object(Kind::Commit, &raw_data, self.hash_kind)?,
121 Header::Tree => new_object(Kind::Tree, &raw_data, self.hash_kind)?,
122 Header::Tag => new_object(Kind::Tag, &raw_data, self.hash_kind)?,
123 Header::Blob => new_object(Kind::Blob, &raw_data, self.hash_kind)?,
124 Header::RefDelta { base_id } => {
125 let hash = as_hex(base_id.as_bytes());
126
127 let apply_to = self
128 .objects
129 .get(&hash)
130 .with_err(|| error!("object not found: {hash}"))?;
131
132 let mut delta_data = raw_data.as_slice();
133
134 let (base_size, read) = delta::decode_header_size(delta_data)
135 .with_err_msg("could not get delta base size")?;
136 delta_data = &delta_data[read..];
137
138 let (target_size, read) = delta::decode_header_size(delta_data)
139 .with_err_msg("could not get delta target size")?;
140 delta_data = &delta_data[read..];
141
142 if base_size != apply_to.data.len() {
143 bail!(
144 "delta base size mismatch: expected {base_size}, got {}",
145 apply_to.data.len()
146 );
147 }
148
149 let mut target = vec![0; target_size];
150
151 delta::apply(&apply_to.data, &mut target, delta_data)
152 .with_err_msg("could not apply delta")?;
153
154 raw_data.clone_from(&target);
155
156 new_object(apply_to.kind, &target, self.hash_kind)?
157 }
158 Header::OfsDelta { base_distance: _ } => {
159 bail!("ofs-delta not supported")
160 }
161 };
162
163 let hash = as_hex(obj.hash().as_ref());
164 self.objects.insert(hash, obj.kind(), raw_data);
165
166 Ok(Some(obj))
167 }
168}
169
170impl<R: BufRead> Iterator for PackDecoder<R> {
171 type Item = Result<Object, Error>;
172
173 fn next(&mut self) -> Option<Self::Item> {
174 self.get_next().transpose()
175 }
176}