umadb_core/
header_node.rs

1use crate::common::Position;
2use crate::common::{PageID, Tsn};
3use byteorder::{ByteOrder, LittleEndian};
4use umadb_dcb::{DCBError, DCBResult};
5
6// Node type definitions
7#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct HeaderNode {
9    pub tsn: Tsn,
10    pub free_lists_tree_root_id: PageID,
11    pub events_tree_root_id: PageID,
12    pub tags_tree_root_id: PageID,
13    pub next_page_id: PageID,
14    pub next_position: Position,
15}
16
17impl HeaderNode {
18    pub fn default() -> Self {
19        Self {
20            tsn: Tsn(0),
21            free_lists_tree_root_id: PageID(0),
22            events_tree_root_id: PageID(0),
23            tags_tree_root_id: PageID(0),
24            next_page_id: PageID(0),
25            next_position: Position(0),
26        }
27    }
28
29    /// Writes the serialized HeaderNode into the provided buffer and returns the number of bytes written (48).
30    /// The buffer must be at least 48 bytes long.
31    pub fn serialize_into(&self, buf: &mut [u8]) -> usize {
32        assert!(
33            buf.len() >= 48,
34            "HeaderNode::serialize_into dst must be at least 48 bytes"
35        );
36        // Write fields in little-endian order
37        buf[0..8].copy_from_slice(&self.tsn.0.to_le_bytes());
38        buf[8..16].copy_from_slice(&self.next_page_id.0.to_le_bytes());
39        buf[16..24].copy_from_slice(&self.free_lists_tree_root_id.0.to_le_bytes());
40        buf[24..32].copy_from_slice(&self.events_tree_root_id.0.to_le_bytes());
41        buf[32..40].copy_from_slice(&self.tags_tree_root_id.0.to_le_bytes());
42        buf[40..48].copy_from_slice(&self.next_position.0.to_le_bytes());
43        48
44    }
45
46    /// Creates a HeaderNode from a byte slice
47    /// Expects a slice with 48 bytes:
48    /// - 8 bytes for tsn
49    /// - 8 bytes for next_page_id
50    /// - 8 bytes for free_lists_tree_root_id
51    /// - 8 bytes for events_tree_root_id
52    /// - 8 bytes for tags_tree_root_id
53    /// - 8 bytes for next_position
54    ///
55    /// # Arguments
56    /// * `slice` - The byte slice to deserialize from
57    ///
58    /// # Returns
59    /// * `Result<Self>` - The deserialized HeaderNode or an error
60    pub fn from_slice(slice: &[u8]) -> DCBResult<Self> {
61        if slice.len() != 48 {
62            return Err(DCBError::DeserializationError(format!(
63                "Expected 48 bytes, got {}",
64                slice.len()
65            )));
66        }
67
68        let tsn = LittleEndian::read_u64(&slice[0..8]);
69        let next_page_id = LittleEndian::read_u64(&slice[8..16]);
70        let freetree_root_id = LittleEndian::read_u64(&slice[16..24]);
71        let position_root_id = LittleEndian::read_u64(&slice[24..32]);
72        let tags_root_id = LittleEndian::read_u64(&slice[32..40]);
73        let next_position = LittleEndian::read_u64(&slice[40..48]);
74
75        Ok(HeaderNode {
76            tsn: Tsn(tsn),
77            next_page_id: PageID(next_page_id),
78            free_lists_tree_root_id: PageID(freetree_root_id),
79            events_tree_root_id: PageID(position_root_id),
80            tags_tree_root_id: PageID(tags_root_id),
81            next_position: Position(next_position),
82        })
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89    #[test]
90    fn test_header_serialize() {
91        // Create a HeaderNode with known values
92        let header_node = HeaderNode {
93            tsn: Tsn(42),
94            next_page_id: PageID(123),
95            free_lists_tree_root_id: PageID(456),
96            events_tree_root_id: PageID(789),
97            tags_tree_root_id: PageID(321),
98            next_position: Position(9876543210),
99        };
100
101        // Serialize the HeaderNode
102        let mut serialized = [0u8; 48];
103        header_node.serialize_into(&mut serialized);
104
105        // Verify the serialized output has the correct length
106        assert_eq!(48, serialized.len());
107
108        // Verify the serialized output has the correct byte values
109        // TSN(42) = 42u64 = [42, 0, 0, 0, 0, 0, 0, 0] in little-endian
110        assert_eq!(&42u64.to_le_bytes(), &serialized[0..8]);
111
112        // PageID(123) as u64
113        assert_eq!(&123u64.to_le_bytes(), &serialized[8..16]);
114
115        // PageID(456) as u64
116        assert_eq!(&456u64.to_le_bytes(), &serialized[16..24]);
117
118        // PageID(789) as u64
119        assert_eq!(&789u64.to_le_bytes(), &serialized[24..32]);
120
121        // root_tags_tree_id PageID(321) as u64
122        assert_eq!(&321u64.to_le_bytes(), &serialized[32..40]);
123
124        // next_position 9876543210u64 => little-endian bytes
125        assert_eq!(&9876543210u64.to_le_bytes(), &serialized[40..48]);
126
127        // Deserialize back to a HeaderNode
128        let deserialized =
129            HeaderNode::from_slice(&serialized).expect("Failed to deserialize HeaderNode");
130
131        // Verify that the deserialized node matches the original
132        assert_eq!(header_node.tsn, deserialized.tsn);
133        assert_eq!(header_node.next_page_id, deserialized.next_page_id);
134        assert_eq!(
135            header_node.free_lists_tree_root_id,
136            deserialized.free_lists_tree_root_id
137        );
138        assert_eq!(
139            header_node.events_tree_root_id,
140            deserialized.events_tree_root_id
141        );
142        assert_eq!(header_node.next_position, deserialized.next_position);
143    }
144}