use crate::io::{ErrorData, ErrorKind, SimpleError};
#[cfg(feature = "alloc")]
use crate::io::CustomError;
use core::fmt::{self, Debug, Formatter};
use core::num::NonZero;
use core::ptr::NonNull;
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
#[derive(Clone, Copy)]
pub(super) struct Raw(NonNull<()>);
impl Raw {
pub const fn new_simple(message: &'static SimpleError) -> Self {
let p = NonNull::from_ref(message).cast();
Self(p)
}
pub fn new_inline(kind: ErrorKind) -> Self {
let mut addr = 0usize;
addr |= Tag::Inline as usize;
addr |= (kind as usize) << Tag::BITS;
let addr = unsafe { NonZero::new_unchecked(addr) };
let p = NonNull::without_provenance(addr);
Self(p)
}
#[cfg(feature = "alloc")]
pub fn new_custom(custom: Box<CustomError>) -> Self {
let mut p = Box::into_raw(custom) as *mut ();
p = unsafe { p.byte_add(Tag::Custom as usize) };
let p = unsafe { NonNull::new_unchecked(p) };
Self(p)
}
#[cfg(feature = "std")]
#[must_use]
pub fn new_os(raw: i32) -> Self {
let mut addr = 0usize;
addr |= Tag::Os as usize;
addr |= (raw as usize) << Tag::BITS;
let addr = unsafe { NonZero::new_unchecked(addr) };
let p = NonNull::without_provenance(addr);
Self(p)
}
#[must_use]
pub fn data(self) -> ErrorData {
match self.tag() {
Tag::Simple => {
let p = self.0.as_ptr().cast_const() as *const SimpleError;
let message = unsafe { &*p };
ErrorData::Simple(message)
}
Tag::Inline => {
let kind = self.as_usize() >> Tag::BITS;
let kind = unsafe { ErrorKind::from_usize_unchecked(kind) };
ErrorData::Inline(kind)
}
#[cfg(feature = "alloc")]
Tag::Custom => {
let mut p = self.0.as_ptr() as *mut CustomError;
p = unsafe { p.byte_sub(Tag::Custom as usize) };
ErrorData::Custom(p)
}
#[cfg(feature = "std")]
Tag::Os => {
let raw = (self.as_usize() >> Tag::BITS) as i32;
ErrorData::Os(raw)
}
}
}
#[inline]
#[must_use]
fn tag(self) -> Tag {
const TAG_MASK: usize = !(!0 << Tag::BITS);
let tag = self.as_usize() & TAG_MASK;
match tag {
0b00 => Tag::Simple,
0b01 => Tag::Inline,
#[cfg(feature = "alloc")]
0b10 => Tag::Custom,
#[cfg(feature = "std")]
0b11 => Tag::Os,
_ => unreachable!("tag should not be constructable"),
}
}
#[inline]
#[must_use]
pub fn as_usize(&self) -> usize {
self.0.addr().get()
}
}
impl Debug for Raw {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
Debug::fmt(&self.data(), f)
}
}
unsafe impl Send for Raw {}
unsafe impl Sync for Raw {}
#[repr(usize)]
enum Tag {
Simple = 0b00,
Inline = 0b01,
#[cfg(feature = "alloc")]
Custom = 0b10,
#[cfg(feature = "std")]
Os = 0b11,
}
impl Tag {
pub const BITS: usize = 2;
}