#![allow(clippy::wrong_self_convention)]
use std::marker::PhantomData;
use std::mem;
use crate::internal::object::ptr::{Any, Ptr};
use crate::internal::object::{Object, Type};
mod mask {
pub const QNAN: u64 = 0b01111111_11111100_00000000_00000000_00000000_00000000_00000000_00000000;
pub const TAG: u64 = 0b11111111_11111111_00000000_00000000_00000000_00000000_00000000_00000000;
pub const VALUE: u64 = 0b00000000_00000000_11111111_11111111_11111111_11111111_11111111_11111111;
}
#[rustfmt::skip]
mod ty {
pub const INT : u64 = 0b01111111_11111100_00000000_00000000_00000000_00000000_00000000_00000000;
pub const BOOL : u64 = 0b01111111_11111101_00000000_00000000_00000000_00000000_00000000_00000000;
pub const NONE : u64 = 0b01111111_11111110_00000000_00000000_00000000_00000000_00000000_00000000;
pub const OBJECT : u64 = 0b01111111_11111111_00000000_00000000_00000000_00000000_00000000_00000000;
}
#[allow(dead_code)]
enum PhantomValue {
Float(f64),
Int(i32),
Bool(bool),
None,
Object(Ptr<Any>),
}
pub struct Value {
bits: u64,
_p: PhantomData<PhantomValue>,
}
impl Value {
const fn new(bits: u64) -> Self {
Self {
bits,
_p: PhantomData,
}
}
pub fn float(v: f64) -> Self {
let bits = v.to_bits();
if bits & mask::QNAN == mask::QNAN {
panic!("cannot construct a Value from an f64 which is already a quiet NaN");
}
Self::new(bits)
}
pub fn int(v: i32) -> Self {
let bits = unsafe { std::mem::transmute::<i32, u32>(v) } as u64;
let bits = bits | ty::INT;
Self::new(bits)
}
pub fn bool(v: bool) -> Self {
let bits = v as u64;
let bits = bits | ty::BOOL;
Self::new(bits)
}
pub const fn none() -> Self {
let bits = ty::NONE;
Self::new(bits)
}
pub fn object<T: Object>(ptr: Ptr<T>) -> Self {
let bits = ptr.into_addr() as u64;
let bits = (bits & mask::VALUE) | ty::OBJECT;
Self::new(bits)
}
}
impl Value {
#[inline]
fn value(&self) -> u64 {
self.bits & mask::VALUE
}
#[inline]
fn type_tag(&self) -> u64 {
self.bits & mask::TAG
}
#[inline]
pub fn is_float(&self) -> bool {
(self.bits & mask::QNAN) != mask::QNAN
}
#[inline]
pub fn is_int(&self) -> bool {
self.type_tag() == ty::INT
}
#[inline]
pub fn is_bool(&self) -> bool {
self.type_tag() == ty::BOOL
}
#[inline]
pub fn is_none(&self) -> bool {
self.type_tag() == ty::NONE
}
#[inline]
pub fn is_object(&self) -> bool {
self.type_tag() == ty::OBJECT
}
}
impl Clone for Value {
fn clone(&self) -> Self {
if self.is_object() {
unsafe { Ptr::<Any>::incref_addr(self.value() as usize) }
}
Self {
bits: self.bits,
_p: self._p,
}
}
}
impl Drop for Value {
fn drop(&mut self) {
if self.is_object() {
unsafe {
Ptr::<Any>::from_addr(self.value() as usize);
}
}
}
}
impl Value {
pub fn to_float(self) -> Option<f64> {
if !self.is_float() {
return None;
}
Some(unsafe { self.to_float_unchecked() })
}
pub unsafe fn to_float_unchecked(self) -> f64 {
debug_assert!(self.is_float(), "value is not a float");
f64::from_bits(self.bits)
}
pub fn to_int(self) -> Option<i32> {
if !self.is_int() {
return None;
}
Some(unsafe { self.to_int_unchecked() })
}
pub unsafe fn to_int_unchecked(self) -> i32 {
debug_assert!(self.is_int(), "value is not an int");
self.value() as u32 as i32
}
pub fn to_bool(self) -> Option<bool> {
if !self.is_bool() {
return None;
}
Some(self.value() == 1)
}
pub unsafe fn to_bool_unchecked(self) -> bool {
debug_assert!(self.is_bool(), "value is not a bool");
self.value() != 0
}
pub fn to_none(self) -> Option<()> {
if !self.is_none() {
return None;
}
Some(())
}
pub fn to_object<T: Type>(self) -> Option<Ptr<T>> {
self.try_to_object().ok()
}
pub fn try_to_object<T: Type>(self) -> Result<Ptr<T>, Value> {
self.try_to_any()?.cast::<T>().map_err(Value::object)
}
pub unsafe fn to_object_unchecked<T: Type>(self) -> Ptr<T> {
debug_assert!(
self.is_object(),
"value is not an instance of {}",
std::any::type_name::<T>()
);
let object = self.to_any_unchecked();
debug_assert!(
object.is::<T>(),
"value is not an instance of {}",
std::any::type_name::<T>()
);
object.cast_unchecked()
}
pub fn to_any(self) -> Option<Ptr<Any>> {
self.try_to_any().ok()
}
pub fn try_to_any(self) -> Result<Ptr<Any>, Value> {
if !self.is_object() {
return Err(self);
}
Ok(unsafe { self.to_any_unchecked() })
}
pub unsafe fn to_any_unchecked(self) -> Ptr<Any> {
debug_assert!(self.is_object(), "value is not an object");
let ptr = unsafe { Ptr::from_addr(self.value() as usize) };
mem::forget(self);
ptr
}
}