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, schema::RowSchema};
22
23/// Encodes an inner value to a `[type_byte][payload]` byte vector.
24///
25/// Panics for unsupported types (Int, Uint, Decimal, DictionaryId, Any, None, List, Type).
26pub fn encode_value(value: &Value) -> Vec<u8> {
27	match value {
28		Value::Boolean(v) => vec![Type::Boolean.to_u8(), *v as u8],
29
30		Value::Uint1(v) => vec![Type::Uint1.to_u8(), *v],
31		Value::Uint2(v) => {
32			let mut b = vec![Type::Uint2.to_u8()];
33			b.extend_from_slice(&v.to_le_bytes());
34			b
35		}
36		Value::Uint4(v) => {
37			let mut b = vec![Type::Uint4.to_u8()];
38			b.extend_from_slice(&v.to_le_bytes());
39			b
40		}
41		Value::Uint8(v) => {
42			let mut b = vec![Type::Uint8.to_u8()];
43			b.extend_from_slice(&v.to_le_bytes());
44			b
45		}
46		Value::Uint16(v) => {
47			let mut b = vec![Type::Uint16.to_u8()];
48			b.extend_from_slice(&v.to_le_bytes());
49			b
50		}
51
52		Value::Int1(v) => vec![Type::Int1.to_u8(), *v as u8],
53		Value::Int2(v) => {
54			let mut b = vec![Type::Int2.to_u8()];
55			b.extend_from_slice(&v.to_le_bytes());
56			b
57		}
58		Value::Int4(v) => {
59			let mut b = vec![Type::Int4.to_u8()];
60			b.extend_from_slice(&v.to_le_bytes());
61			b
62		}
63		Value::Int8(v) => {
64			let mut b = vec![Type::Int8.to_u8()];
65			b.extend_from_slice(&v.to_le_bytes());
66			b
67		}
68		Value::Int16(v) => {
69			let mut b = vec![Type::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![Type::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![Type::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![Type::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![Type::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![Type::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![Type::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![Type::Uuid4.to_u8()];
110			b.extend_from_slice(v.as_bytes());
111			b
112		}
113		Value::Uuid7(v) => {
114			let mut b = vec![Type::Uuid7.to_u8()];
115			b.extend_from_slice(v.as_bytes());
116			b
117		}
118		Value::IdentityId(v) => {
119			let mut b = vec![Type::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![Type::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![Type::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
154/// Decodes bytes produced by `encode_value` back into a `Value`.
155pub fn decode_value(bytes: &[u8]) -> Value {
156	let type_byte = bytes[0];
157	let p = &bytes[1..];
158	let ty = Type::from_u8(type_byte);
159
160	match ty {
161		Type::Boolean => Value::Boolean(p[0] != 0),
162
163		Type::Uint1 => Value::Uint1(p[0]),
164		Type::Uint2 => Value::Uint2(u16::from_le_bytes([p[0], p[1]])),
165		Type::Uint4 => Value::Uint4(u32::from_le_bytes([p[0], p[1], p[2], p[3]])),
166		Type::Uint8 => Value::Uint8(u64::from_le_bytes(p[..8].try_into().unwrap())),
167		Type::Uint16 => Value::Uint16(u128::from_le_bytes(p[..16].try_into().unwrap())),
168
169		Type::Int1 => Value::Int1(p[0] as i8),
170		Type::Int2 => Value::Int2(i16::from_le_bytes([p[0], p[1]])),
171		Type::Int4 => Value::Int4(i32::from_le_bytes([p[0], p[1], p[2], p[3]])),
172		Type::Int8 => Value::Int8(i64::from_le_bytes(p[..8].try_into().unwrap())),
173		Type::Int16 => Value::Int16(i128::from_le_bytes(p[..16].try_into().unwrap())),
174
175		Type::Float4 => {
176			let bits = u32::from_le_bytes([p[0], p[1], p[2], p[3]]);
177			Value::Float4(OrderedF32::try_from(f32::from_bits(bits)).unwrap())
178		}
179		Type::Float8 => {
180			let bits = u64::from_le_bytes(p[..8].try_into().unwrap());
181			Value::Float8(OrderedF64::try_from(f64::from_bits(bits)).unwrap())
182		}
183
184		Type::Date => {
185			let days = i32::from_le_bytes([p[0], p[1], p[2], p[3]]);
186			Value::Date(Date::from_days_since_epoch(days).unwrap())
187		}
188		Type::DateTime => {
189			let nanos = u64::from_le_bytes(p[..8].try_into().unwrap());
190			Value::DateTime(DateTime::from_nanos(nanos))
191		}
192		Type::Time => {
193			let nanos = u64::from_le_bytes(p[..8].try_into().unwrap());
194			Value::Time(Time::from_nanos_since_midnight(nanos).unwrap())
195		}
196		Type::Duration => {
197			let months = i32::from_le_bytes([p[0], p[1], p[2], p[3]]);
198			let days = i32::from_le_bytes([p[4], p[5], p[6], p[7]]);
199			let nanos = i64::from_le_bytes(p[8..16].try_into().unwrap());
200			Value::Duration(Duration::new(months, days, nanos).unwrap())
201		}
202
203		Type::Uuid4 => {
204			let b: [u8; 16] = p[..16].try_into().unwrap();
205			Value::Uuid4(Uuid4::from(Uuid::from_bytes(b)))
206		}
207		Type::Uuid7 => {
208			let b: [u8; 16] = p[..16].try_into().unwrap();
209			Value::Uuid7(Uuid7::from(Uuid::from_bytes(b)))
210		}
211		Type::IdentityId => {
212			let b: [u8; 16] = p[..16].try_into().unwrap();
213			Value::IdentityId(IdentityId::from(Uuid7::from(Uuid::from_bytes(b))))
214		}
215
216		Type::Utf8 => {
217			let len = u32::from_le_bytes([p[0], p[1], p[2], p[3]]) as usize;
218			let s = str::from_utf8(&p[4..4 + len]).unwrap();
219			Value::Utf8(s.to_string())
220		}
221		Type::Blob => {
222			let len = u32::from_le_bytes([p[0], p[1], p[2], p[3]]) as usize;
223			Value::Blob(Blob::from_slice(&p[4..4 + len]))
224		}
225
226		_ => unreachable!("unsupported type byte {} in Any decoding", type_byte),
227	}
228}
229
230impl RowSchema {
231	pub fn set_any(&self, row: &mut EncodedRow, index: usize, value: &Value) {
232		debug_assert_eq!(*self.fields()[index].constraint.get_type().inner_type(), Type::Any);
233		let encoded = encode_value(value);
234		self.replace_dynamic_data(row, index, &encoded);
235	}
236
237	pub fn get_any(&self, row: &EncodedRow, index: usize) -> Value {
238		let field = &self.fields()[index];
239		debug_assert_eq!(*field.constraint.get_type().inner_type(), Type::Any);
240
241		let ref_slice = &row.as_slice()[field.offset as usize..field.offset as usize + 8];
242		let offset = u32::from_le_bytes([ref_slice[0], ref_slice[1], ref_slice[2], ref_slice[3]]) as usize;
243		let length = u32::from_le_bytes([ref_slice[4], ref_slice[5], ref_slice[6], ref_slice[7]]) as usize;
244
245		let dynamic_start = self.dynamic_section_start();
246		let data_start = dynamic_start + offset;
247		let data_slice = &row.as_slice()[data_start..data_start + length];
248
249		decode_value(data_slice)
250	}
251}
252
253#[cfg(test)]
254pub mod tests {
255	use std::f64::consts::E;
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::schema::RowSchema;
271
272	#[test]
273	fn test_any_boolean() {
274		let schema = RowSchema::testing(&[Type::Any]);
275		let mut row = schema.allocate();
276		schema.set_any(&mut row, 0, &Value::Boolean(true));
277		assert_eq!(schema.get_any(&row, 0), Value::Boolean(true));
278	}
279
280	#[test]
281	fn test_any_integers() {
282		let schema = RowSchema::testing(&[Type::Any]);
283
284		let cases: &[Value] = &[
285			Value::Int1(-42),
286			Value::Int2(-1000),
287			Value::Int4(-100000),
288			Value::Int8(i64::MIN),
289			Value::Int16(i128::MAX),
290			Value::Uint1(255),
291			Value::Uint2(65535),
292			Value::Uint4(u32::MAX),
293			Value::Uint8(u64::MAX),
294			Value::Uint16(u128::MAX),
295		];
296
297		for case in cases {
298			let mut row = schema.allocate();
299			schema.set_any(&mut row, 0, case);
300			assert_eq!(&schema.get_any(&row, 0), case);
301		}
302	}
303
304	#[test]
305	fn test_any_floats() {
306		let schema = RowSchema::testing(&[Type::Any]);
307
308		let f4 = Value::Float4(OrderedF32::try_from(3.14f32).unwrap());
309		let mut row = schema.allocate();
310		schema.set_any(&mut row, 0, &f4);
311		assert_eq!(schema.get_any(&row, 0), f4);
312
313		let f8 = Value::Float8(OrderedF64::try_from(E).unwrap());
314		let mut row2 = schema.allocate();
315		schema.set_any(&mut row2, 0, &f8);
316		assert_eq!(schema.get_any(&row2, 0), f8);
317	}
318
319	#[test]
320	fn test_any_temporal() {
321		let schema = RowSchema::testing(&[Type::Any]);
322
323		let date = Value::Date(Date::new(2025, 7, 4).unwrap());
324		let mut row = schema.allocate();
325		schema.set_any(&mut row, 0, &date);
326		assert_eq!(schema.get_any(&row, 0), date);
327
328		let dt = Value::DateTime(DateTime::new(2025, 1, 1, 12, 0, 0, 0).unwrap());
329		let mut row2 = schema.allocate();
330		schema.set_any(&mut row2, 0, &dt);
331		assert_eq!(schema.get_any(&row2, 0), dt);
332
333		let t = Value::Time(Time::new(14, 30, 45, 123456789).unwrap());
334		let mut row3 = schema.allocate();
335		schema.set_any(&mut row3, 0, &t);
336		assert_eq!(schema.get_any(&row3, 0), t);
337
338		let dur = Value::Duration(Duration::from_seconds(3600).unwrap());
339		let mut row4 = schema.allocate();
340		schema.set_any(&mut row4, 0, &dur);
341		assert_eq!(schema.get_any(&row4, 0), dur);
342	}
343
344	#[test]
345	fn test_any_uuid() {
346		let schema = RowSchema::testing(&[Type::Any]);
347
348		let u4 = Value::Uuid4(Uuid4::generate());
349		let mut row = schema.allocate();
350		schema.set_any(&mut row, 0, &u4);
351		assert_eq!(schema.get_any(&row, 0), u4);
352
353		let u7 = Value::Uuid7(Uuid7::generate());
354		let mut row2 = schema.allocate();
355		schema.set_any(&mut row2, 0, &u7);
356		assert_eq!(schema.get_any(&row2, 0), u7);
357	}
358
359	#[test]
360	fn test_any_utf8() {
361		let schema = RowSchema::testing(&[Type::Any]);
362		let v = Value::Utf8("hello, world!".to_string());
363		let mut row = schema.allocate();
364		schema.set_any(&mut row, 0, &v);
365		assert_eq!(schema.get_any(&row, 0), v);
366	}
367
368	#[test]
369	fn test_any_blob() {
370		let schema = RowSchema::testing(&[Type::Any]);
371		let v = Value::Blob(Blob::from_slice(&[0xDE, 0xAD, 0xBE, 0xEF]));
372		let mut row = schema.allocate();
373		schema.set_any(&mut row, 0, &v);
374		assert_eq!(schema.get_any(&row, 0), v);
375	}
376
377	#[test]
378	fn test_any_none_via_set_value() {
379		let schema = RowSchema::testing(&[Type::Any]);
380		let mut row = schema.allocate();
381		schema.set_value(&mut row, 0, &Value::none());
382		assert!(!row.is_defined(0));
383		assert_eq!(schema.get_value(&row, 0), Value::none());
384	}
385
386	#[test]
387	fn test_any_roundtrip_via_set_get_value() {
388		let schema = RowSchema::testing(&[Type::Any]);
389
390		let cases: &[Value] = &[
391			Value::Boolean(false),
392			Value::Int4(42),
393			Value::Utf8("test".to_string()),
394			Value::Uint8(1234567890),
395		];
396
397		for inner in cases {
398			let wrapped = Value::any(inner.clone());
399			let mut row = schema.allocate();
400			schema.set_value(&mut row, 0, &wrapped);
401			let retrieved = schema.get_value(&row, 0);
402			assert_eq!(retrieved, wrapped, "roundtrip failed for {:?}", inner);
403		}
404	}
405
406	#[test]
407	fn test_any_multiple_fields() {
408		let schema = RowSchema::testing(&[Type::Any, Type::Int4, Type::Any]);
409		let mut row = schema.allocate();
410
411		schema.set_any(&mut row, 0, &Value::Utf8("first".to_string()));
412		schema.set_i32(&mut row, 1, 99);
413		schema.set_any(&mut row, 2, &Value::Boolean(true));
414
415		assert_eq!(schema.get_any(&row, 0), Value::Utf8("first".to_string()));
416		assert_eq!(schema.get_i32(&row, 1), 99);
417		assert_eq!(schema.get_any(&row, 2), Value::Boolean(true));
418	}
419
420	#[test]
421	fn test_update_any() {
422		let schema = RowSchema::testing(&[Type::Any]);
423		let mut row = schema.allocate();
424
425		schema.set_any(&mut row, 0, &Value::Int4(42));
426		assert_eq!(schema.get_any(&row, 0), Value::Int4(42));
427
428		// Overwrite with a different type
429		schema.set_any(&mut row, 0, &Value::Utf8("hello".to_string()));
430		assert_eq!(schema.get_any(&row, 0), Value::Utf8("hello".to_string()));
431
432		// Overwrite again with boolean
433		schema.set_any(&mut row, 0, &Value::Boolean(true));
434		assert_eq!(schema.get_any(&row, 0), Value::Boolean(true));
435	}
436
437	#[test]
438	fn test_update_any_with_other_dynamic_fields() {
439		let schema = RowSchema::testing(&[Type::Any, Type::Utf8, Type::Any]);
440		let mut row = schema.allocate();
441
442		schema.set_any(&mut row, 0, &Value::Int4(1));
443		schema.set_utf8(&mut row, 1, "middle");
444		schema.set_any(&mut row, 2, &Value::Boolean(false));
445
446		// Update first any with a larger value
447		schema.set_any(&mut row, 0, &Value::Utf8("a long string value".to_string()));
448
449		assert_eq!(schema.get_any(&row, 0), Value::Utf8("a long string value".to_string()));
450		assert_eq!(schema.get_utf8(&row, 1), "middle");
451		assert_eq!(schema.get_any(&row, 2), Value::Boolean(false));
452	}
453}