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	Enum = 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::Decimal,
75		ValueKind::Any,
76		ValueKind::DictionaryId,
77		ValueKind::Type,
78		ValueKind::List,
79		ValueKind::Record,
80		ValueKind::Tuple,
81		ValueKind::Digest,
82		ValueKind::Enum,
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::Enum(_) => ValueKind::Enum,
112			Value::Uint1(_) => ValueKind::Uint1,
113			Value::Uint2(_) => ValueKind::Uint2,
114			Value::Uint4(_) => ValueKind::Uint4,
115			Value::Uint8(_) => ValueKind::Uint8,
116			Value::Uint16(_) => ValueKind::Uint16,
117			Value::Date(_) => ValueKind::Date,
118			Value::DateTime(_) => ValueKind::DateTime,
119			Value::Time(_) => ValueKind::Time,
120			Value::Duration(_) => ValueKind::Duration,
121			Value::IdentityId(_) => ValueKind::IdentityId,
122			Value::Uuid4(_) => ValueKind::Uuid4,
123			Value::Uuid7(_) => ValueKind::Uuid7,
124			Value::Blob(_) => ValueKind::Blob,
125			Value::Decimal(_) => ValueKind::Decimal,
126			Value::Any(_) => ValueKind::Any,
127			Value::DictionaryId(_) => ValueKind::DictionaryId,
128			Value::Type(_) => ValueKind::Type,
129			Value::List(_) => ValueKind::List,
130			Value::Record(_) => ValueKind::Record,
131			Value::Tuple(_) => ValueKind::Tuple,
132			Value::Digest(_) => ValueKind::Digest,
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::Enum(_) => ValueKind::Enum,
149			ValueType::Uint1 => ValueKind::Uint1,
150			ValueType::Uint2 => ValueKind::Uint2,
151			ValueType::Uint4 => ValueKind::Uint4,
152			ValueType::Uint8 => ValueKind::Uint8,
153			ValueType::Uint16 => ValueKind::Uint16,
154			ValueType::Date => ValueKind::Date,
155			ValueType::DateTime => ValueKind::DateTime,
156			ValueType::Time => ValueKind::Time,
157			ValueType::Duration => ValueKind::Duration,
158			ValueType::IdentityId => ValueKind::IdentityId,
159			ValueType::Uuid4 => ValueKind::Uuid4,
160			ValueType::Uuid7 => ValueKind::Uuid7,
161			ValueType::Blob => ValueKind::Blob,
162			ValueType::Decimal {
163				..
164			} => ValueKind::Decimal,
165			ValueType::Any => ValueKind::Any,
166			ValueType::DictionaryId => ValueKind::DictionaryId,
167			ValueType::List(_) => ValueKind::List,
168			ValueType::Record(_) => ValueKind::Record,
169			ValueType::Tuple(_) => ValueKind::Tuple,
170			ValueType::Digest {
171				..
172			} => ValueKind::Digest,
173		}
174	}
175}
176
177#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
178pub struct TypeTag(u8);
179
180impl TypeTag {
181	pub fn new(kind: ValueKind, depth: u8) -> Result<TypeTag, EncodeError> {
182		if depth > MAX_OPTION_DEPTH {
183			return Err(EncodeError::OptionDepthTooDeep {
184				depth: depth as u32,
185				max: MAX_OPTION_DEPTH,
186			});
187		}
188		Ok(TypeTag((depth << 6) | kind.byte()))
189	}
190
191	pub const fn byte(self) -> u8 {
192		self.0
193	}
194
195	pub const fn depth(self) -> u8 {
196		self.0 >> 6
197	}
198
199	pub const fn kind_bits(self) -> u8 {
200		self.0 & 0x3F
201	}
202
203	pub fn kind(self) -> Option<ValueKind> {
204		ValueKind::from_byte(self.kind_bits())
205	}
206
207	pub fn from_byte(byte: u8) -> Result<TypeTag, DecodeError> {
208		let kind_bits = byte & 0x3F;
209		if kind_bits == RESERVED_KIND {
210			return Err(DecodeError::ReservedTag(byte));
211		}
212		if ValueKind::from_byte(kind_bits).is_none() {
213			return Err(DecodeError::UnknownTypeCode(byte));
214		}
215		let tag = TypeTag(byte);
216		if tag.depth() > 0 && kind_bits == ValueKind::None.byte() {
217			return Err(DecodeError::InvalidData(format!(
218				"tag byte 0x{byte:02X} wraps the none kind in option depth {}",
219				tag.depth()
220			)));
221		}
222		Ok(tag)
223	}
224
225	pub fn of_type(ty: &ValueType) -> Result<TypeTag, EncodeError> {
226		let (base, depth) = peel_options(ty);
227		if depth > MAX_OPTION_DEPTH as u32 {
228			return Err(EncodeError::OptionDepthTooDeep {
229				depth,
230				max: MAX_OPTION_DEPTH,
231			});
232		}
233		Self::new(ValueKind::of_type(base), depth as u8)
234	}
235
236	pub fn to_type(self) -> Result<ValueType, DecodeError> {
237		let base = match self.kind().ok_or(DecodeError::UnknownTypeCode(self.0))? {
238			ValueKind::None | ValueKind::Type => {
239				return Err(DecodeError::UnsupportedType(format!(
240					"kind {:?} has no standalone value type",
241					self.kind()
242				)));
243			}
244			ValueKind::Enum => {
245				return Err(DecodeError::UnsupportedType(
246					"kind Enum needs its sumtype, which a type tag does not carry".to_string(),
247				));
248			}
249			ValueKind::Digest => {
250				return Err(DecodeError::UnsupportedType(
251					"kind Digest needs its inner type and accuracy, which a type tag does not carry"
252						.to_string(),
253				));
254			}
255			ValueKind::Boolean => ValueType::Boolean,
256			ValueKind::Float4 => ValueType::Float4,
257			ValueKind::Float8 => ValueType::Float8,
258			ValueKind::Int1 => ValueType::Int1,
259			ValueKind::Int2 => ValueType::Int2,
260			ValueKind::Int4 => ValueType::Int4,
261			ValueKind::Int8 => ValueType::Int8,
262			ValueKind::Int16 => ValueType::Int16,
263			ValueKind::Utf8 => ValueType::Utf8,
264			ValueKind::Uint1 => ValueType::Uint1,
265			ValueKind::Uint2 => ValueType::Uint2,
266			ValueKind::Uint4 => ValueType::Uint4,
267			ValueKind::Uint8 => ValueType::Uint8,
268			ValueKind::Uint16 => ValueType::Uint16,
269			ValueKind::Date => ValueType::Date,
270			ValueKind::DateTime => ValueType::DateTime,
271			ValueKind::Time => ValueType::Time,
272			ValueKind::Duration => ValueType::Duration,
273			ValueKind::IdentityId => ValueType::IdentityId,
274			ValueKind::Uuid4 => ValueType::Uuid4,
275			ValueKind::Uuid7 => ValueType::Uuid7,
276			ValueKind::Blob => ValueType::Blob,
277			ValueKind::Decimal => ValueType::DECIMAL,
278			ValueKind::Any => ValueType::Any,
279			ValueKind::DictionaryId => ValueType::DictionaryId,
280			ValueKind::List => ValueType::List(Box::new(ValueType::Any)),
281			ValueKind::Record => ValueType::Record(Vec::new()),
282			ValueKind::Tuple => ValueType::Tuple(Vec::new()),
283		};
284		Ok((0..self.depth()).fold(base, |ty, _| ValueType::Option(Box::new(ty))))
285	}
286}
287
288pub(crate) fn peel_options(ty: &ValueType) -> (&ValueType, u32) {
289	let mut depth = 0u32;
290	let mut base = ty;
291	while let ValueType::Option(inner) = base {
292		depth += 1;
293		base = inner;
294	}
295	(base, depth)
296}
297
298pub fn value_type_from_tag_byte(byte: u8) -> ValueType {
299	TypeTag::from_byte(byte).and_then(TypeTag::to_type).expect("invalid persisted type tag byte")
300}