1#![doc = include_str!("../tests/i32-error-message.expected")]
34#![doc = include_str!("../tests/2.5k-error-message.expected")]
37#![cfg_attr(not(feature = "std"), no_std)]
59
60extern crate alloc;
61
62use alloc::{
63 alloc::{alloc, handle_alloc_error, Layout},
64 boxed::Box,
65};
66use core::{any, fmt, mem::MaybeUninit, ptr::NonNull};
67
68#[inline(always)]
73pub fn new<T>(x: T) -> Result<Box<T>, ErrorWith<T>> {
74 match imp(x) {
75 Ok(it) => Ok(it),
76 Err(e) => Err(ErrorWith(e)),
77 }
78}
79
80#[inline(always)]
86pub fn or_drop<T>(x: T) -> Result<Box<T>, Error> {
87 match new(x) {
88 Ok(it) => Ok(it),
89 Err(e) => Err(e.without_payload()),
90 }
91}
92
93#[inline(always)]
94fn imp<T>(x: T) -> Result<Box<T>, T> {
95 let layout = Layout::for_value(&x);
96 match layout.size() == 0 {
97 true => {
98 let ptr = NonNull::<T>::dangling().as_ptr();
99 Ok(unsafe { Box::from_raw(ptr) })
101 }
102 false => {
103 let ptr = unsafe { alloc(layout) }.cast::<T>();
105 match ptr.is_null() {
106 true => Err(x),
107 false => {
108 let mut heap = unsafe { Box::<MaybeUninit<T>>::from_raw(ptr.cast()) };
113 heap.write(x);
114 Ok(unsafe { Box::from_raw(Box::into_raw(heap).cast()) })
116 }
117 }
118 }
119 }
120}
121
122pub struct Error {
126 info: fn() -> Info,
128}
129
130impl fmt::Debug for Error {
131 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
132 let Info { layout, name } = self.info();
133 let mut d = f.debug_struct("Error");
134 d.field("layout", &layout).field("name", &name);
135 d.finish()
136 }
137}
138
139impl fmt::Display for Error {
140 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141 write_info(self.info(), f)
142 }
143}
144
145fn write_info(info: Info, f: &mut fmt::Formatter<'_>) -> fmt::Result {
146 let Info { layout, name } = info;
147
148 let mut size = layout.size() as f64;
149 let mut prefix = "";
150 let boundary = 1024.0;
151 for next in [
152 "kibi", "mebi", "gibi", "tebi", "pebi", "exbi", "zebi", "yobi",
153 ] {
154 if size <= boundary {
155 break;
156 }
157 size /= boundary;
158 prefix = next;
159 }
160 let precision = match fract(size) == 0.0 {
161 true => 0,
162 false => 2,
163 };
164 f.write_fmt(format_args!(
165 "memory allocation of {size:.precision$} {prefix}bytes (for type {name}) failed",
166 ))
167}
168
169const fn fract(x: f64) -> f64 {
171 x - trunc(x)
172}
173
174const fn trunc(x: f64) -> f64 {
177 let mut i: u64 = x.to_bits();
180 let e: i64 = (i >> 52 & 0x7ff) as i64 - 0x3ff + 12;
181 let m: u64 = -1i64 as u64 >> e;
182
183 if e >= 52 + 12 {
184 return x;
185 }
186
187 if (i & m) == 0 {
188 return x;
189 }
190 i &= !m;
192 f64::from_bits(i)
193}
194
195#[cfg(not(feature = "std"))]
196impl core::error::Error for Error {}
197
198#[cfg(feature = "std")]
199impl std::error::Error for Error {}
200
201impl Error {
202 #[inline(always)]
203 fn info(&self) -> Info {
204 (self.info)()
205 }
206 #[inline(always)]
210 pub fn handle(self) -> ! {
211 handle_alloc_error(self.layout())
212 }
213 #[inline(always)]
215 pub fn layout(&self) -> Layout {
216 self.info().layout
217 }
218}
219
220#[cfg(feature = "std")]
221impl From<Error> for std::io::Error {
222 fn from(value: Error) -> Self {
225 let kind = std::io::ErrorKind::OutOfMemory;
226
227 match or_drop(value) {
232 Ok(source) => {
233 std::io::Error::new(kind, source as Box<dyn std::error::Error + Send + Sync>)
234 }
235 Err(_cannot_preserve) => std::io::Error::from(kind),
236 }
237 }
238}
239
240#[cfg(feature = "std")]
241impl From<Error> for std::io::ErrorKind {
242 fn from(_: Error) -> Self {
243 std::io::ErrorKind::OutOfMemory
244 }
245}
246
247trait Indirect: Sized {
249 fn info() -> Info {
250 Info {
251 layout: Layout::new::<Self>(),
252 name: any::type_name::<Self>(),
253 }
254 }
255}
256impl<T: Sized> Indirect for T {}
257
258#[derive(Debug, Clone, Copy)]
259struct Info {
260 layout: Layout,
261 name: &'static str,
262}
263
264#[derive(Debug)]
267pub struct ErrorWith<T>(pub T);
268
269impl<T> ErrorWith<T> {
270 fn info(&self) -> Info {
271 Info {
272 layout: Layout::for_value(&self.0),
273 name: any::type_name::<T>(),
274 }
275 }
276 pub fn without_payload(self) -> Error {
277 Error { info: T::info }
278 }
279}
280
281impl<T> fmt::Display for ErrorWith<T> {
282 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
283 write_info(self.info(), f)
284 }
285}
286
287#[cfg(not(feature = "std"))]
288impl<T: fmt::Debug> core::error::Error for ErrorWith<T> {}
289
290#[cfg(feature = "std")]
291impl<T: fmt::Debug> std::error::Error for ErrorWith<T> {}
292
293impl<T> From<ErrorWith<T>> for Error {
294 fn from(value: ErrorWith<T>) -> Self {
295 value.without_payload()
296 }
297}
298
299#[cfg(feature = "std")]
300impl<T> From<ErrorWith<T>> for std::io::Error {
301 fn from(value: ErrorWith<T>) -> Self {
304 Error::from(value).into()
305 }
306}
307
308#[cfg(feature = "std")]
309impl<T> From<ErrorWith<T>> for std::io::ErrorKind {
310 fn from(_: ErrorWith<T>) -> Self {
311 std::io::ErrorKind::OutOfMemory
312 }
313}
314
315#[cfg(test)]
316mod tests {
317 use super::*;
318
319 static_assertions::assert_eq_size!(Error, *const u8);
320 static_assertions::assert_impl_all!(Error: Send, Sync);
321}