use super::*;
use core::num::NonZeroU16;
const TAG_SHIFT: u64 = 48;
const TAG_MASK: u64 = 0x0000FFFF00000000;
const DAT_MASK: u64 = 0x00000000FFFFFFFF;
const DAT_UNMASK: u64 = 0xFFFFFFFF00000000;
pub trait NaNTag: From<NonZeroU16> + Into<NonZeroU16> + Clone + Copy {}
impl NaNTag for NonZeroU16 {}
pub trait NaNDat: From<u32> + Into<u32> + Clone + Copy {}
impl NaNDat for u32 {}
#[repr(C)]
#[derive(Clone, Copy)]
pub union RawNaNVal<TAG, DAT> where TAG: NaNTag, DAT: NaNDat {
f: f64,
u: u64,
p: core::marker::PhantomData<(TAG,DAT)>
}
impl<TAG: NaNTag, DAT: NaNDat> RawNaNVal<TAG, DAT> {
pub fn from_float(f: f64) -> Option<Self> {
let new = Self {f};
if new.has_tag() {
return None
}
Some(new)
}
pub fn from_float_unchecked(f: f64) -> Self {
let new = Self {f};
debug_assert!(!new.has_tag(), "a float must not have tag");
new
}
pub fn from_tag(tag: TAG) -> Self {
let tag: u16 = tag.into().get();
let tag = (tag as u64) << TAG_SHIFT;
let tag = tag & TAG_MASK; Self {u: NAN_SIGNAL | tag}
}
pub fn from_tag_and_data(tag: TAG, dat: DAT) -> Self {
let tag: u16 = tag.into().get();
let tag = (tag as u64) << TAG_SHIFT;
let tag = tag & TAG_MASK;
let dat: u32 = dat.into();
let dat = dat as u64;
let dat = dat & DAT_MASK;
Self {u: NAN_SIGNAL | tag | dat}
}
pub unsafe fn get_tag_unchecked(&self) -> TAG {
debug_assert!(self.has_tag(), "invalid tag");
let tag = (self.u & TAG_MASK) >> TAG_SHIFT;
NonZeroU16::new_unchecked(tag as u16).into()
}
pub fn get_dat_raw_unchecked(&self) -> DAT {
debug_assert!(self.has_tag(), "invalid tag");
unsafe {((self.u & DAT_MASK) as u32).into()}
}
pub fn has_tag(&self) -> bool {
unsafe {
(self.u & NAN_MASK) == NAN_SIGNAL
}
}
pub fn has_f64(&self) -> bool {
unsafe {
(self.u & NAN_MASK) != NAN_SIGNAL
}
}
pub fn set_tag_unchecked(&mut self, tag: TAG) {
debug_assert!(self.has_tag(), "retagging a float is invalid");
let tag: u16 = tag.into().get();
let tag = (tag as u64) << TAG_SHIFT;
let tag = tag & TAG_MASK; unsafe {
self.u &= NAN_UNMASK; self.u |= NAN_SIGNAL | tag; }
}
pub fn set_dat_unchecked(&mut self, dat: DAT) {
debug_assert!(self.has_tag(), "partially rewriting a float is invalid");
let dat: u32 = dat.into();
let dat = dat as u64;
let dat = dat & DAT_MASK; unsafe {
self.u &= DAT_UNMASK; self.u |= dat; }
}
pub fn set_tag_and_dat(&mut self, tag: TAG, dat: DAT) {
let tag: u16 = tag.into().get();
let tag = (tag as u64) << TAG_SHIFT;
let tag = tag & TAG_MASK;
let dat: u32 = dat.into();
let dat = dat as u64;
let dat = dat & DAT_MASK;
self.u = NAN_SIGNAL | tag | dat;
}
pub fn with_dat(&self, dat: DAT) -> Option<Self> {
if self.has_tag() {
let mut n = *self;
n.set_dat_unchecked(dat);
return Some(n)
}
None
}
pub fn set_f64(&mut self, f: f64) {
self.f = f;
}
pub fn get_f64(&self) -> Option<f64> {
if self.has_tag() {
None
} else {
Some(unsafe {self.f})
}
}
pub fn get_tag(&self) -> Option<TAG> {
if self.has_tag() {
let tag = unsafe {self.get_tag_unchecked()};
Some(tag)
} else {
None
}
}
pub fn get_dat(&self) -> Option<DAT> {
if self.has_tag() {
Some(self.get_dat_raw_unchecked())
} else {
None
}
}
pub fn get_tag_and_dat(&self) -> Option<(TAG, DAT)> {
if self.has_tag() {
Some((
unsafe{self.get_tag_unchecked()},
self.get_dat_raw_unchecked()
))
} else {
None
}
}
}
impl<TAG: NaNTag, DAT: NaNDat> TryFrom<RawNaNVal<TAG, DAT>> for f64 {
type Error = ();
fn try_from(value: RawNaNVal<TAG, DAT>) -> Result<Self, Self::Error> {
value.get_f64().ok_or(())
}
}
impl<TAG: NaNTag, DAT: NaNDat> TryFrom<f64> for RawNaNVal<TAG, DAT> {
type Error = ();
fn try_from(f: f64) -> Result<Self, Self::Error> {
let new = Self {f};
new.has_tag().then(||new).ok_or(())
}
}
impl<TAG: NaNTag, DAT: NaNDat> From<TAG> for RawNaNVal<TAG, DAT> {
fn from(tag: TAG) -> Self {
Self::from_tag(tag)
}
}
impl<TAG: NaNTag, DAT: NaNDat> From<(TAG, DAT)> for RawNaNVal<TAG, DAT> {
fn from((tag, dat): (TAG, DAT)) -> Self {
Self::from_tag_and_data(tag, dat)
}
}
#[test]
fn size() {
assert!(core::mem::size_of::<RawNaNVal<NonZeroU16, u32>>() == 8)
}