use shape_value::HeapKind;
use std::sync::Arc;
use super::jit_kinds::{read_heap_kind, unified_box, unified_unbox};
pub const NAN_BASE: u64 = 0x7FF0_0000_0000_0000;
pub const TAG_MASK: u64 = 0xFFFF_0000_0000_0000;
pub const TAG_BASE: u64 = 0xFFF8_0000_0000_0000;
pub const TAG_SHIFT: u32 = 48;
pub const PAYLOAD_MASK: u64 = 0x0000_FFFF_FFFF_FFFF;
pub const CANONICAL_NAN: u64 = 0x7FF8_0000_0000_0000;
pub const I48_MAX: i64 = (1_i64 << 47) - 1;
pub const I48_MIN: i64 = -(1_i64 << 47);
pub const UNIFIED_HEAP_FLAG: u64 = 1 << 47;
pub const UNIFIED_PTR_MASK: u64 = PAYLOAD_MASK & !UNIFIED_HEAP_FLAG;
const HEAP_OWNED_BIT: u64 = 1;
pub const HEAP_PTR_MASK: u64 = !HEAP_OWNED_BIT;
const TAG_HEAP_BITS: u64 = 0b000;
const TAG_INT_BITS: u64 = 0b001;
const TAG_BOOL_BITS: u64 = 0b010;
const TAG_NONE_BITS: u64 = 0b011;
const TAG_UNIT_BITS: u64 = 0b100;
const TAG_FUNCTION_BITS: u64 = 0b101;
#[inline]
const fn make_tagged(tag: u64, payload: u64) -> u64 {
TAG_BASE | (tag << TAG_SHIFT) | (payload & PAYLOAD_MASK)
}
#[inline]
fn is_tagged(bits: u64) -> bool {
bits & TAG_BASE == TAG_BASE
}
#[inline]
fn get_tag(bits: u64) -> u64 {
(bits >> TAG_SHIFT) & 0b111
}
pub const TAG_NULL: u64 = make_tagged(TAG_NONE_BITS, 0);
pub const TAG_BOOL_FALSE: u64 = make_tagged(TAG_BOOL_BITS, 0);
pub const TAG_BOOL_TRUE: u64 = make_tagged(TAG_BOOL_BITS, 1);
pub const TAG_UNIT: u64 = make_tagged(TAG_UNIT_BITS, 0);
pub const TAG_NONE: u64 = TAG_NULL;
pub const TAG_NUMBER: u64 = 0x0000_0000_0000_0000;
pub const TAG_DATA_ROW: u64 = TAG_BASE | (TAG_INT_BITS << TAG_SHIFT);
pub const JIT_LEGACY_HK_BASE: u16 = 256;
pub const HK_STRING: u16 = HeapKind::String as u16;
pub const HK_TYPED_OBJECT: u16 = HeapKind::TypedObject as u16;
pub const HK_CLOSURE: u16 = HeapKind::Closure as u16;
pub const HK_DECIMAL: u16 = HeapKind::Decimal as u16;
pub const HK_BIG_INT: u16 = HeapKind::BigInt as u16;
pub const HK_DATATABLE: u16 = HeapKind::DataTable as u16;
pub const HK_HASHMAP: u16 = HeapKind::HashMap as u16;
pub const HK_FUTURE: u16 = HeapKind::Future as u16;
pub const HK_TASK_GROUP: u16 = HeapKind::TaskGroup as u16;
pub const HK_FILTER_EXPR: u16 = HeapKind::FilterExpr as u16;
pub const HK_ARRAY: u16 = JIT_LEGACY_HK_BASE; pub const HK_HOST_CLOSURE: u16 = JIT_LEGACY_HK_BASE + 1; pub const HK_TYPED_TABLE: u16 = JIT_LEGACY_HK_BASE + 2; pub const HK_ROW_VIEW: u16 = JIT_LEGACY_HK_BASE + 3; pub const HK_COLUMN_REF: u16 = JIT_LEGACY_HK_BASE + 4; pub const HK_INDEXED_TABLE: u16 = JIT_LEGACY_HK_BASE + 5; pub const HK_RANGE: u16 = JIT_LEGACY_HK_BASE + 6; pub const HK_ENUM: u16 = JIT_LEGACY_HK_BASE + 7; pub const HK_SOME: u16 = JIT_LEGACY_HK_BASE + 8; pub const HK_OK: u16 = JIT_LEGACY_HK_BASE + 9; pub const HK_ERR: u16 = JIT_LEGACY_HK_BASE + 10; pub const HK_TRAIT_OBJECT: u16 = JIT_LEGACY_HK_BASE + 11; pub const HK_EXPR_PROXY: u16 = JIT_LEGACY_HK_BASE + 12; pub const HK_TIME: u16 = JIT_LEGACY_HK_BASE + 13; pub const HK_DURATION: u16 = JIT_LEGACY_HK_BASE + 14; pub const HK_TIMESPAN: u16 = JIT_LEGACY_HK_BASE + 15; pub const HK_TIMEFRAME: u16 = JIT_LEGACY_HK_BASE + 16; pub const HK_TIME_REFERENCE: u16 = JIT_LEGACY_HK_BASE + 17; pub const HK_DATETIME_EXPR: u16 = JIT_LEGACY_HK_BASE + 18; pub const HK_DATA_DATETIME_REF: u16 = JIT_LEGACY_HK_BASE + 19; pub const HK_TYPE_ANNOTATION: u16 = JIT_LEGACY_HK_BASE + 20; pub const HK_TYPE_ANNOTATED_VALUE: u16 = JIT_LEGACY_HK_BASE + 21; pub const HK_PRINT_RESULT: u16 = JIT_LEGACY_HK_BASE + 22; pub const HK_SIMULATION_CALL: u16 = JIT_LEGACY_HK_BASE + 23; pub const HK_FUNCTION_REF: u16 = JIT_LEGACY_HK_BASE + 24; pub const HK_DATA_REFERENCE: u16 = JIT_LEGACY_HK_BASE + 25; pub const HK_INT_ARRAY: u16 = JIT_LEGACY_HK_BASE + 26; pub const HK_FLOAT_ARRAY: u16 = JIT_LEGACY_HK_BASE + 27; pub const HK_BOOL_ARRAY: u16 = JIT_LEGACY_HK_BASE + 28; pub const HK_MATRIX: u16 = JIT_LEGACY_HK_BASE + 29; pub const HK_I8_ARRAY: u16 = JIT_LEGACY_HK_BASE + 30; pub const HK_I16_ARRAY: u16 = JIT_LEGACY_HK_BASE + 31; pub const HK_I32_ARRAY: u16 = JIT_LEGACY_HK_BASE + 32; pub const HK_U8_ARRAY: u16 = JIT_LEGACY_HK_BASE + 33; pub const HK_U16_ARRAY: u16 = JIT_LEGACY_HK_BASE + 34; pub const HK_U32_ARRAY: u16 = JIT_LEGACY_HK_BASE + 35; pub const HK_U64_ARRAY: u16 = JIT_LEGACY_HK_BASE + 36; pub const HK_F32_ARRAY: u16 = JIT_LEGACY_HK_BASE + 37; pub const HK_FLOAT_ARRAY_SLICE: u16 = JIT_LEGACY_HK_BASE + 38;
const _: () = {
assert!(
JIT_LEGACY_HK_BASE >= 192,
"JIT_LEGACY_HK_BASE must sit above the HeapKind / jit_kinds.rs / v2_struct.rs blocks"
);
};
const _: () = {
assert!(
TAG_NULL & 0x8000_0000_0000_0000 != 0,
"TAG_NULL must be in negative NaN space"
);
assert!(
TAG_BOOL_FALSE & 0x8000_0000_0000_0000 != 0,
"TAG_BOOL must be in negative NaN space"
);
assert!(
TAG_UNIT & 0x8000_0000_0000_0000 != 0,
"TAG_UNIT must be in negative NaN space"
);
assert!(
TAG_DATA_ROW & 0x8000_0000_0000_0000 != 0,
"TAG_DATA_ROW must be in negative NaN space"
);
};
#[inline]
pub fn is_number(bits: u64) -> bool {
!is_tagged(bits)
}
#[inline]
pub fn unbox_number(bits: u64) -> f64 {
f64::from_bits(bits)
}
#[inline]
pub const fn box_number(n: f64) -> u64 {
f64::to_bits(n)
}
#[inline]
pub const fn box_bool(b: bool) -> u64 {
if b { TAG_BOOL_TRUE } else { TAG_BOOL_FALSE }
}
#[inline]
pub fn box_function(fn_id: u16) -> u64 {
make_tagged(TAG_FUNCTION_BITS, fn_id as u64)
}
#[inline]
pub fn is_inline_function(bits: u64) -> bool {
is_tagged(bits) && get_tag(bits) == TAG_FUNCTION_BITS
}
#[inline]
pub fn unbox_function_id(bits: u64) -> u16 {
(bits & PAYLOAD_MASK) as u16
}
#[inline]
pub fn is_heap(bits: u64) -> bool {
is_tagged(bits) && get_tag(bits) == TAG_HEAP_BITS
}
#[inline]
pub fn heap_kind(bits: u64) -> Option<u16> {
if !is_heap(bits) {
return None;
}
Some(unsafe { read_heap_kind(unbox_heap_pointer(bits) as u64) })
}
#[inline]
pub fn is_heap_kind(bits: u64, expected_kind: u16) -> bool {
heap_kind(bits) == Some(expected_kind)
}
#[inline]
pub fn unbox_heap_pointer(bits: u64) -> *const u8 {
(bits & PAYLOAD_MASK & HEAP_PTR_MASK & !UNIFIED_HEAP_FLAG) as *const u8
}
#[inline]
pub fn is_ok_tag(bits: u64) -> bool {
is_heap_kind(bits, HK_OK)
}
#[inline]
pub fn is_err_tag(bits: u64) -> bool {
is_heap_kind(bits, HK_ERR)
}
#[inline]
pub fn is_result_tag(bits: u64) -> bool {
is_ok_tag(bits) || is_err_tag(bits)
}
#[inline]
pub fn box_ok(inner_bits: u64) -> u64 {
unified_box(HK_OK, inner_bits)
}
#[inline]
pub fn box_err(inner_bits: u64) -> u64 {
unified_box(HK_ERR, inner_bits)
}
#[inline]
pub unsafe fn unbox_result_inner(bits: u64) -> u64 {
*unsafe { unified_unbox::<u64>(bits) }
}
#[inline]
pub fn unbox_result_pointer(bits: u64) -> *const u64 {
let ptr = unbox_heap_pointer(bits);
if ptr.is_null() {
std::ptr::null()
} else {
unsafe { (ptr.add(super::jit_kinds::JIT_ALLOC_DATA_OFFSET)) as *const u64 }
}
}
#[inline]
pub fn is_some_tag(bits: u64) -> bool {
is_heap_kind(bits, HK_SOME)
}
#[inline]
pub fn is_none_tag(bits: u64) -> bool {
bits == TAG_NULL
}
#[inline]
pub fn is_option_tag(bits: u64) -> bool {
is_some_tag(bits) || is_none_tag(bits)
}
#[inline]
pub fn box_some(inner_bits: u64) -> u64 {
unified_box(HK_SOME, inner_bits)
}
#[inline]
pub unsafe fn unbox_some_inner(bits: u64) -> u64 {
*unsafe { unified_unbox::<u64>(bits) }
}
#[inline]
pub const fn box_data_row(row_index: usize) -> u64 {
TAG_DATA_ROW | ((row_index as u64) & PAYLOAD_MASK)
}
#[inline]
pub const fn unbox_data_row(bits: u64) -> usize {
(bits & PAYLOAD_MASK) as usize
}
#[inline]
pub fn is_data_row(bits: u64) -> bool {
is_tagged(bits) && get_tag(bits) == TAG_INT_BITS
}
#[inline]
pub fn box_column_ref(ptr: *const f64, len: usize) -> u64 {
unified_box(HK_COLUMN_REF, (ptr, len))
}
#[inline]
pub unsafe fn unbox_column_ref(bits: u64) -> (*const f64, usize) {
*unsafe { unified_unbox::<(*const f64, usize)>(bits) }
}
#[inline]
pub fn is_column_ref(bits: u64) -> bool {
is_heap_kind(bits, HK_COLUMN_REF)
}
#[inline]
pub unsafe fn extract_column(bits: u64) -> Option<&'static [f64]> {
if !is_column_ref(bits) {
return None;
}
let (ptr, len) = unsafe { unbox_column_ref(bits) };
if ptr.is_null() || len == 0 {
return None;
}
Some(unsafe { std::slice::from_raw_parts(ptr, len) })
}
#[inline]
pub fn box_column_result(data: Vec<f64>) -> u64 {
let len = data.len();
let leaked = Box::leak(data.into_boxed_slice());
box_column_ref(leaked.as_ptr(), len)
}
#[inline]
pub fn box_typed_object(ptr: *const u8) -> u64 {
unified_box(HK_TYPED_OBJECT, ptr)
}
#[inline]
pub fn unbox_typed_object(bits: u64) -> *const u8 {
*unsafe { unified_unbox::<*const u8>(bits) }
}
#[inline]
pub fn is_typed_object(bits: u64) -> bool {
is_heap_kind(bits, HK_TYPED_OBJECT)
}
#[inline]
pub fn box_string(s: String) -> u64 {
unified_box(HK_STRING, Arc::new(s))
}
#[inline]
pub fn box_str(s: &str) -> u64 {
unified_box(HK_STRING, Arc::new(s.to_string()))
}
#[inline]
pub unsafe fn unbox_string(bits: u64) -> &'static str {
let arc: &Arc<String> = unsafe { unified_unbox::<Arc<String>>(bits) };
arc.as_str()
}
#[cfg(test)]
mod tests {
use super::*;
use super::super::jit_kinds::UnifiedValue;
#[test]
fn test_inline_types_in_negative_nan_space() {
assert!(TAG_NULL & 0x8000_0000_0000_0000 != 0);
assert!(TAG_BOOL_FALSE & 0x8000_0000_0000_0000 != 0);
assert!(TAG_BOOL_TRUE & 0x8000_0000_0000_0000 != 0);
assert!(TAG_UNIT & 0x8000_0000_0000_0000 != 0);
}
#[test]
fn test_data_row_in_negative_nan_space() {
assert!(TAG_DATA_ROW & 0x8000_0000_0000_0000 != 0);
assert!(!is_number(TAG_DATA_ROW));
}
#[test]
fn test_nan_base_detects_all_tags() {
assert!(!is_number(TAG_NULL), "TAG_NULL should not be a number");
assert!(
!is_number(TAG_DATA_ROW),
"TAG_DATA_ROW should not be a number"
);
assert!(
!is_number(TAG_BOOL_TRUE),
"TAG_BOOL_TRUE should not be a number"
);
assert!(is_number(box_number(3.14)));
assert!(is_number(box_number(0.0)));
assert!(is_number(box_number(-1.0)));
assert!(is_number(box_number(f64::MAX)));
assert!(is_number(box_number(f64::MIN)));
}
#[test]
fn test_box_unbox_number() {
let n = 3.14f64;
let boxed = box_number(n);
assert!(is_number(boxed));
assert_eq!(unbox_number(boxed), n);
}
#[test]
fn test_box_unbox_bool() {
assert_eq!(box_bool(true), TAG_BOOL_TRUE);
assert_eq!(box_bool(false), TAG_BOOL_FALSE);
}
#[test]
fn test_box_function() {
let bits = box_function(42);
assert!(is_inline_function(bits));
assert_eq!(unbox_function_id(bits), 42);
assert!(!is_number(bits));
assert!(!is_heap(bits));
}
#[test]
fn test_data_row_round_trip() {
let bits = box_data_row(999);
assert!(is_data_row(bits));
assert_eq!(unbox_data_row(bits), 999);
assert!(!is_number(bits));
assert!(!is_heap(bits));
}
#[test]
fn test_typed_object_encoding_via_heap_kind_prefix() {
let fake_ptr = 0x0000_1234_5678_0000u64 as *const u8;
let boxed = box_typed_object(fake_ptr);
assert_ne!(boxed, 0, "allocation pointer is non-null");
assert_eq!(
unsafe { super::super::jit_kinds::read_heap_kind(boxed) },
HK_TYPED_OBJECT,
"heap-kind prefix at offset 0 discriminates the allocation"
);
let recovered = unbox_typed_object(boxed);
assert_eq!(recovered, fake_ptr);
unsafe { UnifiedValue::<*const u8>::heap_drop(boxed) };
}
#[test]
fn test_typed_object_kinded_slot_discriminates_via_kind_label() {
use shape_value::{HeapKind, KindedSlot, NativeKind, TypedObjectStorage, ValueSlot};
use std::sync::Arc;
let ptr = TypedObjectStorage::_new(
0,
Vec::<ValueSlot>::new().into_boxed_slice(),
0,
Arc::from(Vec::<NativeKind>::new().into_boxed_slice()),
);
let slot = KindedSlot::from_typed_object_raw(ptr);
assert_eq!(slot.kind(), NativeKind::Ptr(HeapKind::TypedObject));
}
}