Skip to main content

lxdb_format/
adjacency_record.rs

1use crate::{BinaryRecord, FormatError};
2/// Number of bytes occupied by an encoded adjacency record.
3pub const ADJACENCY_RECORD_SIZE: usize = 16;
4
5/// Binary representation of a token's outgoing relation range.
6///
7/// The token identifier is implicit from this record's position
8/// inside the adjacency section.
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub struct AdjacencyRecord {
11    offset: u64,
12    count: u32,
13}
14
15impl AdjacencyRecord {
16    pub const SIZE: usize = ADJACENCY_RECORD_SIZE;
17
18    pub const fn new(offset: u64, count: u32) -> Self {
19        Self { offset, count }
20    }
21
22    pub const fn offset(self) -> u64 {
23        self.offset
24    }
25
26    pub const fn count(self) -> u32 {
27        self.count
28    }
29
30    pub const fn end(self) -> u64 {
31        self.offset + self.count as u64
32    }
33
34    pub fn encode(self) -> [u8; ADJACENCY_RECORD_SIZE] {
35        let mut bytes = [0_u8; ADJACENCY_RECORD_SIZE];
36
37        bytes[0..8].copy_from_slice(&self.offset.to_le_bytes());
38        bytes[8..12].copy_from_slice(&self.count.to_le_bytes());
39
40        // bytes[12..16] remain reserved and must be zero.
41
42        bytes
43    }
44
45    pub fn decode(bytes: &[u8]) -> Result<Self, FormatError> {
46        if bytes.len() != Self::SIZE {
47            return Err(FormatError::UnexpectedRecordSize {
48                record: "adjacency record",
49                expected: Self::SIZE,
50                found: bytes.len(),
51            });
52        }
53
54        let offset = u64::from_le_bytes(
55            bytes[0..8].try_into().expect("adjacency offset must occupy eight bytes"),
56        );
57
58        let count = u32::from_le_bytes(
59            bytes[8..12].try_into().expect("adjacency count must occupy four bytes"),
60        );
61
62        Ok(Self::new(offset, count))
63    }
64}
65
66impl BinaryRecord for AdjacencyRecord {
67    const SIZE: usize = AdjacencyRecord::SIZE;
68
69    fn decode(bytes: &[u8]) -> Result<Self, FormatError> {
70        AdjacencyRecord::decode(bytes)
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use super::{ADJACENCY_RECORD_SIZE, AdjacencyRecord};
77
78    #[test]
79    fn encodes_adjacency_record() {
80        let record = AdjacencyRecord::new(1_024, 17);
81
82        let bytes = record.encode();
83
84        assert_eq!(bytes.len(), ADJACENCY_RECORD_SIZE);
85
86        assert_eq!(
87            u64::from_le_bytes([
88                bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
89            ]),
90            1_024
91        );
92
93        assert_eq!(u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11],]), 17);
94
95        assert_eq!(&bytes[12..16], &[0; 4]);
96    }
97
98    #[test]
99    fn calculates_relation_range_end() {
100        let record = AdjacencyRecord::new(25, 12);
101
102        assert_eq!(record.end(), 37);
103    }
104
105    #[test]
106    fn supports_tokens_without_relations() {
107        let record = AdjacencyRecord::new(50, 0);
108
109        let bytes = record.encode();
110
111        assert_eq!(record.offset(), 50);
112        assert_eq!(record.count(), 0);
113        assert_eq!(record.end(), 50);
114        assert_eq!(&bytes[8..12], &[0; 4]);
115    }
116}