Skip to main content

bincode_next/
error.rs

1//! Errors that can be encountering by Encoding and Decoding.
2// Necessary to allow dead code since we don't use all variants in all configurations
3#![allow(dead_code)]
4
5#[doc(hidden)]
6#[macro_export]
7macro_rules! bincode_error {
8    (
9        $(#[$meta:meta])*
10        $vis:vis enum $name:ident {
11            $(
12                $(#[$variant_meta:meta])*
13                $variant:ident $( { $( $(#[$field_meta:meta])* $field:ident : $ftype:ty ),* $(,)? } )? $( ( $( $(#[$tuple_meta:meta])* $tname:ident : $ttype:ty ),* $(,)? ) )?
14            ),* $(,)?
15        }
16    ) => {
17        $(#[$meta])*
18        $vis enum $name {
19            $(
20                $(#[$variant_meta])*
21                $variant $( { $( $(#[$field_meta])* $field : $ftype ),* } )? $( ( $( $(#[$tuple_meta])* $ttype ),* ) )?,
22            )*
23        }
24
25        pastey::paste! {
26            $(
27                $(#[$variant_meta])*
28                #[doc(hidden)]
29                #[cold]
30                #[track_caller]
31                #[inline(never)]
32                pub const fn [<cold_ $name:snake _ $variant:snake>]<T>(
33                    $($($field : $ftype),*)?
34                    $($($tname : $ttype),*)?
35                ) -> core::result::Result<T, $name> {
36                    core::result::Result::Err($name::$variant $( { $($field),* } )? $( ( $( $tname ),* ) )?)
37                }
38            )*
39        }
40    };
41}
42
43bincode_error! {
44    /// Errors that can be encountered by encoding a type
45    #[non_exhaustive]
46    #[derive(Debug)]
47    pub enum EncodeError {
48    /// The writer ran out of storage.
49    UnexpectedEnd,
50
51    /// The `RefCell<T>` is already borrowed
52    RefCellAlreadyBorrowed {
53        /// The inner borrow error
54        inner: core::cell::BorrowError,
55        /// the type name of the `RefCell` being encoded that is currently borrowed.
56        type_name: &'static str,
57    },
58
59    /// An uncommon error occurred, see the inner text for more information
60    Other(inner: &'static str),
61
62    /// An uncommon error occurred, see the inner text for more information
63    #[cfg(feature = "alloc")]
64    OtherString(inner: alloc::string::String),
65
66    /// A `std::path::Path` was being encoded but did not contain a valid `&str` representation
67    #[cfg(feature = "std")]
68    InvalidPathCharacters,
69
70    /// The targeted writer encountered an `std::io::Error`
71    #[cfg(feature = "std")]
72    Io {
73        /// The encountered error
74        inner: std::io::Error,
75        /// The amount of bytes that were written before the error occurred
76        index: usize,
77    },
78
79    /// The encoder tried to encode a `Mutex` or `RwLock`, but the locking failed
80    #[cfg(feature = "std")]
81    LockFailed {
82        /// The type name of the mutex for debugging purposes
83        type_name: &'static str,
84    },
85
86    /// The encoder tried to encode a `SystemTime`, but it was before `SystemTime::UNIX_EPOCH`
87    #[cfg(feature = "std")]
88    InvalidSystemTime {
89        /// The error that was thrown by the `SystemTime`
90        inner: std::time::SystemTimeError,
91        /// The `SystemTime` that caused the error
92        time: std::boxed::Box<std::time::SystemTime>,
93    },
94
95    #[cfg(feature = "serde")]
96    /// A serde-specific error that occurred while decoding.
97    Serde(inner: crate::features::serde::EncodeError),
98}
99}
100
101impl core::fmt::Display for EncodeError {
102    fn fmt(
103        &self,
104        f: &mut core::fmt::Formatter<'_>,
105    ) -> core::fmt::Result {
106        // TODO: Improve this?
107        write!(f, "{self:?}")
108    }
109}
110
111impl core::error::Error for EncodeError {
112    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
113        match self {
114            | Self::RefCellAlreadyBorrowed { inner, .. } => Some(inner),
115            #[cfg(feature = "std")]
116            | Self::Io { inner, .. } => Some(inner),
117            #[cfg(feature = "std")]
118            | Self::InvalidSystemTime { inner, .. } => Some(inner),
119            | _ => None,
120        }
121    }
122}
123impl core::error::Error for DecodeError {
124    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
125        match self {
126            | Self::Utf8 { inner } => Some(inner),
127            | _ => None,
128        }
129    }
130}
131
132bincode_error! {
133    /// Errors that can be encountered by decoding a type
134    #[non_exhaustive]
135    #[derive(Debug)]
136    pub enum DecodeError {
137    /// The reader reached its end but more bytes were expected.
138    UnexpectedEnd {
139        /// Gives an estimate of how many extra bytes are needed.
140        ///
141        /// **Note**: this is only an estimate and not indicative of the actual bytes needed.
142        ///
143        /// **Note**: Bincode has no look-ahead mechanism. This means that this will only return the amount of bytes to be read for the current action, and not take into account the entire data structure being read.
144        additional: usize,
145    },
146
147    /// The given configuration limit was exceeded
148    LimitExceeded,
149
150    /// Invalid type was found. The decoder tried to read type `expected`, but found type `found` instead.
151    InvalidIntegerType {
152        /// The type that was being read from the reader
153        expected: IntegerType,
154        /// The type that was encoded in the data
155        found: IntegerType,
156    },
157
158    /// The decoder tried to decode any of the `NonZero*` types but the value is zero
159    NonZeroTypeIsZero {
160        /// The type that was being read from the reader
161        non_zero_type: IntegerType,
162    },
163
164    /// Invalid enum variant was found. The decoder tried to decode variant index `found`, but the variant index should be between `min` and `max`.
165    UnexpectedVariant {
166        /// The type name that was being decoded.
167        type_name: &'static str,
168
169        /// The variants that are allowed
170        allowed: &'static AllowedEnumVariants,
171
172        /// The index of the enum that the decoder encountered
173        found: u32,
174    },
175
176    /// The decoder tried to decode a `str`, but an utf8 error was encountered.
177    Utf8 {
178        /// The inner error
179        inner: core::str::Utf8Error,
180    },
181
182    /// The decoder tried to decode a `char` and failed. The given buffer contains the bytes that are read at the moment of failure.
183    InvalidCharEncoding(inner: [u8; 4]),
184
185    /// The decoder tried to decode a `bool` and failed. The given value is what is actually read.
186    InvalidBooleanValue(inner: u8),
187
188    /// Invalid CBOR "additional info" value (28-31) was found.
189    InvalidCborInfo(inner: u8),
190
191    /// The decoder tried to decode an array of length `required`, but the binary data contained an array of length `found`.
192    ArrayLengthMismatch {
193        /// The length of the array required by the rust type.
194        required: usize,
195        /// The length of the array found in the binary format.
196        found: usize,
197    },
198
199    /// The encoded value is outside of the range of the target usize type.
200    ///
201    /// This can happen if an usize was encoded on an architecture with a larger
202    /// usize type and then decoded on an architecture with a smaller one. For
203    /// example going from a 64 bit architecture to a 32 or 16 bit one may
204    /// cause this error.
205    OutsideUsizeRange(inner: u64),
206
207    /// The encoded value is outside of the range of the target isize type.
208    ///
209    /// This can happen if an isize was encoded on an architecture with a larger
210    /// isize type and then decoded on an architecture with a smaller one. For
211    /// example going from a 64 bit architecture to a 32 or 16 bit one may
212    /// cause this error.
213    OutsideIsizeRange(inner: i64),
214
215    /// Tried to decode an enum with no variants
216    EmptyEnum {
217        /// The type that was being decoded
218        type_name: &'static str,
219    },
220
221    /// The decoder tried to decode a Duration and overflowed the number of seconds.
222    InvalidDuration {
223        /// The number of seconds in the duration.
224        secs: u64,
225
226        /// The number of nanoseconds in the duration, which when converted to seconds and added to
227        /// `secs`, overflows a `u64`.
228        nanos: u32,
229    },
230
231    /// The decoder tried to decode a `SystemTime` and overflowed
232    InvalidSystemTime {
233        /// The duration which could not have been added to
234        /// [`UNIX_EPOCH`\](`std::time::SystemTime::UNIX_EPOCH`)
235        duration: core::time::Duration,
236    },
237
238    /// The decoder tried to decode a `CString`, but the incoming data contained a 0 byte
239    #[cfg(feature = "std")]
240    CStringNulError {
241        /// Nul byte position
242        position: usize,
243    },
244
245    /// The reader encountered an IO error but more bytes were expected.
246    #[cfg(feature = "std")]
247    Io {
248        /// The IO error expected
249        inner: std::io::Error,
250
251        /// Gives an estimate of how many extra bytes are needed.
252        ///
253        /// **Note**: this is only an estimate and not indicative of the actual bytes needed.
254        ///
255        /// **Note**: Bincode has no look-ahead mechanism. This means that this will only return the amount of bytes to be read for the current action, and not take into account the entire data structure being read.
256        additional: usize,
257    },
258
259    /// An uncommon error occurred, see the inner text for more information
260    Other(inner: &'static str),
261
262    /// An uncommon error occurred, see the inner text for more information
263    #[cfg(feature = "alloc")]
264    OtherString(inner: alloc::string::String),
265
266    #[cfg(feature = "serde")]
267    /// A serde-specific error that occurred while decoding.
268    Serde(inner: crate::features::serde::DecodeError),
269
270    /// The schema of the type being decoded does not match the schema of the encoded data.
271    SchemaMismatch {
272        /// The hash that was expected by the decoder
273        expected: u64,
274        /// The hash that was found in the encoded data
275        actual: u64,
276    },
277
278    /// The decoder tried to decode a map, but a duplicate key was found.
279    DuplicateMapKey,
280
281    /// The decoder tried to decode a map, but the keys were not in the correct order.
282    InvalidMapOrder,
283}
284}
285
286impl core::fmt::Display for DecodeError {
287    fn fmt(
288        &self,
289        f: &mut core::fmt::Formatter<'_>,
290    ) -> core::fmt::Result {
291        // TODO: Improve this?
292        write!(f, "{self:?}")
293    }
294}
295
296impl DecodeError {
297    /// If the current error is `InvalidIntegerType`, change the `expected` and
298    /// `found` values from `Ux` to `Ix`. This is needed to have correct error
299    /// reporting in `src/varint/decode_signed.rs` since this calls
300    /// `src/varint/decode_unsigned.rs` and needs to correct the `expected` and
301    /// `found` types.
302    #[cold]
303    #[inline(never)]
304    pub(crate) fn change_integer_type_to_signed(self) -> Self {
305        match self {
306            | Self::InvalidIntegerType { expected, found } => {
307                Self::InvalidIntegerType {
308                    expected: expected.into_signed(),
309                    found: found.into_signed(),
310                }
311            },
312            | other => other,
313        }
314    }
315}
316
317/// Indicates which enum variants are allowed
318#[non_exhaustive]
319#[derive(Debug, PartialEq, Eq)]
320pub enum AllowedEnumVariants {
321    /// All values between `min` and `max` (inclusive) are allowed
322    #[allow(missing_docs)]
323    Range { min: u32, max: u32 },
324    /// Each one of these values is allowed
325    Allowed(&'static [u32]),
326}
327
328/// Integer types. Used by [`DecodeError`\]. These types have no purpose other than being shown in errors.
329#[non_exhaustive]
330#[derive(Debug, PartialEq, Eq)]
331#[allow(missing_docs)]
332pub enum IntegerType {
333    U8,
334    U16,
335    U32,
336    U64,
337    U128,
338    Usize,
339
340    I8,
341    I16,
342    I32,
343    I64,
344    I128,
345    Isize,
346
347    Reserved,
348}
349
350impl IntegerType {
351    /// Change the `Ux` value to the associated `Ix` value.
352    /// Returns the old value if `self` is already `Ix`.
353    pub(crate) const fn into_signed(self) -> Self {
354        match self {
355            | Self::U8 => Self::I8,
356            | Self::U16 => Self::I16,
357            | Self::U32 => Self::I32,
358            | Self::U64 => Self::I64,
359            | Self::U128 => Self::I128,
360            | Self::Usize => Self::Isize,
361
362            | other => other,
363        }
364    }
365}