Skip to main content

musli_zerocopy/
error.rs

1use core::alloc::Layout;
2use core::any::type_name;
3use core::fmt;
4use core::ops::{Range, RangeFrom};
5use core::str::Utf8Error;
6
7use crate::buf::AllocError;
8
9mod sealed {
10    pub trait Sealed {}
11    impl Sealed for () {}
12}
13
14/// Helper trait to convert any type into a type-erased [`Repr`] used for diagnostics.
15pub trait IntoRepr
16where
17    Self: self::sealed::Sealed,
18{
19    #[doc(hidden)]
20    fn into_repr(self) -> Repr;
21}
22
23impl IntoRepr for () {
24    fn into_repr(self) -> Repr {
25        Repr {
26            kind: ReprKind::Unit,
27        }
28    }
29}
30
31macro_rules! impl_into_repr {
32    ($($ty:ty, $repr:ident),* $(,)?) => {
33        $(
34        impl self::sealed::Sealed for $ty {
35        }
36
37        impl IntoRepr for $ty {
38            fn into_repr(self) -> Repr {
39                Repr {
40                    kind: ReprKind::$repr(self),
41                }
42            }
43        }
44        )*
45    }
46}
47
48impl_into_repr! {
49    u8, U8,
50    u16, U16,
51    u32, U32,
52    u64, U64,
53    u128, U128,
54    usize, Usize,
55    i8, I8,
56    i16, I16,
57    i32, I32,
58    i64, I64,
59    i128, I128,
60    isize, Isize,
61}
62
63#[derive(Debug)]
64#[cfg_attr(test, derive(PartialEq))]
65enum ReprKind {
66    Unit,
67    U8(u8),
68    U16(u16),
69    U32(u32),
70    U64(u64),
71    U128(u128),
72    I8(i8),
73    I16(i16),
74    I32(i32),
75    I64(i64),
76    I128(i128),
77    Usize(usize),
78    Isize(isize),
79}
80
81/// Indicates the representation that was tried to coerce into an enum.
82#[derive(Debug)]
83#[cfg_attr(test, derive(PartialEq))]
84#[non_exhaustive]
85pub struct Repr {
86    kind: ReprKind,
87}
88
89impl fmt::Display for Repr {
90    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91        match &self.kind {
92            ReprKind::Unit => write!(f, "()"),
93            ReprKind::U8(value) => write!(f, "{value} (u8)"),
94            ReprKind::U16(value) => write!(f, "{value} (u16)"),
95            ReprKind::U32(value) => write!(f, "{value} (u32)"),
96            ReprKind::U64(value) => write!(f, "{value} (u64)"),
97            ReprKind::U128(value) => write!(f, "{value} (u128)"),
98            ReprKind::I8(value) => write!(f, "{value} (i8)"),
99            ReprKind::I16(value) => write!(f, "{value} (i16)"),
100            ReprKind::I32(value) => write!(f, "{value} (i32)"),
101            ReprKind::I64(value) => write!(f, "{value} (i64)"),
102            ReprKind::I128(value) => write!(f, "{value} (i128)"),
103            ReprKind::Usize(value) => write!(f, "{value} (usize)"),
104            ReprKind::Isize(value) => write!(f, "{value} (isize)"),
105        }
106    }
107}
108
109/// Errors produced through coercion.
110#[derive(Debug)]
111#[cfg_attr(test, derive(PartialEq))]
112pub struct CoerceError {
113    kind: CoerceErrorKind,
114}
115
116impl CoerceError {
117    #[inline]
118    pub(crate) const fn new(kind: CoerceErrorKind) -> Self {
119        Self { kind }
120    }
121}
122
123impl fmt::Display for CoerceError {
124    #[inline]
125    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126        self.kind.fmt(f)
127    }
128}
129
130impl core::error::Error for CoerceError {}
131
132#[derive(Debug)]
133#[cfg_attr(test, derive(PartialEq))]
134#[non_exhaustive]
135pub(crate) enum CoerceErrorKind {
136    SplitAt { at: usize, len: usize },
137    SliceLengthOverflow { item: usize, len: usize },
138    LengthOverflow { len: usize, size: usize },
139    InvalidLayout { size: Option<usize>, align: usize },
140    InvalidOffsetRange { offset: usize, end: usize },
141    Underflow { at: usize, len: usize },
142    Overflow { at: usize, len: usize },
143}
144
145impl fmt::Display for CoerceErrorKind {
146    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147        match *self {
148            Self::SplitAt { at, len } => {
149                write!(f, "Split point {at} is out of bounds 0..={len}")
150            }
151            Self::SliceLengthOverflow { item, len } => {
152                write!(
153                    f,
154                    "Length overflowed when trying to take {item} elements of size {len}"
155                )
156            }
157            Self::LengthOverflow { len, size } => {
158                write!(
159                    f,
160                    "Length overflowed when trying to take {len} elements of size {size}"
161                )
162            }
163            Self::InvalidLayout {
164                size: Some(size),
165                align,
166            } => {
167                write!(f, "Invalid layout for size {size} and alignment {align}")
168            }
169            Self::InvalidLayout { size: None, align } => {
170                write!(
171                    f,
172                    "Invalid layout for overflowing size and alignment {align}"
173                )
174            }
175            Self::InvalidOffsetRange { offset, end } => {
176                write!(f, "Offset {offset} not in valid range 0-{end}")
177            }
178            Self::Underflow { at, len } => {
179                write!(f, "Arithmetic underflow calculating {at} - {len}")
180            }
181            Self::Overflow { at, len } => {
182                write!(f, "Arithmetic overflow calculating {at} + {len}")
183            }
184        }
185    }
186}
187
188/// Müsli's zero copy error type.
189#[derive(Debug)]
190#[cfg_attr(test, derive(PartialEq))]
191pub struct Error {
192    kind: ErrorKind,
193}
194
195impl Error {
196    #[inline]
197    pub(crate) const fn new(kind: ErrorKind) -> Self {
198        Self { kind }
199    }
200
201    #[inline(always)]
202    #[doc(hidden)]
203    pub fn __invalid_enum_discriminant<T>(discriminant: impl IntoRepr) -> Self {
204        Self::new(ErrorKind::InvalidDiscriminant {
205            name: type_name::<T>(),
206            discriminant: discriminant.into_repr(),
207        })
208    }
209}
210
211impl fmt::Display for Error {
212    #[inline]
213    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
214        self.kind.fmt(f)
215    }
216}
217
218impl core::error::Error for Error {}
219
220impl From<AllocError> for Error {
221    #[inline]
222    fn from(error: AllocError) -> Self {
223        Self::new(ErrorKind::AllocError { error })
224    }
225}
226
227impl From<CoerceError> for Error {
228    #[inline]
229    fn from(error: CoerceError) -> Self {
230        Self::new(ErrorKind::CoerceError { error })
231    }
232}
233
234#[derive(Debug)]
235#[cfg_attr(test, derive(PartialEq))]
236#[non_exhaustive]
237pub(crate) enum ErrorKind {
238    AllocError {
239        error: AllocError,
240    },
241    Utf8Error {
242        error: Utf8Error,
243    },
244    CoerceError {
245        error: CoerceError,
246    },
247    AlignmentRangeMismatch {
248        addr: usize,
249        range: Range<usize>,
250        align: usize,
251    },
252    AlignmentRangeFromMismatch {
253        range: RangeFrom<usize>,
254        align: usize,
255    },
256    LayoutMismatch {
257        range: Range<usize>,
258        layout: Layout,
259    },
260    OutOfRangeBounds {
261        range: Range<usize>,
262        len: usize,
263    },
264    OutOfRangeFromBounds {
265        range: RangeFrom<usize>,
266        len: usize,
267    },
268    NonZeroZeroed {
269        range: Range<usize>,
270    },
271    IndexOutOfBounds {
272        index: usize,
273        len: usize,
274    },
275    ControlRangeOutOfBounds {
276        range: Range<usize>,
277        len: usize,
278    },
279    StrideOutOfBounds {
280        index: usize,
281        len: usize,
282    },
283    InvalidDiscriminant {
284        name: &'static str,
285        discriminant: Repr,
286    },
287    InvalidCharRepr {
288        repr: u32,
289    },
290    InvalidBoolRepr {
291        repr: u8,
292    },
293    StackOverflow {
294        capacity: usize,
295    },
296    #[cfg(feature = "alloc")]
297    CapacityError,
298    #[cfg(feature = "alloc")]
299    FailedPhf,
300}
301
302impl fmt::Display for ErrorKind {
303    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
304        match self {
305            ErrorKind::AllocError { error } => error.fmt(f),
306            ErrorKind::Utf8Error { error } => error.fmt(f),
307            ErrorKind::CoerceError { error } => error.fmt(f),
308            ErrorKind::AlignmentRangeMismatch { addr, range, align } => {
309                write!(
310                    f,
311                    "Alignment mismatch, expected alignment {align} for range {range:?} at address {addr:x}"
312                )
313            }
314            ErrorKind::AlignmentRangeFromMismatch { range, align } => {
315                write!(
316                    f,
317                    "Alignment mismatch, expected alignment {align} for range {range:?}"
318                )
319            }
320            ErrorKind::LayoutMismatch { range, layout } => {
321                write!(
322                    f,
323                    "Layout mismatch, expected {layout:?} for range {range:?}"
324                )
325            }
326            ErrorKind::OutOfRangeBounds { range, len } => {
327                write!(f, "Range {range:?} out of bound 0-{len}")
328            }
329            ErrorKind::OutOfRangeFromBounds { range, len } => {
330                write!(f, "Range {range:?} out of bound 0-{len}")
331            }
332            ErrorKind::NonZeroZeroed { range } => {
333                write!(f, "Expected non-zero range at {range:?}")
334            }
335            ErrorKind::IndexOutOfBounds { index, len } => {
336                write!(f, "Index {index} out of bound 0-{len}")
337            }
338            ErrorKind::ControlRangeOutOfBounds { range, len } => {
339                write!(f, "Control range {range:?} out of bound 0-{len}")
340            }
341            ErrorKind::StrideOutOfBounds { index, len } => {
342                write!(f, "Stride at index {index} out of bound 0-{len}")
343            }
344            ErrorKind::InvalidDiscriminant { name, discriminant } => {
345                write!(f, "Invalid discriminant {discriminant} for enum {name}")
346            }
347            ErrorKind::InvalidCharRepr { repr } => {
348                write!(f, "Invalid char representation {repr}")
349            }
350            ErrorKind::InvalidBoolRepr { repr } => {
351                write!(f, "Invalid bool representation {repr}")
352            }
353            ErrorKind::StackOverflow { capacity } => {
354                write!(f, "Stack with capacity {capacity} overflowed")
355            }
356            #[cfg(feature = "alloc")]
357            ErrorKind::CapacityError => {
358                write!(f, "Out of capacity")
359            }
360            #[cfg(feature = "alloc")]
361            ErrorKind::FailedPhf => {
362                write!(f, "Failed to construct perfect hash for map")
363            }
364        }
365    }
366}