1use crate::constants::*;
2use crate::core::record::Record;
3use crate::storage::seq_token::seq_token;
4use std::sync::atomic::Ordering;
5
6pub trait RecordFormat: Send + Sync {
8 fn record_header_size(&self, key_len: usize) -> usize;
10
11 fn total_size(&self, key_len: usize, value_len: usize) -> usize;
13
14 fn serialize_record(&self, record: &Record, include_value: bool) -> Vec<u8>;
16
17 fn serialize_record_into(&self, record: &Record, include_value: bool, data: &mut Vec<u8>) {
19 data.extend_from_slice(&self.serialize_record(record, include_value));
20 }
21
22 fn parse_record(&self, data: &[u8]) -> Option<(Vec<u8>, usize, u64, u64)>;
24
25 fn value_offset(&self, key_len: usize) -> usize;
27}
28
29fn serialization_buffer(
30 format: &dyn RecordFormat,
31 record: &Record,
32 include_value: bool,
33) -> Vec<u8> {
34 let value_len = if include_value { record.value_len } else { 0 };
35 Vec::with_capacity(format.total_size(record.key.len(), value_len) - SECTOR_HEADER_SIZE)
36}
37
38pub struct FormatV1;
40static FORMAT_V1: FormatV1 = FormatV1;
41
42impl RecordFormat for FormatV1 {
43 fn record_header_size(&self, key_len: usize) -> usize {
44 SECTOR_HEADER_SIZE + 2 + key_len + 8 + 8 }
46
47 fn total_size(&self, key_len: usize, value_len: usize) -> usize {
48 self.record_header_size(key_len) + value_len
49 }
50
51 fn serialize_record(&self, record: &Record, include_value: bool) -> Vec<u8> {
52 let mut data = serialization_buffer(self, record, include_value);
53 self.serialize_record_into(record, include_value, &mut data);
54 data
55 }
56
57 fn serialize_record_into(&self, record: &Record, include_value: bool, data: &mut Vec<u8>) {
58 data.extend_from_slice(&(record.key.len() as u16).to_le_bytes());
59 data.extend_from_slice(&record.key);
60 data.extend_from_slice(&(record.value_len as u64).to_le_bytes());
61 data.extend_from_slice(&record.timestamp.to_le_bytes());
62
63 if include_value {
64 if let Some(value) = record.value.read().as_ref() {
65 data.extend_from_slice(value);
66 }
67 }
68 }
69
70 fn parse_record(&self, data: &[u8]) -> Option<(Vec<u8>, usize, u64, u64)> {
71 if data.len() < SECTOR_HEADER_SIZE + 2 {
72 return None;
73 }
74
75 let mut offset = SECTOR_HEADER_SIZE + 2;
76 let key_len = u16::from_le_bytes(
77 data[SECTOR_HEADER_SIZE..SECTOR_HEADER_SIZE + 2]
78 .try_into()
79 .ok()?,
80 ) as usize;
81
82 if offset + key_len + 16 > data.len() {
83 return None;
84 }
85
86 let key = data[offset..offset + key_len].to_vec();
87 offset += key_len;
88
89 let value_len = u64::from_le_bytes(data[offset..offset + 8].try_into().ok()?) as usize;
90 offset += 8;
91
92 let timestamp = u64::from_le_bytes(data[offset..offset + 8].try_into().ok()?);
93
94 Some((key, value_len, timestamp, 0)) }
96
97 fn value_offset(&self, key_len: usize) -> usize {
98 SECTOR_HEADER_SIZE + 2 + key_len + 8 + 8
99 }
100}
101
102pub struct FormatV2;
104static FORMAT_V2: FormatV2 = FormatV2;
105
106impl RecordFormat for FormatV2 {
107 fn record_header_size(&self, key_len: usize) -> usize {
108 SECTOR_HEADER_SIZE + 2 + key_len + 8 + 8 + 8 }
110
111 fn total_size(&self, key_len: usize, value_len: usize) -> usize {
112 self.record_header_size(key_len) + value_len
113 }
114
115 fn serialize_record(&self, record: &Record, include_value: bool) -> Vec<u8> {
116 let mut data = serialization_buffer(self, record, include_value);
117 self.serialize_record_into(record, include_value, &mut data);
118 data
119 }
120
121 fn serialize_record_into(&self, record: &Record, include_value: bool, data: &mut Vec<u8>) {
122 data.extend_from_slice(&(record.key.len() as u16).to_le_bytes());
123 data.extend_from_slice(&record.key);
124 data.extend_from_slice(&(record.value_len as u64).to_le_bytes());
125 data.extend_from_slice(&record.timestamp.to_le_bytes());
126 data.extend_from_slice(&record.ttl_expiry.load(Ordering::Acquire).to_le_bytes());
127
128 if include_value {
129 if let Some(value) = record.value.read().as_ref() {
130 data.extend_from_slice(value);
131 }
132 }
133 }
134
135 fn parse_record(&self, data: &[u8]) -> Option<(Vec<u8>, usize, u64, u64)> {
136 if data.len() < SECTOR_HEADER_SIZE + 2 {
137 return None;
138 }
139
140 let mut offset = SECTOR_HEADER_SIZE + 2;
141 let key_len = u16::from_le_bytes(
142 data[SECTOR_HEADER_SIZE..SECTOR_HEADER_SIZE + 2]
143 .try_into()
144 .ok()?,
145 ) as usize;
146
147 if offset + key_len + 24 > data.len() {
148 return None;
150 }
151
152 let key = data[offset..offset + key_len].to_vec();
153 offset += key_len;
154
155 let value_len = u64::from_le_bytes(data[offset..offset + 8].try_into().ok()?) as usize;
156 offset += 8;
157
158 let timestamp = u64::from_le_bytes(data[offset..offset + 8].try_into().ok()?);
159 offset += 8;
160
161 let ttl_expiry = u64::from_le_bytes(data[offset..offset + 8].try_into().ok()?);
162
163 Some((key, value_len, timestamp, ttl_expiry))
164 }
165
166 fn value_offset(&self, key_len: usize) -> usize {
167 SECTOR_HEADER_SIZE + 2 + key_len + 8 + 8 + 8
168 }
169}
170
171pub fn sector_holds_record(data: &[u8], record: &Record) -> bool {
180 if data.len() < SECTOR_HEADER_SIZE + 2 {
181 return false;
182 }
183 if u16::from_le_bytes([data[0], data[1]]) != SECTOR_MARKER {
184 return false;
185 }
186 let key_len =
187 u16::from_le_bytes([data[SECTOR_HEADER_SIZE], data[SECTOR_HEADER_SIZE + 1]]) as usize;
188 if key_len != record.key.len() {
189 return false;
190 }
191 let key_at = SECTOR_HEADER_SIZE + 2;
192 let value_len_at = key_at + key_len;
193 if value_len_at + 16 > data.len() {
194 return false;
195 }
196 if data[key_at..value_len_at] != record.key[..] {
197 return false;
198 }
199 let Ok(value_len) = data[value_len_at..value_len_at + 8].try_into() else {
200 return false;
201 };
202 if u64::from_le_bytes(value_len) as usize != record.value_len {
203 return false;
204 }
205 let Ok(timestamp) = data[value_len_at + 8..value_len_at + 16].try_into() else {
206 return false;
207 };
208 u64::from_le_bytes(timestamp) == record.timestamp
209}
210
211#[cfg(test)]
212pub(crate) fn fill_retirement_extent(retired: &mut [u8], sector: u64, sectors: usize) {
213 debug_assert_eq!(retired.len(), sectors * FEOX_BLOCK_SIZE);
214 retired.fill(0);
215 fill_retirement_markers(retired, sector, sectors);
216}
217
218pub(crate) fn fill_retirement_markers(retired: &mut [u8], sector: u64, remaining: usize) {
219 debug_assert_eq!(retired.len() % FEOX_BLOCK_SIZE, 0);
220 let blocks = retired.len() / FEOX_BLOCK_SIZE;
221 debug_assert!(blocks <= remaining);
222 for offset in 0..blocks {
223 let start = offset * FEOX_BLOCK_SIZE;
224 fill_retirement_marker(
225 &mut retired[start..start + DELETION_MARKER_SIZE],
226 sector + offset as u64,
227 remaining - offset,
228 );
229 }
230}
231
232pub(crate) fn fill_retirement_marker(marker: &mut [u8], sector: u64, remaining: usize) {
233 write_retirement_marker(marker, sector, remaining, RETIREMENT_COMPLETE);
234}
235
236#[cfg(test)]
237pub(crate) fn retirement_block(sector: u64, remaining: usize) -> Vec<u8> {
238 let mut retired = vec![0u8; FEOX_BLOCK_SIZE];
239 fill_retirement_marker(&mut retired[..DELETION_MARKER_SIZE], sector, remaining);
240 retired
241}
242
243#[cfg(test)]
244pub(crate) fn pending_retirement_block(sector: u64, remaining: usize) -> Vec<u8> {
245 let mut retired = vec![0u8; FEOX_BLOCK_SIZE];
246 write_retirement_marker(
247 &mut retired[..DELETION_MARKER_SIZE],
248 sector,
249 remaining,
250 RETIREMENT_PENDING,
251 );
252 retired
253}
254
255pub(crate) fn retirement_marker_token(sector: u64, marker: &[u8]) -> u16 {
256 let mut protected = [0; 17];
257 protected[..16].copy_from_slice(&marker[..16]);
258 protected[16] = marker[18];
259 seq_token(sector, &protected)
260}
261
262fn write_retirement_marker(marker: &mut [u8], sector: u64, remaining: usize, state: u8) {
263 marker[..8].copy_from_slice(DELETION_MARKER);
264 marker[8..16].copy_from_slice(&(remaining as u64).to_le_bytes());
265 marker[18] = state;
266 let token = retirement_marker_token(sector, marker);
267 marker[16..18].copy_from_slice(&token.to_le_bytes());
268}
269
270pub fn get_format(version: u32) -> Box<dyn RecordFormat> {
271 match version {
272 1 => Box::new(FormatV1),
273 2 | 3 => Box::new(FormatV2),
274 _ => Box::new(FormatV2),
275 }
276}
277
278pub(crate) fn get_format_ref(version: u32) -> &'static dyn RecordFormat {
279 match version {
280 1 => &FORMAT_V1,
281 2 | 3 => &FORMAT_V2,
282 _ => &FORMAT_V2,
283 }
284}