1use super::{IndexEntry, crc::Crc32, encode::write_typed_header};
3use crate::{
4 Error, ObjectFormat, ObjectId, ObjectKind, Result,
5 io::{SliceWriter, Write},
6 object::Hash,
7};
8
9#[derive(Clone, Copy, Debug, Eq, PartialEq)]
10pub enum CompressedKind {
11 Object(ObjectKind),
12 Delta {
15 base_id: ObjectId,
16 },
17}
18
19#[derive(Clone, Copy, Debug, Eq, PartialEq)]
20pub struct CompressedEntry {
21 pub id: ObjectId,
22 pub kind: CompressedKind,
23 pub inflated_size: u64,
25 pub compressed_size: u64,
27}
28
29struct Active {
30 id: ObjectId,
31 offset: u64,
32 remaining: u64,
33 crc: Crc32,
34}
35struct Segment {
36 objects: u32,
37 remaining: u64,
38}
39
40pub struct PackComposer {
51 format: ObjectFormat,
52 remaining: u32,
53 limit: u64,
54 position: u64,
55 hash: Hash,
56 active: Option<Active>,
57 segment: Option<Segment>,
58 checksum: Option<ObjectId>,
59 failure: Option<Error>,
60}
61
62impl PackComposer {
63 pub fn new(
64 format: ObjectFormat,
65 count: u32,
66 max_pack_bytes: u64,
67 output: &mut impl Write,
68 ) -> Result<Self> {
69 if max_pack_bytes < 12 + format.id_length() as u64 {
70 return Err(Error::LimitExceeded);
71 }
72 let mut header = [0; 12];
73 header[..8].copy_from_slice(b"PACK\0\0\0\x02");
74 header[8..].copy_from_slice(&count.to_be_bytes());
75 output.write_all(&header)?;
76 let mut hash = format.hash();
79 hash.update(&header);
80 Ok(Self {
81 format,
82 remaining: count,
83 limit: max_pack_bytes,
84 position: 12,
85 hash,
86 active: None,
87 segment: None,
88 checksum: None,
89 failure: None,
90 })
91 }
92
93 pub fn position(&self) -> u64 {
94 self.position
95 }
96 pub fn checksum(&self) -> Option<ObjectId> {
97 self.checksum
98 }
99
100 pub fn start_entry(&mut self, entry: CompressedEntry, output: &mut impl Write) -> Result<()> {
101 let result = self.begin(entry, output);
102 self.record(result)
103 }
104
105 pub fn write_compressed(&mut self, bytes: &[u8], output: &mut impl Write) -> Result<()> {
106 let result = if self.segment.is_some() {
107 self.copy_segment(bytes, output)
108 } else {
109 self.copy(bytes, output)
110 };
111 self.record(result)
112 }
113
114 pub fn start_segment(&mut self, objects: u32, bytes: u64) -> Result<()> {
120 let result = self.begin_segment(objects, bytes);
121 self.record(result)
122 }
123
124 pub fn finish_segment(&mut self) -> Result<()> {
125 let result = (|| {
126 self.ready()?;
127 let segment = self.segment.as_ref().ok_or(Error::InvalidRequest)?;
128 if segment.remaining != 0 {
129 return Err(Error::InvalidObject);
130 }
131 self.remaining -= segment.objects;
132 self.segment = None;
133 Ok(())
134 })();
135 self.record(result)
136 }
137
138 pub fn finish_entry(&mut self) -> Result<IndexEntry> {
141 let result = self.close_entry();
142 self.record(result)
143 }
144
145 pub fn finish(&mut self, output: &mut impl Write) -> Result<ObjectId> {
148 let result = self.close(output);
149 self.record(result)
150 }
151
152 fn ready(&self) -> Result<()> {
153 self.failure.map_or(Ok(()), Err)
154 }
155
156 fn record<T>(&mut self, result: Result<T>) -> Result<T> {
157 if let Err(error) = result {
158 self.failure = Some(error);
159 self.checksum = None;
160 }
161 result
162 }
163
164 fn begin(&mut self, entry: CompressedEntry, output: &mut impl Write) -> Result<()> {
165 self.ready()?;
166 if self.active.is_some()
167 || self.segment.is_some()
168 || self.remaining == 0
169 || self.checksum.is_some()
170 {
171 return Err(Error::InvalidRequest);
172 }
173 if entry.id.format() != self.format {
174 return Err(Error::InvalidObjectId);
175 }
176 if entry.compressed_size < 8 {
177 return Err(Error::InvalidObject);
178 }
179 let mut storage = [0; 64];
180 let mut header = SliceWriter::new(&mut storage);
181 match entry.kind {
182 CompressedKind::Object(kind) => {
183 write_typed_header(kind as u8, entry.inflated_size, &mut header)?
184 }
185 CompressedKind::Delta { base_id } => {
186 if base_id.format() != self.format {
187 return Err(Error::InvalidObjectId);
188 }
189 write_typed_header(7, entry.inflated_size, &mut header)?;
190 header.write_all(base_id.as_bytes())?;
191 }
192 }
193 let bytes = header.finish();
194 let end = self
195 .position
196 .checked_add(bytes.len() as u64)
197 .and_then(|end| end.checked_add(entry.compressed_size))
198 .and_then(|end| end.checked_add(self.format.id_length() as u64))
199 .ok_or(Error::LimitExceeded)?;
200 if end > self.limit {
201 return Err(Error::LimitExceeded);
202 }
203 output.write_all(bytes)?;
204 self.hash.update(bytes);
205 let mut crc = Crc32::new();
206 crc.update(bytes);
207 self.active = Some(Active {
208 id: entry.id,
209 offset: self.position,
210 remaining: entry.compressed_size,
211 crc,
212 });
213 self.position += bytes.len() as u64;
214 Ok(())
215 }
216
217 fn copy(&mut self, bytes: &[u8], output: &mut impl Write) -> Result<()> {
218 self.ready()?;
219 let active = self.active.as_mut().ok_or(Error::InvalidRequest)?;
220 if bytes.len() as u64 > active.remaining {
221 return Err(Error::InvalidObject);
222 }
223 output.write_all(bytes)?;
224 self.hash.update(bytes);
225 active.crc.update(bytes);
226 active.remaining -= bytes.len() as u64;
227 self.position += bytes.len() as u64;
228 Ok(())
229 }
230
231 fn close_entry(&mut self) -> Result<IndexEntry> {
232 self.ready()?;
233 let active = self.active.as_ref().ok_or(Error::InvalidRequest)?;
234 if active.remaining != 0 {
235 return Err(Error::InvalidObject);
236 }
237 let entry = IndexEntry {
238 id: active.id,
239 offset: active.offset,
240 crc32: active.crc.value(),
241 };
242 self.remaining -= 1;
243 self.active = None;
244 Ok(entry)
245 }
246
247 fn begin_segment(&mut self, objects: u32, bytes: u64) -> Result<()> {
248 self.ready()?;
249 if self.active.is_some()
250 || self.segment.is_some()
251 || self.checksum.is_some()
252 || objects > self.remaining
253 || (objects == 0) != (bytes == 0)
254 {
255 return Err(Error::InvalidRequest);
256 }
257 let end = self
258 .position
259 .checked_add(bytes)
260 .and_then(|end| end.checked_add(self.format.id_length() as u64))
261 .ok_or(Error::LimitExceeded)?;
262 if end > self.limit {
263 return Err(Error::LimitExceeded);
264 }
265 self.segment = Some(Segment {
266 objects,
267 remaining: bytes,
268 });
269 Ok(())
270 }
271
272 fn copy_segment(&mut self, bytes: &[u8], output: &mut impl Write) -> Result<()> {
273 self.ready()?;
274 let segment = self.segment.as_mut().ok_or(Error::InvalidRequest)?;
275 if bytes.len() as u64 > segment.remaining {
276 return Err(Error::InvalidObject);
277 }
278 output.write_all(bytes)?;
279 self.hash.update(bytes);
280 self.position += bytes.len() as u64;
281 segment.remaining -= bytes.len() as u64;
282 Ok(())
283 }
284
285 fn close(&mut self, output: &mut impl Write) -> Result<ObjectId> {
286 self.ready()?;
287 if let Some(id) = self.checksum {
288 return Ok(id);
289 }
290 if self.remaining != 0 || self.active.is_some() || self.segment.is_some() {
291 return Err(Error::InvalidObject);
292 }
293 let id = self.hash.clone().finish();
294 output.write_all(id.as_bytes())?;
295 self.position += id.as_bytes().len() as u64;
296 self.checksum = Some(id);
297 Ok(id)
298 }
299}