numbat_codec/
top_de.rs

1use alloc::boxed::Box;
2use alloc::string::String;
3use alloc::vec::Vec;
4use core::num::NonZeroUsize;
5
6use crate::codec_err::DecodeError;
7use crate::nested_de::*;
8use crate::top_de_input::TopDecodeInput;
9use crate::transmute::*;
10use crate::TypeInfo;
11
12/// Trait that allows zero-copy read of values from an underlying API in big endian format.
13///
14/// 'Top' stands for the fact that values are deserialized on their own,
15/// so we have the benefit of knowing their length.
16/// This is useful in many scnearios, such as not having to encode Vec length and others.
17///
18/// The opther optimization that can be done when deserializing top-level objects
19/// is using special functions from the underlying API that do some of the work for the deserializer.
20/// These include getting values directly as i64/u64 or wrapping them directly into an owned Box<[u8]>.
21///
22/// BigInt/BigUint handling is not included here, because these are API-dependent
23/// and would overly complicate the trait.
24///
25pub trait TopDecode: Sized {
26	#[doc(hidden)]
27	const TYPE_INFO: TypeInfo = TypeInfo::Unknown;
28
29	/// Attempt to deserialize the value from input.
30	fn top_decode<I: TopDecodeInput>(input: I) -> Result<Self, DecodeError>;
31
32	/// Version of `top_decode` that exits quickly in case of error.
33	/// Its purpose is to create smaller implementations
34	/// in cases where the application is supposed to exit directly on decode error.
35	fn top_decode_or_exit<I: TopDecodeInput, ExitCtx: Clone>(
36		input: I,
37		c: ExitCtx,
38		exit: fn(ExitCtx, DecodeError) -> !,
39	) -> Self {
40		match Self::top_decode(input) {
41			Ok(v) => v,
42			Err(e) => exit(c, e),
43		}
44	}
45
46	/// Allows types to provide optimized implementations for their boxed version.
47	/// Especially designed for byte arrays that can be transmuted directly from the input sometimes.
48	#[doc(hidden)]
49	#[inline]
50	fn top_decode_boxed<I: TopDecodeInput>(input: I) -> Result<Box<Self>, DecodeError> {
51		Ok(Box::new(Self::top_decode(input)?))
52	}
53
54	#[doc(hidden)]
55	#[inline]
56	fn top_decode_boxed_or_exit<I: TopDecodeInput, ExitCtx: Clone>(
57		input: I,
58		c: ExitCtx,
59		exit: fn(ExitCtx, DecodeError) -> !,
60	) -> Box<Self> {
61		Box::new(Self::top_decode_or_exit(input, c, exit))
62	}
63}
64
65/// Top-decodes the result using the NestedDecode implementation.
66pub fn top_decode_from_nested<T, I>(input: I) -> Result<T, DecodeError>
67where
68	I: TopDecodeInput,
69	T: NestedDecode,
70{
71	let bytes = input.into_boxed_slice_u8();
72	let mut_slice = &mut &*bytes;
73	let result = T::dep_decode(mut_slice)?;
74	if !mut_slice.is_empty() {
75		return Err(DecodeError::INPUT_TOO_LONG);
76	}
77	Ok(result)
78}
79
80/// Top-decodes the result using the NestedDecode implementation.
81/// Uses the fast-exit mechanism in case of error.
82pub fn top_decode_from_nested_or_exit<T, I, ExitCtx: Clone>(
83	input: I,
84	c: ExitCtx,
85	exit: fn(ExitCtx, DecodeError) -> !,
86) -> T
87where
88	I: TopDecodeInput,
89	T: NestedDecode,
90{
91	let bytes = input.into_boxed_slice_u8();
92	let mut_slice = &mut &*bytes;
93	let result = T::dep_decode_or_exit(mut_slice, c.clone(), exit);
94	if !mut_slice.is_empty() {
95		exit(c, DecodeError::INPUT_TOO_LONG);
96	}
97	result
98}
99
100impl TopDecode for () {
101	const TYPE_INFO: TypeInfo = TypeInfo::Unit;
102
103	fn top_decode<I: TopDecodeInput>(_: I) -> Result<Self, DecodeError> {
104		Ok(())
105	}
106
107	fn top_decode_or_exit<I: TopDecodeInput, ExitCtx: Clone>(
108		_: I,
109		_: ExitCtx,
110		_: fn(ExitCtx, DecodeError) -> !,
111	) -> Self {
112	}
113}
114
115impl<T: TopDecode> TopDecode for Box<T> {
116	fn top_decode<I: TopDecodeInput>(input: I) -> Result<Self, DecodeError> {
117		T::top_decode_boxed(input)
118	}
119
120	fn top_decode_or_exit<I: TopDecodeInput, ExitCtx: Clone>(
121		input: I,
122		c: ExitCtx,
123		exit: fn(ExitCtx, DecodeError) -> !,
124	) -> Self {
125		T::top_decode_boxed_or_exit(input, c, exit)
126	}
127}
128
129// Allowed to implement this because [T] cannot implement NestedDecode, being ?Sized.
130impl<T: NestedDecode> TopDecode for Box<[T]> {
131	fn top_decode<I: TopDecodeInput>(input: I) -> Result<Self, DecodeError> {
132		if let TypeInfo::U8 = T::TYPE_INFO {
133			let bytes = input.into_boxed_slice_u8();
134			let cast_bytes: Box<[T]> = unsafe { core::mem::transmute(bytes) };
135			Ok(cast_bytes)
136		} else {
137			let vec = Vec::<T>::top_decode(input)?;
138			Ok(vec_into_boxed_slice(vec))
139		}
140	}
141
142	/// Quick exit for any of the contained types
143	fn top_decode_or_exit<I: TopDecodeInput, ExitCtx: Clone>(
144		input: I,
145		c: ExitCtx,
146		exit: fn(ExitCtx, DecodeError) -> !,
147	) -> Self {
148		if let TypeInfo::U8 = T::TYPE_INFO {
149			let bytes = input.into_boxed_slice_u8();
150			let cast_bytes: Box<[T]> = unsafe { core::mem::transmute(bytes) };
151			cast_bytes
152		} else {
153			let vec = Vec::<T>::top_decode_or_exit(input, c, exit);
154			vec_into_boxed_slice(vec)
155		}
156	}
157}
158
159impl<T: NestedDecode> TopDecode for Vec<T> {
160	fn top_decode<I: TopDecodeInput>(input: I) -> Result<Self, DecodeError> {
161		if let TypeInfo::U8 = T::TYPE_INFO {
162			let bytes = input.into_boxed_slice_u8();
163			let bytes_vec = boxed_slice_into_vec(bytes);
164			let cast_vec: Vec<T> = unsafe { core::mem::transmute(bytes_vec) };
165			Ok(cast_vec)
166		} else {
167			let bytes = input.into_boxed_slice_u8();
168			let mut_slice = &mut &*bytes;
169			let mut result: Vec<T> = Vec::new();
170			while !mut_slice.is_empty() {
171				result.push(T::dep_decode(mut_slice)?);
172			}
173			Ok(result)
174		}
175	}
176
177	fn top_decode_or_exit<I: TopDecodeInput, ExitCtx: Clone>(
178		input: I,
179		c: ExitCtx,
180		exit: fn(ExitCtx, DecodeError) -> !,
181	) -> Self {
182		if let TypeInfo::U8 = T::TYPE_INFO {
183			let bytes = input.into_boxed_slice_u8();
184			let bytes_vec = boxed_slice_into_vec(bytes);
185			let cast_vec: Vec<T> = unsafe { core::mem::transmute(bytes_vec) };
186			cast_vec
187		} else {
188			let bytes = input.into_boxed_slice_u8();
189			let mut_slice = &mut &*bytes;
190			let mut result: Vec<T> = Vec::new();
191			while !mut_slice.is_empty() {
192				result.push(T::dep_decode_or_exit(mut_slice, c.clone(), exit));
193			}
194			result
195		}
196	}
197}
198
199impl TopDecode for String {
200	fn top_decode<I: TopDecodeInput>(input: I) -> Result<Self, DecodeError> {
201		let raw = Vec::<u8>::top_decode(input)?;
202		match String::from_utf8(raw) {
203			Ok(s) => Ok(s),
204			Err(_) => Err(DecodeError::UTF8_DECODE_ERROR),
205		}
206	}
207
208	fn top_decode_or_exit<I: TopDecodeInput, ExitCtx: Clone>(
209		input: I,
210		c: ExitCtx,
211		exit: fn(ExitCtx, DecodeError) -> !,
212	) -> Self {
213		let raw = Vec::<u8>::top_decode_or_exit(input, c.clone(), exit);
214		match String::from_utf8(raw) {
215			Ok(s) => s,
216			Err(_) => exit(c, DecodeError::UTF8_DECODE_ERROR),
217		}
218	}
219}
220
221impl TopDecode for Box<str> {
222	fn top_decode<I: TopDecodeInput>(input: I) -> Result<Self, DecodeError> {
223		Ok(String::top_decode(input)?.into_boxed_str())
224	}
225
226	fn top_decode_or_exit<I: TopDecodeInput, ExitCtx: Clone>(
227		input: I,
228		c: ExitCtx,
229		exit: fn(ExitCtx, DecodeError) -> !,
230	) -> Self {
231		String::top_decode_or_exit(input, c, exit).into_boxed_str()
232	}
233}
234
235macro_rules! decode_num_unsigned {
236	($ty:ty, $bounds_ty:ty, $type_info:expr) => {
237		impl TopDecode for $ty {
238			const TYPE_INFO: TypeInfo = $type_info;
239
240			fn top_decode<I: TopDecodeInput>(input: I) -> Result<Self, DecodeError> {
241				let arg_u64 = input.into_u64();
242				let max = <$bounds_ty>::MAX as u64;
243				if arg_u64 > max {
244					Err(DecodeError::INPUT_TOO_LONG)
245				} else {
246					Ok(arg_u64 as $ty)
247				}
248			}
249
250			fn top_decode_or_exit<I: TopDecodeInput, ExitCtx: Clone>(
251				input: I,
252				c: ExitCtx,
253				exit: fn(ExitCtx, DecodeError) -> !,
254			) -> Self {
255				let arg_u64 = input.into_u64();
256				let max = <$bounds_ty>::MAX as u64;
257				if arg_u64 > max {
258					exit(c, DecodeError::INPUT_TOO_LONG)
259				} else {
260					arg_u64 as $ty
261				}
262			}
263		}
264	};
265}
266
267decode_num_unsigned!(u8, u8, TypeInfo::U8);
268decode_num_unsigned!(u16, u16, TypeInfo::U16);
269decode_num_unsigned!(u32, u32, TypeInfo::U32);
270decode_num_unsigned!(usize, u32, TypeInfo::USIZE); // even if usize can be 64 bits on some platforms, we always deserialize as max 32 bits
271decode_num_unsigned!(u64, u64, TypeInfo::U64);
272
273macro_rules! decode_num_signed {
274	($ty:ty, $bounds_ty:ty, $type_info:expr) => {
275		impl TopDecode for $ty {
276			const TYPE_INFO: TypeInfo = $type_info;
277
278			fn top_decode<I: TopDecodeInput>(input: I) -> Result<Self, DecodeError> {
279				let arg_i64 = input.into_i64();
280				let min = <$bounds_ty>::MIN as i64;
281				let max = <$bounds_ty>::MAX as i64;
282				if arg_i64 < min || arg_i64 > max {
283					Err(DecodeError::INPUT_OUT_OF_RANGE)
284				} else {
285					Ok(arg_i64 as $ty)
286				}
287			}
288
289			fn top_decode_or_exit<I: TopDecodeInput, ExitCtx: Clone>(
290				input: I,
291				c: ExitCtx,
292				exit: fn(ExitCtx, DecodeError) -> !,
293			) -> Self {
294				let arg_i64 = input.into_i64();
295				let min = <$bounds_ty>::MIN as i64;
296				let max = <$bounds_ty>::MAX as i64;
297				if arg_i64 < min || arg_i64 > max {
298					exit(c, DecodeError::INPUT_OUT_OF_RANGE)
299				} else {
300					arg_i64 as $ty
301				}
302			}
303		}
304	};
305}
306
307decode_num_signed!(i8, i8, TypeInfo::I8);
308decode_num_signed!(i16, i16, TypeInfo::I16);
309decode_num_signed!(i32, i32, TypeInfo::I32);
310decode_num_signed!(isize, i32, TypeInfo::ISIZE); // even if isize can be 64 bits on some platforms, we always deserialize as max 32 bits
311decode_num_signed!(i64, i64, TypeInfo::I64);
312
313impl TopDecode for bool {
314	const TYPE_INFO: TypeInfo = TypeInfo::Bool;
315
316	fn top_decode<I: TopDecodeInput>(input: I) -> Result<Self, DecodeError> {
317		match input.into_u64() {
318			0 => Ok(false),
319			1 => Ok(true),
320			_ => Err(DecodeError::INPUT_OUT_OF_RANGE),
321		}
322	}
323
324	fn top_decode_or_exit<I: TopDecodeInput, ExitCtx: Clone>(
325		input: I,
326		c: ExitCtx,
327		exit: fn(ExitCtx, DecodeError) -> !,
328	) -> Self {
329		match input.into_u64() {
330			0 => false,
331			1 => true,
332			_ => exit(c, DecodeError::INPUT_OUT_OF_RANGE),
333		}
334	}
335}
336
337impl<T: NestedDecode> TopDecode for Option<T> {
338	fn top_decode<I: TopDecodeInput>(input: I) -> Result<Self, DecodeError> {
339		let bytes = input.into_boxed_slice_u8();
340		if bytes.is_empty() {
341			Ok(None)
342		} else {
343			let item = dep_decode_from_byte_slice::<T>(&bytes[1..])?;
344			Ok(Some(item))
345		}
346	}
347
348	fn top_decode_or_exit<I: TopDecodeInput, ExitCtx: Clone>(
349		input: I,
350		c: ExitCtx,
351		exit: fn(ExitCtx, DecodeError) -> !,
352	) -> Self {
353		let bytes = input.into_boxed_slice_u8();
354		if bytes.is_empty() {
355			None
356		} else {
357			let item = dep_decode_from_byte_slice_or_exit(&bytes[1..], c, exit);
358			Some(item)
359		}
360	}
361}
362
363macro_rules! tuple_impls {
364    ($($len:expr => ($($n:tt $name:ident)+))+) => {
365        $(
366            impl<$($name),+> TopDecode for ($($name,)+)
367            where
368                $($name: NestedDecode,)+
369            {
370                fn top_decode<I: TopDecodeInput>(input: I) -> Result<Self, DecodeError> {
371                    top_decode_from_nested(input)
372                }
373
374                fn top_decode_or_exit<I: TopDecodeInput, ExitCtx: Clone>(input: I, c: ExitCtx, exit: fn(ExitCtx, DecodeError) -> !) -> Self {
375                    top_decode_from_nested_or_exit(input, c, exit)
376                }
377            }
378        )+
379    }
380}
381
382tuple_impls! {
383	1 => (0 T0)
384	2 => (0 T0 1 T1)
385	3 => (0 T0 1 T1 2 T2)
386	4 => (0 T0 1 T1 2 T2 3 T3)
387	5 => (0 T0 1 T1 2 T2 3 T3 4 T4)
388	6 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5)
389	7 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6)
390	8 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7)
391	9 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8)
392	10 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9)
393	11 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10)
394	12 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11)
395	13 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12)
396	14 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13)
397	15 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14)
398	16 => (0 T0 1 T1 2 T2 3 T3 4 T4 5 T5 6 T6 7 T7 8 T8 9 T9 10 T10 11 T11 12 T12 13 T13 14 T14 15 T15)
399}
400
401impl TopDecode for NonZeroUsize {
402	fn top_decode<I: TopDecodeInput>(input: I) -> Result<Self, DecodeError> {
403		if let Some(nz) = NonZeroUsize::new(usize::top_decode(input)?) {
404			Ok(nz)
405		} else {
406			Err(DecodeError::INVALID_VALUE)
407		}
408	}
409
410	fn top_decode_or_exit<I: TopDecodeInput, ExitCtx: Clone>(
411		input: I,
412		c: ExitCtx,
413		exit: fn(ExitCtx, DecodeError) -> !,
414	) -> Self {
415		if let Some(nz) = NonZeroUsize::new(usize::top_decode_or_exit(input, c.clone(), exit)) {
416			nz
417		} else {
418			exit(c, DecodeError::INVALID_VALUE)
419		}
420	}
421}
422
423////////////////////////////////////////////////////////////////////////////////
424
425#[cfg(test)]
426mod tests {
427	use super::super::test_struct::*;
428	use super::*;
429	use crate::test_util::check_top_decode;
430	use core::fmt::Debug;
431
432	fn deser_ok<V>(element: V, bytes: &[u8])
433	where
434		V: TopDecode + PartialEq + Debug + 'static,
435	{
436		let deserialized: V = check_top_decode::<V>(&bytes[..]);
437		assert_eq!(deserialized, element);
438	}
439
440	#[test]
441	fn test_top_numbers_decompacted() {
442		// unsigned positive
443		deser_ok(5u8, &[5]);
444		deser_ok(5u16, &[5]);
445		deser_ok(5u32, &[5]);
446		deser_ok(5u64, &[5]);
447		deser_ok(5usize, &[5]);
448		// signed positive
449		deser_ok(5i8, &[5]);
450		deser_ok(5i16, &[5]);
451		deser_ok(5i32, &[5]);
452		deser_ok(5i64, &[5]);
453		deser_ok(5isize, &[5]);
454		// signed negative
455		deser_ok(-5i8, &[251]);
456		deser_ok(-5i16, &[251]);
457		deser_ok(-5i32, &[251]);
458		deser_ok(-5i64, &[251]);
459		deser_ok(-5isize, &[251]);
460		// non zero usize
461		deser_ok(NonZeroUsize::new(5).unwrap(), &[5]);
462	}
463
464	#[test]
465	fn test_top_numbers_decompacted_2() {
466		deser_ok(-1i32, &[255]);
467		deser_ok(-1i32, &[255, 255]);
468		deser_ok(-1i32, &[255, 255, 255, 255]);
469		deser_ok(-1i64, &[255, 255, 255, 255, 255, 255, 255, 255]);
470	}
471
472	#[test]
473	fn test_top_decode_str() {
474		deser_ok(String::from("abc"), &[b'a', b'b', b'c']);
475		deser_ok(String::from("abc").into_boxed_str(), &[b'a', b'b', b'c']);
476	}
477
478	#[test]
479	fn test_struct() {
480		let test = Test {
481			int: 1,
482			seq: [5, 6].to_vec(),
483			another_byte: 7,
484		};
485		deser_ok(test, &[0, 1, 0, 0, 0, 2, 5, 6, 7]);
486	}
487
488	#[test]
489	fn test_enum() {
490		let u = E::Unit;
491		let expected: &[u8] = &[/*variant index*/ 0, 0, 0, 0];
492		deser_ok(u, expected);
493
494		let n = E::Newtype(1);
495		let expected: &[u8] = &[/*variant index*/ 0, 0, 0, 1, /*data*/ 0, 0, 0, 1];
496		deser_ok(n, expected);
497
498		let t = E::Tuple(1, 2);
499		let expected: &[u8] = &[
500			/*variant index*/ 0, 0, 0, 2, /*(*/ 0, 0, 0, 1, /*,*/ 0, 0, 0,
501			2, /*)*/
502		];
503		deser_ok(t, expected);
504
505		let s = E::Struct { a: 1 };
506		let expected: &[u8] = &[/*variant index*/ 0, 0, 0, 3, /*data*/ 0, 0, 0, 1];
507		deser_ok(s, expected);
508	}
509}