use super::{cons::*, IntoRawBits64};
use core::num::NonZeroU64;
pub const CELL_MARKER_BITS: u64 = SIGN_BIT | NAN_BITS;
pub const CELL_MARKER_MASK: u64 = SIGN_BIT | NAN_BITS;
pub const CELL_TAG_BITS: u64 = 0x0007000000000000;
#[derive(Copy, Clone)]
#[repr(u64)]
pub enum CellTag {
Tag1 = 0x0001000000000000,
Tag2 = 0x0002000000000000,
Tag3 = 0x0003000000000000,
Tag4 = 0x0004000000000000,
Tag5 = 0x0005000000000000,
Tag6 = 0x0006000000000000,
Tag7 = 0x0007000000000000,
}
impl TryFrom<u64> for CellTag {
type Error = (); fn try_from(value: u64) -> Result<Self, Self::Error> {
Ok(match value {
0x0001000000000000 => Self::Tag1,
0x0002000000000000 => Self::Tag2,
0x0003000000000000 => Self::Tag3,
0x0004000000000000 => Self::Tag4,
0x0005000000000000 => Self::Tag5,
0x0006000000000000 => Self::Tag6,
0x0007000000000000 => Self::Tag7,
_ => return Err(())
})
}
}
pub const CELL_TAG_1: u64 = CellTag::Tag1 as u64;
pub const CELL_TAG_2: u64 = CellTag::Tag2 as u64;
pub const CELL_TAG_3: u64 = CellTag::Tag3 as u64;
pub const CELL_TAG_4: u64 = CellTag::Tag4 as u64;
pub const CELL_TAG_5: u64 = CellTag::Tag5 as u64;
pub const CELL_TAG_6: u64 = CellTag::Tag6 as u64;
pub const CELL_TAG_7: u64 = CellTag::Tag7 as u64;
pub const CELL_DATA_BITS: u64 = !(CELL_MARKER_BITS | CELL_TAG_BITS);
#[test]
pub fn test_cell_bits() {
assert!(CELL_MARKER_BITS != CELL_TAG_BITS);
assert!(CELL_MARKER_BITS != CELL_DATA_BITS);
assert!(CELL_TAG_BITS != CELL_DATA_BITS);
assert!(CELL_MARKER_BITS & CELL_TAG_BITS & CELL_DATA_BITS == 0);
assert!(CELL_MARKER_BITS | CELL_TAG_BITS | CELL_DATA_BITS == u64::MAX);
assert!(CELL_MARKER_BITS ^ CELL_TAG_BITS ^ CELL_DATA_BITS == u64::MAX);
}
#[test]
#[cfg(feature = "std")]
pub fn test_ptr_roundtrip() {
let val: &'static [u8] = &[0,1,2,3,4,5,6,7,8,9];
let ptr_before = val.as_ptr() as *const ();
let nan = from_tag_and_pointer(CellTag::Tag5, ptr_before).unwrap();
let ptr_after = unwrap_cell_rawptr(nan).unwrap();
assert!(ptr_before == ptr_after, "Before {ptr_before:?} == After {ptr_after:?}")
}
#[inline(always)]
pub fn is_cell(value: impl IntoRawBits64) -> bool {
(value.as_raw_bits_64() & CELL_MARKER_BITS) == CELL_MARKER_BITS
}
#[inline(always)]
pub fn is_not_cell(value: impl IntoRawBits64) -> bool {
(value.as_raw_bits_64() & CELL_MARKER_BITS) != CELL_MARKER_BITS
}
#[inline(always)]
pub fn unwrap_tag_bits_unchecked(value: impl IntoRawBits64) -> u64 {
value.as_raw_bits_64() & CELL_TAG_BITS
}
#[inline(always)]
pub fn unwrap_tag_bits(value: impl IntoRawBits64) -> Option<u64> {
match is_cell(value) {
true => Some(value.as_raw_bits_64() & CELL_TAG_BITS),
false => None
}
}
#[inline(always)]
pub fn unwrap_tag(value: impl IntoRawBits64) -> Option<CellTag> {
match is_cell(value) {
true => CellTag::try_from(value.as_raw_bits_64() & CELL_TAG_BITS).ok(),
false => None
}
}
#[inline(always)]
pub fn unwrap_cell_unchecked(value: impl IntoRawBits64) -> u64 {
value.as_raw_bits_64() & CELL_DATA_BITS
}
#[inline(always)]
pub fn unwrap_cell(value: impl IntoRawBits64) -> Option<u64> {
match is_cell(value) {
true => Some(unwrap_cell_unchecked(value)),
false => None
}
}
#[inline(always)]
pub fn unwrap_cell_nonzero(value: impl IntoRawBits64) -> Option<NonZeroU64> {
match is_cell(value) {
true => NonZeroU64::new(unwrap_cell_unchecked(value)),
false => None
}
}
#[inline(always)]
pub fn unwrap_cell_rawptr(value: impl IntoRawBits64) -> Option<*const ()> {
match is_cell(value) {
true => Some(unwrap_cell_unchecked(value) as *const ()),
false => None
}
}
pub fn from_tag_and_pointer(tag: CellTag, ptr: *const ()) -> Option<u64> {
from_tag_and_data(tag, ptr as u64)
}
pub fn from_tag_and_data(tag: CellTag, data: u64) -> Option<u64> {
let vtag = (tag as u64) & CELL_TAG_BITS;
let vdata = data & CELL_DATA_BITS;
if vtag != tag as u64 {return None}
if vdata != data {return None}
Some(CELL_MARKER_BITS | vtag | vdata)
}