Skip to main content

reifydb_codec/row/shape/
mod.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4pub mod columns;
5pub mod fingerprint;
6pub mod values;
7
8use std::{
9	fmt,
10	fmt::Debug,
11	iter,
12	ops::Deref,
13	sync::{Arc, OnceLock},
14};
15
16use reifydb_value::{
17	reifydb_assertions,
18	value::{constraint::TypeConstraint, datetime::DateTime, system_columns::SystemColumn, value_type::ValueType},
19};
20use serde::{Deserialize, Serialize};
21
22use super::bytes::{
23	CATALOG_HEADER_SIZE, EncodedRowBuilder, QUEUE_ATTEMPT_HEADER_SIZE, QUEUE_DEDUPLICATION_HEADER_SIZE,
24	QUEUE_HEADER_SIZE, RINGBUFFER_HEADER_SIZE, RowBuilder, SHAPE_HEADER_SIZE, read_created_at, read_defined_at,
25	read_sequence, read_storage_time, read_updated_at, write_fingerprint,
26};
27use crate::row::{
28	catalog::EncodedCatalogRowBuilder,
29	operator::{
30		EncodedOperatorRowBuilder, OPERATOR_HEADER_SIZE, read_time as read_operator_time,
31		write_time as write_operator_time,
32	},
33	pod::{EncodedPodRowBuilder, POD_HEADER_SIZE},
34	queue::EncodedQueueRowBuilder,
35	queue_attempt::EncodedQueueAttemptRowBuilder,
36	queue_deduplication::EncodedQueueDeduplicationRowBuilder,
37	ringbuffer::EncodedRingBufferRowBuilder,
38	series::EncodedSeriesRowBuilder,
39	shape::fingerprint::{RowShapeFingerprint, compute_fingerprint},
40	table::EncodedTableRowBuilder,
41};
42
43#[repr(u8)]
44#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
45pub enum RowFamily {
46	Catalog = 0x01,
47	Pod = 0x02,
48	Table = 0x03,
49	Series = 0x04,
50	RingBuffer = 0x05,
51	Queue = 0x06,
52	Operator = 0x07,
53	QueueAttempt = 0x08,
54	QueueDeduplication = 0x09,
55}
56
57impl RowFamily {
58	pub const fn header_size(self) -> usize {
59		match self {
60			Self::Catalog => CATALOG_HEADER_SIZE,
61			Self::Pod => POD_HEADER_SIZE,
62			Self::Table => SHAPE_HEADER_SIZE,
63			Self::Series => SHAPE_HEADER_SIZE,
64			Self::RingBuffer => RINGBUFFER_HEADER_SIZE,
65			Self::Queue => QUEUE_HEADER_SIZE,
66			Self::Operator => OPERATOR_HEADER_SIZE,
67			Self::QueueAttempt => QUEUE_ATTEMPT_HEADER_SIZE,
68			Self::QueueDeduplication => QUEUE_DEDUPLICATION_HEADER_SIZE,
69		}
70	}
71
72	pub const fn system_columns(self) -> &'static [SystemColumn] {
73		match self {
74			Self::RingBuffer => &[
75				SystemColumn::CreatedAt,
76				SystemColumn::UpdatedAt,
77				SystemColumn::Time,
78				SystemColumn::Sequence,
79			],
80			Self::Table | Self::Series | Self::Queue | Self::QueueAttempt | Self::QueueDeduplication => {
81				&[SystemColumn::CreatedAt, SystemColumn::UpdatedAt, SystemColumn::Time]
82			}
83			Self::Operator => &[SystemColumn::Time],
84			Self::Pod | Self::Catalog => &[],
85		}
86	}
87
88	#[inline]
89	pub fn updated_at(self, row: &[u8]) -> DateTime {
90		match self.system_columns().contains(&SystemColumn::UpdatedAt) {
91			true => read_updated_at(row),
92			false => panic!("{self:?} rows carry no updated_at"),
93		}
94	}
95
96	pub const fn from_u8(value: u8) -> Option<Self> {
97		match value {
98			0x01 => Some(Self::Catalog),
99			0x02 => Some(Self::Pod),
100			0x03 => Some(Self::Table),
101			0x04 => Some(Self::Series),
102			0x05 => Some(Self::RingBuffer),
103			0x06 => Some(Self::Queue),
104			0x07 => Some(Self::Operator),
105			0x08 => Some(Self::QueueAttempt),
106			0x09 => Some(Self::QueueDeduplication),
107			_ => None,
108		}
109	}
110}
111
112#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
113pub struct RowShapeField {
114	pub name: String,
115
116	pub constraint: TypeConstraint,
117
118	pub offset: u32,
119
120	pub size: u32,
121}
122
123impl RowShapeField {
124	pub fn new(name: impl Into<String>, constraint: TypeConstraint) -> Self {
125		let storage_type = constraint.storage_type();
126		Self {
127			name: name.into(),
128			constraint,
129			offset: 0,
130			size: storage_type.size() as u32,
131		}
132	}
133
134	pub fn unconstrained(name: impl Into<String>, field_type: ValueType) -> Self {
135		Self::new(name, TypeConstraint::unconstrained(field_type))
136	}
137}
138
139pub struct RowShape(Arc<Inner>);
140
141#[derive(Debug, Serialize, Deserialize)]
142pub struct Inner {
143	pub fingerprint: RowShapeFingerprint,
144
145	pub family: RowFamily,
146
147	pub fields: Vec<RowShapeField>,
148
149	#[serde(skip)]
150	cached_layout: OnceLock<usize>,
151}
152
153impl PartialEq for Inner {
154	fn eq(&self, other: &Self) -> bool {
155		self.fingerprint == other.fingerprint && self.family == other.family && self.fields == other.fields
156	}
157}
158
159impl Eq for Inner {}
160
161impl Deref for RowShape {
162	type Target = Inner;
163
164	fn deref(&self) -> &Self::Target {
165		&self.0
166	}
167}
168
169impl Clone for RowShape {
170	fn clone(&self) -> Self {
171		Self(self.0.clone())
172	}
173}
174
175impl Debug for RowShape {
176	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
177		self.0.fmt(f)
178	}
179}
180
181impl PartialEq for RowShape {
182	fn eq(&self, other: &Self) -> bool {
183		self.0.as_ref() == other.0.as_ref()
184	}
185}
186
187impl Eq for RowShape {}
188
189impl RowShape {
190	pub fn new(family: RowFamily, fields: Vec<RowShapeField>) -> Self {
191		let fields = Self::compute_layout(family, fields);
192		let fingerprint = compute_fingerprint(family, &fields);
193
194		Self(Arc::new(Inner {
195			fingerprint,
196			family,
197			fields,
198			cached_layout: OnceLock::new(),
199		}))
200	}
201
202	pub fn from_parts(family: RowFamily, fingerprint: RowShapeFingerprint, fields: Vec<RowShapeField>) -> Self {
203		Self(Arc::new(Inner {
204			fingerprint,
205			family,
206			fields,
207			cached_layout: OnceLock::new(),
208		}))
209	}
210
211	pub fn family(&self) -> RowFamily {
212		self.family
213	}
214
215	pub fn header_size(&self) -> usize {
216		self.family.header_size()
217	}
218
219	pub fn fingerprint(&self) -> RowShapeFingerprint {
220		self.fingerprint
221	}
222
223	pub fn fields(&self) -> &[RowShapeField] {
224		&self.fields
225	}
226
227	pub fn field_count(&self) -> usize {
228		self.fields.len()
229	}
230
231	pub fn find_field_index(&self, name: &str) -> Option<usize> {
232		self.fields.iter().position(|f| f.name == name)
233	}
234
235	pub fn get_field(&self, index: usize) -> Option<&RowShapeField> {
236		self.fields.get(index)
237	}
238
239	pub fn field_names(&self) -> impl Iterator<Item = &str> {
240		self.fields.iter().map(|f| f.name.as_str())
241	}
242
243	fn compute_layout(family: RowFamily, mut fields: Vec<RowShapeField>) -> Vec<RowShapeField> {
244		let bitvec_size = fields.len().div_ceil(8);
245		let mut offset: u32 = (family.header_size() + bitvec_size) as u32;
246
247		for field in fields.iter_mut() {
248			field.size = field.constraint.storage_type().size() as u32;
249			field.offset = offset;
250			offset += field.size;
251		}
252
253		fields
254	}
255
256	pub fn bitvec_size(&self) -> usize {
257		self.fields.len().div_ceil(8)
258	}
259
260	pub fn data_offset(&self) -> usize {
261		self.header_size() + self.bitvec_size()
262	}
263
264	#[inline]
265	pub fn is_defined(&self, row: &[u8], index: usize) -> bool {
266		read_defined_at(row, self.header_size(), index)
267	}
268
269	#[inline]
270	pub(crate) fn set_valid(&self, row: &mut impl RowBuilder, index: usize, valid: bool) {
271		row.set_valid_at(self.header_size(), index, valid);
272	}
273
274	#[inline]
275	pub fn time(&self, row: &[u8]) -> Option<DateTime> {
276		match (self.family.system_columns().contains(&SystemColumn::Time), self.family) {
277			(false, _) => None,
278			(true, RowFamily::Operator) => read_operator_time(row),
279			(true, _) => read_storage_time(row),
280		}
281	}
282
283	#[inline]
284	pub fn created_at(&self, row: &[u8]) -> DateTime {
285		match self.family.system_columns().contains(&SystemColumn::CreatedAt) {
286			true => read_created_at(row),
287			false => panic!("{} rows carry no created_at", format!("{:?}", self.family).to_lowercase()),
288		}
289	}
290
291	#[inline]
292	pub fn sequence(&self, row: &[u8]) -> u64 {
293		match self.family.system_columns().contains(&SystemColumn::Sequence) {
294			true => read_sequence(row),
295			false => panic!("{} rows carry no sequence", format!("{:?}", self.family).to_lowercase()),
296		}
297	}
298
299	#[inline]
300	pub fn updated_at(&self, row: &[u8]) -> DateTime {
301		match self.family.system_columns().contains(&SystemColumn::UpdatedAt) {
302			true => read_updated_at(row),
303			false => panic!("{} rows carry no updated_at", format!("{:?}", self.family).to_lowercase()),
304		}
305	}
306
307	fn get_cached_layout(&self) -> usize {
308		*self.cached_layout.get_or_init(|| match self.fields.last() {
309			Some(last) => last.offset as usize + last.size as usize,
310			None => self.header_size() + self.bitvec_size(),
311		})
312	}
313
314	pub fn total_static_size(&self) -> usize {
315		self.get_cached_layout()
316	}
317
318	pub fn dynamic_section_start(&self) -> usize {
319		self.total_static_size()
320	}
321
322	pub fn dynamic_section_size(&self, row: &[u8]) -> usize {
323		row.len().saturating_sub(self.total_static_size())
324	}
325
326	pub(crate) fn read_dynamic_ref(&self, row: &[u8], index: usize) -> Option<(usize, usize)> {
327		if !self.is_defined(row, index) {
328			return None;
329		}
330		let field = &self.fields()[index];
331		match field.constraint.get_type().inner_type() {
332			ValueType::Utf8
333			| ValueType::Blob
334			| ValueType::Any
335			| ValueType::Digest {
336				..
337			} => {
338				let ref_slice = &row[field.offset as usize..field.offset as usize + 8];
339				let offset =
340					u32::from_le_bytes([ref_slice[0], ref_slice[1], ref_slice[2], ref_slice[3]])
341						as usize;
342				let length =
343					u32::from_le_bytes([ref_slice[4], ref_slice[5], ref_slice[6], ref_slice[7]])
344						as usize;
345				Some((offset, length))
346			}
347			_ => None,
348		}
349	}
350
351	pub(crate) fn write_dynamic_ref(&self, row: &mut impl RowBuilder, index: usize, offset: usize, length: usize) {
352		let field = &self.fields()[index];
353		match field.constraint.get_type().inner_type() {
354			ValueType::Utf8
355			| ValueType::Blob
356			| ValueType::Any
357			| ValueType::Digest {
358				..
359			} => {
360				let ref_slice =
361					&mut row.as_mut_slice()[field.offset as usize..field.offset as usize + 8];
362				ref_slice[0..4].copy_from_slice(&(offset as u32).to_le_bytes());
363				ref_slice[4..8].copy_from_slice(&(length as u32).to_le_bytes());
364			}
365			_ => {}
366		}
367	}
368
369	pub(crate) fn replace_dynamic_data(&self, row: &mut impl RowBuilder, index: usize, new_data: &[u8]) {
370		if let Some((old_offset, old_length)) = self.read_dynamic_ref(row.as_slice(), index) {
371			let delta = new_data.len() as isize - old_length as isize;
372
373			let refs_to_update: Vec<(usize, usize, usize)> = if delta != 0 {
374				self.fields()
375					.iter()
376					.enumerate()
377					.filter(|(i, _)| *i != index && self.is_defined(row.as_slice(), *i))
378					.filter_map(|(i, _)| {
379						self.read_dynamic_ref(row.as_slice(), i)
380							.filter(|(off, _)| *off > old_offset)
381							.map(|(off, len)| (i, off, len))
382					})
383					.collect()
384			} else {
385				vec![]
386			};
387
388			let dynamic_start = self.dynamic_section_start();
389			let abs_start = dynamic_start + old_offset;
390			let abs_end = abs_start + old_length;
391			row.splice(abs_start..abs_end, new_data.iter().copied());
392
393			self.write_dynamic_ref(row, index, old_offset, new_data.len());
394
395			for (i, off, len) in refs_to_update {
396				let new_off = (off as isize + delta) as usize;
397				self.write_dynamic_ref(row, i, new_off, len);
398			}
399		} else {
400			let dynamic_offset = self.dynamic_section_size(row.as_slice());
401			row.extend_from_slice(new_data);
402			self.write_dynamic_ref(row, index, dynamic_offset, new_data.len());
403		}
404		self.set_valid(row, index, true);
405	}
406
407	pub(crate) fn remove_dynamic_data(&self, row: &mut impl RowBuilder, index: usize) {
408		if let Some((old_offset, old_length)) = self.read_dynamic_ref(row.as_slice(), index) {
409			let refs_to_update: Vec<(usize, usize, usize)> = self
410				.fields()
411				.iter()
412				.enumerate()
413				.filter(|(i, _)| *i != index && self.is_defined(row.as_slice(), *i))
414				.filter_map(|(i, _)| {
415					self.read_dynamic_ref(row.as_slice(), i)
416						.filter(|(off, _)| *off > old_offset)
417						.map(|(off, len)| (i, off, len))
418				})
419				.collect();
420
421			let dynamic_start = self.dynamic_section_start();
422			let abs_start = dynamic_start + old_offset;
423			let abs_end = abs_start + old_length;
424			row.splice(abs_start..abs_end, iter::empty());
425
426			for (i, off, len) in refs_to_update {
427				let new_off = off - old_length;
428				self.write_dynamic_ref(row, i, new_off, len);
429			}
430		}
431	}
432
433	fn allocate(&self) -> EncodedRowBuilder {
434		let total_size = self.get_cached_layout();
435		let mut row = EncodedRowBuilder::zeroed(total_size);
436		match self.family {
437			RowFamily::Pod => {}
438			RowFamily::Operator => write_operator_time(row.as_mut_slice(), DateTime::MAX),
439			_ => write_fingerprint(row.as_mut_slice(), self.fingerprint),
440		}
441		reifydb_assertions! {
442			assert!(
443				row.len() == total_size,
444				"allocated row length does not match the shape total_static_size, so any field accessor using pre-computed offsets will read from garbage memory (row_len={} total_size={})",
445				row.len(),
446				total_size
447			);
448		}
449		row
450	}
451
452	pub fn allocate_catalog(&self) -> EncodedCatalogRowBuilder {
453		assert_eq!(self.family, RowFamily::Catalog, "allocate_catalog on a shape of another family");
454		EncodedCatalogRowBuilder::wrap(self.allocate())
455	}
456
457	pub fn allocate_pod(&self) -> EncodedPodRowBuilder {
458		assert_eq!(self.family, RowFamily::Pod, "allocate_pod on a shape of another family");
459		EncodedPodRowBuilder::wrap(self.allocate())
460	}
461
462	pub fn allocate_operator(&self) -> EncodedOperatorRowBuilder {
463		assert_eq!(self.family, RowFamily::Operator, "allocate_operator on a shape of another family");
464		EncodedOperatorRowBuilder::wrap(self.allocate())
465	}
466
467	pub fn allocate_table(&self) -> EncodedTableRowBuilder {
468		assert_eq!(self.family, RowFamily::Table, "allocate_table on a shape of another family");
469		EncodedTableRowBuilder::wrap(self.allocate())
470	}
471
472	pub fn allocate_series(&self) -> EncodedSeriesRowBuilder {
473		assert_eq!(self.family, RowFamily::Series, "allocate_series on a shape of another family");
474		EncodedSeriesRowBuilder::wrap(self.allocate())
475	}
476
477	pub fn allocate_ringbuffer(&self) -> EncodedRingBufferRowBuilder {
478		assert_eq!(self.family, RowFamily::RingBuffer, "allocate_ringbuffer on a shape of another family");
479		EncodedRingBufferRowBuilder::wrap(self.allocate())
480	}
481
482	pub fn allocate_queue(&self) -> EncodedQueueRowBuilder {
483		assert_eq!(self.family, RowFamily::Queue, "allocate_queue on a shape of another family");
484		EncodedQueueRowBuilder::wrap(self.allocate())
485	}
486
487	pub fn allocate_queue_attempt(&self) -> EncodedQueueAttemptRowBuilder {
488		assert_eq!(self.family, RowFamily::QueueAttempt, "allocate_queue_attempt on a shape of another family");
489		EncodedQueueAttemptRowBuilder::wrap(self.allocate())
490	}
491
492	pub fn allocate_queue_deduplication(&self) -> EncodedQueueDeduplicationRowBuilder {
493		assert_eq!(
494			self.family,
495			RowFamily::QueueDeduplication,
496			"allocate_queue_deduplication on a shape of another family"
497		);
498		EncodedQueueDeduplicationRowBuilder::wrap(self.allocate())
499	}
500
501	pub fn set_none(&self, row: &mut impl RowBuilder, index: usize) {
502		self.remove_dynamic_data(row, index);
503		self.set_valid(row, index, false);
504	}
505
506	pub fn testing(family: RowFamily, types: &[ValueType]) -> Self {
507		RowShape::new(
508			family,
509			types.iter()
510				.enumerate()
511				.map(|(i, t)| RowShapeField::unconstrained(format!("f{}", i), t.clone()))
512				.collect(),
513		)
514	}
515}