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
401macro_rules! array_impls {
402    ($($n: tt,)+) => {
403        $(
404            impl<T: NestedDecode> TopDecode for [T; $n] {
405                fn top_decode<I: TopDecodeInput>(input: I) -> Result<Self, DecodeError> {
406                    top_decode_from_nested(input)
407                }
408
409                fn top_decode_or_exit<I: TopDecodeInput, ExitCtx: Clone>(input: I, c: ExitCtx, exit: fn(ExitCtx, DecodeError) -> !) -> Self {
410                    top_decode_from_nested_or_exit(input, c, exit)
411                }
412
413                fn top_decode_boxed<I: TopDecodeInput>(input: I) -> Result<Box<Self>, DecodeError> {
414                    if let TypeInfo::U8 = T::TYPE_INFO {
415                        // transmute directly
416                        let bs = input.into_boxed_slice_u8();
417                        if bs.len() != $n {
418                            return Err(DecodeError::ARRAY_DECODE_ERROR);
419                        }
420                        let raw = Box::into_raw(bs);
421                        let array_box = unsafe { Box::<[T; $n]>::from_raw(raw as *mut [T; $n]) };
422                        Ok(array_box)
423                    } else {
424                        Ok(Box::new(Self::top_decode(input)?))
425                    }
426                }
427
428                fn top_decode_boxed_or_exit<I: TopDecodeInput, ExitCtx: Clone>(input: I, c: ExitCtx, exit: fn(ExitCtx, DecodeError) -> !) -> Box<Self> {
429                    if let TypeInfo::U8 = T::TYPE_INFO {
430                        // transmute directly
431                        let bs = input.into_boxed_slice_u8();
432                        if bs.len() != $n {
433                            exit(c, DecodeError::ARRAY_DECODE_ERROR);
434                        }
435                        let raw = Box::into_raw(bs);
436                        let array_box = unsafe { Box::<[T; $n]>::from_raw(raw as *mut [T; $n]) };
437                        array_box
438                    } else {
439                        Box::new(Self::top_decode_or_exit(input, c, exit))
440                    }
441                }
442            }
443        )+
444    }
445}
446
447#[rustfmt::skip]
448array_impls!(
449	1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
450	17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
451	32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51,
452	52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71,
453	72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91,
454	92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108,
455	109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124,
456	125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140,
457	141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156,
458	157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172,
459	173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188,
460	189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204,
461	205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220,
462	221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236,
463	237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252,
464	253, 254, 255, 256, 384, 512, 768, 1024, 2048, 4096, 8192, 16384, 32768,
465);
466
467impl TopDecode for NonZeroUsize {
468	fn top_decode<I: TopDecodeInput>(input: I) -> Result<Self, DecodeError> {
469		if let Some(nz) = NonZeroUsize::new(usize::top_decode(input)?) {
470			Ok(nz)
471		} else {
472			Err(DecodeError::INVALID_VALUE)
473		}
474	}
475
476	fn top_decode_or_exit<I: TopDecodeInput, ExitCtx: Clone>(
477		input: I,
478		c: ExitCtx,
479		exit: fn(ExitCtx, DecodeError) -> !,
480	) -> Self {
481		if let Some(nz) = NonZeroUsize::new(usize::top_decode_or_exit(input, c.clone(), exit)) {
482			nz
483		} else {
484			exit(c, DecodeError::INVALID_VALUE)
485		}
486	}
487}
488
489////////////////////////////////////////////////////////////////////////////////
490
491#[cfg(test)]
492mod tests {
493	use super::super::test_struct::*;
494	use super::*;
495	use crate::test_util::check_top_decode;
496	use core::fmt::Debug;
497
498	fn deser_ok<V>(element: V, bytes: &[u8])
499	where
500		V: TopDecode + PartialEq + Debug + 'static,
501	{
502		let deserialized: V = check_top_decode::<V>(&bytes[..]);
503		assert_eq!(deserialized, element);
504	}
505
506	#[test]
507	fn test_top_numbers_decompacted() {
508		// unsigned positive
509		deser_ok(5u8, &[5]);
510		deser_ok(5u16, &[5]);
511		deser_ok(5u32, &[5]);
512		deser_ok(5u64, &[5]);
513		deser_ok(5usize, &[5]);
514		// signed positive
515		deser_ok(5i8, &[5]);
516		deser_ok(5i16, &[5]);
517		deser_ok(5i32, &[5]);
518		deser_ok(5i64, &[5]);
519		deser_ok(5isize, &[5]);
520		// signed negative
521		deser_ok(-5i8, &[251]);
522		deser_ok(-5i16, &[251]);
523		deser_ok(-5i32, &[251]);
524		deser_ok(-5i64, &[251]);
525		deser_ok(-5isize, &[251]);
526		// non zero usize
527		deser_ok(NonZeroUsize::new(5).unwrap(), &[5]);
528	}
529
530	#[test]
531	fn test_top_numbers_decompacted_2() {
532		deser_ok(-1i32, &[255]);
533		deser_ok(-1i32, &[255, 255]);
534		deser_ok(-1i32, &[255, 255, 255, 255]);
535		deser_ok(-1i64, &[255, 255, 255, 255, 255, 255, 255, 255]);
536	}
537
538	#[test]
539	fn test_top_decode_str() {
540		deser_ok(String::from("abc"), &[b'a', b'b', b'c']);
541		deser_ok(String::from("abc").into_boxed_str(), &[b'a', b'b', b'c']);
542	}
543
544	#[test]
545	fn test_struct() {
546		let test = Test {
547			int: 1,
548			seq: [5, 6].to_vec(),
549			another_byte: 7,
550		};
551		deser_ok(test, &[0, 1, 0, 0, 0, 2, 5, 6, 7]);
552	}
553
554	#[test]
555	fn test_enum() {
556		let u = E::Unit;
557		let expected: &[u8] = &[/*variant index*/ 0, 0, 0, 0];
558		deser_ok(u, expected);
559
560		let n = E::Newtype(1);
561		let expected: &[u8] = &[/*variant index*/ 0, 0, 0, 1, /*data*/ 0, 0, 0, 1];
562		deser_ok(n, expected);
563
564		let t = E::Tuple(1, 2);
565		let expected: &[u8] = &[
566			/*variant index*/ 0, 0, 0, 2, /*(*/ 0, 0, 0, 1, /*,*/ 0, 0, 0,
567			2, /*)*/
568		];
569		deser_ok(t, expected);
570
571		let s = E::Struct { a: 1 };
572		let expected: &[u8] = &[/*variant index*/ 0, 0, 0, 3, /*data*/ 0, 0, 0, 1];
573		deser_ok(s, expected);
574	}
575}