omendb_core/omen/
section.rs

1//! Section entries for .omen file format
2
3/// Section types
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
5#[repr(u16)]
6pub enum SectionType {
7    #[default]
8    None = 0,
9    Vectors = 1,
10    Graph = 2,
11    MetadataIndex = 3,
12    MetadataRaw = 4,
13    TextIndex = 5,
14    Wal = 6,
15    HnswIndex = 7,
16}
17
18impl From<u16> for SectionType {
19    fn from(v: u16) -> Self {
20        match v {
21            1 => Self::Vectors,
22            2 => Self::Graph,
23            3 => Self::MetadataIndex,
24            4 => Self::MetadataRaw,
25            5 => Self::TextIndex,
26            6 => Self::Wal,
27            7 => Self::HnswIndex,
28            _ => Self::None,
29        }
30    }
31}
32
33/// Section entry in header (24 bytes)
34#[derive(Debug, Clone, Copy, Default)]
35pub struct SectionEntry {
36    pub section_type: SectionType,
37    pub flags: u16,
38    pub offset: u64,
39    pub length: u64,
40}
41
42impl SectionEntry {
43    /// Create a new section entry
44    #[must_use]
45    pub fn new(section_type: SectionType, offset: u64, length: u64) -> Self {
46        Self {
47            section_type,
48            flags: 0,
49            offset,
50            length,
51        }
52    }
53
54    /// Serialize to bytes (24 bytes)
55    #[must_use]
56    pub fn to_bytes(&self) -> [u8; 24] {
57        let mut buf = [0u8; 24];
58        buf[0..2].copy_from_slice(&(self.section_type as u16).to_le_bytes());
59        buf[2..4].copy_from_slice(&self.flags.to_le_bytes());
60        // 4 bytes padding for alignment
61        buf[8..16].copy_from_slice(&self.offset.to_le_bytes());
62        buf[16..24].copy_from_slice(&self.length.to_le_bytes());
63        buf
64    }
65
66    /// Parse from bytes
67    #[must_use]
68    pub fn from_bytes(buf: &[u8; 24]) -> Self {
69        // Direct array indexing - infallible for fixed-size input buffer
70        Self {
71            section_type: SectionType::from(u16::from_le_bytes([buf[0], buf[1]])),
72            flags: u16::from_le_bytes([buf[2], buf[3]]),
73            offset: u64::from_le_bytes([
74                buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15],
75            ]),
76            length: u64::from_le_bytes([
77                buf[16], buf[17], buf[18], buf[19], buf[20], buf[21], buf[22], buf[23],
78            ]),
79        }
80    }
81
82    /// Check if section is valid (has data)
83    #[must_use]
84    pub fn is_valid(&self) -> bool {
85        self.section_type != SectionType::None && self.length > 0
86    }
87}
88
89#[cfg(test)]
90mod tests {
91    use super::*;
92
93    #[test]
94    fn test_section_entry_roundtrip() {
95        let entry = SectionEntry::new(SectionType::Vectors, 4096, 1024 * 1024);
96        let bytes = entry.to_bytes();
97        let parsed = SectionEntry::from_bytes(&bytes);
98
99        assert_eq!(parsed.section_type, SectionType::Vectors);
100        assert_eq!(parsed.offset, 4096);
101        assert_eq!(parsed.length, 1024 * 1024);
102    }
103}