Skip to main content

memapi2/
error.rs

1use {
2    crate::{Layout, data::type_props::SizedProps},
3    core::fmt::{Debug, Display, Formatter, Result as FmtResult}
4};
5
6/// Helper macro to implement the Error trait based on its availability. Uses [`std::error::Error`]
7/// if available, or [`core::error::Error`] if not and on a Rust version which supports it.
8macro_rules! impl_error {
9    ($ty:ident) => {
10        #[cfg(feature = "std")]
11        impl std::error::Error for $ty {}
12        #[cfg(not(feature = "std"))]
13        // error_in_core stable since 1.81
14        #[rustversion::since(1.81)]
15        impl core::error::Error for $ty {}
16    };
17}
18
19/// Errors for allocator operations.
20#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21#[repr(u8)]
22pub enum Error {
23    /// The underlying allocator failed to allocate using the given layout; see the contained cause.
24    ///
25    /// The cause may or may not be accurate depending on the type and environment.
26    AllocFailed(Layout, Cause),
27    /// The layout computed with the given size and alignment is invalid; see the contained reason.
28    InvalidLayout(usize, usize, LayoutErr),
29    /// A zero-sized allocation was requested. This is treated as an error as several allocators do
30    /// not support such requests or respond to them strangely.
31    ///
32    /// In most reasonable cases, [`layout.dangling()`](Layout::dangling) can and should be used
33    /// instead.
34    ZeroSizedLayout,
35    /// An attempt was made to deallocate a dangling pointer.
36    DanglingDeallocation,
37    /// Attempted to grow to a smaller size.
38    GrowSmallerNewLayout(usize, usize),
39    /// Attempted to shrink to a larger size.
40    ShrinkLargerNewLayout(usize, usize),
41    /// An arithmetic error.
42    ArithmeticError(ArithErr),
43    /// An unwinding panic occurred in a function which does not support unwinding; likely FFI.
44    #[allow(unused_qualifications)]
45    CaughtUnwind,
46    /// Any other kind of error, in the form of a string.
47    Other(&'static str)
48}
49
50impl Display for Error {
51    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
52        use Error::{
53            AllocFailed,
54            ArithmeticError,
55            CaughtUnwind,
56            DanglingDeallocation,
57            GrowSmallerNewLayout,
58            InvalidLayout,
59            Other,
60            ShrinkLargerNewLayout,
61            ZeroSizedLayout
62        };
63
64        match self {
65            AllocFailed(l, cause) => write!(
66                f,
67                "allocation failed:\n\tlayout:\n\t\tsize: {}\n\t\talign: {}\n\tcause: {}",
68                l.size(),
69                l.align(),
70                cause
71            ),
72            InvalidLayout(sz, aln, e) => write!(
73                f,
74                "computed invalid layout:\n\tsize: {}\n\talign: {}\n\treason: {}",
75                sz, aln, e
76            ),
77            ZeroSizedLayout => {
78                write!(f, "received a zero-sized layout")
79            }
80            DanglingDeallocation => write!(f, "attempted to deallocate a dangling pointer"),
81            GrowSmallerNewLayout(old, new) => {
82                write!(f, "attempted to grow from a size of {} to a smaller size of {}", old, new)
83            }
84            ShrinkLargerNewLayout(old, new) => {
85                write!(f, "attempted to shrink from a size of {} to a larger size of {}", old, new)
86            }
87            ArithmeticError(overflow) => write!(f, "{}", overflow),
88            CaughtUnwind => {
89                write!(f, "unwind caught in unsupported function")
90            }
91            Other(other) => write!(f, "{}", other)
92        }
93    }
94}
95
96impl_error! { Error }
97
98/// The cause of an error.
99#[derive(Debug, Copy, Clone, PartialEq, Eq)]
100#[repr(u8)]
101pub enum Cause {
102    /// The cause is unknown.
103    ///
104    /// This most commonly means an [`OSErr`](Cause::OSErr) occurred, but `os_err_reporting` is
105    /// disabled or the allocator does not support OS error reporting.
106    Unknown,
107    /// The allocator ran out of memory.
108    ///
109    /// This should only be used when the __allocator__ runs out of memory and doesn't grow. Use
110    /// [`OSErr`](Cause::OSErr) if the system runs out of memory.
111    OutOfMemory,
112    /// Any other cause, in the form of a string.
113    Other(&'static str),
114    #[cfg(feature = "os_err_reporting")]
115    /// The cause is described in the contained OS error.
116    ///
117    /// The error may or may not be accurate depending on the environment.
118    OSErr(i32)
119}
120
121impl Display for Cause {
122    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
123        match self {
124            Cause::Unknown => write!(f, "unknown"),
125            Cause::OutOfMemory => write!(f, "out of memory"),
126            Cause::Other(other) => write!(f, "{}", other),
127            #[cfg(feature = "os_err_reporting")]
128            Cause::OSErr(e) => write!(f, "os error:\n\t{}", std::io::Error::from_raw_os_error(*e))
129        }
130    }
131}
132
133impl_error! { Cause }
134
135/// An error that can occur when computing a layout.
136#[derive(Debug, Clone, Copy, PartialEq, Eq)]
137#[repr(u8)]
138pub enum LayoutErr {
139    /// The alignment was zero.
140    ZeroAlign,
141    /// The alignment was not a power of two.
142    NonPowerOfTwoAlign,
143    /// The requested size was greater than
144    /// [`USIZE_MAX_NO_HIGH_BIT`](crate::helpers::USIZE_MAX_NO_HIGH_BIT) when
145    /// rounded up to the nearest multiple of the requested alignment.
146    ExceedsMax,
147    /// An arithmetic error occurred.
148    ArithErr(ArithErr),
149    /// An error occurred while rounding the alignment of the requested layout up to a value
150    /// compatible with C's `aligned_alloc`.
151    CRoundUp
152}
153
154impl Display for LayoutErr {
155    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
156        match self {
157            LayoutErr::ZeroAlign => write!(f, "alignment is zero"),
158            LayoutErr::NonPowerOfTwoAlign => {
159                write!(f, "alignment isn't a power of two")
160            }
161            LayoutErr::ExceedsMax => write!(f, "size would overflow when rounded up to alignment"),
162            LayoutErr::ArithErr(overflow) => write!(f, "layout err: {}", overflow),
163            LayoutErr::CRoundUp => {
164                write!(f, "failed to round layout alignment up to a multiple of {}", usize::SZ)
165            }
166        }
167    }
168}
169
170impl_error! { LayoutErr }
171
172/// An arithmetic operation which would cause an "overflow."
173///
174/// Divide-by-zero errors and exponentiation to powers larger than [`u32::MAX`] are considered
175/// overflows.
176#[derive(Debug, Copy, Clone, PartialEq, Eq)]
177pub struct ArithErr(pub usize, pub ArithOp, pub usize);
178
179impl Display for ArithErr {
180    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
181        write!(f, "arithmetic operation would overflow: {} {} {}", self.0, self.1, self.2)
182    }
183}
184
185impl_error! { ArithErr }
186
187/// An arithmetic operation.
188#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
189#[repr(u8)]
190pub enum ArithOp {
191    /// Addition. (+)
192    Add,
193    /// Subtraction. (-)
194    Sub,
195    /// Multiplication. (*)
196    Mul,
197    /// Division. (/)
198    Div,
199    /// Modulo. (%)
200    Rem,
201    /// Exponentiation. (**)
202    Pow
203}
204
205impl Display for ArithOp {
206    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
207        match self {
208            ArithOp::Add => write!(f, "+"),
209            ArithOp::Sub => write!(f, "-"),
210            ArithOp::Mul => write!(f, "*"),
211            ArithOp::Div => write!(f, "/"),
212            ArithOp::Rem => write!(f, "%"),
213            ArithOp::Pow => write!(f, "**")
214        }
215    }
216}