Skip to main content

tuwunel_database/
de.rs

1//! Deserialization for the database's compact record codec.
2//!
3//! Compound values are divided into records by [`crate::SEP`]. A trailing type
4//! that accepts empty input can decode a missing final field from an older,
5//! shorter tuple, while writing the extended tuple emits an additional
6//! separator before that appended field.
7
8use serde::{
9	Deserialize, de,
10	de::{DeserializeSeed, Visitor},
11};
12use tuwunel_core::{
13	Error, Result, arrayvec::ArrayVec, checked, debug::DebugInspect, err, unhandled,
14	utils::string,
15};
16
17/// Deserializes a value from database record bytes.
18///
19/// The result may borrow from `buf` according to `T`'s deserialization
20/// implementation. Debug builds additionally verify that decoding consumed the
21/// input, apart from one trailing record separator.
22///
23/// # Panics
24///
25/// Panics if `T` requests a Serde data-model operation unsupported by this
26/// codec. In debug builds, decoding also panics when record-layout invariants
27/// are violated or unexpected trailing bytes remain.
28#[cfg_attr(
29	unabridged,
30	tracing::instrument(
31		name = "deserialize",
32		level = "trace",
33		skip_all,
34		fields(len = %buf.len()),
35	)
36)]
37pub fn from_slice<'a, T>(buf: &'a [u8]) -> Result<T>
38where
39	T: Deserialize<'a>,
40{
41	let mut deserializer = Deserializer { buf, pos: 0, rec: 0, seq: 0 };
42
43	T::deserialize(&mut deserializer).debug_inspect(|_| {
44		deserializer
45			.finished()
46			.expect("deserialization failed to consume trailing bytes");
47	})
48}
49
50/// Cursor state for decoding the compact database record format.
51///
52/// The byte position and record counters advance independently so incomplete
53/// tuple tails can be presented to inner deserializers as empty input.
54/// `next_element_seed` compares the record count with the expected sequence
55/// length when deciding whether iteration is complete.
56pub(crate) struct Deserializer<'de> {
57	buf: &'de [u8],
58	pos: usize,
59	rec: usize,
60	seq: usize,
61}
62
63/// Skips one encoded record when deserialized inside a sequence.
64///
65/// At the top level, the directive consumes all remaining records so the input
66/// finishes cleanly. It produces only the unit value represented by this
67/// marker.
68#[derive(Clone, Copy, Debug, Deserialize)]
69pub struct Ignore;
70
71/// Skips the current record and every record that follows it.
72///
73/// Place this directive inside a sequence to discard its remaining encoded
74/// elements. It consumes the trailing input and produces only the unit value
75/// represented by this marker.
76#[derive(Clone, Copy, Debug, Deserialize)]
77pub struct IgnoreAll;
78
79impl<'de> Deserializer<'de> {
80	const SEP: u8 = crate::ser::SEP;
81
82	/// Determine if the input was fully consumed and error if bytes remaining.
83	/// This is intended for debug assertions; not optimized for parsing logic.
84	fn finished(&self) -> Result {
85		let pos = self.pos;
86		let len = self.buf.len();
87		let parsed = &self.buf[0..pos];
88		let unparsed = &self.buf[pos..];
89		let remain = self.remaining()?;
90		let trailing_sep = remain == 1 && unparsed[0] == Self::SEP;
91		(remain == 0 || trailing_sep)
92			.then_some(())
93			.ok_or(err!(SerdeDe(
94				"{remain} trailing of {len} bytes not deserialized.\n{parsed:?}\n{unparsed:?}",
95			)))
96	}
97
98	/// Called at the start of arrays and tuples
99	#[inline]
100	fn sequence_start(&mut self, len: usize) {
101		debug_assert!(self.seq == 0, "Nested sequences are not handled at this time");
102		self.seq = len;
103	}
104
105	/// Consume the current record to ignore it. Inside a sequence the next
106	/// record is skipped but at the top-level all records are skipped such that
107	/// deserialization completes with self.finished() == Ok.
108	#[inline]
109	fn record_ignore(&mut self) {
110		if self.seq > 0 {
111			self.record_next();
112		} else {
113			self.record_ignore_all();
114		}
115	}
116
117	/// Consume the current and all remaining records to ignore them. Similar to
118	/// Ignore at the top-level, but it can be provided in a sequence to Ignore
119	/// all remaining elements.
120	#[inline]
121	fn record_ignore_all(&mut self) { self.record_trail(); }
122
123	/// Consume the current record. The position pointer is moved to the start
124	/// of the next record. Slice of the current record is returned.
125	#[inline]
126	fn record_next(&mut self) -> &'de [u8] {
127		self.buf[self.pos..]
128			.split(|b| *b == Deserializer::SEP)
129			.inspect(|record| self.inc_pos(record.len()))
130			.next()
131			.expect("remainder of buf even if SEP was not found")
132	}
133
134	/// Peek at the first byte of the current record. If all records were
135	/// consumed None is returned instead.
136	#[inline]
137	fn record_peek_byte(&self) -> Option<u8> {
138		let started = self.pos != 0 || self.rec > 0;
139		let buf = &self.buf[self.pos..];
140		debug_assert!(
141			!started || buf[0] == Self::SEP,
142			"Missing expected record separator at current position"
143		);
144
145		buf.get::<usize>(started.into()).copied()
146	}
147
148	/// Consume the record separator such that the position cleanly points to
149	/// the start of the next record. When input is exhausted but the
150	/// sequence is not, advance no bytes; the caller deserializes from an
151	/// empty slice. See `next_element_seed` for the additive-tail mechanic
152	/// this enables.
153	#[inline]
154	fn record_start(&mut self) {
155		let started = self.pos != 0 || self.rec > 0;
156		let input_done = self.pos >= self.buf.len();
157		let output_done = self.rec >= self.seq;
158		let incomplete = input_done && !output_done;
159		debug_assert!(
160			!started || incomplete || self.buf.get(self.pos) == Some(&Self::SEP),
161			"Missing expected record separator at current position"
162		);
163
164		let inc = started && !incomplete;
165		self.inc_pos(inc.into());
166		self.inc_rec(1);
167	}
168
169	/// Consume all remaining bytes, which may include record separators,
170	/// returning a raw slice.
171	#[inline]
172	fn record_trail(&mut self) -> &'de [u8] {
173		let record = &self.buf[self.pos..];
174		self.inc_pos(record.len());
175		record
176	}
177
178	/// Increment the position pointer.
179	#[inline]
180	#[cfg_attr(
181		unabridged,
182		tracing::instrument(
183			level = "trace",
184			skip(self),
185			fields(
186				len = self.buf.len(),
187				rem = self.remaining().unwrap_or_default().saturating_sub(n),
188			),
189		)
190	)]
191	fn inc_pos(&mut self, n: usize) {
192		self.pos = self.pos.saturating_add(n);
193		debug_assert!(self.pos <= self.buf.len(), "pos out of range");
194	}
195
196	#[inline]
197	fn inc_rec(&mut self, n: usize) { self.rec = self.rec.saturating_add(n); }
198
199	/// Unconsumed input bytes.
200	#[inline]
201	fn remaining(&self) -> Result<usize> {
202		let pos = self.pos;
203		let len = self.buf.len();
204		checked!(len - pos)
205	}
206}
207
208impl<'a, 'de: 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
209	type Error = Error;
210
211	#[cfg_attr(unabridged, tracing::instrument(level = "trace", skip_all))]
212	fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value>
213	where
214		V: Visitor<'de>,
215	{
216		self.sequence_start(1);
217		visitor.visit_seq(self)
218	}
219
220	#[cfg_attr(
221		unabridged,
222		tracing::instrument(level = "trace", skip(self, visitor))
223	)]
224	fn deserialize_tuple<V>(self, len: usize, visitor: V) -> Result<V::Value>
225	where
226		V: Visitor<'de>,
227	{
228		self.sequence_start(len);
229		visitor.visit_seq(self)
230	}
231
232	#[cfg_attr(
233		unabridged,
234		tracing::instrument(level = "trace", skip(self, visitor))
235	)]
236	fn deserialize_tuple_struct<V>(
237		self,
238		_name: &'static str,
239		len: usize,
240		visitor: V,
241	) -> Result<V::Value>
242	where
243		V: Visitor<'de>,
244	{
245		self.sequence_start(len);
246		visitor.visit_seq(self)
247	}
248
249	#[cfg_attr(unabridged, tracing::instrument(level = "trace", skip_all))]
250	fn deserialize_map<V>(self, visitor: V) -> Result<V::Value>
251	where
252		V: Visitor<'de>,
253	{
254		let input = self.record_next();
255		let mut d = serde_json::Deserializer::from_slice(input);
256		d.deserialize_map(visitor).map_err(Into::into)
257	}
258
259	#[cfg_attr(
260		unabridged,
261		tracing::instrument(level = "trace", skip(self, visitor))
262	)]
263	fn deserialize_struct<V>(
264		self,
265		name: &'static str,
266		fields: &'static [&'static str],
267		visitor: V,
268	) -> Result<V::Value>
269	where
270		V: Visitor<'de>,
271	{
272		let input = self.record_next();
273		let mut d = serde_json::Deserializer::from_slice(input);
274		d.deserialize_struct(name, fields, visitor)
275			.map_err(Into::into)
276	}
277
278	#[cfg_attr(
279		unabridged,
280		tracing::instrument(level = "trace", skip(self, visitor))
281	)]
282	fn deserialize_unit_struct<V>(self, name: &'static str, visitor: V) -> Result<V::Value>
283	where
284		V: Visitor<'de>,
285	{
286		match name {
287			| "Ignore" => self.record_ignore(),
288			| "IgnoreAll" => self.record_ignore_all(),
289			| _ => unhandled!("Unrecognized deserialization Directive {name:?}"),
290		}
291
292		visitor.visit_unit()
293	}
294
295	#[cfg_attr(
296		unabridged,
297		tracing::instrument(level = "trace", skip(self, visitor))
298	)]
299	fn deserialize_newtype_struct<V>(self, name: &'static str, visitor: V) -> Result<V::Value>
300	where
301		V: Visitor<'de>,
302	{
303		match name {
304			| "$serde_json::private::RawValue" => visitor.visit_map(self),
305			| "Json" => visitor
306				.visit_newtype_struct(&mut serde_json::Deserializer::from_slice(
307					self.record_trail(),
308				))
309				.map_err(|e| Self::Error::SerdeDe(format!("{name}: {e}").into())),
310
311			| "Cbor" => visitor
312				.visit_newtype_struct(&mut minicbor_serde::Deserializer::new(self.record_trail()))
313				.map_err(|e| Self::Error::SerdeDe(format!("{name}: {e}").into())),
314
315			| _ => visitor.visit_newtype_struct(self),
316		}
317	}
318
319	#[cfg_attr(
320		unabridged,
321		tracing::instrument(level = "trace", skip(self, _visitor))
322	)]
323	fn deserialize_enum<V>(
324		self,
325		_name: &'static str,
326		_variants: &'static [&'static str],
327		_visitor: V,
328	) -> Result<V::Value>
329	where
330		V: Visitor<'de>,
331	{
332		unhandled!("deserialize Enum not implemented")
333	}
334
335	#[cfg_attr(unabridged, tracing::instrument(level = "trace", skip_all))]
336	fn deserialize_option<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
337		if self
338			.buf
339			.get(self.pos)
340			.is_none_or(|b| *b == Deserializer::SEP)
341		{
342			visitor.visit_none()
343		} else {
344			visitor.visit_some(self)
345		}
346	}
347
348	#[cfg_attr(unabridged, tracing::instrument(level = "trace", skip_all))]
349	fn deserialize_bool<V: Visitor<'de>>(self, _visitor: V) -> Result<V::Value> {
350		unhandled!("deserialize bool not implemented")
351	}
352
353	#[cfg_attr(unabridged, tracing::instrument(level = "trace", skip_all))]
354	fn deserialize_i8<V: Visitor<'de>>(self, _visitor: V) -> Result<V::Value> {
355		unhandled!("deserialize i8 not implemented")
356	}
357
358	#[cfg_attr(unabridged, tracing::instrument(level = "trace", skip_all))]
359	fn deserialize_i16<V: Visitor<'de>>(self, _visitor: V) -> Result<V::Value> {
360		unhandled!("deserialize i16 not implemented")
361	}
362
363	#[cfg_attr(unabridged, tracing::instrument(level = "trace", skip_all))]
364	fn deserialize_i32<V: Visitor<'de>>(self, _visitor: V) -> Result<V::Value> {
365		unhandled!("deserialize i32 not implemented")
366	}
367
368	#[cfg_attr(unabridged, tracing::instrument(level = "trace", skip_all))]
369	fn deserialize_i64<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
370		const BYTES: usize = size_of::<i64>();
371
372		let end = self.pos.saturating_add(BYTES).min(self.buf.len());
373		let bytes: ArrayVec<u8, BYTES> = self.buf[self.pos..end].try_into()?;
374		let bytes = bytes
375			.into_inner()
376			.map_err(|_| Self::Error::SerdeDe("i64 buffer underflow".into()))?;
377
378		self.inc_pos(BYTES);
379		visitor.visit_i64(i64::from_be_bytes(bytes))
380	}
381
382	#[cfg_attr(unabridged, tracing::instrument(level = "trace", skip_all))]
383	fn deserialize_u8<V: Visitor<'de>>(self, _visitor: V) -> Result<V::Value> {
384		unhandled!(
385			"deserialize u8 not implemented; try dereferencing the Handle for [u8] access \
386			 instead"
387		)
388	}
389
390	#[cfg_attr(unabridged, tracing::instrument(level = "trace", skip_all))]
391	fn deserialize_u16<V: Visitor<'de>>(self, _visitor: V) -> Result<V::Value> {
392		unhandled!("deserialize u16 not implemented")
393	}
394
395	#[cfg_attr(unabridged, tracing::instrument(level = "trace", skip_all))]
396	fn deserialize_u32<V: Visitor<'de>>(self, _visitor: V) -> Result<V::Value> {
397		unhandled!("deserialize u32 not implemented")
398	}
399
400	#[cfg_attr(unabridged, tracing::instrument(level = "trace", skip_all))]
401	fn deserialize_u64<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
402		const BYTES: usize = size_of::<u64>();
403
404		let end = self.pos.saturating_add(BYTES).min(self.buf.len());
405		let bytes: ArrayVec<u8, BYTES> = self.buf[self.pos..end].try_into()?;
406		let bytes = bytes
407			.into_inner()
408			.map_err(|_| Self::Error::SerdeDe("u64 buffer underflow".into()))?;
409
410		self.inc_pos(BYTES);
411		visitor.visit_u64(u64::from_be_bytes(bytes))
412	}
413
414	#[cfg_attr(unabridged, tracing::instrument(level = "trace", skip_all))]
415	fn deserialize_f32<V: Visitor<'de>>(self, _visitor: V) -> Result<V::Value> {
416		unhandled!("deserialize f32 not implemented")
417	}
418
419	#[cfg_attr(unabridged, tracing::instrument(level = "trace", skip_all))]
420	fn deserialize_f64<V: Visitor<'de>>(self, _visitor: V) -> Result<V::Value> {
421		unhandled!("deserialize f64 not implemented")
422	}
423
424	#[cfg_attr(unabridged, tracing::instrument(level = "trace", skip_all))]
425	fn deserialize_char<V: Visitor<'de>>(self, _visitor: V) -> Result<V::Value> {
426		unhandled!("deserialize char not implemented")
427	}
428
429	#[cfg_attr(unabridged, tracing::instrument(level = "trace", skip_all))]
430	fn deserialize_str<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
431		let input = self.record_next();
432		let out = deserialize_str(input)?;
433		visitor.visit_borrowed_str(out)
434	}
435
436	#[cfg_attr(unabridged, tracing::instrument(level = "trace", skip_all))]
437	fn deserialize_string<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
438		let input = self.record_next();
439		let out = string::string_from_bytes(input)?;
440		visitor.visit_string(out)
441	}
442
443	#[cfg_attr(unabridged, tracing::instrument(level = "trace", skip_all))]
444	fn deserialize_bytes<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
445		let input = self.record_trail();
446		visitor.visit_borrowed_bytes(input)
447	}
448
449	#[cfg_attr(unabridged, tracing::instrument(level = "trace", skip_all))]
450	fn deserialize_byte_buf<V: Visitor<'de>>(self, _visitor: V) -> Result<V::Value> {
451		unhandled!("deserialize Byte Buf not implemented")
452	}
453
454	#[cfg_attr(unabridged, tracing::instrument(level = "trace", skip_all))]
455	fn deserialize_unit<V: Visitor<'de>>(self, _visitor: V) -> Result<V::Value> {
456		unhandled!("deserialize Unit not implemented")
457	}
458
459	// this only used for $serde_json::private::RawValue at this time; see MapAccess
460	#[cfg_attr(unabridged, tracing::instrument(level = "trace", skip_all))]
461	fn deserialize_identifier<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
462		let input = "$serde_json::private::RawValue";
463		visitor.visit_borrowed_str(input)
464	}
465
466	#[cfg_attr(unabridged, tracing::instrument(level = "trace", skip_all))]
467	fn deserialize_ignored_any<V: Visitor<'de>>(self, _visitor: V) -> Result<V::Value> {
468		unhandled!("deserialize Ignored Any not implemented")
469	}
470
471	#[cfg_attr(
472		unabridged,
473		tracing::instrument(level = "trace", skip_all, fields(?self.buf))
474	)]
475	fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value> {
476		const TYPE_PRE_1_91: &str = "serde_json::value::de::<impl serde_core::de::Deserialize \
477		                             for serde_json::value::Value>::deserialize::ValueVisitor";
478		const TYPE: &str = "serde_json::value::de::<impl serde_core::de::Deserialize<'_> for \
479		                    serde_json::value::Value>::deserialize::ValueVisitor";
480		debug_assert!(
481			matches!(tuwunel_core::debug::type_name::<V>(), TYPE | TYPE_PRE_1_91),
482			"deserialize_any: type not expected {0}",
483			tuwunel_core::debug::type_name::<V>()
484		);
485
486		match self.record_peek_byte() {
487			| Some(b'{') => self.deserialize_map(visitor),
488			| Some(b'[') => serde_json::Deserializer::from_slice(self.record_next())
489				.deserialize_seq(visitor)
490				.map_err(Into::into),
491
492			| _ => self.deserialize_str(visitor),
493		}
494	}
495}
496
497impl<'a, 'de: 'a> de::SeqAccess<'de> for &'a mut Deserializer<'de> {
498	type Error = Error;
499
500	#[cfg_attr(
501		unabridged,
502		tracing::instrument(level = "trace", skip(self, seed))
503	)]
504	fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>
505	where
506		T: DeserializeSeed<'de>,
507	{
508		// Finished parsing the input.
509		let finished = self.pos >= self.buf.len();
510
511		// Completely satisfied the output.
512		let complete = self.rec >= self.seq;
513
514		// Early-return only when both input and output are exhausted. If
515		// input is exhausted but the tuple is not, fall through:
516		// `record_start` does not advance pos and the inner deserializer
517		// runs against an empty slice. Tail types that visit an empty slice
518		// successfully (`&str` -> "", `&[u8]` -> &[], `Option<_>` -> None)
519		// round-trip; non-tolerant tails (numerics, typed Matrix IDs) error.
520		// This enables additive evolution of record-key tuples without a
521		// migration.
522		//
523		// Returning early before the input is exhausted trips the
524		// `finished()` check; before the tuple is exhausted, serde's length
525		// check.
526		if finished && complete {
527			return Ok(None);
528		}
529
530		self.record_start();
531		seed.deserialize(&mut **self).map(Some)
532	}
533}
534
535// this only used for $serde_json::private::RawValue at this time. our db
536// schema doesn't have its own map format; we use json for that anyway
537impl<'a, 'de: 'a> de::MapAccess<'de> for &'a mut Deserializer<'de> {
538	type Error = Error;
539
540	#[cfg_attr(
541		unabridged,
542		tracing::instrument(level = "trace", skip(self, seed))
543	)]
544	fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>>
545	where
546		K: DeserializeSeed<'de>,
547	{
548		seed.deserialize(&mut **self).map(Some)
549	}
550
551	#[cfg_attr(
552		unabridged,
553		tracing::instrument(level = "trace", skip(self, seed))
554	)]
555	fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value>
556	where
557		V: DeserializeSeed<'de>,
558	{
559		seed.deserialize(&mut **self)
560	}
561}
562
563// activate when stable; too soon now
564//#[cfg(debug_assertions)]
565#[inline]
566fn deserialize_str(input: &[u8]) -> Result<&str> { string::str_from_bytes(input) }
567
568//#[cfg(not(debug_assertions))]
569#[cfg(disable)]
570#[inline]
571fn deserialize_str(input: &[u8]) -> Result<&str> {
572	// SAFETY: Strings were written by the serializer to the database. Assuming no
573	// database corruption, the string will be valid. Database corruption is
574	// detected via rocksdb checksums.
575	unsafe { std::str::from_utf8_unchecked(input) }
576}