1#[cfg(feature = "alloc")]
3use alloc::vec::Vec;
4use {
5 crate::{
6 ReadResult, SchemaRead, SchemaReadContext, SchemaReadOwned, SchemaWrite, WriteResult,
7 config::{Config, ConfigCore},
8 error,
9 io::{Reader, Writer},
10 },
11 core::mem::MaybeUninit,
12};
13
14pub trait Serialize<C: Config>: SchemaWrite<C> {
16 #[cfg(feature = "alloc")]
18 fn serialize(src: &Self::Src, config: C) -> WriteResult<Vec<u8>> {
19 let capacity = Self::size_of(src)?;
20 let mut buffer = Vec::with_capacity(capacity);
21 let mut writer = buffer.spare_capacity_mut();
22 Self::serialize_into(writer.by_ref(), src, config)?;
23 let len = writer.len();
24 unsafe {
25 #[allow(clippy::arithmetic_side_effects)]
26 buffer.set_len(capacity - len);
27 }
28 Ok(buffer)
29 }
30
31 #[inline]
46 #[expect(unused_variables)]
47 fn serialize_into(mut dst: impl Writer, src: &Self::Src, config: C) -> WriteResult<()> {
48 Self::write(dst.by_ref(), src)?;
49 dst.finish()?;
50 Ok(())
51 }
52
53 #[inline]
55 #[expect(unused_variables)]
56 fn serialized_size(src: &Self::Src, config: C) -> WriteResult<u64> {
57 Self::size_of(src).map(|size| size as u64)
58 }
59}
60
61impl<T, C: Config> Serialize<C> for T where T: SchemaWrite<C> + ?Sized {}
62
63macro_rules! maybe_size_limit {
64 ($config:ty, $src:expr, $kind:ident, |$reader:ident| $body:expr $(,)?) => {{
65 let src = $src;
66
67 match <$config as $crate::config::ConfigCore>::DESERIALIZATION_SIZE_LIMIT {
68 Some(limit) => {
69 let $reader = maybe_size_limit!(@reader $kind, src, limit);
70 $body
71 }
72 None => {
73 let $reader = src;
74 $body
75 }
76 }
77 }};
78
79 (@reader generic, $src:ident, $limit:ident) => {
80 $crate::io::LimitReader::new($src, $limit)
81 };
82
83 (@reader slice, $src:ident, $limit:ident) => {{
84 let src = $src;
85 let len = $limit.min(src.len());
86 &src[..len]
87 }};
88
89 (@reader slice_mut, $src:ident, $limit:ident) => {{
90 let src = $src;
91 let len = $limit.min(src.len());
92 &mut src[..len]
93 }};
94}
95
96pub trait Deserialize<'de, C: Config>: SchemaRead<'de, C> {
98 #[inline(always)]
100 #[expect(unused_variables)]
101 fn deserialize(src: &'de [u8], config: C) -> ReadResult<Self::Dst> {
102 maybe_size_limit!(C, src, slice, |reader| Self::get(reader))
103 }
104
105 #[inline]
107 #[expect(unused_variables)]
108 fn deserialize_into(
109 src: &'de [u8],
110 dst: &mut MaybeUninit<Self::Dst>,
111 config: C,
112 ) -> ReadResult<()> {
113 maybe_size_limit!(C, src, slice, |reader| Self::read(reader, dst))
114 }
115}
116
117impl<'de, T, C: Config> Deserialize<'de, C> for T where T: SchemaRead<'de, C> {}
118
119pub trait DeserializeOwned<C: Config>: SchemaReadOwned<C> {
121 #[inline(always)]
123 fn deserialize_from<'de>(
124 src: impl Reader<'de>,
125 ) -> ReadResult<<Self as SchemaRead<'de, C>>::Dst> {
126 maybe_size_limit!(C, src, generic, |reader| Self::get(reader))
127 }
128
129 #[inline]
131 fn deserialize_from_into<'de>(
132 src: impl Reader<'de>,
133 dst: &mut MaybeUninit<<Self as SchemaRead<'de, C>>::Dst>,
134 ) -> ReadResult<()> {
135 maybe_size_limit!(C, src, generic, |reader| Self::read(reader, dst))
136 }
137}
138
139impl<T, C: Config> DeserializeOwned<C> for T where T: SchemaReadOwned<C> {}
140
141#[cfg(feature = "alloc")]
155pub fn serialize<T, C: Config>(src: &T, config: C) -> WriteResult<Vec<u8>>
156where
157 T: SchemaWrite<C, Src = T> + ?Sized,
158{
159 T::serialize(src, config)
160}
161
162#[inline]
167pub fn serialize_into<T, C: Config>(dst: impl Writer, src: &T, config: C) -> WriteResult<()>
168where
169 T: SchemaWrite<C, Src = T> + ?Sized,
170{
171 T::serialize_into(dst, src, config)
172}
173
174#[inline]
176pub fn serialized_size<T, C: Config>(src: &T, config: C) -> WriteResult<u64>
177where
178 T: SchemaWrite<C, Src = T> + ?Sized,
179{
180 T::serialized_size(src, config)
181}
182
183#[inline(always)]
199pub fn deserialize<'de, T, C: Config>(src: &'de [u8], config: C) -> ReadResult<T>
200where
201 T: SchemaRead<'de, C, Dst = T>,
202{
203 T::deserialize(src, config)
204}
205
206#[inline(always)]
224#[expect(unused_variables)]
225pub fn deserialize_exact<'de, T, C: Config>(mut src: &'de [u8], config: C) -> ReadResult<T>
226where
227 T: SchemaRead<'de, C, Dst = T>,
228{
229 let value = match C::DESERIALIZATION_SIZE_LIMIT {
230 Some(limit) => {
231 let mut limited = &src[..limit.min(src.len())];
232 let initial_len = limited.len();
233 let value = T::get(limited.by_ref())?;
234
235 #[expect(clippy::arithmetic_side_effects)]
236 let consumed = initial_len - limited.len();
237 src = &src[consumed..];
238
239 value
240 }
241 None => T::get(src.by_ref())?,
242 };
243
244 if src.is_empty() {
245 Ok(value)
246 } else {
247 Err(error::trailing_bytes())
248 }
249}
250
251#[inline(always)]
253#[expect(unused_variables)]
254pub fn deserialize_with_context<'de, Ctx, T, C: Config>(
255 ctx: Ctx,
256 src: &'de [u8],
257 config: C,
258) -> ReadResult<T>
259where
260 T: SchemaReadContext<'de, C, Ctx, Dst = T>,
261{
262 maybe_size_limit!(C, src, slice, |reader| T::get_with_context(ctx, reader))
263}
264
265#[inline(always)]
267#[expect(unused_variables)]
268pub fn deserialize_mut<'de, T, C: Config>(src: &'de mut [u8], config: C) -> ReadResult<T>
269where
270 T: SchemaRead<'de, C, Dst = T>,
271{
272 maybe_size_limit!(C, src, slice_mut, |reader| T::get(reader))
273}
274
275#[inline(always)]
277#[expect(unused_variables)]
278pub fn deserialize_from<'de, T, C: Config>(src: impl Reader<'de>, config: C) -> ReadResult<T>
279where
280 T: SchemaReadOwned<C, Dst = T>,
281{
282 T::deserialize_from(src)
283}
284
285pub unsafe trait ZeroCopy<C: ConfigCore>: 'static {
297 #[inline(always)]
299 #[expect(unused_variables)]
300 fn from_bytes<'de>(bytes: &'de [u8], config: C) -> ReadResult<&'de Self>
301 where
302 Self: SchemaRead<'de, C, Dst = Self> + Sized,
303 {
304 maybe_size_limit!(
305 C,
306 bytes,
307 slice,
308 |reader| <&Self as SchemaRead<'de, C>>::get(reader)
309 )
310 }
311
312 #[inline(always)]
314 #[expect(unused_variables)]
315 fn from_bytes_mut<'de>(bytes: &'de mut [u8], config: C) -> ReadResult<&'de mut Self>
316 where
317 Self: SchemaRead<'de, C, Dst = Self> + Sized,
318 {
319 maybe_size_limit!(C, bytes, slice_mut, |reader| <&mut Self as SchemaRead<
320 'de,
321 C,
322 >>::get(reader))
323 }
324}
325
326#[cfg(test)]
327mod tests {
328 use {
329 super::*,
330 crate::{ReadError, config::Configuration, io::ReadError as IoReadError},
331 };
332
333 #[test]
334 fn configured_deserialization_limit_is_enforced() {
335 let bytes = 42u64.to_le_bytes();
336 let limited = Configuration::default().with_deserialization_size_limit::<4>();
337
338 assert!(matches!(
339 deserialize::<u64, _>(&bytes, limited),
340 Err(ReadError::Io(IoReadError::ReadSizeLimit(8)))
341 ));
342 assert!(matches!(
343 deserialize_from::<u64, _>(bytes.as_slice(), limited),
344 Err(ReadError::Io(IoReadError::ReadSizeLimit(8)))
345 ));
346
347 let exact = Configuration::default().with_deserialization_size_limit::<8>();
348 assert_eq!(deserialize::<u64, _>(&bytes, exact).unwrap(), 42);
349 assert_eq!(deserialize_exact::<u64, _>(&bytes, exact).unwrap(), 42);
350
351 assert!(matches!(
352 deserialize_exact::<u64, _>(&bytes, limited),
353 Err(ReadError::Io(IoReadError::ReadSizeLimit(8)))
354 ));
355
356 let mut trailing = [0; 9];
357 trailing[..8].copy_from_slice(&bytes);
358 assert!(matches!(
359 deserialize_exact::<u64, _>(&trailing, exact),
360 Err(ReadError::TrailingBytes)
361 ));
362 let larger = Configuration::default().with_deserialization_size_limit::<9>();
363 assert!(matches!(
364 deserialize_exact::<u64, _>(&trailing, larger),
365 Err(ReadError::TrailingBytes)
366 ));
367
368 let disabled = limited.disable_deserialization_size_limit();
369 assert_eq!(deserialize::<u64, _>(&bytes, disabled).unwrap(), 42);
370 }
371
372 #[cfg(feature = "alloc")]
376 #[test]
377 fn slice_and_generic_paths_agree_at_every_limit() {
378 macro_rules! check {
379 ($ty:ty, $val:expr, $($limit:literal),+) => {$({
380 let bytes = crate::serialize(&$val).unwrap();
381 let cfg = Configuration::default().with_deserialization_size_limit::<$limit>();
382 let via_slice = deserialize::<$ty, _>(&bytes, cfg);
383 let via_reader = deserialize_from::<$ty, _>(bytes.as_slice(), cfg);
384 assert_eq!(
385 via_slice.is_ok(),
386 via_reader.is_ok(),
387 "{} at limit {}: slice={:?} reader={:?}",
388 stringify!($ty),
389 $limit,
390 via_slice.as_ref().err(),
391 via_reader.as_ref().err(),
392 );
393 if let (Err(a), Err(b)) = (via_slice, via_reader) {
394 assert_eq!(
395 alloc::format!("{a:?}"),
396 alloc::format!("{b:?}"),
397 "{} at limit {}: differing errors",
398 stringify!($ty),
399 $limit,
400 );
401 }
402 })+};
403 }
404
405 check!(
407 alloc::vec::Vec<u32>,
408 alloc::vec![1u32, 2, 3],
409 0,
410 1,
411 7,
412 8,
413 9,
414 12,
415 16,
416 19,
417 20,
418 21,
419 100
420 );
421 check!(
423 alloc::vec::Vec<alloc::string::String>,
424 alloc::vec![
425 alloc::string::String::from("ab"),
426 alloc::string::String::from("cde")
427 ],
428 0,
429 8,
430 16,
431 18,
432 26,
433 29,
434 30,
435 31
436 );
437 check!(u64, 42u64, 0, 4, 7, 8, 9);
439 check!(core::ops::Range<u32>, 0u32..5, 0, 4, 7, 8, 9);
440 }
441
442 #[test]
443 fn every_entrypoint_enforces_the_limit() {
444 let bytes = 42u64.to_le_bytes();
445 let short = Configuration::default().with_deserialization_size_limit::<4>();
446 let exact = Configuration::default().with_deserialization_size_limit::<8>();
447
448 let mut dst = MaybeUninit::<u64>::uninit();
449 assert!(matches!(
450 <u64 as Deserialize<_>>::deserialize_into(&bytes, &mut dst, short),
451 Err(ReadError::Io(IoReadError::ReadSizeLimit(8)))
452 ));
453 assert!(<u64 as Deserialize<_>>::deserialize_into(&bytes, &mut dst, exact).is_ok());
454
455 fn from_into<C: Config>(src: &[u8], _config: C) -> ReadResult<u64> {
457 let mut dst = MaybeUninit::uninit();
458 <u64 as DeserializeOwned<C>>::deserialize_from_into(src, &mut dst)?;
459 Ok(unsafe { dst.assume_init() })
461 }
462 assert!(matches!(
463 from_into(bytes.as_slice(), short),
464 Err(ReadError::Io(IoReadError::ReadSizeLimit(8)))
465 ));
466 assert_eq!(from_into(bytes.as_slice(), exact).unwrap(), 42);
467
468 let mut mutable = bytes;
469 assert!(matches!(
470 deserialize_mut::<u64, _>(&mut mutable, short),
471 Err(ReadError::Io(IoReadError::ReadSizeLimit(8)))
472 ));
473 assert_eq!(deserialize_mut::<u64, _>(&mut mutable, exact).unwrap(), 42);
474
475 #[cfg(feature = "alloc")]
476 {
477 let elements = crate::serialize(&[1u32, 2, 3]).unwrap();
479 assert_eq!(elements.len(), 12);
480 assert!(matches!(
481 deserialize_with_context::<_, alloc::vec::Vec<u32>, _>(
482 crate::schema::context::Len(3),
483 &elements,
484 short,
485 ),
486 Err(ReadError::Io(IoReadError::ReadSizeLimit(_)))
487 ));
488 let twelve = Configuration::default().with_deserialization_size_limit::<12>();
489 assert_eq!(
490 deserialize_with_context::<_, alloc::vec::Vec<u32>, _>(
491 crate::schema::context::Len(3),
492 &elements,
493 twelve,
494 )
495 .unwrap(),
496 alloc::vec![1, 2, 3],
497 );
498 }
499 }
500
501 #[test]
502 fn limit_larger_than_input_is_clamped() {
503 let bytes = 42u64.to_le_bytes();
504 let generous = Configuration::default().with_deserialization_size_limit::<1024>();
505
506 assert_eq!(deserialize::<u64, _>(&bytes, generous).unwrap(), 42);
507 assert_eq!(deserialize_exact::<u64, _>(&bytes, generous).unwrap(), 42);
508 assert_eq!(
509 deserialize_from::<u64, _>(bytes.as_slice(), generous).unwrap(),
510 42
511 );
512
513 let mut mutable = bytes;
514 assert_eq!(
515 deserialize_mut::<u64, _>(&mut mutable, generous).unwrap(),
516 42
517 );
518
519 let mut one = [7u8];
520 assert_eq!(*<u8 as ZeroCopy<_>>::from_bytes(&one, generous).unwrap(), 7);
521 assert_eq!(
522 *<u8 as ZeroCopy<_>>::from_bytes_mut(&mut one, generous).unwrap(),
523 7
524 );
525
526 let mut trailing = [0u8; 9];
528 trailing[..8].copy_from_slice(&bytes);
529 assert!(matches!(
530 deserialize_exact::<u64, _>(&trailing, generous),
531 Err(ReadError::TrailingBytes)
532 ));
533 }
534
535 #[test]
536 fn zero_copy_deserialization_honors_limit() {
537 let bytes = [42u8];
538 let limited = Configuration::default().with_deserialization_size_limit::<0>();
539
540 assert!(matches!(
541 <u8 as ZeroCopy<_>>::from_bytes(&bytes, limited),
542 Err(ReadError::Io(IoReadError::ReadSizeLimit(1)))
543 ));
544
545 let mut bytes = bytes;
546 assert!(matches!(
547 <u8 as ZeroCopy<_>>::from_bytes_mut(&mut bytes, limited),
548 Err(ReadError::Io(IoReadError::ReadSizeLimit(1)))
549 ));
550 }
551}