/home/runner/work/feoxdb/feoxdb/src/storage/metadata.rs
Line | Count | Source |
1 | | use crate::constants::*; |
2 | | use crate::error::{FeoxError, Result}; |
3 | | use crate::storage::seq_token::crc32c; |
4 | | |
5 | | /// Metadata version - increment this when changing the metadata structure |
6 | | /// Version 1: Initial format |
7 | | /// Version 2: Added TTL support (8 bytes ttl_expiry field in records) |
8 | | /// Version 3: Checksummed metadata and sector-bound record tokens |
9 | | const METADATA_VERSION: u32 = 3; |
10 | | const METADATA_ENCODED_SIZE: usize = 136; |
11 | | const VERSION_OFFSET: usize = 8; |
12 | | const TOTAL_RECORDS_OFFSET: usize = 16; |
13 | | const TOTAL_SIZE_OFFSET: usize = 24; |
14 | | const DEVICE_SIZE_OFFSET: usize = 32; |
15 | | const BLOCK_SIZE_OFFSET: usize = 40; |
16 | | const FRAGMENTATION_OFFSET: usize = 44; |
17 | | const CREATION_TIME_OFFSET: usize = 48; |
18 | | const LAST_UPDATE_TIME_OFFSET: usize = 56; |
19 | | const RESERVED_OFFSET: usize = 64; |
20 | | const RESERVED_SIZE: usize = 68; |
21 | | const CHECKSUM_MAGIC: &[u8; 4] = b"FM3C"; |
22 | | const CHECKSUM_OFFSET: usize = 4; |
23 | | const CHECKSUM_COMPLEMENT_OFFSET: usize = 8; |
24 | | const CHECKSUM_DATA_OFFSET: usize = 12; |
25 | | const GENERATION_OFFSET: usize = 12; |
26 | | |
27 | | #[derive(Debug, Clone, Copy)] |
28 | | pub struct Metadata { |
29 | | pub signature: [u8; FEOX_SIGNATURE_SIZE], |
30 | | pub version: u32, |
31 | | pub total_records: u64, |
32 | | pub total_size: u64, |
33 | | pub device_size: u64, |
34 | | pub block_size: u32, |
35 | | pub fragmentation: u32, |
36 | | pub creation_time: u64, |
37 | | pub last_update_time: u64, |
38 | | reserved: [u8; RESERVED_SIZE], |
39 | | } |
40 | | |
41 | | impl Default for Metadata { |
42 | 0 | fn default() -> Self { |
43 | 0 | Self::new() |
44 | 0 | } |
45 | | } |
46 | | |
47 | | impl Metadata { |
48 | 311 | pub fn new() -> Self { |
49 | 311 | let now = std::time::SystemTime::now() |
50 | 311 | .duration_since(std::time::UNIX_EPOCH) |
51 | 311 | .unwrap_or_else(|_| std::time::Duration::from_secs0 (0)) |
52 | 311 | .as_secs(); |
53 | | |
54 | 311 | let mut metadata = Self { |
55 | 311 | signature: *FEOX_SIGNATURE, |
56 | 311 | version: METADATA_VERSION, |
57 | 311 | total_records: 0, |
58 | 311 | total_size: 0, |
59 | 311 | device_size: 0, |
60 | 311 | block_size: FEOX_BLOCK_SIZE as u32, |
61 | 311 | fragmentation: 0, |
62 | 311 | creation_time: now, |
63 | 311 | last_update_time: now, |
64 | 311 | reserved: [0; RESERVED_SIZE], |
65 | 311 | }; |
66 | 311 | metadata.refresh_checksum(); |
67 | 311 | metadata |
68 | 311 | } |
69 | | |
70 | 329 | pub fn validate(&self) -> bool { |
71 | 329 | if self.signature != *FEOX_SIGNATURE { |
72 | 55 | return false; |
73 | 274 | } |
74 | | |
75 | 274 | if self.block_size != FEOX_BLOCK_SIZE as u32 { |
76 | 0 | return false; |
77 | 274 | } |
78 | | |
79 | 274 | if self.version == 0 || self.version > METADATA_VERSION { |
80 | 1 | return false; |
81 | 273 | } |
82 | | |
83 | 273 | if self.device_size == 0 || self.device_size > MAX_DEVICE_SIZE { |
84 | 0 | return false; |
85 | 273 | } |
86 | | |
87 | 273 | let has_checksum = &self.reserved[..CHECKSUM_MAGIC.len()] == CHECKSUM_MAGIC; |
88 | 273 | if self.version >= 3 && !has_checksum192 { |
89 | 0 | return false; |
90 | 273 | } |
91 | 273 | if !has_checksum { |
92 | 32 | return true; |
93 | 241 | } |
94 | | |
95 | 241 | let checksum = u32::from_le_bytes( |
96 | 241 | self.reserved[CHECKSUM_OFFSET..CHECKSUM_OFFSET + 4] |
97 | 241 | .try_into() |
98 | 241 | .unwrap(), |
99 | | ); |
100 | 241 | let complement = u32::from_le_bytes( |
101 | 241 | self.reserved[CHECKSUM_COMPLEMENT_OFFSET..CHECKSUM_COMPLEMENT_OFFSET + 4] |
102 | 241 | .try_into() |
103 | 241 | .unwrap(), |
104 | | ); |
105 | 241 | complement == !checksum && checksum == self.checksum() |
106 | 329 | } |
107 | | |
108 | 302 | pub fn update(&mut self) { |
109 | 302 | self.last_update_time = std::time::SystemTime::now() |
110 | 302 | .duration_since(std::time::UNIX_EPOCH) |
111 | 302 | .unwrap_or_else(|_| std::time::Duration::from_secs0 (0)) |
112 | 302 | .as_secs(); |
113 | 302 | self.refresh_checksum(); |
114 | 302 | } |
115 | | |
116 | 303 | pub fn encode(&self) -> [u8; METADATA_ENCODED_SIZE] { |
117 | 303 | let mut bytes = [0; METADATA_ENCODED_SIZE]; |
118 | 303 | bytes[..FEOX_SIGNATURE_SIZE].copy_from_slice(&self.signature); |
119 | 303 | bytes[VERSION_OFFSET..VERSION_OFFSET + 4].copy_from_slice(&self.version.to_le_bytes()); |
120 | 303 | bytes[TOTAL_RECORDS_OFFSET..TOTAL_RECORDS_OFFSET + 8] |
121 | 303 | .copy_from_slice(&self.total_records.to_le_bytes()); |
122 | 303 | bytes[TOTAL_SIZE_OFFSET..TOTAL_SIZE_OFFSET + 8] |
123 | 303 | .copy_from_slice(&self.total_size.to_le_bytes()); |
124 | 303 | bytes[DEVICE_SIZE_OFFSET..DEVICE_SIZE_OFFSET + 8] |
125 | 303 | .copy_from_slice(&self.device_size.to_le_bytes()); |
126 | 303 | bytes[BLOCK_SIZE_OFFSET..BLOCK_SIZE_OFFSET + 4] |
127 | 303 | .copy_from_slice(&self.block_size.to_le_bytes()); |
128 | 303 | bytes[FRAGMENTATION_OFFSET..FRAGMENTATION_OFFSET + 4] |
129 | 303 | .copy_from_slice(&self.fragmentation.to_le_bytes()); |
130 | 303 | bytes[CREATION_TIME_OFFSET..CREATION_TIME_OFFSET + 8] |
131 | 303 | .copy_from_slice(&self.creation_time.to_le_bytes()); |
132 | 303 | bytes[LAST_UPDATE_TIME_OFFSET..LAST_UPDATE_TIME_OFFSET + 8] |
133 | 303 | .copy_from_slice(&self.last_update_time.to_le_bytes()); |
134 | 303 | bytes[RESERVED_OFFSET..RESERVED_OFFSET + RESERVED_SIZE].copy_from_slice(&self.reserved); |
135 | 303 | bytes |
136 | 303 | } |
137 | | |
138 | 330 | pub fn from_bytes(bytes: &[u8]) -> Option<Self> { |
139 | 330 | if bytes.len() < METADATA_ENCODED_SIZE { |
140 | 1 | return None; |
141 | 329 | } |
142 | | |
143 | 329 | let metadata = Self { |
144 | 329 | signature: bytes[..FEOX_SIGNATURE_SIZE].try_into().ok()?0 , |
145 | 329 | version: u32::from_le_bytes(bytes[VERSION_OFFSET..VERSION_OFFSET + 4].try_into().ok()?0 ), |
146 | 329 | total_records: u64::from_le_bytes( |
147 | 329 | bytes[TOTAL_RECORDS_OFFSET..TOTAL_RECORDS_OFFSET + 8] |
148 | 329 | .try_into() |
149 | 329 | .ok()?0 , |
150 | | ), |
151 | 329 | total_size: u64::from_le_bytes( |
152 | 329 | bytes[TOTAL_SIZE_OFFSET..TOTAL_SIZE_OFFSET + 8] |
153 | 329 | .try_into() |
154 | 329 | .ok()?0 , |
155 | | ), |
156 | 329 | device_size: u64::from_le_bytes( |
157 | 329 | bytes[DEVICE_SIZE_OFFSET..DEVICE_SIZE_OFFSET + 8] |
158 | 329 | .try_into() |
159 | 329 | .ok()?0 , |
160 | | ), |
161 | 329 | block_size: u32::from_le_bytes( |
162 | 329 | bytes[BLOCK_SIZE_OFFSET..BLOCK_SIZE_OFFSET + 4] |
163 | 329 | .try_into() |
164 | 329 | .ok()?0 , |
165 | | ), |
166 | 329 | fragmentation: u32::from_le_bytes( |
167 | 329 | bytes[FRAGMENTATION_OFFSET..FRAGMENTATION_OFFSET + 4] |
168 | 329 | .try_into() |
169 | 329 | .ok()?0 , |
170 | | ), |
171 | 329 | creation_time: u64::from_le_bytes( |
172 | 329 | bytes[CREATION_TIME_OFFSET..CREATION_TIME_OFFSET + 8] |
173 | 329 | .try_into() |
174 | 329 | .ok()?0 , |
175 | | ), |
176 | 329 | last_update_time: u64::from_le_bytes( |
177 | 329 | bytes[LAST_UPDATE_TIME_OFFSET..LAST_UPDATE_TIME_OFFSET + 8] |
178 | 329 | .try_into() |
179 | 329 | .ok()?0 , |
180 | | ), |
181 | 329 | reserved: bytes[RESERVED_OFFSET..RESERVED_OFFSET + RESERVED_SIZE] |
182 | 329 | .try_into() |
183 | 329 | .ok()?0 , |
184 | | }; |
185 | | |
186 | 329 | if metadata.validate() { |
187 | 271 | Some(metadata) |
188 | | } else { |
189 | 58 | None |
190 | | } |
191 | 330 | } |
192 | | |
193 | 579 | pub(crate) fn generation(&self) -> u64 { |
194 | 579 | u64::from_le_bytes( |
195 | 579 | self.reserved[GENERATION_OFFSET..GENERATION_OFFSET + 8] |
196 | 579 | .try_into() |
197 | 579 | .unwrap(), |
198 | | ) |
199 | 579 | } |
200 | | |
201 | 255 | pub(crate) fn advance_generation(&mut self) -> Result<()> { |
202 | 255 | let generation = self |
203 | 255 | .generation() |
204 | 255 | .checked_add(1) |
205 | 255 | .ok_or(FeoxError::InvalidMetadata)?0 ; |
206 | 255 | self.reserved[GENERATION_OFFSET..GENERATION_OFFSET + 8] |
207 | 255 | .copy_from_slice(&generation.to_le_bytes()); |
208 | 255 | self.refresh_checksum(); |
209 | 255 | Ok(()) |
210 | 255 | } |
211 | | |
212 | 868 | fn refresh_checksum(&mut self) { |
213 | 868 | self.reserved[..CHECKSUM_MAGIC.len()].copy_from_slice(CHECKSUM_MAGIC); |
214 | 868 | let checksum = self.checksum(); |
215 | 868 | self.reserved[CHECKSUM_OFFSET..CHECKSUM_OFFSET + 4] |
216 | 868 | .copy_from_slice(&checksum.to_le_bytes()); |
217 | 868 | self.reserved[CHECKSUM_COMPLEMENT_OFFSET..CHECKSUM_COMPLEMENT_OFFSET + 4] |
218 | 868 | .copy_from_slice(&(!checksum).to_le_bytes()); |
219 | 868 | } |
220 | | |
221 | 1.10k | fn checksum(&self) -> u32 { |
222 | 1.10k | let mut checksum = crc32c(0, &self.signature); |
223 | 1.10k | checksum = crc32c(checksum, &self.version.to_le_bytes()); |
224 | 1.10k | checksum = crc32c(checksum, &self.total_records.to_le_bytes()); |
225 | 1.10k | checksum = crc32c(checksum, &self.total_size.to_le_bytes()); |
226 | 1.10k | checksum = crc32c(checksum, &self.device_size.to_le_bytes()); |
227 | 1.10k | checksum = crc32c(checksum, &self.block_size.to_le_bytes()); |
228 | 1.10k | checksum = crc32c(checksum, &self.fragmentation.to_le_bytes()); |
229 | 1.10k | checksum = crc32c(checksum, &self.creation_time.to_le_bytes()); |
230 | 1.10k | checksum = crc32c(checksum, &self.last_update_time.to_le_bytes()); |
231 | 1.10k | crc32c(checksum, &self.reserved[CHECKSUM_DATA_OFFSET..]) |
232 | 1.10k | } |
233 | | } |