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::{encoded::EncodedValues, schema::Schema};
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			let (seconds, nanos) = v.to_parts();
93			b.extend_from_slice(&seconds.to_le_bytes());
94			b.extend_from_slice(&nanos.to_le_bytes());
95			b
96		}
97		Value::Time(v) => {
98			let mut b = vec![Type::Time.to_u8()];
99			b.extend_from_slice(&v.to_nanos_since_midnight().to_le_bytes());
100			b
101		}
102		Value::Duration(v) => {
103			let mut b = vec![Type::Duration.to_u8()];
104			b.extend_from_slice(&v.get_months().to_le_bytes());
105			b.extend_from_slice(&v.get_days().to_le_bytes());
106			b.extend_from_slice(&v.get_nanos().to_le_bytes());
107			b
108		}
109
110		Value::Uuid4(v) => {
111			let mut b = vec![Type::Uuid4.to_u8()];
112			b.extend_from_slice(v.as_bytes());
113			b
114		}
115		Value::Uuid7(v) => {
116			let mut b = vec![Type::Uuid7.to_u8()];
117			b.extend_from_slice(v.as_bytes());
118			b
119		}
120		Value::IdentityId(v) => {
121			let mut b = vec![Type::IdentityId.to_u8()];
122			b.extend_from_slice(v.as_bytes());
123			b
124		}
125
126		Value::Utf8(v) => {
127			let s = v.as_bytes();
128			let mut b = vec![Type::Utf8.to_u8()];
129			b.extend_from_slice(&(s.len() as u32).to_le_bytes());
130			b.extend_from_slice(s);
131			b
132		}
133		Value::Blob(v) => {
134			let s = v.as_bytes();
135			let mut b = vec![Type::Blob.to_u8()];
136			b.extend_from_slice(&(s.len() as u32).to_le_bytes());
137			b.extend_from_slice(s);
138			b
139		}
140
141		Value::Int(_)
142		| Value::Uint(_)
143		| Value::Decimal(_)
144		| Value::DictionaryId(_)
145		| Value::Any(_)
146		| Value::None {
147			..
148		}
149		| Value::Type(_)
150		| Value::List(_)
151		| Value::Record(_)
152		| Value::Tuple(_) => unreachable!("unsupported value type in Any encoding: {:?}", value),
153	}
154}
155
156/// Decodes bytes produced by `encode_value` back into a `Value`.
157pub fn decode_value(bytes: &[u8]) -> Value {
158	let type_byte = bytes[0];
159	let p = &bytes[1..];
160	let ty = Type::from_u8(type_byte);
161
162	match ty {
163		Type::Boolean => Value::Boolean(p[0] != 0),
164
165		Type::Uint1 => Value::Uint1(p[0]),
166		Type::Uint2 => Value::Uint2(u16::from_le_bytes([p[0], p[1]])),
167		Type::Uint4 => Value::Uint4(u32::from_le_bytes([p[0], p[1], p[2], p[3]])),
168		Type::Uint8 => Value::Uint8(u64::from_le_bytes(p[..8].try_into().unwrap())),
169		Type::Uint16 => Value::Uint16(u128::from_le_bytes(p[..16].try_into().unwrap())),
170
171		Type::Int1 => Value::Int1(p[0] as i8),
172		Type::Int2 => Value::Int2(i16::from_le_bytes([p[0], p[1]])),
173		Type::Int4 => Value::Int4(i32::from_le_bytes([p[0], p[1], p[2], p[3]])),
174		Type::Int8 => Value::Int8(i64::from_le_bytes(p[..8].try_into().unwrap())),
175		Type::Int16 => Value::Int16(i128::from_le_bytes(p[..16].try_into().unwrap())),
176
177		Type::Float4 => {
178			let bits = u32::from_le_bytes([p[0], p[1], p[2], p[3]]);
179			Value::Float4(OrderedF32::try_from(f32::from_bits(bits)).unwrap())
180		}
181		Type::Float8 => {
182			let bits = u64::from_le_bytes(p[..8].try_into().unwrap());
183			Value::Float8(OrderedF64::try_from(f64::from_bits(bits)).unwrap())
184		}
185
186		Type::Date => {
187			let days = i32::from_le_bytes([p[0], p[1], p[2], p[3]]);
188			Value::Date(Date::from_days_since_epoch(days).unwrap())
189		}
190		Type::DateTime => {
191			let seconds = i64::from_le_bytes(p[..8].try_into().unwrap());
192			let nanos = u32::from_le_bytes([p[8], p[9], p[10], p[11]]);
193			Value::DateTime(DateTime::from_parts(seconds, nanos).unwrap())
194		}
195		Type::Time => {
196			let nanos = u64::from_le_bytes(p[..8].try_into().unwrap());
197			Value::Time(Time::from_nanos_since_midnight(nanos).unwrap())
198		}
199		Type::Duration => {
200			let months = i32::from_le_bytes([p[0], p[1], p[2], p[3]]);
201			let days = i32::from_le_bytes([p[4], p[5], p[6], p[7]]);
202			let nanos = i64::from_le_bytes(p[8..16].try_into().unwrap());
203			Value::Duration(Duration::new(months, days, nanos))
204		}
205
206		Type::Uuid4 => {
207			let b: [u8; 16] = p[..16].try_into().unwrap();
208			Value::Uuid4(Uuid4::from(Uuid::from_bytes(b)))
209		}
210		Type::Uuid7 => {
211			let b: [u8; 16] = p[..16].try_into().unwrap();
212			Value::Uuid7(Uuid7::from(Uuid::from_bytes(b)))
213		}
214		Type::IdentityId => {
215			let b: [u8; 16] = p[..16].try_into().unwrap();
216			Value::IdentityId(IdentityId::from(Uuid7::from(Uuid::from_bytes(b))))
217		}
218
219		Type::Utf8 => {
220			let len = u32::from_le_bytes([p[0], p[1], p[2], p[3]]) as usize;
221			let s = str::from_utf8(&p[4..4 + len]).unwrap();
222			Value::Utf8(s.to_string())
223		}
224		Type::Blob => {
225			let len = u32::from_le_bytes([p[0], p[1], p[2], p[3]]) as usize;
226			Value::Blob(Blob::from_slice(&p[4..4 + len]))
227		}
228
229		_ => unreachable!("unsupported type byte {} in Any decoding", type_byte),
230	}
231}
232
233impl Schema {
234	pub fn set_any(&self, row: &mut EncodedValues, index: usize, value: &Value) {
235		let field = &self.fields()[index];
236		debug_assert_eq!(*field.constraint.get_type().inner_type(), Type::Any);
237		debug_assert!(!row.is_defined(index), "Any field {} already set", index);
238
239		let encoded = encode_value(value);
240		let encoded_len = encoded.len();
241
242		let dynamic_offset = self.dynamic_section_size(row);
243
244		row.0.extend_from_slice(&encoded);
245
246		let ref_slice = &mut row.0.make_mut()[field.offset as usize..field.offset as usize + 8];
247		ref_slice[0..4].copy_from_slice(&(dynamic_offset as u32).to_le_bytes());
248		ref_slice[4..8].copy_from_slice(&(encoded_len as u32).to_le_bytes());
249
250		row.set_valid(index, true);
251	}
252
253	pub fn get_any(&self, row: &EncodedValues, index: usize) -> Value {
254		let field = &self.fields()[index];
255		debug_assert_eq!(*field.constraint.get_type().inner_type(), Type::Any);
256
257		let ref_slice = &row.as_slice()[field.offset as usize..field.offset as usize + 8];
258		let offset = u32::from_le_bytes([ref_slice[0], ref_slice[1], ref_slice[2], ref_slice[3]]) as usize;
259		let length = u32::from_le_bytes([ref_slice[4], ref_slice[5], ref_slice[6], ref_slice[7]]) as usize;
260
261		let dynamic_start = self.dynamic_section_start();
262		let data_start = dynamic_start + offset;
263		let data_slice = &row.as_slice()[data_start..data_start + length];
264
265		decode_value(data_slice)
266	}
267}
268
269#[cfg(test)]
270pub mod tests {
271	use std::f64::consts::E;
272
273	use reifydb_type::value::{
274		Value,
275		blob::Blob,
276		date::Date,
277		datetime::DateTime,
278		duration::Duration,
279		ordered_f32::OrderedF32,
280		ordered_f64::OrderedF64,
281		time::Time,
282		r#type::Type,
283		uuid::{Uuid4, Uuid7},
284	};
285
286	use crate::encoded::schema::Schema;
287
288	#[test]
289	fn test_any_boolean() {
290		let schema = Schema::testing(&[Type::Any]);
291		let mut row = schema.allocate();
292		schema.set_any(&mut row, 0, &Value::Boolean(true));
293		assert_eq!(schema.get_any(&row, 0), Value::Boolean(true));
294	}
295
296	#[test]
297	fn test_any_integers() {
298		let schema = Schema::testing(&[Type::Any]);
299
300		let cases: &[Value] = &[
301			Value::Int1(-42),
302			Value::Int2(-1000),
303			Value::Int4(-100000),
304			Value::Int8(i64::MIN),
305			Value::Int16(i128::MAX),
306			Value::Uint1(255),
307			Value::Uint2(65535),
308			Value::Uint4(u32::MAX),
309			Value::Uint8(u64::MAX),
310			Value::Uint16(u128::MAX),
311		];
312
313		for case in cases {
314			let mut row = schema.allocate();
315			schema.set_any(&mut row, 0, case);
316			assert_eq!(&schema.get_any(&row, 0), case);
317		}
318	}
319
320	#[test]
321	fn test_any_floats() {
322		let schema = Schema::testing(&[Type::Any]);
323
324		let f4 = Value::Float4(OrderedF32::try_from(3.14f32).unwrap());
325		let mut row = schema.allocate();
326		schema.set_any(&mut row, 0, &f4);
327		assert_eq!(schema.get_any(&row, 0), f4);
328
329		let f8 = Value::Float8(OrderedF64::try_from(E).unwrap());
330		let mut row2 = schema.allocate();
331		schema.set_any(&mut row2, 0, &f8);
332		assert_eq!(schema.get_any(&row2, 0), f8);
333	}
334
335	#[test]
336	fn test_any_temporal() {
337		let schema = Schema::testing(&[Type::Any]);
338
339		let date = Value::Date(Date::new(2025, 7, 4).unwrap());
340		let mut row = schema.allocate();
341		schema.set_any(&mut row, 0, &date);
342		assert_eq!(schema.get_any(&row, 0), date);
343
344		let dt = Value::DateTime(DateTime::new(2025, 1, 1, 12, 0, 0, 0).unwrap());
345		let mut row2 = schema.allocate();
346		schema.set_any(&mut row2, 0, &dt);
347		assert_eq!(schema.get_any(&row2, 0), dt);
348
349		let t = Value::Time(Time::new(14, 30, 45, 123456789).unwrap());
350		let mut row3 = schema.allocate();
351		schema.set_any(&mut row3, 0, &t);
352		assert_eq!(schema.get_any(&row3, 0), t);
353
354		let dur = Value::Duration(Duration::from_seconds(3600));
355		let mut row4 = schema.allocate();
356		schema.set_any(&mut row4, 0, &dur);
357		assert_eq!(schema.get_any(&row4, 0), dur);
358	}
359
360	#[test]
361	fn test_any_uuid() {
362		let schema = Schema::testing(&[Type::Any]);
363
364		let u4 = Value::Uuid4(Uuid4::generate());
365		let mut row = schema.allocate();
366		schema.set_any(&mut row, 0, &u4);
367		assert_eq!(schema.get_any(&row, 0), u4);
368
369		let u7 = Value::Uuid7(Uuid7::generate());
370		let mut row2 = schema.allocate();
371		schema.set_any(&mut row2, 0, &u7);
372		assert_eq!(schema.get_any(&row2, 0), u7);
373	}
374
375	#[test]
376	fn test_any_utf8() {
377		let schema = Schema::testing(&[Type::Any]);
378		let v = Value::Utf8("hello, world!".to_string());
379		let mut row = schema.allocate();
380		schema.set_any(&mut row, 0, &v);
381		assert_eq!(schema.get_any(&row, 0), v);
382	}
383
384	#[test]
385	fn test_any_blob() {
386		let schema = Schema::testing(&[Type::Any]);
387		let v = Value::Blob(Blob::from_slice(&[0xDE, 0xAD, 0xBE, 0xEF]));
388		let mut row = schema.allocate();
389		schema.set_any(&mut row, 0, &v);
390		assert_eq!(schema.get_any(&row, 0), v);
391	}
392
393	#[test]
394	fn test_any_none_via_set_value() {
395		let schema = Schema::testing(&[Type::Any]);
396		let mut row = schema.allocate();
397		schema.set_value(&mut row, 0, &Value::none());
398		assert!(!row.is_defined(0));
399		assert_eq!(schema.get_value(&row, 0), Value::none());
400	}
401
402	#[test]
403	fn test_any_roundtrip_via_set_get_value() {
404		let schema = Schema::testing(&[Type::Any]);
405
406		let cases: &[Value] = &[
407			Value::Boolean(false),
408			Value::Int4(42),
409			Value::Utf8("test".to_string()),
410			Value::Uint8(1234567890),
411		];
412
413		for inner in cases {
414			let wrapped = Value::any(inner.clone());
415			let mut row = schema.allocate();
416			schema.set_value(&mut row, 0, &wrapped);
417			let retrieved = schema.get_value(&row, 0);
418			assert_eq!(retrieved, wrapped, "roundtrip failed for {:?}", inner);
419		}
420	}
421
422	#[test]
423	fn test_any_multiple_fields() {
424		let schema = Schema::testing(&[Type::Any, Type::Int4, Type::Any]);
425		let mut row = schema.allocate();
426
427		schema.set_any(&mut row, 0, &Value::Utf8("first".to_string()));
428		schema.set_i32(&mut row, 1, 99);
429		schema.set_any(&mut row, 2, &Value::Boolean(true));
430
431		assert_eq!(schema.get_any(&row, 0), Value::Utf8("first".to_string()));
432		assert_eq!(schema.get_i32(&row, 1), 99);
433		assert_eq!(schema.get_any(&row, 2), Value::Boolean(true));
434	}
435}