//! a pack data file use std::path::Path; /// The offset to an entry into the pack data file, relative to its beginning. pub type Offset = u64; /// An identifier to uniquely identify all packs loaded within a known context or namespace. pub type Id = u32; /// An representing an full- or delta-object within a pack #[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Entry { /// The entry's header pub header: entry::Header, /// The decompressed size of the entry in bytes. /// /// Note that for non-delta entries this will be the size of the object itself. pub decompressed_size: u64, /// absolute offset to compressed object data in the pack, just behind the entry's header pub data_offset: Offset, } mod file; pub use file::{Header, decode, verify}; /// pub mod header; /// pub mod init { pub use super::header::decode::Error; } /// pub mod entry; /// #[cfg(feature = "streaming-input")] pub mod input; /// Utilities to encode pack data entries and write them to a `Write` implementation to resemble a pack data file. #[cfg(feature = "generate")] pub mod output; /// A slice into a pack file denoting a pack entry. /// /// An entry can be decoded into an object. pub type EntryRange = std::ops::Range; /// Supported versions of a pack data file #[derive(Default, PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum Version { /// The default pack data version. /// /// This is the version generated by Git and by `gix-pack` writers. #[default] V2, /// A pack data version accepted by Git and recognized by `gix-pack` readers. /// /// Git does not generate this version, and `gix-pack` writers currently reject it. /// Entries are decoded with the same layout as [`V2`](Version::V2); the difference /// visible to this crate is the version number stored in the pack header. V3, } /// A pack data file, read from disk on demand rather than held in memory. #[allow(missing_docs)] pub struct File { file: std::fs::File, len: usize, path: std::path::PathBuf, pub id: Id, version: Version, num_objects: u32, hash_len: usize, object_hash: gix_hash::Kind, alloc_limit_bytes: Option, } /// Information about the pack data file itself impl File { /// The pack data version of this file pub fn version(&self) -> Version { self.version } /// The number of objects stored in this pack data file pub fn num_objects(&self) -> u32 { self.num_objects } /// The length of all pack data, including the pack header and the pack trailer pub fn data_len(&self) -> usize { self.len } /// The kind of hash we use internally. pub fn object_hash(&self) -> gix_hash::Kind { self.object_hash } /// The maximum size of a single allocation caused by user-controlled on-disk pack data. /// /// A value of `None` means no additional limit is enforced. pub fn alloc_limit_bytes(&self) -> Option { self.alloc_limit_bytes } /// The position of the byte one past the last pack entry, or in other terms, the first byte of the trailing hash. pub fn pack_end(&self) -> usize { self.len - self.hash_len } /// The path to the pack data file on disk pub fn path(&self) -> &Path { &self.path } pub(crate) fn read_exact_at(&self, offset: usize, buf: &mut [u8]) -> std::io::Result<()> { use std::os::unix::fs::FileExt; self.file.read_exact_at(buf, offset as u64) } pub(crate) fn read_span(&self, slice: EntryRange) -> Option> { let start = usize::try_from(slice.start).ok()?; let end = usize::try_from(slice.end).ok()?; if start > end || end > self.len { return None; } let mut buf = vec![0u8; end - start]; self.read_exact_at(start, &mut buf).ok()?; Some(buf) } #[allow(missing_docs)] pub fn read_into(&self, slice: EntryRange, buf: &mut Vec) -> bool { let (Ok(start), Ok(end)) = (usize::try_from(slice.start), usize::try_from(slice.end)) else { return false; }; if start > end || end > self.len { return false; } buf.clear(); buf.resize(end - start, 0); self.read_exact_at(start, buf).is_ok() } pub(crate) fn materialized(&self) -> std::io::Result { crate::MMap::map(&self.file) } /// Returns the pack data at the given slice if its range is contained in the pack data. pub fn entry_slice(&self, slice: EntryRange) -> Option> { self.read_span(slice) } /// Returns the CRC32 of the pack data indicated by `pack_offset` and the `size` of the data. /// /// _Note:_ finding the right size is only possible by decompressing /// the pack entry beforehand, or by using the (to be sorted) offsets stored in an index file. /// /// # Panics /// /// If `pack_offset` or `size` are pointing to a range outside of the pack data. pub fn entry_crc32(&self, pack_offset: Offset, size: usize) -> u32 { let buf = self .read_span(pack_offset..pack_offset + size as u64) .expect("entry range within pack data"); gix_features::hash::crc32(&buf) } } /// pub mod delta;