summaryrefslogtreecommitdiffstats
path: root/vendor/gix-chunk/src/file/index.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gix-chunk/src/file/index.rs')
-rw-r--r--vendor/gix-chunk/src/file/index.rs107
1 files changed, 107 insertions, 0 deletions
diff --git a/vendor/gix-chunk/src/file/index.rs b/vendor/gix-chunk/src/file/index.rs
new file mode 100644
index 000000000..5b59f6767
--- /dev/null
+++ b/vendor/gix-chunk/src/file/index.rs
@@ -0,0 +1,107 @@
+use std::ops::Range;
+
+use crate::file::Index;
+
+///
+pub mod offset_by_kind {
+ use std::fmt::{Display, Formatter};
+
+ /// The error returned by [Index::offset_by_kind()][super::Index::offset_by_id()].
+ #[allow(missing_docs)]
+ #[derive(Debug)]
+ pub struct Error {
+ pub kind: crate::Id,
+ }
+
+ impl Display for Error {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ write!(
+ f,
+ "Chunk named {:?} was not found in chunk file index",
+ std::str::from_utf8(&self.kind).unwrap_or("<non-ascii>")
+ )
+ }
+ }
+
+ impl std::error::Error for Error {}
+}
+
+///
+pub mod data_by_kind {
+ /// The error returned by [Index::data_by_kind()][super::Index::data_by_id()].
+ #[derive(Debug, thiserror::Error)]
+ #[allow(missing_docs)]
+ pub enum Error {
+ #[error("The chunk wasn't found in the file index")]
+ NotFound(#[from] super::offset_by_kind::Error),
+ #[error("The offsets into the file couldn't be represented by usize")]
+ FileTooLarge,
+ }
+}
+
+/// An entry of a chunk file index
+pub struct Entry {
+ /// The kind of the chunk file
+ pub kind: crate::Id,
+ /// The offset, relative to the beginning of the file, at which to find the chunk and its end.
+ pub offset: Range<crate::file::Offset>,
+}
+
+impl Index {
+ /// The size of a single index entry in bytes
+ pub const ENTRY_SIZE: usize = std::mem::size_of::<u32>() + std::mem::size_of::<u64>();
+ /// The smallest possible size of an index, consisting only of the sentinel value pointing past itself.
+ pub const EMPTY_SIZE: usize = Index::ENTRY_SIZE;
+
+ /// Returns the size in bytes an index with `num_entries` would take.
+ pub const fn size_for_entries(num_entries: usize) -> usize {
+ Self::ENTRY_SIZE * (num_entries + 1/*sentinel*/)
+ }
+
+ /// Find a chunk of `kind` and return its offset into the data if found
+ pub fn offset_by_id(&self, kind: crate::Id) -> Result<Range<crate::file::Offset>, offset_by_kind::Error> {
+ self.chunks
+ .iter()
+ .find_map(|c| (c.kind == kind).then(|| c.offset.clone()))
+ .ok_or(offset_by_kind::Error { kind })
+ }
+
+ /// Find a chunk of `kind` and return its offset as usize range into the data if found.
+ ///
+ ///
+ /// # Panics
+ ///
+ /// - if the usize conversion fails, which isn't expected as memory maps can't be created if files are too large
+ /// to require such offsets.
+ pub fn usize_offset_by_id(&self, kind: crate::Id) -> Result<Range<usize>, offset_by_kind::Error> {
+ self.chunks
+ .iter()
+ .find_map(|c| (c.kind == kind).then(|| crate::range::into_usize_or_panic(c.offset.clone())))
+ .ok_or(offset_by_kind::Error { kind })
+ }
+
+ /// Like [`Index::usize_offset_by_id()`] but with support for validation and transformation using a function.
+ pub fn validated_usize_offset_by_id<T>(
+ &self,
+ kind: crate::Id,
+ validate: impl FnOnce(Range<usize>) -> T,
+ ) -> Result<T, offset_by_kind::Error> {
+ self.chunks
+ .iter()
+ .find_map(|c| (c.kind == kind).then(|| crate::range::into_usize_or_panic(c.offset.clone())))
+ .map(validate)
+ .ok_or(offset_by_kind::Error { kind })
+ }
+
+ /// Find a chunk of `kind` and return its data slice based on its offset.
+ pub fn data_by_id<'a>(&self, data: &'a [u8], kind: crate::Id) -> Result<&'a [u8], data_by_kind::Error> {
+ let offset = self.offset_by_id(kind)?;
+ Ok(&data[crate::range::into_usize(offset).ok_or(data_by_kind::Error::FileTooLarge)?])
+ }
+
+ /// Return the end offset lf the last chunk, which is the highest offset as well.
+ /// It's definitely available as we have one or more chunks.
+ pub fn highest_offset(&self) -> crate::file::Offset {
+ self.chunks.last().expect("at least one chunk").offset.end
+ }
+}