Skip to main content

git_zero_core/zlib/
encode.rs

1//! Stored-block zlib encoding with bounded scratch.
2use super::adler::Adler32;
3use crate::{Error, ObjectReader, Repository, Result, io::Write};
4
5pub fn write_parts(output: &mut impl Write, parts: &[&[u8]]) -> Result<()> {
6    let mut encoder = Encoder::new(output)?;
7    for part in parts {
8        encoder.write_part(part)?;
9    }
10    encoder.finish()
11}
12
13/// Encode an optional loose-object header and a body, keeping block boundaries
14/// identical to `write_parts` regardless of scratch size or provider short reads.
15pub(crate) fn write_reader<R: Repository + ?Sized>(
16    output: &mut impl Write,
17    header: &[u8],
18    reader: &mut ObjectReader<'_, R>,
19    scratch: &mut [u8],
20) -> Result<()> {
21    if reader.remaining() != 0 && scratch.is_empty() {
22        return Err(Error::BufferTooSmall);
23    }
24    let mut encoder = Encoder::new(output)?;
25    encoder.write_part(header)?;
26    while reader.remaining() != 0 {
27        let length = reader.remaining().min(u64::from(u16::MAX)) as u16;
28        encoder.block_header(length)?;
29        let mut remaining = usize::from(length);
30        while remaining != 0 {
31            let capacity = remaining.min(scratch.len());
32            let length = reader.read(&mut scratch[..capacity])?;
33            encoder.write_bytes(&scratch[..length])?;
34            remaining -= length;
35        }
36    }
37    encoder.finish()
38}
39
40struct Encoder<'a, W> {
41    output: &'a mut W,
42    adler: Adler32,
43}
44
45impl<'a, W: Write> Encoder<'a, W> {
46    fn new(output: &'a mut W) -> Result<Self> {
47        output.write_all(&[0x78, 0x01])?;
48        Ok(Self {
49            output,
50            adler: Adler32::new(),
51        })
52    }
53
54    fn block_header(&mut self, length: u16) -> Result<()> {
55        self.output.write_all(&[0])?; // BFINAL=0, BTYPE=00, byte-aligned.
56        self.output.write_all(&length.to_le_bytes())?;
57        self.output.write_all(&(!length).to_le_bytes())
58    }
59
60    fn write_bytes(&mut self, bytes: &[u8]) -> Result<()> {
61        self.output.write_all(bytes)?;
62        self.adler.update(bytes);
63        Ok(())
64    }
65
66    fn write_part(&mut self, bytes: &[u8]) -> Result<()> {
67        for block in bytes.chunks(u16::MAX as usize) {
68            self.block_header(block.len() as u16)?;
69            self.write_bytes(block)?;
70        }
71        Ok(())
72    }
73
74    fn finish(self) -> Result<()> {
75        // Empty final block, including for empty input.
76        self.output.write_all(&[1, 0, 0, 0xff, 0xff])?;
77        self.output.write_all(&self.adler.value().to_be_bytes())
78    }
79}