Skip to main content

reifydb_codec/value/
mod.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use std::{collections::HashMap, str::from_utf8};
5
6use bigdecimal::BigDecimal;
7use num_bigint::BigInt;
8use reifydb_value::{
9	params::Params,
10	value::{
11		Value,
12		blob::Blob,
13		date::Date,
14		datetime::DateTime,
15		decimal::Decimal,
16		dictionary::DictionaryEntryId,
17		duration::Duration,
18		identity::IdentityId,
19		int::Int,
20		ordered_f32::OrderedF32,
21		ordered_f64::OrderedF64,
22		time::Time,
23		uint::Uint,
24		uuid::{Uuid4, Uuid7},
25	},
26};
27use uuid::Uuid;
28
29use crate::{
30	error::{DecodeError, EncodeError},
31	reader::Reader,
32	tag::ValueKind,
33	typeinfo::{decode_value_type, encode_value_type},
34};
35
36pub fn encode_value(value: &Value) -> Result<Vec<u8>, EncodeError> {
37	let mut buf = Vec::new();
38	encode_value_into(value, &mut buf)?;
39	Ok(buf)
40}
41
42pub fn encode_value_into(value: &Value, buf: &mut Vec<u8>) -> Result<(), EncodeError> {
43	buf.push(ValueKind::of_value(value).byte());
44	match value {
45		Value::None {
46			inner,
47		} => encode_value_type(inner, buf)?,
48		Value::Boolean(b) => buf.push(*b as u8),
49		Value::Float4(f) => buf.extend_from_slice(&f.to_le_bytes()),
50		Value::Float8(f) => buf.extend_from_slice(&f.to_le_bytes()),
51		Value::Int1(v) => buf.extend_from_slice(&v.to_le_bytes()),
52		Value::Int2(v) => buf.extend_from_slice(&v.to_le_bytes()),
53		Value::Int4(v) => buf.extend_from_slice(&v.to_le_bytes()),
54		Value::Int8(v) => buf.extend_from_slice(&v.to_le_bytes()),
55		Value::Int16(v) => buf.extend_from_slice(&v.to_le_bytes()),
56		Value::Utf8(s) => encode_len_prefixed(s.as_bytes(), buf),
57		Value::Uint1(v) => buf.extend_from_slice(&v.to_le_bytes()),
58		Value::Uint2(v) => buf.extend_from_slice(&v.to_le_bytes()),
59		Value::Uint4(v) => buf.extend_from_slice(&v.to_le_bytes()),
60		Value::Uint8(v) => buf.extend_from_slice(&v.to_le_bytes()),
61		Value::Uint16(v) => buf.extend_from_slice(&v.to_le_bytes()),
62		Value::Date(d) => buf.extend_from_slice(&d.to_days_since_epoch().to_le_bytes()),
63		Value::DateTime(dt) => buf.extend_from_slice(&dt.to_nanos().to_le_bytes()),
64		Value::Time(t) => buf.extend_from_slice(&t.to_nanos_since_midnight().to_le_bytes()),
65		Value::Duration(d) => {
66			buf.extend_from_slice(&d.get_months().to_le_bytes());
67			buf.extend_from_slice(&d.get_days().to_le_bytes());
68			buf.extend_from_slice(&d.get_nanos().to_le_bytes());
69		}
70		Value::IdentityId(id) => buf.extend_from_slice(id.0.0.as_bytes()),
71		Value::Uuid4(u) => buf.extend_from_slice(u.0.as_bytes()),
72		Value::Uuid7(u) => buf.extend_from_slice(u.0.as_bytes()),
73		Value::Blob(b) => encode_len_prefixed(b.as_bytes(), buf),
74		Value::Int(v) => encode_len_prefixed(&v.0.to_signed_bytes_le(), buf),
75		Value::Uint(v) => encode_len_prefixed(&v.0.to_signed_bytes_le(), buf),
76		Value::Decimal(v) => encode_len_prefixed(v.to_string().as_bytes(), buf),
77		Value::Any(inner) => encode_value_into(inner, buf)?,
78		Value::DictionaryId(id) => match id {
79			DictionaryEntryId::U1(v) => {
80				buf.push(1);
81				buf.extend_from_slice(&v.to_le_bytes());
82			}
83			DictionaryEntryId::U2(v) => {
84				buf.push(2);
85				buf.extend_from_slice(&v.to_le_bytes());
86			}
87			DictionaryEntryId::U4(v) => {
88				buf.push(4);
89				buf.extend_from_slice(&v.to_le_bytes());
90			}
91			DictionaryEntryId::U8(v) => {
92				buf.push(8);
93				buf.extend_from_slice(&v.to_le_bytes());
94			}
95			DictionaryEntryId::U16(v) => {
96				buf.push(16);
97				buf.extend_from_slice(&v.to_le_bytes());
98			}
99		},
100		Value::Type(ty) => encode_value_type(ty, buf)?,
101		Value::List(items) | Value::Tuple(items) => {
102			buf.extend_from_slice(&(items.len() as u32).to_le_bytes());
103			for item in items {
104				encode_value_into(item, buf)?;
105			}
106		}
107		Value::Record(fields) => {
108			buf.extend_from_slice(&(fields.len() as u32).to_le_bytes());
109			for (key, field_value) in fields {
110				encode_len_prefixed(key.as_bytes(), buf);
111				encode_value_into(field_value, buf)?;
112			}
113		}
114	}
115	Ok(())
116}
117
118fn encode_len_prefixed(bytes: &[u8], buf: &mut Vec<u8>) {
119	buf.extend_from_slice(&(bytes.len() as u32).to_le_bytes());
120	buf.extend_from_slice(bytes);
121}
122
123pub fn decode_value(bytes: &[u8]) -> Result<Value, DecodeError> {
124	let mut r = Reader::new(bytes);
125	let value = decode_value_from(&mut r)?;
126	if !r.is_empty() {
127		return Err(DecodeError::TrailingBytes(r.remaining()));
128	}
129	Ok(value)
130}
131
132pub fn decode_value_from(r: &mut Reader) -> Result<Value, DecodeError> {
133	let byte = r.u8()?;
134	let kind = ValueKind::from_byte(byte).ok_or(DecodeError::UnknownTypeCode(byte))?;
135	match kind {
136		ValueKind::None => Ok(Value::None {
137			inner: decode_value_type(r)?,
138		}),
139		ValueKind::Boolean => Ok(Value::Boolean(r.u8()? != 0)),
140		ValueKind::Float4 => OrderedF32::try_from(r.f32()?)
141			.map(Value::Float4)
142			.map_err(|e| DecodeError::InvalidData(format!("invalid float4: {e}"))),
143		ValueKind::Float8 => OrderedF64::try_from(r.f64()?)
144			.map(Value::Float8)
145			.map_err(|e| DecodeError::InvalidData(format!("invalid float8: {e}"))),
146		ValueKind::Int1 => Ok(Value::Int1(r.i8()?)),
147		ValueKind::Int2 => Ok(Value::Int2(r.i16()?)),
148		ValueKind::Int4 => Ok(Value::Int4(r.i32()?)),
149		ValueKind::Int8 => Ok(Value::Int8(r.i64()?)),
150		ValueKind::Int16 => Ok(Value::Int16(r.i128()?)),
151		ValueKind::Utf8 => Ok(Value::Utf8(decode_len_prefixed_str(r)?.to_string())),
152		ValueKind::Uint1 => Ok(Value::Uint1(r.u8()?)),
153		ValueKind::Uint2 => Ok(Value::Uint2(r.u16()?)),
154		ValueKind::Uint4 => Ok(Value::Uint4(r.u32()?)),
155		ValueKind::Uint8 => Ok(Value::Uint8(r.u64()?)),
156		ValueKind::Uint16 => Ok(Value::Uint16(r.u128()?)),
157		ValueKind::Date => {
158			let days = r.i32()?;
159			Date::from_days_since_epoch(days)
160				.map(Value::Date)
161				.ok_or_else(|| DecodeError::InvalidData(format!("invalid date: {days}")))
162		}
163		ValueKind::DateTime => Ok(Value::DateTime(DateTime::from_nanos(r.u64()?))),
164		ValueKind::Time => {
165			let nanos = r.u64()?;
166			Time::from_nanos_since_midnight(nanos)
167				.map(Value::Time)
168				.ok_or_else(|| DecodeError::InvalidData(format!("invalid time: {nanos}")))
169		}
170		ValueKind::Duration => {
171			let months = r.i32()?;
172			let days = r.i32()?;
173			let nanos = r.i64()?;
174			Duration::new(months, days, nanos)
175				.map(Value::Duration)
176				.map_err(|e| DecodeError::InvalidData(format!("invalid duration: {e}")))
177		}
178		ValueKind::IdentityId => Ok(Value::IdentityId(IdentityId::new(Uuid7(decode_uuid(r)?)))),
179		ValueKind::Uuid4 => Ok(Value::Uuid4(Uuid4(decode_uuid(r)?))),
180		ValueKind::Uuid7 => Ok(Value::Uuid7(Uuid7(decode_uuid(r)?))),
181		ValueKind::Blob => Ok(Value::Blob(Blob::new(decode_len_prefixed_bytes(r)?.to_vec()))),
182		ValueKind::Int => Ok(Value::Int(Int(BigInt::from_signed_bytes_le(decode_len_prefixed_bytes(r)?)))),
183		ValueKind::Uint => Ok(Value::Uint(Uint(BigInt::from_signed_bytes_le(decode_len_prefixed_bytes(r)?)))),
184		ValueKind::Decimal => {
185			let s = decode_len_prefixed_str(r)?;
186			let dec: BigDecimal =
187				s.parse().map_err(|e| DecodeError::InvalidData(format!("invalid decimal: {e}")))?;
188			Ok(Value::Decimal(Decimal::new(dec)))
189		}
190		ValueKind::Any => Ok(Value::Any(Box::new(decode_value_from(r)?))),
191		ValueKind::DictionaryId => {
192			let width = r.u8()?;
193			let id = match width {
194				1 => DictionaryEntryId::U1(r.u8()?),
195				2 => DictionaryEntryId::U2(r.u16()?),
196				4 => DictionaryEntryId::U4(r.u32()?),
197				8 => DictionaryEntryId::U8(r.u64()?),
198				16 => DictionaryEntryId::U16(r.u128()?),
199				other => {
200					return Err(DecodeError::InvalidData(format!(
201						"invalid dictionary id width: {other}"
202					)));
203				}
204			};
205			Ok(Value::DictionaryId(id))
206		}
207		ValueKind::Type => Ok(Value::Type(decode_value_type(r)?)),
208		ValueKind::List => Ok(Value::List(decode_value_sequence(r)?)),
209		ValueKind::Tuple => Ok(Value::Tuple(decode_value_sequence(r)?)),
210		ValueKind::Record => {
211			let count = r.u32()?;
212			let mut fields = Vec::with_capacity((count as usize).min(4096));
213			for _ in 0..count {
214				let key = decode_len_prefixed_str(r)?.to_string();
215				fields.push((key, decode_value_from(r)?));
216			}
217			Ok(Value::Record(fields))
218		}
219	}
220}
221
222fn decode_value_sequence(r: &mut Reader) -> Result<Vec<Value>, DecodeError> {
223	let count = r.u32()?;
224	let mut items = Vec::with_capacity((count as usize).min(4096));
225	for _ in 0..count {
226		items.push(decode_value_from(r)?);
227	}
228	Ok(items)
229}
230
231fn decode_len_prefixed_bytes<'a>(r: &mut Reader<'a>) -> Result<&'a [u8], DecodeError> {
232	let len = r.u32()? as usize;
233	r.take(len)
234}
235
236fn decode_len_prefixed_str<'a>(r: &mut Reader<'a>) -> Result<&'a str, DecodeError> {
237	from_utf8(decode_len_prefixed_bytes(r)?).map_err(|e| DecodeError::InvalidData(format!("invalid UTF-8: {e}")))
238}
239
240fn decode_uuid(r: &mut Reader) -> Result<Uuid, DecodeError> {
241	Ok(Uuid::from_bytes(r.take(16)?.try_into().unwrap()))
242}
243
244pub fn encode_params(params: &Params) -> Result<Vec<u8>, EncodeError> {
245	let mut buf = Vec::new();
246	match params {
247		Params::None => buf.push(0),
248		Params::Positional(values) => {
249			buf.push(1);
250			buf.extend_from_slice(&(values.len() as u32).to_le_bytes());
251			for value in values.iter() {
252				encode_value_into(value, &mut buf)?;
253			}
254		}
255		Params::Named(map) => {
256			buf.push(2);
257			buf.extend_from_slice(&(map.len() as u32).to_le_bytes());
258			for (key, value) in map.iter() {
259				encode_len_prefixed(key.as_bytes(), &mut buf);
260				encode_value_into(value, &mut buf)?;
261			}
262		}
263	}
264	Ok(buf)
265}
266
267pub fn decode_params(bytes: &[u8]) -> Result<Params, DecodeError> {
268	let mut r = Reader::new(bytes);
269	let discriminant = r.u8()?;
270	let params = match discriminant {
271		0 => Params::None,
272		1 => {
273			let count = r.u32()?;
274			let mut values = Vec::with_capacity((count as usize).min(4096));
275			for _ in 0..count {
276				values.push(decode_value_from(&mut r)?);
277			}
278			Params::Positional(values.into())
279		}
280		2 => {
281			let count = r.u32()?;
282			let mut map = HashMap::with_capacity((count as usize).min(4096));
283			for _ in 0..count {
284				let key = decode_len_prefixed_str(&mut r)?.to_string();
285				map.insert(key, decode_value_from(&mut r)?);
286			}
287			Params::Named(map.into())
288		}
289		other => {
290			return Err(DecodeError::InvalidData(format!("invalid params discriminant: {other}")));
291		}
292	};
293	if !r.is_empty() {
294		return Err(DecodeError::TrailingBytes(r.remaining()));
295	}
296	Ok(params)
297}