503 lines
12 KiB
Rust
503 lines
12 KiB
Rust
#![allow(dead_code)]
|
|
|
|
use std::fmt;
|
|
use std::num::NonZeroU8;
|
|
|
|
use sptr::Strict;
|
|
|
|
const SIGN_MASK: u64 = 0x7FFF_FFFF_FFFF_FFFF;
|
|
const QUIET_NAN: u64 = 0x7FF8_0000_0000_0000;
|
|
const NEG_QUIET_NAN: u64 = 0xFFF8_0000_0000_0000;
|
|
|
|
pub(crate) trait ArrayExt<const LEN: usize> {
|
|
type Elem;
|
|
fn truncate_to<const M: usize>(self) -> [Self::Elem; M];
|
|
}
|
|
|
|
impl<T: Default + Copy, const N: usize> ArrayExt<N> for [T; N] {
|
|
type Elem = T;
|
|
fn truncate_to<const M: usize>(self) -> [Self::Elem; M] {
|
|
let copy_len = usize::min(N, M);
|
|
let mut out = [T::default(); M];
|
|
out[0..copy_len].copy_from_slice(&self[0..copy_len]);
|
|
out
|
|
}
|
|
}
|
|
|
|
pub(crate) trait RawStore: Sized {
|
|
fn to_val(self, value: &mut Value);
|
|
fn from_val(value: &Value) -> Self;
|
|
}
|
|
|
|
impl RawStore for [u8; 6] {
|
|
#[inline]
|
|
fn to_val(self, value: &mut Value) {
|
|
value.set_data(self);
|
|
}
|
|
|
|
#[inline]
|
|
fn from_val(value: &Value) -> Self {
|
|
*value.data()
|
|
}
|
|
}
|
|
|
|
impl RawStore for bool {
|
|
#[inline]
|
|
fn to_val(self, value: &mut Value) {
|
|
value.set_data([u8::from(self)].truncate_to());
|
|
}
|
|
|
|
#[inline]
|
|
fn from_val(value: &Value) -> Self {
|
|
value.data()[0] == 1
|
|
}
|
|
}
|
|
|
|
macro_rules! int_store {
|
|
($ty:ty) => {
|
|
impl RawStore for $ty {
|
|
#[inline]
|
|
fn to_val(self, value: &mut Value) {
|
|
let bytes = self.to_ne_bytes();
|
|
value.set_data(bytes.truncate_to());
|
|
}
|
|
|
|
#[inline]
|
|
fn from_val(value: &Value) -> Self {
|
|
<$ty>::from_ne_bytes(value.data().truncate_to())
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
int_store!(u8);
|
|
int_store!(u16);
|
|
int_store!(u32);
|
|
|
|
int_store!(i8);
|
|
int_store!(i16);
|
|
int_store!(i32);
|
|
|
|
fn store_ptr<P: Strict + Copy>(value: &mut Value, ptr: P) {
|
|
#[cfg(target_pointer_width = "64")]
|
|
{
|
|
assert!(
|
|
ptr.addr() <= 0x0000_FFFF_FFFF_FFFF,
|
|
"Pointer too large to store in NaN box"
|
|
);
|
|
|
|
let val = (unsafe { value.whole_mut() } as *mut [u8; 8]).cast::<P>();
|
|
|
|
let ptr = Strict::map_addr(ptr, |addr| {
|
|
addr | (usize::from(value.header().into_raw()) << 48)
|
|
});
|
|
|
|
unsafe { val.write(ptr) };
|
|
}
|
|
|
|
#[cfg(target_pointer_width = "32")]
|
|
{
|
|
let _ = (value, ptr);
|
|
unimplemented!("32-bit pointer storage not supported");
|
|
}
|
|
}
|
|
|
|
fn load_ptr<P: Strict>(value: &Value) -> P {
|
|
#[cfg(target_pointer_width = "64")]
|
|
{
|
|
let val = (unsafe { value.whole() } as *const [u8; 8]).cast::<P>();
|
|
let ptr = unsafe { val.read() };
|
|
Strict::map_addr(ptr, |addr| addr & 0x0000_FFFF_FFFF_FFFF)
|
|
}
|
|
|
|
#[cfg(target_pointer_width = "32")]
|
|
{
|
|
let _ = value;
|
|
unimplemented!("32-bit pointer storage not supported");
|
|
}
|
|
}
|
|
|
|
impl<T> RawStore for *const T {
|
|
fn to_val(self, value: &mut Value) {
|
|
store_ptr::<*const T>(value, self);
|
|
}
|
|
|
|
fn from_val(value: &Value) -> Self {
|
|
load_ptr::<*const T>(value)
|
|
}
|
|
}
|
|
|
|
impl<T> RawStore for *mut T {
|
|
fn to_val(self, value: &mut Value) {
|
|
store_ptr::<*mut T>(value, self);
|
|
}
|
|
|
|
fn from_val(value: &Value) -> Self {
|
|
load_ptr::<*mut T>(value)
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
|
enum TagVal {
|
|
_P1,
|
|
_P2,
|
|
_P3,
|
|
_P4,
|
|
_P5,
|
|
_P6,
|
|
_P7,
|
|
|
|
_N1,
|
|
_N2,
|
|
_N3,
|
|
_N4,
|
|
_N5,
|
|
_N6,
|
|
_N7,
|
|
}
|
|
|
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
|
pub(crate) struct RawTag(TagVal);
|
|
|
|
impl RawTag {
|
|
pub(crate) const P1: RawTag = RawTag(TagVal::_P1);
|
|
pub(crate) const P2: RawTag = RawTag(TagVal::_P2);
|
|
pub(crate) const P3: RawTag = RawTag(TagVal::_P3);
|
|
pub(crate) const P4: RawTag = RawTag(TagVal::_P4);
|
|
pub(crate) const P5: RawTag = RawTag(TagVal::_P5);
|
|
pub(crate) const P6: RawTag = RawTag(TagVal::_P6);
|
|
pub(crate) const P7: RawTag = RawTag(TagVal::_P7);
|
|
|
|
pub(crate) const N1: RawTag = RawTag(TagVal::_N1);
|
|
pub(crate) const N2: RawTag = RawTag(TagVal::_N2);
|
|
pub(crate) const N3: RawTag = RawTag(TagVal::_N3);
|
|
pub(crate) const N4: RawTag = RawTag(TagVal::_N4);
|
|
pub(crate) const N5: RawTag = RawTag(TagVal::_N5);
|
|
pub(crate) const N6: RawTag = RawTag(TagVal::_N6);
|
|
pub(crate) const N7: RawTag = RawTag(TagVal::_N7);
|
|
|
|
#[inline]
|
|
#[must_use]
|
|
pub(crate) fn new(neg: bool, val: NonZeroU8) -> RawTag {
|
|
unsafe { Self::new_unchecked(neg, val.get() & 0x07) }
|
|
}
|
|
|
|
#[inline]
|
|
#[must_use]
|
|
pub(crate) fn new_checked(neg: bool, val: u8) -> Option<RawTag> {
|
|
Some(RawTag(match (neg, val) {
|
|
(false, 1) => TagVal::_P1,
|
|
(false, 2) => TagVal::_P2,
|
|
(false, 3) => TagVal::_P3,
|
|
(false, 4) => TagVal::_P4,
|
|
(false, 5) => TagVal::_P5,
|
|
(false, 6) => TagVal::_P6,
|
|
(false, 7) => TagVal::_P7,
|
|
|
|
(true, 1) => TagVal::_N1,
|
|
(true, 2) => TagVal::_N2,
|
|
(true, 3) => TagVal::_N3,
|
|
(true, 4) => TagVal::_N4,
|
|
(true, 5) => TagVal::_N5,
|
|
(true, 6) => TagVal::_N6,
|
|
(true, 7) => TagVal::_N7,
|
|
|
|
_ => return None,
|
|
}))
|
|
}
|
|
|
|
/// # Safety
|
|
///
|
|
/// `val` must be in the range `1..8`
|
|
#[inline]
|
|
#[must_use]
|
|
pub(crate) const unsafe fn new_unchecked(neg: bool, val: u8) -> RawTag {
|
|
RawTag(match (neg, val) {
|
|
(false, 1) => TagVal::_P1,
|
|
(false, 2) => TagVal::_P2,
|
|
(false, 3) => TagVal::_P3,
|
|
(false, 4) => TagVal::_P4,
|
|
(false, 5) => TagVal::_P5,
|
|
(false, 6) => TagVal::_P6,
|
|
(false, 7) => TagVal::_P7,
|
|
|
|
(true, 1) => TagVal::_N1,
|
|
(true, 2) => TagVal::_N2,
|
|
(true, 3) => TagVal::_N3,
|
|
(true, 4) => TagVal::_N4,
|
|
(true, 5) => TagVal::_N5,
|
|
(true, 6) => TagVal::_N6,
|
|
(true, 7) => TagVal::_N7,
|
|
|
|
_ => unsafe { core::hint::unreachable_unchecked() },
|
|
})
|
|
}
|
|
|
|
#[inline]
|
|
#[must_use]
|
|
pub(crate) fn is_neg(self) -> bool {
|
|
matches!(self.0, |TagVal::_N1| TagVal::_N2
|
|
| TagVal::_N3
|
|
| TagVal::_N4
|
|
| TagVal::_N5
|
|
| TagVal::_N6
|
|
| TagVal::_N7)
|
|
}
|
|
|
|
#[inline]
|
|
#[must_use]
|
|
pub(crate) fn val(self) -> NonZeroU8 {
|
|
match self.0 {
|
|
TagVal::_P1 | TagVal::_N1 => NonZeroU8::MIN,
|
|
TagVal::_P2 | TagVal::_N2 => NonZeroU8::MIN.saturating_add(1),
|
|
TagVal::_P3 | TagVal::_N3 => NonZeroU8::MIN.saturating_add(2),
|
|
TagVal::_P4 | TagVal::_N4 => NonZeroU8::MIN.saturating_add(3),
|
|
TagVal::_P5 | TagVal::_N5 => NonZeroU8::MIN.saturating_add(4),
|
|
TagVal::_P6 | TagVal::_N6 => NonZeroU8::MIN.saturating_add(5),
|
|
TagVal::_P7 | TagVal::_N7 => NonZeroU8::MIN.saturating_add(6),
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
#[must_use]
|
|
pub(crate) const fn neg_val(self) -> (bool, u8) {
|
|
match self.0 {
|
|
TagVal::_P1 => (false, 1),
|
|
TagVal::_P2 => (false, 2),
|
|
TagVal::_P3 => (false, 3),
|
|
TagVal::_P4 => (false, 4),
|
|
TagVal::_P5 => (false, 5),
|
|
TagVal::_P6 => (false, 6),
|
|
TagVal::_P7 => (false, 7),
|
|
TagVal::_N1 => (true, 1),
|
|
TagVal::_N2 => (true, 2),
|
|
TagVal::_N3 => (true, 3),
|
|
TagVal::_N4 => (true, 4),
|
|
TagVal::_N5 => (true, 5),
|
|
TagVal::_N6 => (true, 6),
|
|
TagVal::_N7 => (true, 7),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for RawTag {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
f.debug_struct("RawTag")
|
|
.field("neg", &self.is_neg())
|
|
.field("val", &self.val())
|
|
.finish()
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
|
#[repr(transparent)]
|
|
struct Header(u16);
|
|
|
|
impl Header {
|
|
#[inline]
|
|
fn new(tag: RawTag) -> Header {
|
|
let (neg, val) = tag.neg_val();
|
|
Header(0x7FF8 | (u16::from(neg) << 15) | u16::from(val))
|
|
}
|
|
|
|
#[inline]
|
|
const fn tag(self) -> RawTag {
|
|
unsafe { RawTag::new_unchecked(self.get_sign(), self.get_tag()) }
|
|
}
|
|
|
|
#[inline]
|
|
const fn get_sign(self) -> bool {
|
|
self.0 & 0x8000 != 0
|
|
}
|
|
|
|
#[inline]
|
|
const fn get_tag(self) -> u8 {
|
|
(self.0 & 0x0007) as u8
|
|
}
|
|
|
|
#[inline]
|
|
fn into_raw(self) -> u16 {
|
|
self.0
|
|
}
|
|
}
|
|
|
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
|
#[repr(C, align(8))]
|
|
pub(crate) struct Value {
|
|
#[cfg(target_endian = "big")]
|
|
header: Header,
|
|
data: [u8; 6],
|
|
#[cfg(target_endian = "little")]
|
|
header: Header,
|
|
}
|
|
|
|
impl Value {
|
|
#[inline]
|
|
pub(crate) fn new(tag: RawTag, data: [u8; 6]) -> Value {
|
|
Value {
|
|
header: Header::new(tag),
|
|
data,
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
pub(crate) fn empty(tag: RawTag) -> Value {
|
|
Value::new(tag, [0; 6])
|
|
}
|
|
|
|
pub(crate) fn store<T: RawStore>(tag: RawTag, val: T) -> Value {
|
|
let mut v = Value::new(tag, [0; 6]);
|
|
T::to_val(val, &mut v);
|
|
v
|
|
}
|
|
|
|
pub(crate) fn load<T: RawStore>(self) -> T {
|
|
T::from_val(&self)
|
|
}
|
|
|
|
#[inline]
|
|
#[must_use]
|
|
pub(crate) const fn tag(&self) -> RawTag {
|
|
self.header.tag()
|
|
}
|
|
|
|
#[inline]
|
|
fn header(&self) -> &Header {
|
|
&self.header
|
|
}
|
|
|
|
#[inline]
|
|
pub(crate) fn set_data(&mut self, val: [u8; 6]) {
|
|
self.data = val;
|
|
}
|
|
|
|
#[inline]
|
|
#[must_use]
|
|
pub(crate) fn data(&self) -> &[u8; 6] {
|
|
&self.data
|
|
}
|
|
|
|
#[inline]
|
|
#[must_use]
|
|
pub(crate) fn data_mut(&mut self) -> &mut [u8; 6] {
|
|
&mut self.data
|
|
}
|
|
|
|
#[inline]
|
|
#[must_use]
|
|
unsafe fn whole(&self) -> &[u8; 8] {
|
|
let ptr = (self as *const Value).cast::<[u8; 8]>();
|
|
unsafe { &*ptr }
|
|
}
|
|
|
|
#[inline]
|
|
#[must_use]
|
|
unsafe fn whole_mut(&mut self) -> &mut [u8; 8] {
|
|
let ptr = (self as *mut Value).cast::<[u8; 8]>();
|
|
unsafe { &mut *ptr }
|
|
}
|
|
}
|
|
|
|
#[repr(C)]
|
|
#[derive(Copy, Clone)]
|
|
pub(crate) union RawBox {
|
|
float: f64,
|
|
value: Value,
|
|
bits: u64,
|
|
#[cfg(target_pointer_width = "64")]
|
|
ptr: *const (),
|
|
#[cfg(target_pointer_width = "32")]
|
|
ptr: (u32, *const ()),
|
|
}
|
|
|
|
impl RawBox {
|
|
#[inline]
|
|
#[must_use]
|
|
pub(crate) fn from_float(val: f64) -> RawBox {
|
|
match (val.is_nan(), val.is_sign_positive()) {
|
|
(true, true) => RawBox {
|
|
float: f64::from_bits(QUIET_NAN),
|
|
},
|
|
(true, false) => RawBox {
|
|
float: f64::from_bits(NEG_QUIET_NAN),
|
|
},
|
|
(false, _) => RawBox { float: val },
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
#[must_use]
|
|
pub(crate) fn from_value(value: Value) -> RawBox {
|
|
RawBox { value }
|
|
}
|
|
|
|
#[inline]
|
|
#[must_use]
|
|
pub(crate) const fn tag(&self) -> Option<RawTag> {
|
|
if self.is_value() {
|
|
Some(unsafe { self.value.tag() })
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
#[must_use]
|
|
pub(crate) fn is_float(&self) -> bool {
|
|
(unsafe { !self.float.is_nan() } || unsafe { self.bits & SIGN_MASK == QUIET_NAN })
|
|
}
|
|
|
|
#[inline]
|
|
#[must_use]
|
|
pub(crate) const fn is_value(&self) -> bool {
|
|
(unsafe { self.float.is_nan() } && unsafe { self.bits & SIGN_MASK != QUIET_NAN })
|
|
}
|
|
|
|
#[inline]
|
|
#[must_use]
|
|
pub(crate) fn float(&self) -> Option<&f64> {
|
|
if self.is_float() {
|
|
Some(unsafe { &self.float })
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
#[must_use]
|
|
pub(crate) fn value(&self) -> Option<&Value> {
|
|
if self.is_value() {
|
|
Some(unsafe { &self.value })
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
pub(crate) fn into_float_unchecked(self) -> f64 {
|
|
unsafe { self.float }
|
|
}
|
|
|
|
#[inline]
|
|
#[must_use]
|
|
pub(crate) fn to_bits(self) -> u64 {
|
|
unsafe { self.bits }
|
|
}
|
|
}
|
|
|
|
impl fmt::Debug for RawBox {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
match self.float() {
|
|
Some(val) => f.debug_tuple("RawBox::Float").field(val).finish(),
|
|
None => {
|
|
let val = self.value().expect("RawBox is neither float nor value");
|
|
|
|
f.debug_struct("RawBox::Data")
|
|
.field("tag", &val.tag())
|
|
.field("data", val.data())
|
|
.finish()
|
|
}
|
|
}
|
|
}
|
|
}
|