Skip to main content

reifydb_core/encoded/
any.rs

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