Skip to content

Commit

Permalink
Start implementing NaN boxing
Browse files Browse the repository at this point in the history
  • Loading branch information
mattkleiny committed Aug 10, 2024
1 parent eb7af9f commit fa93466
Show file tree
Hide file tree
Showing 2 changed files with 201 additions and 0 deletions.
2 changes: 2 additions & 0 deletions core/common/src/memory.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
//! Memory management tools
pub use nan::*;
pub use stack::*;

mod nan;
mod stack;
199 changes: 199 additions & 0 deletions core/common/src/memory/nan.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
//! A NaN boxed value
//!
//! This is a technique for storing a value in a single f64, where the value is
//! stored in the bits of the f64, and NaN representations are used to encode
//! the tag of the value itself.
use std::ops::{Deref, DerefMut};

/// A NaN boxed value.
///
/// The value is stored in the bits of the f64, and NaN representations are used
/// to encode the tag of the value itself. The type must be [`ToNanValue`] in
/// order to be stored.
#[repr(transparent)]
#[derive(Copy, Clone)]
pub struct NaN<V> {
value: NanValue,
_marker: std::marker::PhantomData<V>,
}

#[derive(Copy, Clone)]
pub union NanValue {
as_u64: u64,
as_f64: f64,
}

const NANISH_MASK: u64 = 0x7ffc000000000000;
const BOOL_MASK: u64 = 0x7ffe000000000002;
const I32_MASK: u64 = 0x7ffc000000000000;
const TRUE_MASK: u64 = BOOL_MASK | 3;
const FALSE_MASK: u64 = BOOL_MASK | 2;
const POINTER_MASK: u64 = 0xfffc000000000000;
const NULL_MASK: u64 = 0x7ffe000000000000;

impl NanValue {
pub fn from_bool(value: bool) -> Self {
NanValue::from_u64(if value { TRUE_MASK } else { FALSE_MASK })
}

pub fn from_i32(value: i32) -> Self {
NanValue::from_u64(value as u64 | I32_MASK)
}

pub fn from_ptr<T>(value: *const T) -> Self {
NanValue::from_u64(value as u64 | POINTER_MASK)
}

pub fn from_u64(value: u64) -> Self {
NanValue { as_u64: value }
}

pub fn from_f64(value: f64) -> Self {
NanValue { as_f64: value }
}

pub fn is_f64(&self) -> bool {
unsafe { self.as_u64 & NANISH_MASK != NANISH_MASK }
}

pub fn is_bool(&self) -> bool {
unsafe { self.as_u64 & BOOL_MASK == BOOL_MASK }
}

pub fn is_i32(&self) -> bool {
unsafe { self.as_u64 & NANISH_MASK == I32_MASK }
}

pub fn is_ptr(&self) -> bool {
unsafe { self.as_u64 & NANISH_MASK == POINTER_MASK }
}

pub fn is_null(&self) -> bool {
unsafe { self.as_u64 == NULL_MASK }
}

pub fn as_f64(&self) -> f64 {
unsafe { self.as_f64 }
}

pub fn as_bool(&self) -> bool {
unsafe { self.as_u64 == TRUE_MASK }
}

pub fn as_i32(&self) -> i32 {
unsafe { self.as_u64 as i32 }
}

pub fn as_ptr<T>(&self) -> *const T {
unsafe { (self.as_u64 & 0xFFFFFFFFFFFF) as *const T }
}
}

impl<V: ToNanValue> NaN<V> {
/// Creates a new NaN box with the given value.
#[inline]
pub fn new(value: V) -> Self {
todo!()
}

/// Returns a reference to the inner value.
#[inline]
pub fn as_ref(&self) -> &V {
todo!()
}

/// Returns a mutable reference to the inner value.
#[inline]
pub fn as_mut(&mut self) -> &mut V {
todo!()
}

/// Converts the NaN box into the inner value.
#[inline]
pub fn into_inner(self) -> V {
todo!()
}

/// Erases the type information and returns the inner value.
pub fn erase(self) -> NaN<()> {
todo!()
}
}

impl NaN<()> {
/// Determines if the NaN box is a specific type.
pub fn is<T: ToNanValue>(&self) -> bool {
todo!()
}

/// Attempts to reify the NaN box as a specific type.
pub fn reify<T: ToNanValue>(self) -> Option<NaN<T>> {
todo!()
}
}

impl<T: ToNanValue> Deref for NaN<T> {
type Target = T;

#[inline]
fn deref(&self) -> &Self::Target {
self.as_ref()
}
}

impl<T: ToNanValue> DerefMut for NaN<T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
self.as_mut()
}
}

/// A trait for types that can be stored in a NaN box.
pub trait ToNanValue: Sized {
/// Converts the value into a NaN box.
#[inline]
fn to_nan_box(self) -> NaN<Self> {
NaN::new(self)
}

/// Converts a NaN box into the inner value.
#[inline]
fn from_nan_box(nan: NaN<Self>) -> Self {
nan.into_inner()
}

/// Determines if the given value is of the current type.
fn matches(value: NanValue) -> bool {
todo!()
}
}

macro_rules! impl_nan_tag {
($type:ty) => {
impl ToNanValue for $type {}
};
}

impl_nan_tag!(bool);
impl_nan_tag!(u8);
impl_nan_tag!(u16);
impl_nan_tag!(u32);
impl_nan_tag!(i8);
impl_nan_tag!(i16);
impl_nan_tag!(i32);
impl_nan_tag!(f32);
impl_nan_tag!(f64);

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_nan_value_type_check() {
assert!(NanValue::from_bool(true).is_bool());
assert!(NanValue::from_bool(false).is_bool());
assert!(NanValue::from_i32(42).is_i32());
assert!(NanValue::from_f64(42.0).is_f64());
}
}

0 comments on commit fa93466

Please sign in to comment.