1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
use crate::file::Index;
use std::ops::Range;

///
pub mod offset_by_kind {
    use std::fmt::{Display, Formatter};

    /// The error returned by [Index::offset_by_kind()][super::Index::offset_by_kind()].
    #[allow(missing_docs)]
    #[derive(Debug)]
    pub struct Error {
        pub kind: crate::Kind,
    }

    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 {
    use quick_error::quick_error;
    quick_error! {
        /// The error returned by [Index::data_by_kind()][super::Index::data_by_kind()].
        #[derive(Debug)]
        #[allow(missing_docs)]
        pub enum Error {
            NotFound(err: super::offset_by_kind::Error) {
                display("The chunk wasn't found in the file index")
                from()
                source(err)
            }
            FileTooLarge {
                display("The offsets into the file couldn't be represented by usize")
            }
        }
    }
}

/// An entry of a chunk file index
pub struct Entry {
    /// The kind of the chunk file
    pub kind: crate::Kind,
    /// 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;

    /// Find a chunk of `kind` and return its offset into the data if found
    pub fn offset_by_kind(&self, kind: crate::Kind) -> 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 data slice based on its offset.
    pub fn data_by_kind<'a>(&self, data: &'a [u8], kind: crate::Kind) -> Result<&'a [u8], data_by_kind::Error> {
        let offset = self.offset_by_kind(kind)?;
        Ok(&data[crate::into_usize_range(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
    }
}