Skip to main content

reifydb_codec/
tag.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use reifydb_value::value::{Value, value_type::ValueType};
5
6use crate::error::{DecodeError, EncodeError};
7
8pub const MAX_OPTION_DEPTH: u8 = 3;
9pub const RESERVED_KIND: u8 = 63;
10pub const EXTENDED_TYPE_TAG: u8 = 0xFF;
11
12#[repr(u8)]
13#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
14pub enum ValueKind {
15	None = 0,
16	Boolean = 1,
17	Float4 = 2,
18	Float8 = 3,
19	Int1 = 4,
20	Int2 = 5,
21	Int4 = 6,
22	Int8 = 7,
23	Int16 = 8,
24	Utf8 = 9,
25	Uint1 = 10,
26	Uint2 = 11,
27	Uint4 = 12,
28	Uint8 = 13,
29	Uint16 = 14,
30	Date = 15,
31	DateTime = 16,
32	Time = 17,
33	Duration = 18,
34	IdentityId = 19,
35	Uuid4 = 20,
36	Uuid7 = 21,
37	Blob = 22,
38	Int = 23,
39	Uint = 24,
40	Decimal = 25,
41	Any = 26,
42	DictionaryId = 27,
43	Type = 28,
44	List = 29,
45	Record = 30,
46	Tuple = 31,
47}
48
49impl ValueKind {
50	pub const ALL: [ValueKind; 32] = [
51		ValueKind::None,
52		ValueKind::Boolean,
53		ValueKind::Float4,
54		ValueKind::Float8,
55		ValueKind::Int1,
56		ValueKind::Int2,
57		ValueKind::Int4,
58		ValueKind::Int8,
59		ValueKind::Int16,
60		ValueKind::Utf8,
61		ValueKind::Uint1,
62		ValueKind::Uint2,
63		ValueKind::Uint4,
64		ValueKind::Uint8,
65		ValueKind::Uint16,
66		ValueKind::Date,
67		ValueKind::DateTime,
68		ValueKind::Time,
69		ValueKind::Duration,
70		ValueKind::IdentityId,
71		ValueKind::Uuid4,
72		ValueKind::Uuid7,
73		ValueKind::Blob,
74		ValueKind::Int,
75		ValueKind::Uint,
76		ValueKind::Decimal,
77		ValueKind::Any,
78		ValueKind::DictionaryId,
79		ValueKind::Type,
80		ValueKind::List,
81		ValueKind::Record,
82		ValueKind::Tuple,
83	];
84
85	pub const fn byte(self) -> u8 {
86		self as u8
87	}
88
89	pub fn from_byte(byte: u8) -> Option<ValueKind> {
90		if byte < Self::ALL.len() as u8 {
91			Some(Self::ALL[byte as usize])
92		} else {
93			None
94		}
95	}
96
97	pub fn of_value(value: &Value) -> ValueKind {
98		match value {
99			Value::None {
100				..
101			} => ValueKind::None,
102			Value::Boolean(_) => ValueKind::Boolean,
103			Value::Float4(_) => ValueKind::Float4,
104			Value::Float8(_) => ValueKind::Float8,
105			Value::Int1(_) => ValueKind::Int1,
106			Value::Int2(_) => ValueKind::Int2,
107			Value::Int4(_) => ValueKind::Int4,
108			Value::Int8(_) => ValueKind::Int8,
109			Value::Int16(_) => ValueKind::Int16,
110			Value::Utf8(_) => ValueKind::Utf8,
111			Value::Uint1(_) => ValueKind::Uint1,
112			Value::Uint2(_) => ValueKind::Uint2,
113			Value::Uint4(_) => ValueKind::Uint4,
114			Value::Uint8(_) => ValueKind::Uint8,
115			Value::Uint16(_) => ValueKind::Uint16,
116			Value::Date(_) => ValueKind::Date,
117			Value::DateTime(_) => ValueKind::DateTime,
118			Value::Time(_) => ValueKind::Time,
119			Value::Duration(_) => ValueKind::Duration,
120			Value::IdentityId(_) => ValueKind::IdentityId,
121			Value::Uuid4(_) => ValueKind::Uuid4,
122			Value::Uuid7(_) => ValueKind::Uuid7,
123			Value::Blob(_) => ValueKind::Blob,
124			Value::Int(_) => ValueKind::Int,
125			Value::Uint(_) => ValueKind::Uint,
126			Value::Decimal(_) => ValueKind::Decimal,
127			Value::Any(_) => ValueKind::Any,
128			Value::DictionaryId(_) => ValueKind::DictionaryId,
129			Value::Type(_) => ValueKind::Type,
130			Value::List(_) => ValueKind::List,
131			Value::Record(_) => ValueKind::Record,
132			Value::Tuple(_) => ValueKind::Tuple,
133		}
134	}
135
136	pub fn of_type(ty: &ValueType) -> ValueKind {
137		match ty {
138			ValueType::Option(inner) => Self::of_type(inner),
139			ValueType::Boolean => ValueKind::Boolean,
140			ValueType::Float4 => ValueKind::Float4,
141			ValueType::Float8 => ValueKind::Float8,
142			ValueType::Int1 => ValueKind::Int1,
143			ValueType::Int2 => ValueKind::Int2,
144			ValueType::Int4 => ValueKind::Int4,
145			ValueType::Int8 => ValueKind::Int8,
146			ValueType::Int16 => ValueKind::Int16,
147			ValueType::Utf8 => ValueKind::Utf8,
148			ValueType::Uint1 => ValueKind::Uint1,
149			ValueType::Uint2 => ValueKind::Uint2,
150			ValueType::Uint4 => ValueKind::Uint4,
151			ValueType::Uint8 => ValueKind::Uint8,
152			ValueType::Uint16 => ValueKind::Uint16,
153			ValueType::Date => ValueKind::Date,
154			ValueType::DateTime => ValueKind::DateTime,
155			ValueType::Time => ValueKind::Time,
156			ValueType::Duration => ValueKind::Duration,
157			ValueType::IdentityId => ValueKind::IdentityId,
158			ValueType::Uuid4 => ValueKind::Uuid4,
159			ValueType::Uuid7 => ValueKind::Uuid7,
160			ValueType::Blob => ValueKind::Blob,
161			ValueType::Int => ValueKind::Int,
162			ValueType::Uint => ValueKind::Uint,
163			ValueType::Decimal => ValueKind::Decimal,
164			ValueType::Any => ValueKind::Any,
165			ValueType::DictionaryId => ValueKind::DictionaryId,
166			ValueType::List(_) => ValueKind::List,
167			ValueType::Record(_) => ValueKind::Record,
168			ValueType::Tuple(_) => ValueKind::Tuple,
169		}
170	}
171}
172
173#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
174pub struct TypeTag(u8);
175
176impl TypeTag {
177	pub fn new(kind: ValueKind, depth: u8) -> Result<TypeTag, EncodeError> {
178		if depth > MAX_OPTION_DEPTH {
179			return Err(EncodeError::OptionDepthTooDeep {
180				depth: depth as u32,
181				max: MAX_OPTION_DEPTH,
182			});
183		}
184		Ok(TypeTag((depth << 6) | kind.byte()))
185	}
186
187	pub const fn byte(self) -> u8 {
188		self.0
189	}
190
191	pub const fn depth(self) -> u8 {
192		self.0 >> 6
193	}
194
195	pub const fn kind_bits(self) -> u8 {
196		self.0 & 0x3F
197	}
198
199	pub fn kind(self) -> Option<ValueKind> {
200		ValueKind::from_byte(self.kind_bits())
201	}
202
203	pub fn from_byte(byte: u8) -> Result<TypeTag, DecodeError> {
204		let kind_bits = byte & 0x3F;
205		if kind_bits == RESERVED_KIND {
206			return Err(DecodeError::ReservedTag(byte));
207		}
208		if ValueKind::from_byte(kind_bits).is_none() {
209			return Err(DecodeError::UnknownTypeCode(byte));
210		}
211		let tag = TypeTag(byte);
212		if tag.depth() > 0 && kind_bits == ValueKind::None.byte() {
213			return Err(DecodeError::InvalidData(format!(
214				"tag byte 0x{byte:02X} wraps the none kind in option depth {}",
215				tag.depth()
216			)));
217		}
218		Ok(tag)
219	}
220
221	pub fn of_type(ty: &ValueType) -> Result<TypeTag, EncodeError> {
222		let (base, depth) = peel_options(ty);
223		if depth > MAX_OPTION_DEPTH as u32 {
224			return Err(EncodeError::OptionDepthTooDeep {
225				depth,
226				max: MAX_OPTION_DEPTH,
227			});
228		}
229		Self::new(ValueKind::of_type(base), depth as u8)
230	}
231
232	pub fn to_type(self) -> Result<ValueType, DecodeError> {
233		let base = match self.kind().ok_or(DecodeError::UnknownTypeCode(self.0))? {
234			ValueKind::None | ValueKind::Type => {
235				return Err(DecodeError::UnsupportedType(format!(
236					"kind {:?} has no standalone value type",
237					self.kind()
238				)));
239			}
240			ValueKind::Boolean => ValueType::Boolean,
241			ValueKind::Float4 => ValueType::Float4,
242			ValueKind::Float8 => ValueType::Float8,
243			ValueKind::Int1 => ValueType::Int1,
244			ValueKind::Int2 => ValueType::Int2,
245			ValueKind::Int4 => ValueType::Int4,
246			ValueKind::Int8 => ValueType::Int8,
247			ValueKind::Int16 => ValueType::Int16,
248			ValueKind::Utf8 => ValueType::Utf8,
249			ValueKind::Uint1 => ValueType::Uint1,
250			ValueKind::Uint2 => ValueType::Uint2,
251			ValueKind::Uint4 => ValueType::Uint4,
252			ValueKind::Uint8 => ValueType::Uint8,
253			ValueKind::Uint16 => ValueType::Uint16,
254			ValueKind::Date => ValueType::Date,
255			ValueKind::DateTime => ValueType::DateTime,
256			ValueKind::Time => ValueType::Time,
257			ValueKind::Duration => ValueType::Duration,
258			ValueKind::IdentityId => ValueType::IdentityId,
259			ValueKind::Uuid4 => ValueType::Uuid4,
260			ValueKind::Uuid7 => ValueType::Uuid7,
261			ValueKind::Blob => ValueType::Blob,
262			ValueKind::Int => ValueType::Int,
263			ValueKind::Uint => ValueType::Uint,
264			ValueKind::Decimal => ValueType::Decimal,
265			ValueKind::Any => ValueType::Any,
266			ValueKind::DictionaryId => ValueType::DictionaryId,
267			ValueKind::List => ValueType::List(Box::new(ValueType::Any)),
268			ValueKind::Record => ValueType::Record(Vec::new()),
269			ValueKind::Tuple => ValueType::Tuple(Vec::new()),
270		};
271		Ok((0..self.depth()).fold(base, |ty, _| ValueType::Option(Box::new(ty))))
272	}
273}
274
275pub(crate) fn peel_options(ty: &ValueType) -> (&ValueType, u32) {
276	let mut depth = 0u32;
277	let mut base = ty;
278	while let ValueType::Option(inner) = base {
279		depth += 1;
280		base = inner;
281	}
282	(base, depth)
283}
284
285pub fn type_tag_byte(ty: &ValueType) -> u8 {
286	TypeTag::of_type(ty).expect("option nesting exceeds the type tag capacity").byte()
287}
288
289pub fn value_type_from_tag_byte(byte: u8) -> ValueType {
290	TypeTag::from_byte(byte).and_then(TypeTag::to_type).expect("invalid persisted type tag byte")
291}