Skip to main content

reifydb_core/encoded/
any.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use std::str;
5
6use reifydb_value::{
7	reifydb_assertions,
8	value::{
9		Value,
10		blob::Blob,
11		date::Date,
12		datetime::DateTime,
13		duration::Duration,
14		identity::IdentityId,
15		ordered_f32::OrderedF32,
16		ordered_f64::OrderedF64,
17		time::Time,
18		uuid::{Uuid4, Uuid7},
19		value_type::ValueType,
20	},
21};
22use uuid::Uuid;
23
24use crate::encoded::{row::EncodedRow, shape::RowShape};
25
26pub fn encode_value(value: &Value) -> Vec<u8> {
27	match value {
28		Value::Boolean(v) => vec![ValueType::Boolean.to_u8(), *v as u8],
29
30		Value::Uint1(v) => vec![ValueType::Uint1.to_u8(), *v],
31		Value::Uint2(v) => {
32			let mut b = vec![ValueType::Uint2.to_u8()];
33			b.extend_from_slice(&v.to_le_bytes());
34			b
35		}
36		Value::Uint4(v) => {
37			let mut b = vec![ValueType::Uint4.to_u8()];
38			b.extend_from_slice(&v.to_le_bytes());
39			b
40		}
41		Value::Uint8(v) => {
42			let mut b = vec![ValueType::Uint8.to_u8()];
43			b.extend_from_slice(&v.to_le_bytes());
44			b
45		}
46		Value::Uint16(v) => {
47			let mut b = vec![ValueType::Uint16.to_u8()];
48			b.extend_from_slice(&v.to_le_bytes());
49			b
50		}
51
52		Value::Int1(v) => vec![ValueType::Int1.to_u8(), *v as u8],
53		Value::Int2(v) => {
54			let mut b = vec![ValueType::Int2.to_u8()];
55			b.extend_from_slice(&v.to_le_bytes());
56			b
57		}
58		Value::Int4(v) => {
59			let mut b = vec![ValueType::Int4.to_u8()];
60			b.extend_from_slice(&v.to_le_bytes());
61			b
62		}
63		Value::Int8(v) => {
64			let mut b = vec![ValueType::Int8.to_u8()];
65			b.extend_from_slice(&v.to_le_bytes());
66			b
67		}
68		Value::Int16(v) => {
69			let mut b = vec![ValueType::Int16.to_u8()];
70			b.extend_from_slice(&v.to_le_bytes());
71			b
72		}
73
74		Value::Float4(v) => {
75			let mut b = vec![ValueType::Float4.to_u8()];
76			b.extend_from_slice(&v.value().to_bits().to_le_bytes());
77			b
78		}
79		Value::Float8(v) => {
80			let mut b = vec![ValueType::Float8.to_u8()];
81			b.extend_from_slice(&v.value().to_bits().to_le_bytes());
82			b
83		}
84
85		Value::Date(v) => {
86			let mut b = vec![ValueType::Date.to_u8()];
87			b.extend_from_slice(&v.to_days_since_epoch().to_le_bytes());
88			b
89		}
90		Value::DateTime(v) => {
91			let mut b = vec![ValueType::DateTime.to_u8()];
92			b.extend_from_slice(&v.to_nanos().to_le_bytes());
93			b
94		}
95		Value::Time(v) => {
96			let mut b = vec![ValueType::Time.to_u8()];
97			b.extend_from_slice(&v.to_nanos_since_midnight().to_le_bytes());
98			b
99		}
100		Value::Duration(v) => {
101			let mut b = vec![ValueType::Duration.to_u8()];
102			b.extend_from_slice(&v.get_months().to_le_bytes());
103			b.extend_from_slice(&v.get_days().to_le_bytes());
104			b.extend_from_slice(&v.get_nanos().to_le_bytes());
105			b
106		}
107
108		Value::Uuid4(v) => {
109			let mut b = vec![ValueType::Uuid4.to_u8()];
110			b.extend_from_slice(v.as_bytes());
111			b
112		}
113		Value::Uuid7(v) => {
114			let mut b = vec![ValueType::Uuid7.to_u8()];
115			b.extend_from_slice(v.as_bytes());
116			b
117		}
118		Value::IdentityId(v) => {
119			let mut b = vec![ValueType::IdentityId.to_u8()];
120			b.extend_from_slice(v.as_bytes());
121			b
122		}
123
124		Value::Utf8(v) => {
125			let s = v.as_bytes();
126			let mut b = vec![ValueType::Utf8.to_u8()];
127			b.extend_from_slice(&(s.len() as u32).to_le_bytes());
128			b.extend_from_slice(s);
129			b
130		}
131		Value::Blob(v) => {
132			let s = v.as_bytes();
133			let mut b = vec![ValueType::Blob.to_u8()];
134			b.extend_from_slice(&(s.len() as u32).to_le_bytes());
135			b.extend_from_slice(s);
136			b
137		}
138
139		Value::Int(_)
140		| Value::Uint(_)
141		| Value::Decimal(_)
142		| Value::DictionaryId(_)
143		| Value::Any(_)
144		| Value::None {
145			..
146		}
147		| Value::Type(_)
148		| Value::List(_)
149		| Value::Record(_)
150		| Value::Tuple(_) => unreachable!("unsupported value type in Any encoding: {:?}", value),
151	}
152}
153
154pub fn decode_value(bytes: &[u8]) -> Value {
155	let type_byte = bytes[0];
156	let p = &bytes[1..];
157	let ty = ValueType::from_u8(type_byte);
158
159	match ty {
160		ValueType::Boolean => Value::Boolean(p[0] != 0),
161
162		ValueType::Uint1 => Value::Uint1(p[0]),
163		ValueType::Uint2 => Value::Uint2(u16::from_le_bytes([p[0], p[1]])),
164		ValueType::Uint4 => Value::Uint4(u32::from_le_bytes([p[0], p[1], p[2], p[3]])),
165		ValueType::Uint8 => Value::Uint8(u64::from_le_bytes(p[..8].try_into().unwrap())),
166		ValueType::Uint16 => Value::Uint16(u128::from_le_bytes(p[..16].try_into().unwrap())),
167
168		ValueType::Int1 => Value::Int1(p[0] as i8),
169		ValueType::Int2 => Value::Int2(i16::from_le_bytes([p[0], p[1]])),
170		ValueType::Int4 => Value::Int4(i32::from_le_bytes([p[0], p[1], p[2], p[3]])),
171		ValueType::Int8 => Value::Int8(i64::from_le_bytes(p[..8].try_into().unwrap())),
172		ValueType::Int16 => Value::Int16(i128::from_le_bytes(p[..16].try_into().unwrap())),
173
174		ValueType::Float4 => {
175			let bits = u32::from_le_bytes([p[0], p[1], p[2], p[3]]);
176			Value::Float4(OrderedF32::try_from(f32::from_bits(bits)).unwrap())
177		}
178		ValueType::Float8 => {
179			let bits = u64::from_le_bytes(p[..8].try_into().unwrap());
180			Value::Float8(OrderedF64::try_from(f64::from_bits(bits)).unwrap())
181		}
182
183		ValueType::Date => {
184			let days = i32::from_le_bytes([p[0], p[1], p[2], p[3]]);
185			Value::Date(Date::from_days_since_epoch(days).unwrap())
186		}
187		ValueType::DateTime => {
188			let nanos = u64::from_le_bytes(p[..8].try_into().unwrap());
189			Value::DateTime(DateTime::from_nanos(nanos))
190		}
191		ValueType::Time => {
192			let nanos = u64::from_le_bytes(p[..8].try_into().unwrap());
193			Value::Time(Time::from_nanos_since_midnight(nanos).unwrap())
194		}
195		ValueType::Duration => {
196			let months = i32::from_le_bytes([p[0], p[1], p[2], p[3]]);
197			let days = i32::from_le_bytes([p[4], p[5], p[6], p[7]]);
198			let nanos = i64::from_le_bytes(p[8..16].try_into().unwrap());
199			Value::Duration(Duration::new(months, days, nanos).unwrap())
200		}
201
202		ValueType::Uuid4 => {
203			let b: [u8; 16] = p[..16].try_into().unwrap();
204			Value::Uuid4(Uuid4::from(Uuid::from_bytes(b)))
205		}
206		ValueType::Uuid7 => {
207			let b: [u8; 16] = p[..16].try_into().unwrap();
208			Value::Uuid7(Uuid7::from(Uuid::from_bytes(b)))
209		}
210		ValueType::IdentityId => {
211			let b: [u8; 16] = p[..16].try_into().unwrap();
212			Value::IdentityId(IdentityId::from(Uuid7::from(Uuid::from_bytes(b))))
213		}
214
215		ValueType::Utf8 => {
216			let len = u32::from_le_bytes([p[0], p[1], p[2], p[3]]) as usize;
217			let s = str::from_utf8(&p[4..4 + len]).unwrap();
218			Value::Utf8(s.to_string())
219		}
220		ValueType::Blob => {
221			let len = u32::from_le_bytes([p[0], p[1], p[2], p[3]]) as usize;
222			Value::Blob(Blob::from_slice(&p[4..4 + len]))
223		}
224
225		_ => unreachable!("unsupported type byte {} in Any decoding", type_byte),
226	}
227}
228
229impl RowShape {
230	pub fn set_any(&self, row: &mut EncodedRow, index: usize, value: &Value) {
231		reifydb_assertions! {
232			assert!(
233				row.len() >= self.total_static_size(),
234				"row/shape size mismatch: row.len()={} < total_static_size()={}",
235				row.len(),
236				self.total_static_size()
237			);
238			assert_eq!(*self.fields()[index].constraint.get_type().inner_type(), ValueType::Any);
239		}
240		let encoded = encode_value(value);
241		self.replace_dynamic_data(row, index, &encoded);
242	}
243
244	pub fn get_any(&self, row: &EncodedRow, index: usize) -> Value {
245		let field = &self.fields()[index];
246		reifydb_assertions! {
247			assert!(
248				row.len() >= self.total_static_size(),
249				"row/shape size mismatch: row.len()={} < total_static_size()={}",
250				row.len(),
251				self.total_static_size()
252			);
253			assert_eq!(*field.constraint.get_type().inner_type(), ValueType::Any);
254		}
255
256		let ref_slice = &row.as_slice()[field.offset as usize..field.offset as usize + 8];
257		let offset = u32::from_le_bytes([ref_slice[0], ref_slice[1], ref_slice[2], ref_slice[3]]) as usize;
258		let length = u32::from_le_bytes([ref_slice[4], ref_slice[5], ref_slice[6], ref_slice[7]]) as usize;
259
260		let dynamic_start = self.dynamic_section_start();
261		let data_start = dynamic_start + offset;
262		let data_slice = &row.as_slice()[data_start..data_start + length];
263
264		decode_value(data_slice)
265	}
266}
267
268#[cfg(test)]
269pub mod tests {
270	use std::f64::consts::E;
271
272	use reifydb_runtime::context::{
273		clock::{Clock, MockClock},
274		rng::Rng,
275	};
276	use reifydb_value::value::{
277		Value,
278		blob::Blob,
279		date::Date,
280		datetime::DateTime,
281		ordered_f32::OrderedF32,
282		ordered_f64::OrderedF64,
283		time::Time,
284		uuid::{Uuid4, Uuid7},
285		value_type::ValueType,
286	};
287
288	use crate::encoded::shape::RowShape;
289
290	fn test_clock_and_rng() -> (MockClock, Clock, Rng) {
291		let mock = MockClock::from_millis(1000);
292		let clock = Clock::Mock(mock.clone());
293		let rng = Rng::seeded(42);
294		(mock, clock, rng)
295	}
296
297	#[test]
298	fn test_any_boolean() {
299		let shape = RowShape::testing(&[ValueType::Any]);
300		let mut row = shape.allocate();
301		shape.set_any(&mut row, 0, &Value::Boolean(true));
302		assert_eq!(shape.get_any(&row, 0), Value::Boolean(true));
303	}
304
305	#[test]
306	fn test_any_integers() {
307		let shape = RowShape::testing(&[ValueType::Any]);
308
309		let cases: &[Value] = &[
310			Value::Int1(-42),
311			Value::Int2(-1000),
312			Value::Int4(-100000),
313			Value::Int8(i64::MIN),
314			Value::Int16(i128::MAX),
315			Value::Uint1(255),
316			Value::Uint2(65535),
317			Value::Uint4(u32::MAX),
318			Value::Uint8(u64::MAX),
319			Value::Uint16(u128::MAX),
320		];
321
322		for case in cases {
323			let mut row = shape.allocate();
324			shape.set_any(&mut row, 0, case);
325			assert_eq!(&shape.get_any(&row, 0), case);
326		}
327	}
328
329	#[test]
330	fn test_any_floats() {
331		let shape = RowShape::testing(&[ValueType::Any]);
332
333		let f4 = Value::Float4(OrderedF32::try_from(3.14f32).unwrap());
334		let mut row = shape.allocate();
335		shape.set_any(&mut row, 0, &f4);
336		assert_eq!(shape.get_any(&row, 0), f4);
337
338		let f8 = Value::Float8(OrderedF64::try_from(E).unwrap());
339		let mut row2 = shape.allocate();
340		shape.set_any(&mut row2, 0, &f8);
341		assert_eq!(shape.get_any(&row2, 0), f8);
342	}
343
344	#[test]
345	fn test_any_temporal() {
346		let shape = RowShape::testing(&[ValueType::Any]);
347
348		let date = Value::Date(Date::new(2025, 7, 4).unwrap());
349		let mut row = shape.allocate();
350		shape.set_any(&mut row, 0, &date);
351		assert_eq!(shape.get_any(&row, 0), date);
352
353		let dt = Value::DateTime(DateTime::new(2025, 1, 1, 12, 0, 0, 0).unwrap());
354		let mut row2 = shape.allocate();
355		shape.set_any(&mut row2, 0, &dt);
356		assert_eq!(shape.get_any(&row2, 0), dt);
357
358		let t = Value::Time(Time::new(14, 30, 45, 123456789).unwrap());
359		let mut row3 = shape.allocate();
360		shape.set_any(&mut row3, 0, &t);
361		assert_eq!(shape.get_any(&row3, 0), t);
362
363		let dur = Value::duration_seconds(3600);
364		let mut row4 = shape.allocate();
365		shape.set_any(&mut row4, 0, &dur);
366		assert_eq!(shape.get_any(&row4, 0), dur);
367	}
368
369	#[test]
370	fn test_any_uuid() {
371		let (_, clock, rng) = test_clock_and_rng();
372		let shape = RowShape::testing(&[ValueType::Any]);
373
374		let u4 = Value::Uuid4(Uuid4::generate());
375		let mut row = shape.allocate();
376		shape.set_any(&mut row, 0, &u4);
377		assert_eq!(shape.get_any(&row, 0), u4);
378
379		let u7 = Value::Uuid7(Uuid7::generate(&clock, &rng));
380		let mut row2 = shape.allocate();
381		shape.set_any(&mut row2, 0, &u7);
382		assert_eq!(shape.get_any(&row2, 0), u7);
383	}
384
385	#[test]
386	fn test_any_utf8() {
387		let shape = RowShape::testing(&[ValueType::Any]);
388		let v = Value::Utf8("hello, world!".to_string());
389		let mut row = shape.allocate();
390		shape.set_any(&mut row, 0, &v);
391		assert_eq!(shape.get_any(&row, 0), v);
392	}
393
394	#[test]
395	fn test_any_blob() {
396		let shape = RowShape::testing(&[ValueType::Any]);
397		let v = Value::Blob(Blob::from_slice(&[0xDE, 0xAD, 0xBE, 0xEF]));
398		let mut row = shape.allocate();
399		shape.set_any(&mut row, 0, &v);
400		assert_eq!(shape.get_any(&row, 0), v);
401	}
402
403	#[test]
404	fn test_any_none_via_set_value() {
405		let shape = RowShape::testing(&[ValueType::Any]);
406		let mut row = shape.allocate();
407		shape.set_value(&mut row, 0, &Value::none());
408		assert!(!row.is_defined(0));
409		assert_eq!(shape.get_value(&row, 0), Value::none());
410	}
411
412	#[test]
413	fn test_any_roundtrip_via_set_get_value() {
414		let shape = RowShape::testing(&[ValueType::Any]);
415
416		let cases: &[Value] = &[
417			Value::Boolean(false),
418			Value::Int4(42),
419			Value::Utf8("test".to_string()),
420			Value::Uint8(1234567890),
421		];
422
423		for inner in cases {
424			let wrapped = Value::any(inner.clone());
425			let mut row = shape.allocate();
426			shape.set_value(&mut row, 0, &wrapped);
427			let retrieved = shape.get_value(&row, 0);
428			assert_eq!(retrieved, wrapped, "roundtrip failed for {:?}", inner);
429		}
430	}
431
432	#[test]
433	fn test_any_multiple_fields() {
434		let shape = RowShape::testing(&[ValueType::Any, ValueType::Int4, ValueType::Any]);
435		let mut row = shape.allocate();
436
437		shape.set_any(&mut row, 0, &Value::Utf8("first".to_string()));
438		shape.set_i32(&mut row, 1, 99);
439		shape.set_any(&mut row, 2, &Value::Boolean(true));
440
441		assert_eq!(shape.get_any(&row, 0), Value::Utf8("first".to_string()));
442		assert_eq!(shape.get_i32(&row, 1), 99);
443		assert_eq!(shape.get_any(&row, 2), Value::Boolean(true));
444	}
445
446	#[test]
447	fn test_update_any() {
448		let shape = RowShape::testing(&[ValueType::Any]);
449		let mut row = shape.allocate();
450
451		shape.set_any(&mut row, 0, &Value::Int4(42));
452		assert_eq!(shape.get_any(&row, 0), Value::Int4(42));
453
454		// Overwrite with a different type
455		shape.set_any(&mut row, 0, &Value::Utf8("hello".to_string()));
456		assert_eq!(shape.get_any(&row, 0), Value::Utf8("hello".to_string()));
457
458		// Overwrite again with boolean
459		shape.set_any(&mut row, 0, &Value::Boolean(true));
460		assert_eq!(shape.get_any(&row, 0), Value::Boolean(true));
461	}
462
463	#[test]
464	fn test_update_any_with_other_dynamic_fields() {
465		let shape = RowShape::testing(&[ValueType::Any, ValueType::Utf8, ValueType::Any]);
466		let mut row = shape.allocate();
467
468		shape.set_any(&mut row, 0, &Value::Int4(1));
469		shape.set_utf8(&mut row, 1, "middle");
470		shape.set_any(&mut row, 2, &Value::Boolean(false));
471
472		// Update first any with a larger value
473		shape.set_any(&mut row, 0, &Value::Utf8("a long string value".to_string()));
474
475		assert_eq!(shape.get_any(&row, 0), Value::Utf8("a long string value".to_string()));
476		assert_eq!(shape.get_utf8(&row, 1), "middle");
477		assert_eq!(shape.get_any(&row, 2), Value::Boolean(false));
478	}
479}