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	Decimal = 23,
39	Any = 24,
40	DictionaryId = 25,
41	Type = 26,
42	List = 27,
43	Record = 28,
44	Tuple = 29,
45	Digest = 30,
46}
47
48impl ValueKind {
49	pub const ALL: [ValueKind; 31] = [
50		ValueKind::None,
51		ValueKind::Boolean,
52		ValueKind::Float4,
53		ValueKind::Float8,
54		ValueKind::Int1,
55		ValueKind::Int2,
56		ValueKind::Int4,
57		ValueKind::Int8,
58		ValueKind::Int16,
59		ValueKind::Utf8,
60		ValueKind::Uint1,
61		ValueKind::Uint2,
62		ValueKind::Uint4,
63		ValueKind::Uint8,
64		ValueKind::Uint16,
65		ValueKind::Date,
66		ValueKind::DateTime,
67		ValueKind::Time,
68		ValueKind::Duration,
69		ValueKind::IdentityId,
70		ValueKind::Uuid4,
71		ValueKind::Uuid7,
72		ValueKind::Blob,
73		ValueKind::Decimal,
74		ValueKind::Any,
75		ValueKind::DictionaryId,
76		ValueKind::Type,
77		ValueKind::List,
78		ValueKind::Record,
79		ValueKind::Tuple,
80		ValueKind::Digest,
81	];
82
83	pub const fn byte(self) -> u8 {
84		self as u8
85	}
86
87	pub fn from_byte(byte: u8) -> Option<ValueKind> {
88		if byte < Self::ALL.len() as u8 {
89			Some(Self::ALL[byte as usize])
90		} else {
91			None
92		}
93	}
94
95	pub fn of_value(value: &Value) -> ValueKind {
96		match value {
97			Value::None {
98				..
99			} => ValueKind::None,
100			Value::Boolean(_) => ValueKind::Boolean,
101			Value::Float4(_) => ValueKind::Float4,
102			Value::Float8(_) => ValueKind::Float8,
103			Value::Int1(_) => ValueKind::Int1,
104			Value::Int2(_) => ValueKind::Int2,
105			Value::Int4(_) => ValueKind::Int4,
106			Value::Int8(_) => ValueKind::Int8,
107			Value::Int16(_) => ValueKind::Int16,
108			Value::Utf8(_) => ValueKind::Utf8,
109			Value::Uint1(_) => ValueKind::Uint1,
110			Value::Uint2(_) => ValueKind::Uint2,
111			Value::Uint4(_) => ValueKind::Uint4,
112			Value::Uint8(_) => ValueKind::Uint8,
113			Value::Uint16(_) => ValueKind::Uint16,
114			Value::Date(_) => ValueKind::Date,
115			Value::DateTime(_) => ValueKind::DateTime,
116			Value::Time(_) => ValueKind::Time,
117			Value::Duration(_) => ValueKind::Duration,
118			Value::IdentityId(_) => ValueKind::IdentityId,
119			Value::Uuid4(_) => ValueKind::Uuid4,
120			Value::Uuid7(_) => ValueKind::Uuid7,
121			Value::Blob(_) => ValueKind::Blob,
122			Value::Decimal(_) => ValueKind::Decimal,
123			Value::Any(_) => ValueKind::Any,
124			Value::DictionaryId(_) => ValueKind::DictionaryId,
125			Value::Type(_) => ValueKind::Type,
126			Value::List(_) => ValueKind::List,
127			Value::Record(_) => ValueKind::Record,
128			Value::Tuple(_) => ValueKind::Tuple,
129			Value::Digest(_) => ValueKind::Digest,
130		}
131	}
132
133	pub fn of_type(ty: &ValueType) -> ValueKind {
134		match ty {
135			ValueType::Option(inner) => Self::of_type(inner),
136			ValueType::Boolean => ValueKind::Boolean,
137			ValueType::Float4 => ValueKind::Float4,
138			ValueType::Float8 => ValueKind::Float8,
139			ValueType::Int1 => ValueKind::Int1,
140			ValueType::Int2 => ValueKind::Int2,
141			ValueType::Int4 => ValueKind::Int4,
142			ValueType::Int8 => ValueKind::Int8,
143			ValueType::Int16 => ValueKind::Int16,
144			ValueType::Utf8 => ValueKind::Utf8,
145			ValueType::Uint1 => ValueKind::Uint1,
146			ValueType::Uint2 => ValueKind::Uint2,
147			ValueType::Uint4 => ValueKind::Uint4,
148			ValueType::Uint8 => ValueKind::Uint8,
149			ValueType::Uint16 => ValueKind::Uint16,
150			ValueType::Date => ValueKind::Date,
151			ValueType::DateTime => ValueKind::DateTime,
152			ValueType::Time => ValueKind::Time,
153			ValueType::Duration => ValueKind::Duration,
154			ValueType::IdentityId => ValueKind::IdentityId,
155			ValueType::Uuid4 => ValueKind::Uuid4,
156			ValueType::Uuid7 => ValueKind::Uuid7,
157			ValueType::Blob => ValueKind::Blob,
158			ValueType::Decimal {
159				..
160			} => ValueKind::Decimal,
161			ValueType::Any => ValueKind::Any,
162			ValueType::DictionaryId => ValueKind::DictionaryId,
163			ValueType::List(_) => ValueKind::List,
164			ValueType::Record(_) => ValueKind::Record,
165			ValueType::Tuple(_) => ValueKind::Tuple,
166			ValueType::Digest {
167				..
168			} => ValueKind::Digest,
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::Digest => {
241				return Err(DecodeError::UnsupportedType(
242					"kind Digest needs its inner type and accuracy, which a type tag does not carry"
243						.to_string(),
244				));
245			}
246			ValueKind::Boolean => ValueType::Boolean,
247			ValueKind::Float4 => ValueType::Float4,
248			ValueKind::Float8 => ValueType::Float8,
249			ValueKind::Int1 => ValueType::Int1,
250			ValueKind::Int2 => ValueType::Int2,
251			ValueKind::Int4 => ValueType::Int4,
252			ValueKind::Int8 => ValueType::Int8,
253			ValueKind::Int16 => ValueType::Int16,
254			ValueKind::Utf8 => ValueType::Utf8,
255			ValueKind::Uint1 => ValueType::Uint1,
256			ValueKind::Uint2 => ValueType::Uint2,
257			ValueKind::Uint4 => ValueType::Uint4,
258			ValueKind::Uint8 => ValueType::Uint8,
259			ValueKind::Uint16 => ValueType::Uint16,
260			ValueKind::Date => ValueType::Date,
261			ValueKind::DateTime => ValueType::DateTime,
262			ValueKind::Time => ValueType::Time,
263			ValueKind::Duration => ValueType::Duration,
264			ValueKind::IdentityId => ValueType::IdentityId,
265			ValueKind::Uuid4 => ValueType::Uuid4,
266			ValueKind::Uuid7 => ValueType::Uuid7,
267			ValueKind::Blob => ValueType::Blob,
268			ValueKind::Decimal => ValueType::DECIMAL,
269			ValueKind::Any => ValueType::Any,
270			ValueKind::DictionaryId => ValueType::DictionaryId,
271			ValueKind::List => ValueType::List(Box::new(ValueType::Any)),
272			ValueKind::Record => ValueType::Record(Vec::new()),
273			ValueKind::Tuple => ValueType::Tuple(Vec::new()),
274		};
275		Ok((0..self.depth()).fold(base, |ty, _| ValueType::Option(Box::new(ty))))
276	}
277}
278
279pub(crate) fn peel_options(ty: &ValueType) -> (&ValueType, u32) {
280	let mut depth = 0u32;
281	let mut base = ty;
282	while let ValueType::Option(inner) = base {
283		depth += 1;
284		base = inner;
285	}
286	(base, depth)
287}
288
289pub fn type_tag_byte(ty: &ValueType) -> u8 {
290	TypeTag::of_type(ty).expect("option nesting exceeds the type tag capacity").byte()
291}
292
293pub fn value_type_from_tag_byte(byte: u8) -> ValueType {
294	TypeTag::from_byte(byte).and_then(TypeTag::to_type).expect("invalid persisted type tag byte")
295}