hematite/catalog/
header.rs1use super::ids::TableId;
9use crate::error::Result;
10
11#[derive(Debug, Clone)]
13pub struct DatabaseHeader {
14 pub magic: [u8; 4],
16 pub version: u32,
18 pub schema_root_page: u32,
20 pub next_table_id: u32,
22 pub checksum: u32,
24}
25
26impl DatabaseHeader {
27 pub const MAGIC: [u8; 4] = *b"HMTD";
29 pub const CURRENT_VERSION: u32 = 2;
34 pub const HEADER_PAGE_ID: u32 = 0;
36
37 pub fn new(schema_root_page: u32) -> Self {
39 let mut header = Self {
40 magic: Self::MAGIC,
41 version: Self::CURRENT_VERSION,
42 schema_root_page,
43 next_table_id: 1,
44 checksum: 0,
45 };
46 header.checksum = header.calculate_checksum();
47 header
48 }
49
50 pub fn calculate_checksum(&self) -> u32 {
52 use std::collections::hash_map::DefaultHasher;
53 use std::hash::{Hash, Hasher};
54
55 let mut hasher = DefaultHasher::new();
56 self.magic.hash(&mut hasher);
57 self.version.hash(&mut hasher);
58 self.schema_root_page.hash(&mut hasher);
59 self.next_table_id.hash(&mut hasher);
60 hasher.finish() as u32
61 }
62
63 pub fn verify_checksum(&self) -> bool {
65 self.checksum == self.calculate_checksum()
66 }
67
68 pub fn serialize(&self, bytes: &mut [u8]) -> Result<()> {
70 let offset = 0;
71
72 bytes[offset..offset + 4].copy_from_slice(&self.magic);
74
75 bytes[offset + 4..offset + 8].copy_from_slice(&self.version.to_le_bytes());
77
78 bytes[offset + 8..offset + 12].copy_from_slice(&self.schema_root_page.to_le_bytes());
80
81 bytes[offset + 12..offset + 16].copy_from_slice(&self.next_table_id.to_le_bytes());
83
84 bytes[offset + 16..offset + 20].copy_from_slice(&self.checksum.to_le_bytes());
86
87 for byte in bytes.iter_mut().skip(20) {
89 *byte = 0;
90 }
91
92 Ok(())
93 }
94
95 pub fn deserialize(bytes: &[u8]) -> Result<Self> {
97 let offset = 0;
98
99 let mut magic = [0u8; 4];
101 magic.copy_from_slice(&bytes[offset..offset + 4]);
102
103 if magic != Self::MAGIC {
105 return Err(crate::error::HematiteError::StorageError(
106 "Invalid database file: wrong magic bytes".to_string(),
107 ));
108 }
109
110 let version = u32::from_le_bytes([
112 bytes[offset + 4],
113 bytes[offset + 5],
114 bytes[offset + 6],
115 bytes[offset + 7],
116 ]);
117 if version != Self::CURRENT_VERSION {
118 return Err(crate::error::HematiteError::StorageError(format!(
119 "Unsupported database header version: expected {}, got {}",
120 Self::CURRENT_VERSION,
121 version
122 )));
123 }
124
125 let schema_root_page = u32::from_le_bytes([
127 bytes[offset + 8],
128 bytes[offset + 9],
129 bytes[offset + 10],
130 bytes[offset + 11],
131 ]);
132
133 let next_table_id = u32::from_le_bytes([
135 bytes[offset + 12],
136 bytes[offset + 13],
137 bytes[offset + 14],
138 bytes[offset + 15],
139 ]);
140
141 let checksum = u32::from_le_bytes([
143 bytes[offset + 16],
144 bytes[offset + 17],
145 bytes[offset + 18],
146 bytes[offset + 19],
147 ]);
148
149 let header = Self {
150 magic,
151 version,
152 schema_root_page,
153 next_table_id,
154 checksum,
155 };
156
157 if !header.verify_checksum() {
159 return Err(crate::error::HematiteError::StorageError(
160 "Database header checksum verification failed".to_string(),
161 ));
162 }
163
164 Ok(header)
165 }
166
167 pub fn increment_table_id(&mut self) -> TableId {
169 let table_id = TableId::new(self.next_table_id);
170 self.next_table_id += 1;
171 self.checksum = self.calculate_checksum();
172 table_id
173 }
174}