Skip to main content

lxdb_format/
header.rs

1use crate::{FormatError, magic::MAGIC, version::Version};
2
3/// Number of bytes occupied by the binary LXDB header.
4pub const HEADER_SIZE: usize = 8;
5
6/// Header stored at the beginning of every LXDB dataset.
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub struct Header {
9    version: Version,
10}
11
12impl Header {
13    pub const SIZE: usize = HEADER_SIZE;
14
15    pub const fn new(version: Version) -> Self {
16        Self { version }
17    }
18
19    pub const fn current() -> Self {
20        Self::new(Version::CURRENT)
21    }
22
23    pub const fn version(self) -> Version {
24        self.version
25    }
26
27    pub fn encode(self) -> [u8; HEADER_SIZE] {
28        let mut bytes = [0_u8; HEADER_SIZE];
29
30        bytes[0..4].copy_from_slice(&MAGIC);
31        bytes[4..6].copy_from_slice(&self.version.major().to_le_bytes());
32        bytes[6..8].copy_from_slice(&self.version.minor().to_le_bytes());
33
34        bytes
35    }
36
37    pub fn decode(bytes: &[u8]) -> Result<Self, FormatError> {
38        if bytes.len() != Self::SIZE {
39            return Err(FormatError::UnexpectedRecordSize {
40                record: "LXDB header",
41                expected: Self::SIZE,
42                found: bytes.len(),
43            });
44        }
45
46        if bytes[0..4] != MAGIC {
47            return Err(FormatError::InvalidHeader);
48        }
49
50        let major = u16::from_le_bytes(
51            bytes[4..6].try_into().expect("major version must occupy two bytes"),
52        );
53
54        let minor = u16::from_le_bytes(
55            bytes[6..8].try_into().expect("minor version must occupy two bytes"),
56        );
57
58        Ok(Self::new(Version::new(major, minor)))
59    }
60}
61
62impl Default for Header {
63    fn default() -> Self {
64        Self::current()
65    }
66}
67
68#[cfg(test)]
69mod tests {
70    use super::{HEADER_SIZE, Header};
71    use crate::magic::MAGIC;
72
73    #[test]
74    fn encodes_current_header() {
75        let bytes = Header::current().encode();
76
77        assert_eq!(bytes.len(), HEADER_SIZE);
78        assert_eq!(&bytes[0..4], &MAGIC);
79        assert_eq!(u16::from_le_bytes([bytes[4], bytes[5]]), 0);
80        assert_eq!(u16::from_le_bytes([bytes[6], bytes[7]]), 1);
81    }
82
83    #[test]
84    fn encodes_custom_version() {
85        let bytes = Header::new(crate::version::Version::new(2, 7)).encode();
86
87        assert_eq!(u16::from_le_bytes([bytes[4], bytes[5]]), 2);
88        assert_eq!(u16::from_le_bytes([bytes[6], bytes[7]]), 7);
89    }
90}