use std::io::Write; use crate::data::{entry::Header, input}; impl input::Entry { /// Create a new input entry from a given data `obj` set to be placed at the given `pack_offset`. /// /// This method is useful when arbitrary base entries are created pub fn from_data_obj(obj: &gix_object::Data<'_>, pack_offset: u64) -> Result { let header = to_header(obj.kind); let compressed = compress_data(obj)?; let compressed_size = compressed.len() as u64; let mut entry = input::Entry { header, header_size: header.size(obj.data.len() as u64) as u16, pack_offset, compressed: Some(compressed), compressed_size, crc32: None, decompressed_size: obj.data.len() as u64, trailer: None, }; entry.crc32 = Some(entry.compute_crc32()); Ok(entry) } /// The amount of bytes this entry may consume in a pack data file pub fn bytes_in_pack(&self) -> u64 { self.header_size as u64 + self.compressed_size } /// Update our CRC value by recalculating it from our header and compressed data. pub fn compute_crc32(&self) -> u32 { let mut header_buf = [0u8; 12 + gix_hash::Kind::longest().len_in_bytes()]; let header_len = self .header .write_to(self.decompressed_size, header_buf.as_mut()) .expect("write to memory will not fail"); let state = gix_features::hash::crc32_update(0, &header_buf[..header_len]); gix_features::hash::crc32_update(state, self.compressed.as_ref().expect("we always set it")) } } fn to_header(kind: gix_object::Kind) -> Header { use gix_object::Kind::*; match kind { Tree => Header::Tree, Blob => Header::Blob, Commit => Header::Commit, Tag => Header::Tag, } } fn compress_data(obj: &gix_object::Data<'_>) -> Result, input::Error> { let mut out = gix_features::zlib::stream::deflate::Write::new(Vec::new()); if let Err(err) = std::io::copy(&mut &*obj.data, &mut out) { match err.kind() { std::io::ErrorKind::Other => return Err(input::Error::Io(err)), err => { unreachable!("Should never see other errors than zlib, but got {:?}", err,) } } }; out.flush().expect("zlib flush should never fail"); Ok(out.into_inner()) }