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