Skip to main content

reifydb_codec/row/catalog/
mod.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4//! The catalog storage family: a row whose header is the shape fingerprint and nothing else.
5
6use std::ops::Deref;
7
8use reifydb_value::{
9	byte_size::ByteSize,
10	error::{Error as ValueError, TypeError},
11	util::cowvec::CowVec,
12};
13use thiserror::Error;
14
15use crate::row::{
16	bytes::{CATALOG_HEADER_SIZE, EncodedBytes, EncodedRowBuilder, RowBuilder, read_defined_at, sealed::Sealed},
17	shape::fingerprint::RowShapeFingerprint,
18};
19
20const FINGERPRINT_OFFSET: usize = 0;
21
22#[derive(Debug, Error, PartialEq)]
23pub enum CatalogError {
24	#[error("catalog row is {len} bytes, too short to carry the fingerprint header")]
25	Truncated {
26		len: usize,
27	},
28}
29
30impl From<CatalogError> for ValueError {
31	fn from(err: CatalogError) -> Self {
32		TypeError::SerdeDeserialize {
33			message: err.to_string(),
34		}
35		.into()
36	}
37}
38
39#[inline]
40pub fn read_fingerprint(buf: &[u8]) -> RowShapeFingerprint {
41	RowShapeFingerprint::from_le_bytes(
42		buf[FINGERPRINT_OFFSET..CATALOG_HEADER_SIZE].try_into().expect("the catalog header is length-checked"),
43	)
44}
45
46#[inline]
47pub fn write_fingerprint(buf: &mut [u8], fingerprint: RowShapeFingerprint) {
48	buf[FINGERPRINT_OFFSET..CATALOG_HEADER_SIZE].copy_from_slice(&fingerprint.to_le_bytes());
49}
50
51#[repr(transparent)]
52#[derive(Debug, Clone, PartialEq)]
53pub struct EncodedCatalogRow(EncodedBytes);
54
55impl EncodedCatalogRow {
56	pub fn new(body: &[u8], fingerprint: RowShapeFingerprint) -> Self {
57		let mut buffer = Vec::with_capacity(CATALOG_HEADER_SIZE + body.len());
58		buffer.extend_from_slice(&fingerprint.to_le_bytes());
59		buffer.extend_from_slice(body);
60		Self(EncodedBytes(CowVec::new(buffer)))
61	}
62
63	pub fn view(bytes: &EncodedBytes) -> &Self {
64		// SAFETY: EncodedCatalogRow is repr(transparent) over EncodedBytes, so the pointer cast preserves
65		// layout, and the returned reference borrows the same allocation for the same lifetime.
66		unsafe { &*(bytes as *const EncodedBytes as *const Self) }
67	}
68
69	pub fn bytes(&self) -> &EncodedBytes {
70		&self.0
71	}
72
73	pub fn as_slice(&self) -> &[u8] {
74		self.0.as_slice()
75	}
76
77	pub fn into_bytes(self) -> EncodedBytes {
78		self.0
79	}
80
81	#[inline]
82	pub fn fingerprint(&self) -> RowShapeFingerprint {
83		read_fingerprint(&self.0)
84	}
85
86	pub fn set_fingerprint(&mut self, fingerprint: RowShapeFingerprint) {
87		write_fingerprint(self.0.make_mut(), fingerprint);
88	}
89
90	#[inline]
91	pub fn is_defined(&self, index: usize) -> bool {
92		read_defined_at(&self.0, CATALOG_HEADER_SIZE, index)
93	}
94
95	pub fn body(&self) -> &[u8] {
96		&self.0[CATALOG_HEADER_SIZE..]
97	}
98
99	pub fn body_mut(&mut self) -> &mut [u8] {
100		&mut self.0.make_mut()[CATALOG_HEADER_SIZE..]
101	}
102
103	pub fn len(&self) -> usize {
104		self.0.len()
105	}
106
107	pub fn is_empty(&self) -> bool {
108		self.body().is_empty()
109	}
110
111	pub fn byte_size(&self) -> ByteSize {
112		ByteSize::from(self.0.len() as u64)
113	}
114
115	pub fn thaw(self) -> EncodedCatalogRowBuilder {
116		EncodedCatalogRowBuilder(self.0.thaw())
117	}
118}
119
120impl TryFrom<EncodedBytes> for EncodedCatalogRow {
121	type Error = CatalogError;
122
123	fn try_from(bytes: EncodedBytes) -> Result<Self, Self::Error> {
124		if bytes.len() < CATALOG_HEADER_SIZE {
125			return Err(CatalogError::Truncated {
126				len: bytes.len(),
127			});
128		}
129		Ok(Self(bytes))
130	}
131}
132
133impl From<EncodedCatalogRow> for EncodedBytes {
134	fn from(row: EncodedCatalogRow) -> Self {
135		row.0
136	}
137}
138
139/// The write side of the catalog family: a buffer already carrying a fingerprint header, which
140/// freezes into an [`EncodedCatalogRow`] and never into a row of another family.
141#[repr(transparent)]
142#[derive(Debug, Clone, PartialEq, Eq)]
143pub struct EncodedCatalogRowBuilder(EncodedRowBuilder);
144
145impl EncodedCatalogRowBuilder {
146	pub(crate) fn wrap(builder: EncodedRowBuilder) -> Self {
147		Self(builder)
148	}
149
150	#[inline]
151	pub fn fingerprint(&self) -> RowShapeFingerprint {
152		read_fingerprint(self.as_slice())
153	}
154
155	pub fn set_fingerprint(&mut self, fingerprint: RowShapeFingerprint) {
156		write_fingerprint(self.as_mut_slice(), fingerprint);
157	}
158
159	#[inline]
160	pub fn is_defined(&self, index: usize) -> bool {
161		read_defined_at(self.as_slice(), CATALOG_HEADER_SIZE, index)
162	}
163
164	pub fn body(&self) -> &[u8] {
165		&self.as_slice()[CATALOG_HEADER_SIZE..]
166	}
167
168	pub fn freeze(self) -> EncodedCatalogRow {
169		EncodedCatalogRow(self.0.freeze())
170	}
171}
172
173impl Sealed for EncodedCatalogRowBuilder {
174	fn buffer(&self) -> &Vec<u8> {
175		self.0.buffer()
176	}
177
178	fn buffer_mut(&mut self) -> &mut Vec<u8> {
179		self.0.buffer_mut()
180	}
181
182	fn take_buffer(self) -> Vec<u8> {
183		self.0.take_buffer()
184	}
185}
186
187impl Deref for EncodedCatalogRowBuilder {
188	type Target = [u8];
189
190	fn deref(&self) -> &Self::Target {
191		self.as_slice()
192	}
193}