1use std::ops::Range;
2
3use crate::file::Index;
4
5pub mod offset_by_kind {
7 use std::fmt::{Display, Formatter};
8
9 #[allow(missing_docs)]
11 #[derive(Debug)]
12 pub struct Error {
13 pub kind: crate::Id,
14 }
15
16 impl Display for Error {
17 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
18 write!(
19 f,
20 "Chunk named {:?} was not found in chunk file index",
21 std::str::from_utf8(&self.kind).unwrap_or("<non-ascii>")
22 )
23 }
24 }
25
26 impl std::error::Error for Error {}
27}
28
29pub mod data_by_kind {
31 #[derive(Debug, thiserror::Error)]
33 #[allow(missing_docs)]
34 pub enum Error {
35 #[error("The chunk wasn't found in the file index")]
36 NotFound(#[from] super::offset_by_kind::Error),
37 #[error("The offsets into the file couldn't be represented by usize")]
38 FileTooLarge,
39 }
40}
41
42pub struct Entry {
44 pub kind: crate::Id,
46 pub offset: Range<crate::file::Offset>,
48}
49
50impl Index {
51 pub const ENTRY_SIZE: usize = std::mem::size_of::<u32>() + std::mem::size_of::<u64>();
53 pub const EMPTY_SIZE: usize = Index::ENTRY_SIZE;
55
56 pub const fn size_for_entries(num_entries: usize) -> usize {
58 Self::ENTRY_SIZE * (num_entries + 1)
59 }
60
61 pub fn offset_by_id(&self, kind: crate::Id) -> Result<Range<crate::file::Offset>, offset_by_kind::Error> {
63 self.chunks
64 .iter()
65 .find_map(|c| (c.kind == kind).then(|| c.offset.clone()))
66 .ok_or(offset_by_kind::Error { kind })
67 }
68
69 pub fn usize_offset_by_id(&self, kind: crate::Id) -> Result<Range<usize>, offset_by_kind::Error> {
77 self.chunks
78 .iter()
79 .find_map(|c| (c.kind == kind).then(|| crate::range::into_usize_or_panic(c.offset.clone())))
80 .ok_or(offset_by_kind::Error { kind })
81 }
82
83 pub fn validated_usize_offset_by_id<T>(
85 &self,
86 kind: crate::Id,
87 validate: impl FnOnce(Range<usize>) -> T,
88 ) -> Result<T, offset_by_kind::Error> {
89 self.chunks
90 .iter()
91 .find_map(|c| (c.kind == kind).then(|| crate::range::into_usize_or_panic(c.offset.clone())))
92 .map(validate)
93 .ok_or(offset_by_kind::Error { kind })
94 }
95
96 pub fn data_by_id<'a>(&self, data: &'a [u8], kind: crate::Id) -> Result<&'a [u8], data_by_kind::Error> {
98 let offset = self.offset_by_id(kind)?;
99 Ok(&data[crate::range::into_usize(offset).ok_or(data_by_kind::Error::FileTooLarge)?])
100 }
101
102 pub fn highest_offset(&self) -> crate::file::Offset {
105 self.chunks.last().expect("at least one chunk").offset.end
106 }
107}