1use std::ops::Deref;
5
6use reifydb_value::{
7 encoding::LeBytes,
8 util::cowvec::CowVec,
9 value::{datetime::DateTime, row_number::RowNumber},
10};
11use serde::{Deserialize, Serialize};
12
13use crate::row::shape::fingerprint::RowShapeFingerprint;
14
15const FINGERPRINT_SIZE: usize = 8;
16const CREATED_AT_OFFSET: usize = FINGERPRINT_SIZE;
17const UPDATED_AT_OFFSET: usize = CREATED_AT_OFFSET + DateTime::ENCODED_SIZE;
18const TIME_OFFSET: usize = UPDATED_AT_OFFSET + DateTime::ENCODED_SIZE;
19const FLAGS_OFFSET: usize = TIME_OFFSET + DateTime::ENCODED_SIZE;
20
21pub const SHAPE_HEADER_SIZE: usize = FLAGS_OFFSET + 1;
22
23pub const CATALOG_HEADER_SIZE: usize = FINGERPRINT_SIZE;
24
25const NOT_BEFORE_OFFSET: usize = SHAPE_HEADER_SIZE;
26
27pub const QUEUE_HEADER_SIZE: usize = NOT_BEFORE_OFFSET + DateTime::ENCODED_SIZE;
28
29const OUTCOME_OFFSET: usize = SHAPE_HEADER_SIZE;
30const LOST_OFFSET: usize = OUTCOME_OFFSET + 1;
31const FINISHED_AT_OFFSET: usize = LOST_OFFSET + 1;
32
33pub const QUEUE_ATTEMPT_HEADER_SIZE: usize = FINISHED_AT_OFFSET + DateTime::ENCODED_SIZE;
34
35const DEDUPLICATION_ROW_NUMBER_OFFSET: usize = SHAPE_HEADER_SIZE;
36const EXPIRES_AT_OFFSET: usize = DEDUPLICATION_ROW_NUMBER_OFFSET + RowNumber::ENCODED_SIZE;
37
38pub const QUEUE_DEDUPLICATION_HEADER_SIZE: usize = EXPIRES_AT_OFFSET + DateTime::ENCODED_SIZE;
39
40const HAS_TIME: u8 = 1 << 0;
41
42const HAS_NOT_BEFORE: u8 = 1 << 1;
43
44pub type EncodedBytesIter = Box<dyn EncodedBytesIterator>;
45
46pub trait EncodedBytesIterator: Iterator<Item = EncodedBytes> {}
47
48impl<I: Iterator<Item = EncodedBytes>> EncodedBytesIterator for I {}
49
50#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
51pub struct EncodedBytes(pub CowVec<u8>);
52
53impl Deref for EncodedBytes {
54 type Target = CowVec<u8>;
55
56 fn deref(&self) -> &Self::Target {
57 &self.0
58 }
59}
60
61#[derive(Debug, Clone, PartialEq, Eq)]
62pub(crate) struct EncodedRowBuilder(Vec<u8>);
63
64impl EncodedRowBuilder {
65 pub(crate) fn zeroed(size: usize) -> Self {
66 Self(vec![0u8; size])
67 }
68
69 pub(crate) fn freeze(self) -> EncodedBytes {
70 EncodedBytes(CowVec::new(self.0))
71 }
72}
73
74impl sealed::Sealed for EncodedRowBuilder {
75 fn buffer(&self) -> &Vec<u8> {
76 &self.0
77 }
78
79 fn buffer_mut(&mut self) -> &mut Vec<u8> {
80 &mut self.0
81 }
82
83 fn take_buffer(self) -> Vec<u8> {
84 self.0
85 }
86}
87
88pub(crate) mod sealed {
89 use std::ops::Range;
90
91 pub trait Sealed {
92 fn buffer(&self) -> &Vec<u8>;
93
94 fn buffer_mut(&mut self) -> &mut Vec<u8>;
95
96 fn take_buffer(self) -> Vec<u8>
97 where
98 Self: Sized;
99
100 #[inline]
101 fn set_valid_at(&mut self, header_size: usize, index: usize, valid: bool) {
102 let byte = header_size + index / 8;
103 let bit = index % 8;
104 let buffer = self.buffer_mut();
105 if valid {
106 buffer[byte] |= 1 << bit;
107 } else {
108 buffer[byte] &= !(1 << bit);
109 }
110 }
111
112 #[inline]
113 fn splice(&mut self, range: Range<usize>, data: impl IntoIterator<Item = u8>) {
114 self.buffer_mut().splice(range, data);
115 }
116 }
117}
118
119pub trait RowBuilder: sealed::Sealed {
120 fn as_slice(&self) -> &[u8];
121
122 fn as_mut_slice(&mut self) -> &mut [u8];
123
124 fn len(&self) -> usize;
125
126 fn is_empty(&self) -> bool;
127
128 fn extend_from_slice(&mut self, bytes: &[u8]);
129
130 fn freeze_bytes(self) -> EncodedBytes
131 where
132 Self: Sized;
133}
134
135impl<T: sealed::Sealed> RowBuilder for T {
136 #[inline]
137 fn as_slice(&self) -> &[u8] {
138 self.buffer()
139 }
140
141 #[inline]
142 fn as_mut_slice(&mut self) -> &mut [u8] {
143 self.buffer_mut()
144 }
145
146 #[inline]
147 fn len(&self) -> usize {
148 self.buffer().len()
149 }
150
151 #[inline]
152 fn is_empty(&self) -> bool {
153 self.buffer().is_empty()
154 }
155
156 #[inline]
157 fn extend_from_slice(&mut self, bytes: &[u8]) {
158 self.buffer_mut().extend_from_slice(bytes);
159 }
160
161 #[inline]
162 fn freeze_bytes(self) -> EncodedBytes {
163 EncodedBytes(CowVec::new(self.take_buffer()))
164 }
165}
166
167pub trait SourceRowBuilder: RowBuilder + Sized {
168 fn set_timestamps(&mut self, created_at: DateTime, updated_at: DateTime);
169
170 fn set_time(&mut self, time: DateTime);
171}
172
173impl Deref for EncodedRowBuilder {
174 type Target = [u8];
175
176 fn deref(&self) -> &Self::Target {
177 &self.0
178 }
179}
180
181#[inline]
182pub fn write_fingerprint(buf: &mut [u8], fingerprint: RowShapeFingerprint) {
183 buf[0..FINGERPRINT_SIZE].copy_from_slice(&fingerprint.to_le_bytes());
184}
185
186#[inline]
187pub fn write_timestamps(buf: &mut [u8], created_at: DateTime, updated_at: DateTime) {
188 buf[CREATED_AT_OFFSET..CREATED_AT_OFFSET + DateTime::ENCODED_SIZE].copy_from_slice(&created_at.to_le_bytes());
189 buf[UPDATED_AT_OFFSET..UPDATED_AT_OFFSET + DateTime::ENCODED_SIZE].copy_from_slice(&updated_at.to_le_bytes());
190}
191
192#[inline]
193pub fn write_storage_time(buf: &mut [u8], time: DateTime) {
194 buf[TIME_OFFSET..TIME_OFFSET + DateTime::ENCODED_SIZE].copy_from_slice(&time.to_le_bytes());
195 buf[FLAGS_OFFSET] |= HAS_TIME;
196}
197
198#[inline]
199pub fn write_not_before(buf: &mut [u8], not_before: DateTime) {
200 buf[NOT_BEFORE_OFFSET..NOT_BEFORE_OFFSET + DateTime::ENCODED_SIZE].copy_from_slice(¬_before.to_le_bytes());
201 buf[FLAGS_OFFSET] |= HAS_NOT_BEFORE;
202}
203
204#[inline]
205pub fn write_outcome(buf: &mut [u8], outcome: u8) {
206 buf[OUTCOME_OFFSET] = outcome;
207}
208
209#[inline]
210pub fn write_lost(buf: &mut [u8], lost: bool) {
211 buf[LOST_OFFSET] = lost as u8;
212}
213
214#[inline]
215pub fn write_finished_at(buf: &mut [u8], finished_at: DateTime) {
216 buf[FINISHED_AT_OFFSET..FINISHED_AT_OFFSET + DateTime::ENCODED_SIZE]
217 .copy_from_slice(&finished_at.to_le_bytes());
218}
219
220#[inline]
221pub fn write_deduplication_row_number(buf: &mut [u8], row_number: RowNumber) {
222 buf[DEDUPLICATION_ROW_NUMBER_OFFSET..DEDUPLICATION_ROW_NUMBER_OFFSET + RowNumber::ENCODED_SIZE]
223 .copy_from_slice(&row_number.to_le_bytes());
224}
225
226#[inline]
227pub fn write_expires_at(buf: &mut [u8], expires_at: DateTime) {
228 buf[EXPIRES_AT_OFFSET..EXPIRES_AT_OFFSET + DateTime::ENCODED_SIZE].copy_from_slice(&expires_at.to_le_bytes());
229}
230
231#[inline]
232pub fn read_deduplication_row_number(buf: &[u8]) -> RowNumber {
233 RowNumber::read_le(&buf[DEDUPLICATION_ROW_NUMBER_OFFSET..])
234}
235
236#[inline]
237pub fn read_outcome(buf: &[u8]) -> u8 {
238 buf[OUTCOME_OFFSET]
239}
240
241#[inline]
242pub fn read_lost(buf: &[u8]) -> bool {
243 buf[LOST_OFFSET] != 0
244}
245
246#[inline]
247pub fn read_defined_at(buf: &[u8], header_size: usize, index: usize) -> bool {
248 let byte = header_size + index / 8;
249 let bit = index % 8;
250 (buf[byte] & (1 << bit)) != 0
251}
252
253#[inline]
254pub fn read_fingerprint(buf: &[u8]) -> RowShapeFingerprint {
255 let bytes: [u8; FINGERPRINT_SIZE] = buf[0..FINGERPRINT_SIZE].try_into().unwrap();
256 RowShapeFingerprint::from_le_bytes(bytes)
257}
258
259#[inline]
260fn read_stamp(buf: &[u8], offset: usize) -> DateTime {
261 DateTime::from_le_bytes(buf[offset..offset + DateTime::ENCODED_SIZE].try_into().unwrap())
262}
263
264#[inline]
265fn read_time(buf: &[u8]) -> Option<DateTime> {
266 (buf[FLAGS_OFFSET] & HAS_TIME != 0).then(|| read_stamp(buf, TIME_OFFSET))
267}
268
269#[inline]
270pub fn read_storage_time(buf: &[u8]) -> Option<DateTime> {
271 read_time(buf)
272}
273
274#[inline]
275pub fn read_created_at(buf: &[u8]) -> DateTime {
276 read_stamp(buf, CREATED_AT_OFFSET)
277}
278
279#[inline]
280pub fn read_updated_at(buf: &[u8]) -> DateTime {
281 read_stamp(buf, UPDATED_AT_OFFSET)
282}
283
284#[inline]
285pub fn read_not_before(buf: &[u8]) -> Option<DateTime> {
286 (buf[FLAGS_OFFSET] & HAS_NOT_BEFORE != 0).then(|| read_stamp(buf, NOT_BEFORE_OFFSET))
287}
288
289#[inline]
290pub fn read_finished_at(buf: &[u8]) -> DateTime {
291 read_stamp(buf, FINISHED_AT_OFFSET)
292}
293
294#[inline]
295pub fn read_expires_at(buf: &[u8]) -> DateTime {
296 read_stamp(buf, EXPIRES_AT_OFFSET)
297}
298
299impl EncodedBytes {
300 pub(crate) fn thaw(self) -> EncodedRowBuilder {
301 EncodedRowBuilder(self.0.into_inner())
302 }
303}
304
305impl EncodedBytes {
306 pub fn make_mut(&mut self) -> &mut [u8] {
307 self.0.make_mut()
308 }
309}
310
311#[cfg(test)]
312mod tests {
313 use reifydb_value::{
314 encoding::LeBytes,
315 factory::time::at_nanos,
316 value::{datetime::DateTime, value_type::ValueType},
317 };
318
319 use crate::row::{
320 bytes::{
321 CREATED_AT_OFFSET, FINGERPRINT_SIZE, FLAGS_OFFSET, HAS_TIME, RowBuilder, SHAPE_HEADER_SIZE,
322 TIME_OFFSET, UPDATED_AT_OFFSET,
323 },
324 shape::{RowFamily, RowShape, RowShapeField},
325 };
326
327 fn shape(field_count: usize) -> RowShape {
328 RowShape::new(
329 RowFamily::Table,
330 (0..field_count)
331 .map(|i| RowShapeField::unconstrained(format!("f{i}"), ValueType::Uint8))
332 .collect(),
333 )
334 }
335
336 #[test]
337 fn time_round_trips_independently_of_created_at_and_updated_at() {
338 let shape = shape(1);
341 let mut row = shape.allocate_table();
342
343 row.set_timestamps(at_nanos(11), at_nanos(22));
344 row.set_time(at_nanos(33));
345
346 assert_eq!(shape.created_at(&row), at_nanos(11));
347 assert_eq!(shape.updated_at(&row), at_nanos(22));
348 assert_eq!(shape.time(&row), Some(at_nanos(33)));
349
350 row.set_time(at_nanos(44));
351 assert_eq!(shape.created_at(&row), at_nanos(11), "writing #time must not disturb created_at");
352 assert_eq!(shape.updated_at(&row), at_nanos(22), "writing #time must not disturb updated_at");
353 assert_eq!(shape.time(&row), Some(at_nanos(44)));
354
355 row.set_timestamps(at_nanos(55), at_nanos(66));
356 assert_eq!(shape.time(&row), Some(at_nanos(44)), "writing the wall stamps must not disturb #time");
357 }
358
359 #[test]
360 fn time_survives_a_verbatim_rewrite_that_refreshes_updated_at() {
361 let mut row = shape(1).allocate_table();
364 row.set_timestamps(at_nanos(7), at_nanos(7));
365 row.set_time(at_nanos(1_000));
366
367 let created_at = row.created_at();
368 row.set_timestamps(created_at, at_nanos(99));
369
370 assert_eq!(row.created_at(), at_nanos(7));
371 assert_eq!(row.updated_at(), at_nanos(99), "the rewrite refreshes updated_at");
372 assert_eq!(row.time(), Some(at_nanos(1_000)), "#time is propagated, never re-stamped locally");
373 }
374
375 #[test]
376 fn the_header_slots_end_before_the_bitvec_begins() {
377 assert_eq!(CREATED_AT_OFFSET, FINGERPRINT_SIZE, "the first stamp starts where the fingerprint ends");
381 assert_eq!(UPDATED_AT_OFFSET, CREATED_AT_OFFSET + DateTime::ENCODED_SIZE);
382 assert_eq!(TIME_OFFSET, UPDATED_AT_OFFSET + DateTime::ENCODED_SIZE);
383 assert_eq!(
384 FLAGS_OFFSET,
385 TIME_OFFSET + DateTime::ENCODED_SIZE,
386 "the flags byte sits after the last stamp, whatever a DateTime is worth"
387 );
388 assert_eq!(SHAPE_HEADER_SIZE, FLAGS_OFFSET + 1, "the bitvec must start after the flags byte");
389
390 let shape = shape(9);
391 let mut row = shape.allocate_table();
392
393 for i in 0..9 {
394 shape.set::<u64>(&mut row, i, (i as u64 + 1) * 1_000);
395 }
396 row.set_timestamps(at_nanos(1), at_nanos(2));
397 row.set_time(DateTime::MAX);
398
399 for i in 0..9 {
400 assert_eq!(shape.get::<u64>(&row, i), (i as u64 + 1) * 1_000, "field {i} misread");
401 assert!(row.is_defined(i), "field {i} lost its definedness bit to a header write");
402 }
403 assert_eq!(row.created_at(), at_nanos(1));
404 assert_eq!(row.updated_at(), at_nanos(2));
405 assert_eq!(row.time(), Some(DateTime::MAX));
406 }
407
408 #[test]
409 fn a_row_that_was_never_stamped_carries_no_time() {
410 let shape = shape(3);
414 let mut row = shape.allocate_table();
415
416 assert_eq!(shape.time(&row), None, "a freshly allocated row carries no #time");
417
418 shape.set::<u64>(&mut row, 0, 7u64);
419 row.set_timestamps(at_nanos(1), at_nanos(2));
420
421 assert_eq!(shape.time(&row), None, "writing fields and wall stamps must not conjure a #time");
422 assert_eq!(shape.time(row.clone().freeze().as_slice()), None, "absence must survive the freeze");
423 }
424
425 #[test]
426 fn an_epoch_stamp_is_a_real_time_not_an_absence() {
427 let mut row = shape(1).allocate_table();
430 row.set_time(DateTime::EPOCH);
431
432 assert_eq!(row.time(), Some(DateTime::EPOCH));
433 assert_ne!(row.time(), None, "an explicitly stamped epoch is present, not absent");
434 }
435
436 #[test]
437 fn stamping_time_leaves_every_other_flag_bit_clear() {
438 let shape = shape(4);
441 let mut row = shape.allocate_table();
442
443 assert_eq!(row.as_slice()[FLAGS_OFFSET], 0, "allocation must leave the flags byte clear");
444
445 row.set_time(at_nanos(5));
446 assert_eq!(row.as_slice()[FLAGS_OFFSET], HAS_TIME, "set_time must touch only its own bit");
447
448 row.set_timestamps(at_nanos(1), at_nanos(2));
449 row.set_fingerprint(shape.fingerprint());
450 shape.set::<u64>(&mut row, 3, 42u64);
451 assert_eq!(
452 row.as_slice()[FLAGS_OFFSET],
453 HAS_TIME,
454 "no other header or field write may reach the flags byte"
455 );
456 }
457
458 #[test]
459 fn the_flags_byte_is_not_the_first_bitvec_byte() {
460 let shape = shape(8);
463 let mut row = shape.allocate_table();
464
465 shape.set::<u64>(&mut row, 0, 1u64);
466 assert!(row.is_defined(0));
467 assert_eq!(row.time(), None, "defining field 0 must not set HAS_TIME");
468
469 let mut row = shape.allocate_table();
470 row.set_time(at_nanos(9));
471 for i in 0..8 {
472 assert!(!row.is_defined(i), "stamping #time must not define field {i}");
473 }
474 }
475
476 #[test]
477 fn time_consumes_no_definedness_bit() {
478 let shape = shape(9);
481 let mut row = shape.allocate_table();
482 row.set_time(DateTime::MAX);
483
484 for i in 0..9 {
485 assert!(!row.is_defined(i), "field {i} must start undefined regardless of #time");
486 }
487
488 shape.set::<u64>(&mut row, 3, 42u64);
489 assert!(row.is_defined(3), "bit 3 maps to user field 3, not to a system slot");
490 for i in (0..9).filter(|i| *i != 3) {
491 assert!(!row.is_defined(i), "defining field 3 must not define field {i}");
492 }
493
494 assert_eq!(row.time(), Some(DateTime::MAX), "#time is unaffected by definedness writes");
495 assert_eq!(shape.bitvec_size(), 2, "9 fields still need exactly 2 bitvec bytes");
496 assert_eq!(shape.data_offset(), SHAPE_HEADER_SIZE + 2);
497 }
498
499 #[test]
500 fn a_stamp_slot_holds_exactly_one_datetime_encoding() {
501 let mut row = shape(1).allocate_table();
504 let stamp = at_nanos(0x0102_0304_0506_0708);
505 row.set_time(stamp);
506
507 assert_eq!(&row.as_slice()[TIME_OFFSET..TIME_OFFSET + DateTime::ENCODED_SIZE], &stamp.to_le_bytes());
508 assert_eq!(DateTime::from_le_bytes(stamp.to_le_bytes()), stamp);
509 }
510}