Skip to main content

reifydb_codec/row/catalog/
mod.rs

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