postcard_core/
de.rs

1//! Deserializing tools
2
3use crate::varint::{
4    de_zig_zag_i16, de_zig_zag_i32, de_zig_zag_i64, de_zig_zag_i128, max_of_last_byte, varint_max,
5};
6
7/// Unexpectedly reached the end of the deserialization buffer
8#[derive(Debug, PartialEq, Eq, Clone)]
9pub struct UnexpectedEnd;
10
11impl core::fmt::Display for UnexpectedEnd {
12    #[inline]
13    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
14        f.write_str("UnexpectedEnd")
15    }
16}
17
18/// The deserialization Flavor trait
19///
20/// This is used as the primary way to decode serialized data from some kind of buffer,
21/// or modify that data in a middleware style pattern.
22///
23/// See the module level docs for an example of how flavors are used.
24pub trait Flavor<'de>: 'de {
25    /// The remaining data of this flavor after deserializing has completed.
26    ///
27    /// Typically, this includes the remaining buffer that was not used for
28    /// deserialization, and in cases of more complex flavors, any additional
29    /// information that was decoded or otherwise calculated during
30    /// the deserialization process.
31    type Remainder: 'de;
32
33    /// The source of data retrieved for deserialization.
34    ///
35    /// This is typically some sort of data buffer, or another Flavor, when
36    /// chained behavior is desired
37    type Source: 'de;
38
39    /// The error type specific to pushing methods.
40    ///
41    /// This includes [`Self::pop`], [`Self::try_take_n`], and [`Self::try_take_n_temp`].
42    ///
43    /// If the only error is "no more data", consider using [`UnexpectedEnd`].
44    type PopError: core::fmt::Debug + core::fmt::Display;
45
46    /// The error type specific to [`Self::finalize`].
47    ///
48    /// If this type cannot error when pushing, e.g. for storage flavors that don't
49    /// perform any meaningful finalization actions, consider using
50    /// [`Infallible`](core::convert::Infallible).
51    type FinalizeError: core::fmt::Debug + core::fmt::Display;
52
53    /// Obtain the next byte for deserialization
54    fn pop(&mut self) -> Result<u8, Self::PopError>;
55
56    /// Returns the number of bytes remaining in the message, if known.
57    ///
58    /// # Implementation notes
59    ///
60    /// It is not enforced that this number is exactly correct.
61    /// A flavor may yield less or more bytes than the what is hinted at by
62    /// this function.
63    ///
64    /// `size_hint()` is primarily intended to be used for optimizations such as
65    /// reserving space for deserialized items, but must not be trusted to
66    /// e.g., omit bounds checks in unsafe code. An incorrect implementation of
67    /// `size_hint()` should not lead to memory safety violations.
68    ///
69    /// That said, the implementation should provide a correct estimation,
70    /// because otherwise it would be a violation of the trait’s protocol.
71    ///
72    /// The default implementation returns `None` which is correct for any flavor.
73    fn size_hint(&self) -> Option<usize> {
74        None
75    }
76
77    /// Attempt to take the next `ct` bytes from the serialized message.
78    ///
79    /// This variant borrows the data from the input for zero-copy deserialization. If zero-copy
80    /// deserialization is not necessary, prefer to use `try_take_n_temp` instead.
81    fn try_take_n(&mut self, ct: usize) -> Result<&'de [u8], Self::PopError>;
82
83    /// Attempt to take the next `ct` bytes from the serialized message.
84    ///
85    /// This variant does not guarantee that the returned value is borrowed from the input, so it
86    /// cannot be used for zero-copy deserialization, but it also avoids needing to potentially
87    /// allocate a data in a temporary buffer.
88    ///
89    /// This variant should be used instead of `try_take_n`
90    /// if zero-copy deserialization is not necessary.
91    ///
92    /// It is only necessary to implement this method if the flavor requires storing data in a
93    /// temporary buffer in order to implement the borrow semantics, e.g. the `std::io::Read`
94    /// flavor.
95    fn try_take_n_temp<'a>(&'a mut self, ct: usize) -> Result<&'a [u8], Self::PopError>
96    where
97        'de: 'a,
98    {
99        self.try_take_n(ct)
100    }
101
102    /// Complete the deserialization process.
103    ///
104    /// This is typically called separately, after the `serde` deserialization
105    /// has completed.
106    fn finalize(self) -> Result<Self::Remainder, Self::FinalizeError>;
107}
108
109/// Attempt to take a [bool]
110///
111/// [bool]: https://postcard.jamesmunns.com/wire-format#1---bool
112#[inline]
113pub fn try_take_bool<'de, F: Flavor<'de>>(f: &mut F) -> Result<Option<bool>, F::PopError> {
114    match f.pop()? {
115        0 => Ok(Some(false)),
116        1 => Ok(Some(true)),
117        _ => Ok(None),
118    }
119}
120
121/// Attempt to take a [u8]
122///
123/// [u8]: https://postcard.jamesmunns.com/wire-format#7---u8
124#[inline]
125pub fn try_take_u8<'de, F: Flavor<'de>>(f: &mut F) -> Result<u8, F::PopError> {
126    f.pop()
127}
128
129/// Attempt to take a [u16]
130///
131/// [u16]: https://postcard.jamesmunns.com/wire-format#8---u16
132#[inline]
133pub fn try_take_u16<'de, F: Flavor<'de>>(f: &mut F) -> Result<Option<u16>, F::PopError> {
134    let mut out = 0;
135    for i in 0..varint_max::<u16>() {
136        let val = f.pop()?;
137        let carry = (val & 0x7F) as u16;
138        out |= carry << (7 * i);
139
140        if (val & 0x80) == 0 {
141            if i == varint_max::<u16>() - 1 && val > max_of_last_byte::<u16>() {
142                break;
143            } else {
144                return Ok(Some(out));
145            }
146        }
147    }
148    Ok(None)
149}
150
151/// Attempt to take a [u32]
152///
153/// [u32]: https://postcard.jamesmunns.com/wire-format#9---u32
154#[inline]
155pub fn try_take_u32<'de, F: Flavor<'de>>(f: &mut F) -> Result<Option<u32>, F::PopError> {
156    let mut out = 0;
157    for i in 0..varint_max::<u32>() {
158        let val = f.pop()?;
159        let carry = (val & 0x7F) as u32;
160        out |= carry << (7 * i);
161
162        if (val & 0x80) == 0 {
163            if i == varint_max::<u32>() - 1 && val > max_of_last_byte::<u32>() {
164                break;
165            } else {
166                return Ok(Some(out));
167            }
168        }
169    }
170    Ok(None)
171}
172
173/// Attempt to take a [u64]
174///
175/// [u64]: https://postcard.jamesmunns.com/wire-format#10---u64
176#[inline]
177pub fn try_take_u64<'de, F: Flavor<'de>>(f: &mut F) -> Result<Option<u64>, F::PopError> {
178    let mut out = 0;
179    for i in 0..varint_max::<u64>() {
180        let val = f.pop()?;
181        let carry = (val & 0x7F) as u64;
182        out |= carry << (7 * i);
183
184        if (val & 0x80) == 0 {
185            if i == varint_max::<u64>() - 1 && val > max_of_last_byte::<u64>() {
186                break;
187            } else {
188                return Ok(Some(out));
189            }
190        }
191    }
192    Ok(None)
193}
194
195/// Attempt to take a [u128]
196///
197/// [u128]: https://postcard.jamesmunns.com/wire-format#11---u128
198#[inline]
199pub fn try_take_u128<'de, F: Flavor<'de>>(f: &mut F) -> Result<Option<u128>, F::PopError> {
200    let mut out = 0;
201    for i in 0..varint_max::<u128>() {
202        let val = f.pop()?;
203        let carry = (val & 0x7F) as u128;
204        out |= carry << (7 * i);
205
206        if (val & 0x80) == 0 {
207            if i == varint_max::<u128>() - 1 && val > max_of_last_byte::<u128>() {
208                break;
209            } else {
210                return Ok(Some(out));
211            }
212        }
213    }
214    Ok(None)
215}
216
217/// Attempt to take a [usize]
218///
219/// [usize]: https://postcard.jamesmunns.com/wire-format#isize-and-usize
220#[inline]
221pub fn try_take_usize<'de, F: Flavor<'de>>(f: &mut F) -> Result<Option<usize>, F::PopError> {
222    #[cfg(target_pointer_width = "16")]
223    let u = try_take_u16(f);
224
225    #[cfg(target_pointer_width = "32")]
226    let u = try_take_u32(f);
227
228    #[cfg(target_pointer_width = "64")]
229    let u = try_take_u64(f);
230
231    u.map(|u| u.map(|u| u as usize))
232}
233
234/// Attempt to take a [i8]
235///
236/// [i8]: https://postcard.jamesmunns.com/wire-format#2---i8
237#[inline]
238pub fn try_take_i8<'de, F: Flavor<'de>>(f: &mut F) -> Result<i8, F::PopError> {
239    let u = try_take_u8(f)?;
240    Ok(u as i8)
241}
242
243/// Attempt to take a [i16]
244///
245/// [i16]: https://postcard.jamesmunns.com/wire-format#3---i16
246#[inline]
247pub fn try_take_i16<'de, F: Flavor<'de>>(f: &mut F) -> Result<Option<i16>, F::PopError> {
248    let u = try_take_u16(f)?;
249    Ok(u.map(de_zig_zag_i16))
250}
251
252/// Attempt to take a [i32]
253///
254/// [i32]: https://postcard.jamesmunns.com/wire-format#4---i32
255#[inline]
256pub fn try_take_i32<'de, F: Flavor<'de>>(f: &mut F) -> Result<Option<i32>, F::PopError> {
257    let u = try_take_u32(f)?;
258    Ok(u.map(de_zig_zag_i32))
259}
260
261/// Attempt to take a [i64]
262///
263/// [i64]: https://postcard.jamesmunns.com/wire-format#5---i64
264#[inline]
265pub fn try_take_i64<'de, F: Flavor<'de>>(f: &mut F) -> Result<Option<i64>, F::PopError> {
266    let u = try_take_u64(f)?;
267    Ok(u.map(de_zig_zag_i64))
268}
269
270/// Attempt to take a [i128]
271///
272/// [i128]: https://postcard.jamesmunns.com/wire-format#6---i128
273#[inline]
274pub fn try_take_i128<'de, F: Flavor<'de>>(f: &mut F) -> Result<Option<i128>, F::PopError> {
275    let u = try_take_u128(f)?;
276    Ok(u.map(de_zig_zag_i128))
277}
278
279/// Attempt to take an [isize]
280///
281/// [isize]: https://postcard.jamesmunns.com/wire-format#isize-and-usize
282#[inline]
283pub fn try_take_isize<'de, F: Flavor<'de>>(f: &mut F) -> Result<Option<isize>, F::PopError> {
284    #[cfg(target_pointer_width = "16")]
285    {
286        let i = try_take_i16(f)?;
287        Ok(i.map(|i| i as isize))
288    }
289    #[cfg(target_pointer_width = "32")]
290    {
291        let i = try_take_i32(f)?;
292        Ok(i.map(|i| i as isize))
293    }
294    #[cfg(target_pointer_width = "64")]
295    {
296        let i = try_take_i64(f)?;
297        Ok(i.map(|i| i as isize))
298    }
299}
300
301/// Attempt to take an [f32]
302///
303/// [f32]: https://postcard.jamesmunns.com/wire-format#12---f32
304#[inline]
305pub fn try_take_f32<'de, F: Flavor<'de>>(f: &mut F) -> Result<f32, F::PopError> {
306    let bytes = f.try_take_n_temp(4)?;
307    let mut buf = [0u8; 4];
308    buf.copy_from_slice(bytes);
309    Ok(f32::from_bits(u32::from_le_bytes(buf)))
310}
311
312/// Attempt to take an [f64]
313///
314/// [f64]: https://postcard.jamesmunns.com/wire-format#13---f64
315#[inline]
316pub fn try_take_f64<'de, F: Flavor<'de>>(f: &mut F) -> Result<f64, F::PopError> {
317    let bytes = f.try_take_n_temp(8)?;
318    let mut buf = [0u8; 8];
319    buf.copy_from_slice(bytes);
320    Ok(f64::from_bits(u64::from_le_bytes(buf)))
321}
322
323/// Attempt to take a [byte array]
324///
325/// [byte array]: https://postcard.jamesmunns.com/wire-format#16---byte-array
326#[inline]
327pub fn try_take_bytes<'de, F: Flavor<'de>>(f: &mut F) -> Result<Option<&'de [u8]>, F::PopError> {
328    let len = match try_take_length(f) {
329        Ok(Some(l)) => l,
330        Ok(None) => return Ok(None),
331        Err(e) => return Err(e),
332    };
333    let sli = f.try_take_n(len)?;
334    Ok(Some(sli))
335}
336
337/// Attempt to take a [byte array]
338///
339/// NOTE: This is used for items that are not required to retain a borrow their data from the source.
340///
341/// [byte array]: https://postcard.jamesmunns.com/wire-format#16---byte-array
342#[inline]
343pub fn try_take_bytes_temp<'a, 'de: 'a, F: Flavor<'de>>(
344    f: &'a mut F,
345) -> Result<Option<&'a [u8]>, F::PopError> {
346    let len = match try_take_length(f) {
347        Ok(Some(l)) => l,
348        Ok(None) => return Ok(None),
349        Err(e) => return Err(e),
350    };
351    let sli = f.try_take_n_temp(len)?;
352    Ok(Some(sli))
353}
354
355/// Attempt to take a [string]
356///
357/// [string]: https://postcard.jamesmunns.com/wire-format#15---string
358#[inline]
359pub fn try_take_str<'de, F: Flavor<'de>>(f: &mut F) -> Result<Option<&'de str>, F::PopError> {
360    match try_take_bytes(f) {
361        Ok(Some(sli)) => Ok(core::str::from_utf8(sli).ok()),
362        Ok(None) => Ok(None),
363        Err(e) => Err(e),
364    }
365}
366
367/// Attempt to take a [string]
368///
369/// NOTE: This is used for items that are not required to retain a borrow their data from the source.
370///
371/// [string]: https://postcard.jamesmunns.com/wire-format#15---string
372#[inline]
373pub fn try_take_str_temp<'a, 'de: 'a, F: Flavor<'de>>(
374    f: &'a mut F,
375) -> Result<Option<&'a str>, F::PopError> {
376    match try_take_bytes_temp(f) {
377        Ok(Some(sli)) => Ok(core::str::from_utf8(sli).ok()),
378        Ok(None) => Ok(None),
379        Err(e) => Err(e),
380    }
381}
382
383/// Attempt to take a [char]
384///
385/// [char]: https://postcard.jamesmunns.com/wire-format#14---char
386#[inline]
387pub fn try_take_char<'de, F: Flavor<'de>>(f: &mut F) -> Result<Option<char>, F::PopError> {
388    let Some(sz) = try_take_usize(f)? else {
389        return Ok(None);
390    };
391    if sz > 4 {
392        return Ok(None);
393    }
394    let bytes: &[u8] = f.try_take_n_temp(sz)?;
395
396    // we pass the character through string conversion because
397    // this handles transforming the array of code units to a
398    // codepoint. we can't use char::from_u32() because it expects
399    // an already-processed codepoint.
400    let Ok(strsl) = core::str::from_utf8(bytes) else {
401        return Ok(None);
402    };
403
404    Ok(strsl.chars().next())
405}
406
407/// Attempt to take an [option] discriminant
408///
409/// [option]: https://postcard.jamesmunns.com/wire-format#17---option
410#[inline]
411pub fn try_take_option_discrim<'de, F: Flavor<'de>>(
412    f: &mut F,
413) -> Result<Option<bool>, F::PopError> {
414    try_take_bool(f)
415}
416
417/// Attempt to take a discriminant
418///
419/// Used for:
420///
421/// * [unit variant]
422/// * [newtype variant]
423/// * [tuple variant]
424/// * [struct variant]
425///
426/// [unit variant]: https://postcard.jamesmunns.com/wire-format#20---unit_variant
427/// [newtype variant]: https://postcard.jamesmunns.com/wire-format#22---newtype_variant
428/// [tuple variant]: https://postcard.jamesmunns.com/wire-format#26---tuple_variant
429/// [struct variant]: https://postcard.jamesmunns.com/wire-format#29---struct_variant
430#[inline]
431pub fn try_take_discriminant<'de, F: Flavor<'de>>(f: &mut F) -> Result<Option<u32>, F::PopError> {
432    try_take_u32(f)
433}
434
435/// Attempt to take a length
436///
437/// Used for:
438///
439/// * [seq]
440/// * [map]
441/// * [byte array]
442/// * [string]
443///
444/// [seq]: https://postcard.jamesmunns.com/wire-format#23---seq
445/// [map]: https://postcard.jamesmunns.com/wire-format#27---map
446/// [byte array]: https://postcard.jamesmunns.com/wire-format#16---byte-array
447/// [string]: https://postcard.jamesmunns.com/wire-format#15---string
448#[inline]
449pub fn try_take_length<'de, F: Flavor<'de>>(f: &mut F) -> Result<Option<usize>, F::PopError> {
450    try_take_usize(f)
451}