Skip to main content

reifydb_codec/row/operator/
mod.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use std::{cell::RefCell, collections::BTreeMap, mem, ops::Deref};
5
6use postcard::{from_bytes, to_extend};
7use reifydb_value::{
8	byte_size::ByteSize,
9	encoding::LeBytes,
10	error::{Error as ValueError, TypeError},
11	util::cowvec::CowVec,
12	value::datetime::DateTime,
13};
14use serde::{Serialize, de::DeserializeOwned};
15use thiserror::Error;
16
17use crate::row::bytes::{EncodedBytes, EncodedRowBuilder, RowBuilder, read_defined_at, sealed::Sealed};
18
19const CREATED_AT_OFFSET: usize = 0;
20
21const UPDATED_AT_OFFSET: usize = CREATED_AT_OFFSET + DateTime::ENCODED_SIZE;
22
23const TIME_OFFSET: usize = UPDATED_AT_OFFSET + DateTime::ENCODED_SIZE;
24
25pub const OPERATOR_HEADER_SIZE: usize = TIME_OFFSET + DateTime::ENCODED_SIZE;
26
27impl From<OperatorError> for ValueError {
28	fn from(err: OperatorError) -> Self {
29		match err {
30			OperatorError::Serialization(_) => TypeError::SerdeSerialize {
31				message: err.to_string(),
32			}
33			.into(),
34			_ => TypeError::SerdeDeserialize {
35				message: err.to_string(),
36			}
37			.into(),
38		}
39	}
40}
41
42#[derive(Debug, Error, PartialEq)]
43pub enum OperatorError {
44	#[error("operator state serialization failed: {0}")]
45	Serialization(String),
46
47	#[error("operator state deserialization failed: {0}")]
48	Deserialization(String),
49
50	#[error("operator row is {len} bytes, too short to carry the time header")]
51	Truncated {
52		len: usize,
53	},
54}
55
56#[inline]
57pub fn read_time(buf: &[u8]) -> Option<DateTime> {
58	let time = DateTime::from_le_bytes(
59		buf[TIME_OFFSET..OPERATOR_HEADER_SIZE].try_into().expect("the operator header is length-checked"),
60	);
61	(time != DateTime::MAX).then_some(time)
62}
63
64#[inline]
65pub fn write_time(buf: &mut [u8], time: DateTime) {
66	buf[TIME_OFFSET..OPERATOR_HEADER_SIZE].copy_from_slice(&time.to_le_bytes());
67}
68
69#[inline]
70pub fn read_created_at(buf: &[u8]) -> DateTime {
71	DateTime::from_le_bytes(
72		buf[CREATED_AT_OFFSET..UPDATED_AT_OFFSET].try_into().expect("the operator header is length-checked"),
73	)
74}
75
76#[inline]
77pub fn write_created_at(buf: &mut [u8], created_at: DateTime) {
78	buf[CREATED_AT_OFFSET..UPDATED_AT_OFFSET].copy_from_slice(&created_at.to_le_bytes());
79}
80
81#[inline]
82pub fn read_updated_at(buf: &[u8]) -> DateTime {
83	DateTime::from_le_bytes(
84		buf[UPDATED_AT_OFFSET..TIME_OFFSET].try_into().expect("the operator header is length-checked"),
85	)
86}
87
88#[inline]
89pub fn write_updated_at(buf: &mut [u8], updated_at: DateTime) {
90	buf[UPDATED_AT_OFFSET..TIME_OFFSET].copy_from_slice(&updated_at.to_le_bytes());
91}
92
93#[repr(transparent)]
94#[derive(Debug, Clone, PartialEq)]
95pub struct EncodedOperatorRow(EncodedBytes);
96
97impl EncodedOperatorRow {
98	pub fn new(body: &[u8], time: DateTime) -> Self {
99		let mut buffer = Vec::with_capacity(OPERATOR_HEADER_SIZE + body.len());
100		buffer.extend_from_slice(&DateTime::EPOCH.to_le_bytes());
101		buffer.extend_from_slice(&DateTime::EPOCH.to_le_bytes());
102		buffer.extend_from_slice(&time.to_le_bytes());
103		buffer.extend_from_slice(body);
104		Self(EncodedBytes(CowVec::new(buffer)))
105	}
106
107	pub fn timeless(body: &[u8]) -> Self {
108		Self::new(body, DateTime::MAX)
109	}
110
111	pub fn into_bytes(self) -> EncodedBytes {
112		self.0
113	}
114
115	pub fn bytes(&self) -> &EncodedBytes {
116		&self.0
117	}
118
119	pub fn view(bytes: &EncodedBytes) -> &Self {
120		// SAFETY: EncodedOperatorRow is repr(transparent) over EncodedBytes, so the pointer cast
121		// preserves layout, and the returned reference borrows the same allocation for the same lifetime.
122		unsafe { &*(bytes as *const EncodedBytes as *const Self) }
123	}
124
125	#[inline]
126	pub fn row_time(&self) -> Option<DateTime> {
127		read_time(&self.0)
128	}
129
130	#[inline]
131	pub fn time(&self) -> DateTime {
132		DateTime::from_le_bytes(
133			self.0[TIME_OFFSET..OPERATOR_HEADER_SIZE].try_into().expect("the header is length-checked"),
134		)
135	}
136
137	pub fn set_time(&mut self, time: DateTime) {
138		self.0.make_mut()[TIME_OFFSET..OPERATOR_HEADER_SIZE].copy_from_slice(&time.to_le_bytes());
139	}
140
141	pub fn body(&self) -> &[u8] {
142		&self.0[OPERATOR_HEADER_SIZE..]
143	}
144
145	pub fn body_mut(&mut self) -> &mut [u8] {
146		&mut self.0.make_mut()[OPERATOR_HEADER_SIZE..]
147	}
148
149	pub fn len(&self) -> usize {
150		self.0.len()
151	}
152
153	pub fn is_empty(&self) -> bool {
154		self.body().is_empty()
155	}
156
157	pub fn byte_size(&self) -> ByteSize {
158		ByteSize::from(self.0.len() as u64)
159	}
160}
161
162impl TryFrom<EncodedBytes> for EncodedOperatorRow {
163	type Error = OperatorError;
164
165	fn try_from(bytes: EncodedBytes) -> Result<Self, Self::Error> {
166		if bytes.len() < OPERATOR_HEADER_SIZE {
167			return Err(OperatorError::Truncated {
168				len: bytes.len(),
169			});
170		}
171		Ok(Self(bytes))
172	}
173}
174
175impl From<EncodedOperatorRow> for EncodedBytes {
176	fn from(row: EncodedOperatorRow) -> Self {
177		row.0
178	}
179}
180
181/// The write side of the operator family: a buffer already carrying a time header, which freezes
182/// into an [`EncodedOperatorRow`] and never into a row of another family.
183#[repr(transparent)]
184#[derive(Debug, Clone, PartialEq, Eq)]
185pub struct EncodedOperatorRowBuilder(EncodedRowBuilder);
186
187impl EncodedOperatorRowBuilder {
188	pub(crate) fn wrap(builder: EncodedRowBuilder) -> Self {
189		Self(builder)
190	}
191
192	#[inline]
193	pub fn row_time(&self) -> Option<DateTime> {
194		read_time(self.as_slice())
195	}
196
197	pub fn set_time(&mut self, time: DateTime) {
198		write_time(self.as_mut_slice(), time);
199	}
200
201	#[inline]
202	pub fn created_at(&self) -> DateTime {
203		read_created_at(self.as_slice())
204	}
205
206	#[inline]
207	pub fn updated_at(&self) -> DateTime {
208		read_updated_at(self.as_slice())
209	}
210
211	pub fn set_timestamps(&mut self, created_at: DateTime, updated_at: DateTime) {
212		write_created_at(self.as_mut_slice(), created_at);
213		write_updated_at(self.as_mut_slice(), updated_at);
214	}
215
216	#[inline]
217	pub fn is_defined(&self, index: usize) -> bool {
218		read_defined_at(self.as_slice(), OPERATOR_HEADER_SIZE, index)
219	}
220
221	pub fn body(&self) -> &[u8] {
222		&self.as_slice()[OPERATOR_HEADER_SIZE..]
223	}
224
225	pub fn freeze(self) -> EncodedOperatorRow {
226		EncodedOperatorRow(self.0.freeze())
227	}
228}
229
230impl Sealed for EncodedOperatorRowBuilder {
231	fn buffer(&self) -> &Vec<u8> {
232		self.0.buffer()
233	}
234
235	fn buffer_mut(&mut self) -> &mut Vec<u8> {
236		self.0.buffer_mut()
237	}
238
239	fn take_buffer(self) -> Vec<u8> {
240		self.0.take_buffer()
241	}
242}
243
244impl EncodedOperatorRow {
245	pub fn thaw(self) -> EncodedOperatorRowBuilder {
246		EncodedOperatorRowBuilder(self.0.thaw())
247	}
248}
249
250pub trait OperatorState: Sized + Send + 'static {
251	fn encode_state(&self, now: DateTime) -> Result<EncodedOperatorRow, OperatorError>;
252
253	fn decode_state(row: &EncodedOperatorRow) -> Result<Self, OperatorError>;
254}
255
256thread_local! {
257	static ENCODE_BUFFER: RefCell<Vec<u8>> = const { RefCell::new(Vec::new()) };
258}
259
260pub fn encode<T>(value: &T, now: DateTime) -> Result<EncodedOperatorRow, OperatorError>
261where
262	T: Serialize,
263{
264	let mut buffer = ENCODE_BUFFER.with(|cell| mem::take(&mut *cell.borrow_mut()));
265	buffer.clear();
266	let mut filled = to_extend(value, buffer).map_err(|e| OperatorError::Serialization(e.to_string()))?;
267	let result = EncodedOperatorRow::new(&filled, now);
268	filled.clear();
269	ENCODE_BUFFER.with(|cell| *cell.borrow_mut() = filled);
270	Ok(result)
271}
272
273pub fn decode_body<T>(row: &EncodedOperatorRow) -> Result<T, OperatorError>
274where
275	T: DeserializeOwned,
276{
277	from_bytes(row.body()).map_err(|e| OperatorError::Deserialization(e.to_string()))
278}
279
280pub fn decode<T: OperatorState>(row: &EncodedOperatorRow) -> Result<T, OperatorError> {
281	T::decode_state(row)
282}
283
284pub mod derive {
285	pub use serde::{self, Deserialize, Serialize};
286}
287
288pub trait StateCodec: Sized + Send + 'static + Serialize + DeserializeOwned {}
289
290impl<T> StateCodec for T where T: Sized + Send + 'static + Serialize + DeserializeOwned {}
291
292macro_rules! leaf_operator_state {
293	($($ty:ty),* $(,)?) => {
294		$(impl OperatorState for $ty {
295			fn encode_state(&self, now: DateTime) -> Result<EncodedOperatorRow, OperatorError> {
296				encode(self, now)
297			}
298
299			fn decode_state(row: &EncodedOperatorRow) -> Result<Self, OperatorError> {
300				decode_body::<Self>(row)
301			}
302		})*
303	};
304}
305
306leaf_operator_state!(u64, i64, Vec<u8>, (i64, i64, i64), DateTime);
307
308impl<K, V> OperatorState for BTreeMap<K, V>
309where
310	K: Send + 'static,
311	V: Send + 'static,
312	Self: Serialize + DeserializeOwned,
313{
314	fn encode_state(&self, now: DateTime) -> Result<EncodedOperatorRow, OperatorError> {
315		encode(self, now)
316	}
317
318	fn decode_state(row: &EncodedOperatorRow) -> Result<Self, OperatorError> {
319		decode_body::<Self>(row)
320	}
321}
322
323impl Deref for EncodedOperatorRowBuilder {
324	type Target = [u8];
325
326	fn deref(&self) -> &Self::Target {
327		self.as_slice()
328	}
329}