use crate::nan::raw::{RawStore, RawTag, Value};
use crate::nan::{RawBox, SingleNaNF64};
use crate::utils::ArrayExt;
use std::marker::PhantomData;
use std::num::NonZeroU8;
use std::ops::Deref;
use std::{fmt, mem};
mod sealed {
use super::*;
#[derive(PartialEq)]
pub enum HeapType {
Int = 1,
Ptr = 2,
MutPtr = 3,
Ref = 4,
Box = 6,
}
pub trait HeapInlineSealed<T>: Sized {
fn ty() -> HeapType;
fn write(self, value: &mut Value);
unsafe fn try_read(value: &Value) -> Option<Self>;
}
}
use sealed::{HeapInlineSealed, HeapType};
#[inline]
fn int_ty(bytes: impl Deref<Target = [u8; 6]>) -> IntType {
#[cfg(target_endian = "big")]
let ty = u16::from_ne_bytes([bytes[0], bytes[1]]);
#[cfg(target_endian = "little")]
let ty = u16::from_ne_bytes([bytes[4], bytes[5]]);
IntType::from_u16(ty)
}
#[inline]
fn int_data(bytes: &[u8; 6]) -> &[u8; 4] {
#[cfg(target_endian = "big")]
let data = unsafe {
&*(bytes as *const [u8; 6])
.cast::<u8>()
.offset(2)
.cast::<[u8; 4]>()
};
#[cfg(target_endian = "little")]
let data = unsafe { &*(bytes as *const [u8; 6]).cast::<[u8; 4]>() };
data
}
#[inline]
fn int_data_mut(bytes: &mut [u8; 6]) -> &mut [u8; 4] {
#[cfg(target_endian = "big")]
let data = unsafe {
&mut *(bytes as *mut [u8; 6])
.cast::<u8>()
.offset(2)
.cast::<[u8; 4]>()
};
#[cfg(target_endian = "little")]
let data = unsafe { &mut *(bytes as *mut [u8; 6]).cast::<[u8; 4]>() };
data
}
#[inline]
fn write_int<I: IntInline>(val: I) -> [u8; 6] {
let ty = (I::ty() as u16).to_ne_bytes();
let val = val.to_bytes();
#[cfg(target_endian = "big")]
return [ty[0], ty[1], val[0], val[1], val[2], val[3]];
#[cfg(target_endian = "little")]
return [val[0], val[1], val[2], val[3], ty[0], ty[1]];
}
#[inline]
fn read_int<I: IntInline>(bytes: &[u8; 6]) -> Option<I> {
let ty = int_ty(bytes);
let data = int_data(bytes);
if ty == I::ty() {
Some(unsafe { I::from_bytes(*data) })
} else {
None
}
}
impl HeapType {
#[inline]
fn raw_tag(self) -> RawTag {
RawTag::new(false, unsafe { NonZeroU8::new_unchecked(self as u8) })
}
#[inline]
fn from_raw_tag(tag: RawTag) -> Option<HeapType> {
if tag.is_neg() {
return None;
}
Some(match tag.val().get() {
1 => HeapType::Int,
2 => HeapType::Ptr,
3 => HeapType::MutPtr,
4 => HeapType::Ref,
6 => HeapType::Box,
_ => return None,
})
}
}
#[derive(PartialEq)]
#[repr(u16)]
enum IntType {
Bool = 0,
Char = 1,
U8 = 2,
U16 = 3,
U32 = 4,
I8 = 5,
I16 = 6,
I32 = 7,
F32 = 8,
}
impl IntType {
#[inline]
fn from_u16(val: u16) -> IntType {
match val {
0 => IntType::Bool,
1 => IntType::Char,
2 => IntType::U8,
3 => IntType::U16,
4 => IntType::U32,
5 => IntType::I8,
6 => IntType::I16,
7 => IntType::I32,
8 => IntType::F32,
_ => unreachable!(),
}
}
}
trait IntInline: Sized {
fn ty() -> IntType;
fn to_bytes(self) -> [u8; 4];
unsafe fn from_bytes(bytes: [u8; 4]) -> Self;
#[inline]
unsafe fn ref_bytes(bytes: &[u8; 4]) -> &Self {
unsafe { &*bytes.as_ptr().cast() }
}
#[inline]
unsafe fn mut_bytes(bytes: &mut [u8; 4]) -> &mut Self {
unsafe { &mut *bytes.as_mut_ptr().cast() }
}
}
pub trait HeapInline<T>: HeapInlineSealed<T> {}
pub trait HeapInlineRef<T>: HeapInline<T> {
#[doc(hidden)]
fn try_ref(value: &Value) -> Option<&Self>;
#[doc(hidden)]
fn try_mut(value: &mut Value) -> Option<&mut Self>;
}
macro_rules! impl_int {
($ty:ty, $variant:ident) => {
impl<T> HeapInlineSealed<T> for $ty {
#[inline]
fn ty() -> HeapType {
HeapType::Int
}
#[inline]
fn write(self, value: &mut Value) {
value.set_data(write_int(self))
}
#[inline]
unsafe fn try_read(value: &Value) -> Option<Self> {
read_int(value.data())
}
}
impl<T> HeapInline<T> for $ty {}
impl IntInline for $ty {
fn ty() -> IntType {
IntType::$variant
}
#[inline]
fn to_bytes(self) -> [u8; 4] {
self.to_ne_bytes().truncate_to()
}
#[inline]
unsafe fn from_bytes(bytes: [u8; 4]) -> Self {
<$ty>::from_ne_bytes(bytes.truncate_to())
}
}
impl<T> HeapInlineRef<T> for $ty {
#[inline]
fn try_ref(value: &Value) -> Option<&Self> {
let ty = int_ty(value.data());
let data = int_data(value.data());
if ty == <Self as IntInline>::ty() {
Some(unsafe { Self::ref_bytes(data) })
} else {
None
}
}
#[inline]
fn try_mut(value: &mut Value) -> Option<&mut Self> {
let ty = int_ty(value.data());
let data = int_data_mut(value.data_mut());
if ty == <Self as IntInline>::ty() {
Some(unsafe { Self::mut_bytes(data) })
} else {
None
}
}
}
};
}
impl_int!(u8, U8);
impl_int!(u16, U16);
impl_int!(u32, U32);
impl_int!(i8, I8);
impl_int!(i16, I16);
impl_int!(i32, I32);
impl_int!(f32, F32);
impl IntInline for bool {
#[inline]
fn ty() -> IntType {
IntType::Bool
}
#[inline]
fn to_bytes(self) -> [u8; 4] {
[u8::from(self), 0, 0, 0]
}
#[inline]
unsafe fn from_bytes(bytes: [u8; 4]) -> Self {
bytes[0] == 1
}
}
impl<T> HeapInlineSealed<T> for bool {
#[inline]
fn ty() -> HeapType {
HeapType::Int
}
#[inline]
fn write(self, value: &mut Value) {
value.set_data(write_int(self));
}
#[inline]
unsafe fn try_read(value: &Value) -> Option<Self> {
read_int(value.data())
}
}
impl<T> HeapInline<T> for bool {}
impl IntInline for char {
#[inline]
fn ty() -> IntType {
IntType::Char
}
#[inline]
fn to_bytes(self) -> [u8; 4] {
u32::to_bytes(self as u32)
}
#[inline]
unsafe fn from_bytes(bytes: [u8; 4]) -> Self {
unsafe { char::from_u32_unchecked(u32::from_bytes(bytes)) }
}
}
impl<T> HeapInlineSealed<T> for char {
#[inline]
fn ty() -> HeapType {
HeapType::Int
}
#[inline]
fn write(self, value: &mut Value) {
value.set_data(write_int(self));
}
#[inline]
unsafe fn try_read(value: &Value) -> Option<Self> {
read_int(value.data())
}
}
impl<T> HeapInline<T> for char {}
impl<T> HeapInlineSealed<T> for *const T {
#[inline]
fn ty() -> HeapType {
HeapType::Ptr
}
#[inline]
fn write(self, value: &mut Value) {
RawStore::to_val(self, value);
}
#[inline]
unsafe fn try_read(value: &Value) -> Option<Self> {
Some(RawStore::from_val(value))
}
}
impl<T> HeapInline<T> for *const T {}
impl<T> HeapInlineSealed<T> for *mut T {
#[inline]
fn ty() -> HeapType {
HeapType::MutPtr
}
#[inline]
fn write(self, value: &mut Value) {
RawStore::to_val(self, value);
}
#[inline]
unsafe fn try_read(value: &Value) -> Option<Self> {
Some(RawStore::from_val(value))
}
}
impl<T> HeapInline<T> for *mut T {}
impl<'a, T> HeapInlineSealed<T> for &'a T {
#[inline]
fn ty() -> HeapType {
HeapType::Ref
}
#[inline]
fn write(self, value: &mut Value) {
RawStore::to_val(self as *const T, value);
}
#[inline]
unsafe fn try_read(value: &Value) -> Option<Self> {
Some(unsafe { &*<*const T as RawStore>::from_val(value) })
}
}
impl<'a, T> HeapInline<T> for &'a T {}
pub struct NanBox<'a, T>(RawBox, PhantomData<&'a mut T>);
impl<'a, T> NanBox<'a, T> {
#[inline]
fn from_raw(b: RawBox) -> NanBox<'a, T> {
NanBox(b, PhantomData)
}
#[inline]
#[must_use]
pub fn from_float(val: f64) -> NanBox<'a, T> {
NanBox::from_raw(RawBox::from_float(val))
}
#[must_use]
pub fn from_inline<U: HeapInline<T> + 'a>(val: U) -> NanBox<'a, T> {
let ty = U::ty();
let mut value = Value::empty(ty.raw_tag());
U::write(val, &mut value);
let raw = RawBox::from_value(value);
NanBox::from_raw(raw)
}
#[must_use]
pub fn from_box(val: Box<T>) -> NanBox<'a, T> {
let tag = HeapType::Box.raw_tag();
let value = Value::store(tag, Box::into_raw(val));
let raw = RawBox::from_value(value);
NanBox::from_raw(raw)
}
#[inline]
#[must_use]
pub fn is_float(&self) -> bool {
self.0.is_float()
}
#[inline]
#[must_use]
pub fn try_ref_float(&self) -> Option<&f64> {
self.0.float()
}
#[inline]
#[must_use]
pub fn try_mut_float(&mut self) -> Option<&mut SingleNaNF64> {
self.0.float_mut()
}
#[must_use]
pub fn try_ref_inline<U: HeapInlineRef<T> + 'a>(&self) -> Option<&U> {
self.0.value().and_then(|val| {
if HeapType::from_raw_tag(val.tag()) == Some(<U as HeapInlineSealed<_>>::ty()) {
U::try_ref(val)
} else {
None
}
})
}
#[must_use]
pub fn try_mut_inline<U: HeapInlineRef<T> + 'a>(&mut self) -> Option<&mut U> {
self.0.value_mut().and_then(|val| {
if HeapType::from_raw_tag(val.tag()) == Some(<U as HeapInlineSealed<_>>::ty()) {
U::try_mut(val)
} else {
None
}
})
}
#[must_use]
pub fn try_ref_boxed(&self) -> Option<&T> {
self.0.value().and_then(|val| {
if HeapType::from_raw_tag(val.tag()) == Some(HeapType::Box) {
let ptr = <*const T as RawStore>::from_val(val);
Some(unsafe { &*ptr })
} else {
None
}
})
}
#[must_use]
pub fn try_mut_boxed(&mut self) -> Option<&mut T> {
self.0.value_mut().and_then(|val| {
if HeapType::from_raw_tag(val.tag()) == Some(HeapType::Box) {
let ptr = <*mut T as RawStore>::from_val(val);
Some(unsafe { &mut *ptr })
} else {
None
}
})
}
pub fn try_into_float(self) -> Result<f64, Self> {
self.0.float().copied().ok_or(self)
}
pub fn try_into_inline<U: HeapInline<T> + 'a>(self) -> Result<U, Self> {
self.0
.value()
.and_then(|val| {
if HeapType::from_raw_tag(val.tag()) == Some(U::ty()) {
unsafe { U::try_read(val) }
} else {
None
}
})
.ok_or(self)
}
pub fn try_into_boxed(mut self) -> Result<T, Self> {
let inner = mem::replace(&mut self.0, RawBox::from_float(f64::NAN));
inner
.into_value()
.and_then(|val| {
if HeapType::from_raw_tag(val.tag()) == Some(HeapType::Box) {
let ptr = val.load::<*mut T>();
Ok(unsafe { *Box::from_raw(ptr) })
} else {
Err(RawBox::from_value(val))
}
})
.map_err(NanBox::from_raw)
}
#[inline]
#[must_use]
pub fn into_float_unchecked(mut self) -> f64 {
let inner = mem::replace(&mut self.0, RawBox::from_float(f64::NAN));
inner.into_float_unchecked()
}
}
impl<T> From<f64> for NanBox<'_, T> {
#[inline]
fn from(value: f64) -> Self {
NanBox::from_float(value)
}
}
macro_rules! from_inline {
($ty:ty) => {
impl<'a, T> From<$ty> for NanBox<'a, T> {
#[inline]
fn from(value: $ty) -> Self {
NanBox::from_inline(value)
}
}
};
}
from_inline!(u8);
from_inline!(u16);
from_inline!(u32);
from_inline!(i8);
from_inline!(i16);
from_inline!(i32);
from_inline!(bool);
from_inline!(char);
from_inline!(*const T);
from_inline!(*mut T);
from_inline!(&'a T);
impl<T> From<Box<T>> for NanBox<'_, T> {
fn from(value: Box<T>) -> Self {
NanBox::from_box(value)
}
}
impl<'a, T> Clone for NanBox<'a, T>
where
T: Clone,
{
fn clone(&self) -> Self {
let tag = self.0.tag().and_then(HeapType::from_raw_tag);
match tag {
None => NanBox::from_float(*self.0.float().unwrap()),
Some(HeapType::Int) => {
let data = self.0.value().unwrap().data();
NanBox::from_raw(RawBox::from_value(Value::new(
HeapType::Int.raw_tag(),
*data,
)))
}
Some(tag @ (HeapType::Ptr | HeapType::MutPtr | HeapType::Ref)) => {
let ptr = self.0.value().map(<*const T>::from_val).unwrap();
NanBox::from_raw(RawBox::from_value(Value::store(tag.raw_tag(), ptr)))
}
Some(HeapType::Box) => {
let ptr = self.0.value().map(<*const T>::from_val).unwrap();
let r = unsafe { &*ptr };
NanBox::from_box(Box::new(T::clone(r)))
}
}
}
}
impl<'a, T> PartialEq for NanBox<'a, T>
where
T: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
if self.0.tag() != other.0.tag() {
return false;
}
match self.0.tag().and_then(HeapType::from_raw_tag) {
None => self.0.float() == other.0.float(),
Some(HeapType::Int | HeapType::Ptr | HeapType::MutPtr) => {
self.0.value().map(Value::data) == other.0.value().map(Value::data)
}
Some(HeapType::Ref | HeapType::Box) => {
let ptr1 = self.0.value().map(<*const T>::from_val).unwrap();
let ptr2 = other.0.value().map(<*const T>::from_val).unwrap();
(unsafe { &*ptr1 }) == (unsafe { &*ptr2 })
}
}
}
}
impl<'a, T> fmt::Debug for NanBox<'a, T>
where
T: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let tag = self.0.tag().and_then(HeapType::from_raw_tag);
let variant = match tag {
None => "NanBox::Float",
Some(HeapType::Int) => {
let ty = int_ty(self.0.value().unwrap().data());
match ty {
IntType::Bool => "NanBox::Bool",
IntType::Char => "NanBox::Char",
IntType::U8 => "NanBox::U8",
IntType::U16 => "NanBox::U16",
IntType::U32 => "NanBox::U32",
IntType::I8 => "NanBox::I8",
IntType::I16 => "NanBox::I16",
IntType::I32 => "NanBox::I32",
IntType::F32 => "NanBox::F32",
}
}
Some(HeapType::Ptr) => "NanBox::Ptr",
Some(HeapType::MutPtr) => "NanBox::MutPtr",
Some(HeapType::Ref) => "NanBox::Ref",
Some(HeapType::Box) => "NanBox::Box",
};
let mut tuple = f.debug_tuple(variant);
match tag {
None => {
let val = self.0.float().unwrap();
tuple.field(val);
}
Some(HeapType::Int) => {
let bytes = self.0.value().unwrap().data();
let ty = int_ty(bytes);
let data = int_data(bytes);
match ty {
IntType::Bool => tuple.field(unsafe { bool::ref_bytes(data) }),
IntType::Char => tuple.field(unsafe { char::ref_bytes(data) }),
IntType::U8 => tuple.field(unsafe { u8::ref_bytes(data) }),
IntType::U16 => tuple.field(unsafe { u16::ref_bytes(data) }),
IntType::U32 => tuple.field(unsafe { u32::ref_bytes(data) }),
IntType::I8 => tuple.field(unsafe { i8::ref_bytes(data) }),
IntType::I16 => tuple.field(unsafe { i16::ref_bytes(data) }),
IntType::I32 => tuple.field(unsafe { i32::ref_bytes(data) }),
IntType::F32 => tuple.field(unsafe { f32::ref_bytes(data) }),
};
}
Some(HeapType::Ptr | HeapType::MutPtr) => {
let ptr = self.0.value().map(<*const T>::from_val).unwrap();
tuple.field(&ptr);
}
Some(HeapType::Ref | HeapType::Box) => {
let ptr = self.0.value().map(<*const T>::from_val).unwrap();
let r = unsafe { &*ptr };
tuple.field(r);
}
}
tuple.finish()
}
}
impl<'a, T> Drop for NanBox<'a, T> {
fn drop(&mut self) {
if self.0.tag().and_then(HeapType::from_raw_tag) == Some(HeapType::Box) {
let ptr = self.0.value().map(<*mut T>::from_val).unwrap();
drop(unsafe { Box::from_raw(ptr) });
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_roundtrip_float() {
let a = NanBox::<()>::from_float(1.0);
assert_eq!(a.try_into_float(), Ok(1.0));
let b = NanBox::<()>::from_float(f64::NAN);
assert_eq!(b.try_into_float().unwrap().to_bits(), crate::nan::QUIET_NAN);
}
#[test]
fn test_roundtrip_bool() {
let a = NanBox::<()>::from_inline(true);
assert_eq!(a.try_into_inline::<bool>(), Ok(true));
let b = NanBox::<()>::from_inline(false);
assert_eq!(b.try_into_inline::<bool>(), Ok(false));
}
#[test]
fn test_roundtrip_char() {
let a = NanBox::<()>::from_inline('a');
assert_eq!(a.try_into_inline::<char>(), Ok('a'));
let b = NanBox::<()>::from_inline('😀');
assert_eq!(b.try_into_inline::<char>(), Ok('😀'));
}
#[test]
fn test_roundtrip_ref() {
let r = 1;
let a = NanBox::<i32>::from_inline(&r);
assert_eq!(a.try_into_inline::<&i32>(), Ok(&1));
}
#[test]
fn test_roundtrip_boxed() {
#[derive(Debug, PartialEq)]
struct VeryLarge([u8; 128]);
let r = Box::new(VeryLarge([0x55; 128]));
let a = NanBox::<VeryLarge>::from_box(r);
assert_eq!(a.try_into_boxed(), Ok(VeryLarge([0x55; 128])));
}
#[test]
fn test_ref_u32() {
let mut a = NanBox::<i32>::from_inline(-100i32);
let r1 = a.try_ref_inline::<i32>().unwrap();
assert_eq!(*r1, -100);
let r2 = a.try_mut_inline::<i32>().unwrap();
*r2 = 100;
assert_eq!(*r2, 100);
}
#[test]
fn test_eq() {
let a = NanBox::<i32>::from_float(1.0);
let b = NanBox::<i32>::from_float(-1.0);
let c = NanBox::<i32>::from_inline(true);
let d = NanBox::<i32>::from_inline(1);
let e = NanBox::from_inline(&10);
let f = NanBox::from_box(Box::new(-10));
assert_eq!(a, a);
assert_eq!(b, b);
assert_eq!(c, c);
assert_eq!(d, d);
assert_eq!(e, e);
assert_eq!(f, f);
}
}