nanval/
cell.rs

1//! Handling of values marked as a 'cell' (`SIGN_BIT | NAN_BITS`, with 3 'tag' bits).
2//! 
3//! Bit Layout is as follows:
4//! ```text
5//! s111 1111 1111 1ttt xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx xxxx
6//! ```
7//! 
8//! - Sign `s`; always `1` for a cell-value.
9//! - Tag  `t`, 3 bits; `0b000` is undefined.
10//! - Data `x`, 48 bits.
11//! 
12//! **Note:** Is is *highly* recommended to always refer to these functions via the module; ie: `cell::XXX`.
13
14use super::{cons::*, IntoRawBits64};
15use core::num::NonZeroU64;
16
17/// Indicates that the value is a cell.
18pub const CELL_MARKER_BITS: u64 = SIGN_BIT | NAN_BITS;
19
20/// Masks the bits ([`CELL_MARKER_BITS`]) that indicate that the value is a cell.
21pub const CELL_MARKER_MASK: u64 = SIGN_BIT | NAN_BITS;
22
23/// Masks out the tag of a [`CELL_MARKER_BITS`]-marked value.
24pub const CELL_TAG_BITS: u64 = 0x0007000000000000;
25
26/// Represents all possible variants for a cell-values 3-bit tag.
27/// 
28/// **Note:**  
29/// Tag `0` is intentionally left undefined,
30/// to prevent the value ever accidentally
31/// becoming the original/sentinel `NaN`.
32#[derive(Copy, Clone)]
33#[repr(u64)]
34pub enum CellTag {
35    // Tag0 is intentionally undefined.
36    
37    /// Tag `0b001`.
38    Tag1 = 0x0001000000000000,
39    
40    /// Tag `0b010`.
41    Tag2 = 0x0002000000000000,
42    
43    /// Tag `0b011`.
44    Tag3 = 0x0003000000000000,
45    
46    /// Tag `0b100`.
47    Tag4 = 0x0004000000000000,
48    
49    /// Tag `0b101`.
50    Tag5 = 0x0005000000000000,
51    
52    /// Tag `0b110`.
53    Tag6 = 0x0006000000000000,
54    
55    /// Tag `0b111`.
56    Tag7 = 0x0007000000000000,
57}
58
59impl TryFrom<u64> for CellTag {
60    type Error = (); // error left as unit type
61    fn try_from(value: u64) -> Result<Self, Self::Error> {
62        Ok(match value {
63            0x0001000000000000 => Self::Tag1,
64            0x0002000000000000 => Self::Tag2,
65            0x0003000000000000 => Self::Tag3,
66            0x0004000000000000 => Self::Tag4,
67            0x0005000000000000 => Self::Tag5,
68            0x0006000000000000 => Self::Tag6,
69            0x0007000000000000 => Self::Tag7,
70            _ => return Err(())
71        })
72    }
73}
74
75// All cell-value tag variants, but as constants:
76
77/// Cell Tag `0b001`.
78pub const CELL_TAG_1: u64 = CellTag::Tag1 as u64;
79
80/// Cell Tag `0b010`.
81pub const CELL_TAG_2: u64 = CellTag::Tag2 as u64;
82
83/// Cell Tag `0b011`.
84pub const CELL_TAG_3: u64 = CellTag::Tag3 as u64;
85
86/// Cell Tag `0b100`.
87pub const CELL_TAG_4: u64 = CellTag::Tag4 as u64;
88
89/// Cell Tag `0b101`.
90pub const CELL_TAG_5: u64 = CellTag::Tag5 as u64;
91
92/// Cell Tag `0b110`.
93pub const CELL_TAG_6: u64 = CellTag::Tag6 as u64;
94
95/// Cell Tag `0b111`.
96pub const CELL_TAG_7: u64 = CellTag::Tag7 as u64;
97
98/// Masks out the data of a [`CELL_MARKER_BITS`]-marked value.
99pub const CELL_DATA_BITS: u64 = !(CELL_MARKER_BITS | CELL_TAG_BITS);
100
101/// Ensure that the bit-patterns do not overlap.
102#[test]
103pub fn test_cell_bits() {
104    assert!(CELL_MARKER_BITS != CELL_TAG_BITS);
105    assert!(CELL_MARKER_BITS != CELL_DATA_BITS);
106    assert!(CELL_TAG_BITS != CELL_DATA_BITS);
107    assert!(CELL_MARKER_BITS & CELL_TAG_BITS & CELL_DATA_BITS == 0);
108    assert!(CELL_MARKER_BITS | CELL_TAG_BITS | CELL_DATA_BITS == u64::MAX);
109    assert!(CELL_MARKER_BITS ^ CELL_TAG_BITS ^ CELL_DATA_BITS == u64::MAX);
110}
111
112#[test]
113#[cfg(feature = "std")]
114pub fn test_ptr_roundtrip() {
115    let val: &'static [u8] = &[0,1,2,3,4,5,6,7,8,9];
116    let ptr_before = val.as_ptr() as *const ();
117    let nan = from_tag_and_pointer(CellTag::Tag5, ptr_before).unwrap();
118    let ptr_after = unwrap_cell_rawptr(nan).unwrap();
119    assert!(ptr_before == ptr_after, "Before {ptr_before:?} == After {ptr_after:?}")
120}
121
122/// Returns wether the given value is a cell.
123#[inline(always)]
124pub fn is_cell(value: impl IntoRawBits64) -> bool {
125    (value.as_raw_bits_64() & CELL_MARKER_BITS) == CELL_MARKER_BITS
126}
127
128/// Returns wether the given value is *not* a cell.
129#[inline(always)]
130pub fn is_not_cell(value: impl IntoRawBits64) -> bool {
131    (value.as_raw_bits_64() & CELL_MARKER_BITS) != CELL_MARKER_BITS
132}
133
134/// Returns the tag bits of the given value.
135#[inline(always)]
136pub fn unwrap_tag_bits_unchecked(value: impl IntoRawBits64) -> u64 {
137    value.as_raw_bits_64() & CELL_TAG_BITS
138}
139
140/// Returns the tag bits of the given value, if it is a cell.
141#[inline(always)]
142pub fn unwrap_tag_bits(value: impl IntoRawBits64) -> Option<u64> {
143    match is_cell(value) {
144        true => Some(value.as_raw_bits_64() & CELL_TAG_BITS),
145        false => None
146    }
147}
148
149/// Returns the tag bits of the given value, if it is a cell.
150#[inline(always)]
151pub fn unwrap_tag(value: impl IntoRawBits64) -> Option<CellTag> {
152    match is_cell(value) {
153        true => CellTag::try_from(value.as_raw_bits_64() & CELL_TAG_BITS).ok(),
154        false => None
155    }
156}
157
158/// Unwraps the cell-data of the given value as [`u64`], without checking if it is a cell.
159#[inline(always)]
160pub fn unwrap_cell_unchecked(value: impl IntoRawBits64) -> u64 {
161    value.as_raw_bits_64() & CELL_DATA_BITS
162}
163
164/// Unwraps the cell-data of the given value as [`u64`], if it is a cell.
165#[inline(always)]
166pub fn unwrap_cell(value: impl IntoRawBits64) -> Option<u64> {
167    match is_cell(value) {
168        true => Some(unwrap_cell_unchecked(value)),
169        false => None
170    }
171}
172
173/// Unwraps the cell-data of the given value as [`NonZeroU64`], if it is a cell.
174#[inline(always)]
175pub fn unwrap_cell_nonzero(value: impl IntoRawBits64) -> Option<NonZeroU64> {
176    match is_cell(value) {
177        true => NonZeroU64::new(unwrap_cell_unchecked(value)),
178        false => None
179    }
180}
181
182/// Unwraps the cell-data of the given value as `*const ()`, if it is a cell.
183/// 
184/// # Safety
185/// This function cannot check if the returned pointer is valid.
186#[inline(always)]
187pub fn unwrap_cell_rawptr(value: impl IntoRawBits64) -> Option<*const ()> {
188    match is_cell(value) {
189        true => Some(unwrap_cell_unchecked(value) as *const ()),
190        false => None
191    }
192}
193
194/// Combines the given tag and pointer into a NaN-tagged value.
195/// 
196/// If either the `tag` or the `ptr` don't fit in the limits
197/// imposed by [`CELL_TAG_BITS`] and [`CELL_DATA_BITS`],
198/// this function will return `None`.
199/// 
200/// # Safety
201/// 
202/// It is guaranteed that, if this function returns a `Some`-wrapped value,
203/// that this value can be safely unwrapped via [`unwrap_cell_rawptr`]
204/// yielding exactly the same pointer as it was created from.
205/// 
206/// However, performing any kind of logic- or arithmetic-operations
207/// on the returned value, will result in undefined behaviour.
208pub fn from_tag_and_pointer(tag: CellTag, ptr: *const ()) -> Option<u64> {
209    from_tag_and_data(tag, ptr as u64)
210}
211
212pub fn from_tag_and_data(tag: CellTag, data: u64) -> Option<u64> {
213    let vtag = (tag as u64) & CELL_TAG_BITS;
214    let vdata = data & CELL_DATA_BITS;
215    if vtag != tag as u64 {return None}
216    if vdata != data {return None}
217    Some(CELL_MARKER_BITS | vtag | vdata)
218}