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}