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