use crate::content::ContentNode;
use crate::datatable::DataTable;
use crate::enums::EnumValue;
use crate::heap_value::{
ChannelData, DequeData, HashMapData, HeapValue, NativeScalar, NativeTypeLayout, NativeViewData,
PriorityQueueData, ProjectedRefData, RefProjection, SetData,
};
use crate::slot::ValueSlot;
use crate::value::{FilterNode, HostCallable, PrintResult, VMArray, VTable};
use chrono::{DateTime, FixedOffset, Utc};
use shape_ast::ast::{DataDateTimeRef, DateTimeExpr, Duration, TimeReference, TypeAnnotation};
use shape_ast::data::Timeframe;
use std::collections::HashMap;
use std::sync::Arc;
const REF_TARGET_MODULE_FLAG: u64 = 1 << 47;
const REF_TARGET_INDEX_MASK: u64 = REF_TARGET_MODULE_FLAG - 1;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum RefTarget {
Stack(usize),
ModuleBinding(usize),
Projected(ProjectedRefData),
}
use crate::tags::{
CANONICAL_NAN, I48_MAX, I48_MIN, PAYLOAD_MASK, TAG_BOOL, TAG_FUNCTION, TAG_HEAP, TAG_INT,
TAG_MODULE_FN, TAG_NONE, TAG_REF, TAG_UNIT, get_payload, get_tag, is_tagged, make_tagged,
sign_extend_i48,
};
macro_rules! define_nan_tag_types {
() => {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NanTag {
F64,
I48,
Bool,
None,
Unit,
Function,
ModuleFunction,
Heap,
Ref,
}
#[inline]
fn nan_tag_type_name(tag: u64) -> &'static str {
match tag {
TAG_INT => "int",
TAG_BOOL => "bool",
TAG_NONE => "option",
TAG_UNIT => "unit",
TAG_FUNCTION => "function",
TAG_MODULE_FN => "module_function",
TAG_REF => "reference",
_ => "unknown",
}
}
#[inline]
fn nan_tag_is_truthy(tag: u64, payload: u64) -> bool {
match tag {
TAG_INT => sign_extend_i48(payload) != 0,
TAG_BOOL => payload != 0,
TAG_NONE => false,
TAG_UNIT => false,
TAG_FUNCTION | TAG_MODULE_FN | TAG_REF => true,
_ => true,
}
}
};
}
define_nan_tag_types!();
#[derive(Debug)]
pub enum ArrayView<'a> {
Generic(&'a Arc<Vec<ValueWord>>),
Int(&'a Arc<crate::typed_buffer::TypedBuffer<i64>>),
Float(&'a Arc<crate::typed_buffer::AlignedTypedBuffer>),
Bool(&'a Arc<crate::typed_buffer::TypedBuffer<u8>>),
I8(&'a Arc<crate::typed_buffer::TypedBuffer<i8>>),
I16(&'a Arc<crate::typed_buffer::TypedBuffer<i16>>),
I32(&'a Arc<crate::typed_buffer::TypedBuffer<i32>>),
U8(&'a Arc<crate::typed_buffer::TypedBuffer<u8>>),
U16(&'a Arc<crate::typed_buffer::TypedBuffer<u16>>),
U32(&'a Arc<crate::typed_buffer::TypedBuffer<u32>>),
U64(&'a Arc<crate::typed_buffer::TypedBuffer<u64>>),
F32(&'a Arc<crate::typed_buffer::TypedBuffer<f32>>),
}
impl<'a> ArrayView<'a> {
#[inline]
pub fn len(&self) -> usize {
match self {
ArrayView::Generic(a) => a.len(),
ArrayView::Int(a) => a.len(),
ArrayView::Float(a) => a.len(),
ArrayView::Bool(a) => a.len(),
ArrayView::I8(a) => a.len(),
ArrayView::I16(a) => a.len(),
ArrayView::I32(a) => a.len(),
ArrayView::U8(a) => a.len(),
ArrayView::U16(a) => a.len(),
ArrayView::U32(a) => a.len(),
ArrayView::U64(a) => a.len(),
ArrayView::F32(a) => a.len(),
}
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
pub fn get_nb(&self, idx: usize) -> Option<ValueWord> {
match self {
ArrayView::Generic(a) => a.get(idx).cloned(),
ArrayView::Int(a) => a.get(idx).map(|&i| ValueWord::from_i64(i)),
ArrayView::Float(a) => a.get(idx).map(|&f| ValueWord::from_f64(f)),
ArrayView::Bool(a) => a.get(idx).map(|&b| ValueWord::from_bool(b != 0)),
ArrayView::I8(a) => a.get(idx).map(|&v| ValueWord::from_i64(v as i64)),
ArrayView::I16(a) => a.get(idx).map(|&v| ValueWord::from_i64(v as i64)),
ArrayView::I32(a) => a.get(idx).map(|&v| ValueWord::from_i64(v as i64)),
ArrayView::U8(a) => a.get(idx).map(|&v| ValueWord::from_i64(v as i64)),
ArrayView::U16(a) => a.get(idx).map(|&v| ValueWord::from_i64(v as i64)),
ArrayView::U32(a) => a.get(idx).map(|&v| ValueWord::from_i64(v as i64)),
ArrayView::U64(a) => a.get(idx).map(|&v| {
if v <= i64::MAX as u64 {
ValueWord::from_i64(v as i64)
} else {
ValueWord::from_native_u64(v)
}
}),
ArrayView::F32(a) => a.get(idx).map(|&v| ValueWord::from_f64(v as f64)),
}
}
#[inline]
pub fn first_nb(&self) -> Option<ValueWord> {
self.get_nb(0)
}
#[inline]
pub fn last_nb(&self) -> Option<ValueWord> {
if self.is_empty() {
None
} else {
self.get_nb(self.len() - 1)
}
}
pub fn to_generic(&self) -> Arc<Vec<ValueWord>> {
match self {
ArrayView::Generic(a) => (*a).clone(),
ArrayView::Int(a) => Arc::new(a.iter().map(|&i| ValueWord::from_i64(i)).collect()),
ArrayView::Float(a) => Arc::new(
a.as_slice()
.iter()
.map(|&f| ValueWord::from_f64(f))
.collect(),
),
ArrayView::Bool(a) => {
Arc::new(a.iter().map(|&b| ValueWord::from_bool(b != 0)).collect())
}
ArrayView::I8(a) => {
Arc::new(a.iter().map(|&v| ValueWord::from_i64(v as i64)).collect())
}
ArrayView::I16(a) => {
Arc::new(a.iter().map(|&v| ValueWord::from_i64(v as i64)).collect())
}
ArrayView::I32(a) => {
Arc::new(a.iter().map(|&v| ValueWord::from_i64(v as i64)).collect())
}
ArrayView::U8(a) => {
Arc::new(a.iter().map(|&v| ValueWord::from_i64(v as i64)).collect())
}
ArrayView::U16(a) => {
Arc::new(a.iter().map(|&v| ValueWord::from_i64(v as i64)).collect())
}
ArrayView::U32(a) => {
Arc::new(a.iter().map(|&v| ValueWord::from_i64(v as i64)).collect())
}
ArrayView::U64(a) => Arc::new(
a.iter()
.map(|&v| {
if v <= i64::MAX as u64 {
ValueWord::from_i64(v as i64)
} else {
ValueWord::from_native_u64(v)
}
})
.collect(),
),
ArrayView::F32(a) => {
Arc::new(a.iter().map(|&v| ValueWord::from_f64(v as f64)).collect())
}
}
}
#[inline]
pub fn as_i64_slice(&self) -> Option<&[i64]> {
if let ArrayView::Int(a) = self {
Some(a.as_slice())
} else {
None
}
}
#[inline]
pub fn as_f64_slice(&self) -> Option<&[f64]> {
if let ArrayView::Float(a) = self {
Some(a.as_slice())
} else {
None
}
}
#[inline]
pub fn as_bool_slice(&self) -> Option<&[u8]> {
if let ArrayView::Bool(a) = self {
Some(a.as_slice())
} else {
None
}
}
#[inline]
pub fn as_generic(&self) -> Option<&Arc<Vec<ValueWord>>> {
if let ArrayView::Generic(a) = self {
Some(a)
} else {
None
}
}
#[inline]
pub fn iter_i64(&self) -> Option<std::slice::Iter<'_, i64>> {
if let ArrayView::Int(a) = self {
Some(a.iter())
} else {
None
}
}
#[inline]
pub fn iter_f64(&self) -> Option<std::slice::Iter<'_, f64>> {
if let ArrayView::Float(a) = self {
Some(a.as_slice().iter())
} else {
None
}
}
}
pub enum ArrayViewMut<'a> {
Generic(&'a mut Arc<Vec<ValueWord>>),
Int(&'a mut Arc<crate::typed_buffer::TypedBuffer<i64>>),
Float(&'a mut Arc<crate::typed_buffer::AlignedTypedBuffer>),
Bool(&'a mut Arc<crate::typed_buffer::TypedBuffer<u8>>),
}
impl ArrayViewMut<'_> {
#[inline]
pub fn len(&self) -> usize {
match self {
ArrayViewMut::Generic(a) => a.len(),
ArrayViewMut::Int(a) => a.len(),
ArrayViewMut::Float(a) => a.len(),
ArrayViewMut::Bool(a) => a.len(),
}
}
pub fn pop_vw(&mut self) -> Option<ValueWord> {
match self {
ArrayViewMut::Generic(a) => Arc::make_mut(a).pop(),
ArrayViewMut::Int(a) => Arc::make_mut(a).data.pop().map(ValueWord::from_i64),
ArrayViewMut::Float(a) => Arc::make_mut(a).pop().map(ValueWord::from_f64),
ArrayViewMut::Bool(a) => Arc::make_mut(a)
.data
.pop()
.map(|b| ValueWord::from_bool(b != 0)),
}
}
pub fn push_vw(&mut self, val: ValueWord) -> Result<(), crate::context::VMError> {
match self {
ArrayViewMut::Generic(a) => {
Arc::make_mut(a).push(val);
Ok(())
}
ArrayViewMut::Int(a) => {
if let Some(i) = val.as_i64() {
Arc::make_mut(a).push(i);
Ok(())
} else {
Err(crate::context::VMError::TypeError {
expected: "int",
got: val.type_name(),
})
}
}
ArrayViewMut::Float(a) => {
if let Some(f) = val.as_number_coerce() {
Arc::make_mut(a).push(f);
Ok(())
} else {
Err(crate::context::VMError::TypeError {
expected: "number",
got: val.type_name(),
})
}
}
ArrayViewMut::Bool(a) => {
if let Some(b) = val.as_bool() {
Arc::make_mut(a).push(if b { 1 } else { 0 });
Ok(())
} else {
Err(crate::context::VMError::TypeError {
expected: "bool",
got: val.type_name(),
})
}
}
}
}
}
#[repr(transparent)]
pub struct ValueWord(u64);
impl ValueWord {
#[inline]
pub fn from_f64(v: f64) -> Self {
let bits = v.to_bits();
if v.is_nan() {
Self(CANONICAL_NAN)
} else if is_tagged(bits) {
Self(CANONICAL_NAN)
} else {
Self(bits)
}
}
#[inline]
pub fn from_i64(v: i64) -> Self {
if v >= I48_MIN && v <= I48_MAX {
let payload = (v as u64) & PAYLOAD_MASK;
Self(make_tagged(TAG_INT, payload))
} else {
Self::heap_box(HeapValue::BigInt(v))
}
}
#[inline]
pub fn from_native_scalar(value: NativeScalar) -> Self {
Self::heap_box(HeapValue::NativeScalar(value))
}
#[inline]
pub fn from_native_i8(v: i8) -> Self {
Self::from_native_scalar(NativeScalar::I8(v))
}
#[inline]
pub fn from_native_u8(v: u8) -> Self {
Self::from_native_scalar(NativeScalar::U8(v))
}
#[inline]
pub fn from_native_i16(v: i16) -> Self {
Self::from_native_scalar(NativeScalar::I16(v))
}
#[inline]
pub fn from_native_u16(v: u16) -> Self {
Self::from_native_scalar(NativeScalar::U16(v))
}
#[inline]
pub fn from_native_i32(v: i32) -> Self {
Self::from_native_scalar(NativeScalar::I32(v))
}
#[inline]
pub fn from_native_u32(v: u32) -> Self {
Self::from_native_scalar(NativeScalar::U32(v))
}
#[inline]
pub fn from_native_u64(v: u64) -> Self {
Self::from_native_scalar(NativeScalar::U64(v))
}
#[inline]
pub fn from_native_isize(v: isize) -> Self {
Self::from_native_scalar(NativeScalar::Isize(v))
}
#[inline]
pub fn from_native_usize(v: usize) -> Self {
Self::from_native_scalar(NativeScalar::Usize(v))
}
#[inline]
pub fn from_native_ptr(v: usize) -> Self {
Self::from_native_scalar(NativeScalar::Ptr(v))
}
#[inline]
pub fn from_native_f32(v: f32) -> Self {
Self::from_native_scalar(NativeScalar::F32(v))
}
#[inline]
pub fn from_c_view(ptr: usize, layout: Arc<NativeTypeLayout>) -> Self {
Self::heap_box(HeapValue::NativeView(Box::new(NativeViewData {
ptr,
layout,
mutable: false,
})))
}
#[inline]
pub fn from_c_mut(ptr: usize, layout: Arc<NativeTypeLayout>) -> Self {
Self::heap_box(HeapValue::NativeView(Box::new(NativeViewData {
ptr,
layout,
mutable: true,
})))
}
#[inline]
pub fn from_bool(v: bool) -> Self {
Self(make_tagged(TAG_BOOL, v as u64))
}
#[inline]
pub fn none() -> Self {
Self(make_tagged(TAG_NONE, 0))
}
#[inline]
pub fn unit() -> Self {
Self(make_tagged(TAG_UNIT, 0))
}
#[inline]
pub fn from_function(id: u16) -> Self {
Self(make_tagged(TAG_FUNCTION, id as u64))
}
#[inline]
pub fn from_module_function(index: u32) -> Self {
Self(make_tagged(TAG_MODULE_FN, index as u64))
}
#[inline]
pub fn from_ref(absolute_slot: usize) -> Self {
Self(make_tagged(TAG_REF, absolute_slot as u64))
}
#[inline]
pub fn from_module_binding_ref(binding_idx: usize) -> Self {
Self(make_tagged(
TAG_REF,
REF_TARGET_MODULE_FLAG | (binding_idx as u64 & REF_TARGET_INDEX_MASK),
))
}
#[inline]
pub fn from_projected_ref(base: ValueWord, projection: RefProjection) -> Self {
Self::heap_box(HeapValue::ProjectedRef(Box::new(ProjectedRefData {
base,
projection,
})))
}
#[inline]
#[cfg(not(feature = "gc"))]
pub(crate) fn heap_box(v: HeapValue) -> Self {
let arc = Arc::new(v);
let ptr = Arc::into_raw(arc) as u64;
debug_assert!(
ptr & !PAYLOAD_MASK == 0,
"pointer exceeds 48 bits — platform not supported"
);
Self(make_tagged(TAG_HEAP, ptr & PAYLOAD_MASK))
}
#[inline]
#[cfg(feature = "gc")]
pub(crate) fn heap_box(v: HeapValue) -> Self {
let heap = shape_gc::thread_gc_heap();
let ptr = heap.alloc(v) as u64;
debug_assert!(
ptr & !PAYLOAD_MASK == 0,
"GC pointer exceeds 48 bits — platform not supported"
);
Self(make_tagged(TAG_HEAP, ptr & PAYLOAD_MASK))
}
#[inline]
pub fn from_string(s: Arc<String>) -> Self {
Self::heap_box(HeapValue::String(s))
}
#[inline]
pub fn from_char(c: char) -> Self {
Self::heap_box(HeapValue::Char(c))
}
#[inline]
pub fn as_char(&self) -> Option<char> {
if let Some(HeapValue::Char(c)) = self.as_heap_ref() {
Some(*c)
} else {
std::option::Option::None
}
}
#[inline]
pub fn from_array(a: crate::value::VMArray) -> Self {
Self::heap_box(HeapValue::Array(a))
}
#[inline]
pub fn from_decimal(d: rust_decimal::Decimal) -> Self {
Self::heap_box(HeapValue::Decimal(d))
}
#[inline]
pub fn from_heap_value(v: HeapValue) -> Self {
match v {
HeapValue::BigInt(i) => Self::from_i64(i),
other => Self::heap_box(other),
}
}
#[inline]
pub fn from_datatable(dt: Arc<DataTable>) -> Self {
Self::heap_box(HeapValue::DataTable(dt))
}
#[inline]
pub fn from_typed_table(schema_id: u64, table: Arc<DataTable>) -> Self {
Self::heap_box(HeapValue::TypedTable { schema_id, table })
}
#[inline]
pub fn from_row_view(schema_id: u64, table: Arc<DataTable>, row_idx: usize) -> Self {
Self::heap_box(HeapValue::RowView {
schema_id,
table,
row_idx,
})
}
#[inline]
pub fn from_column_ref(schema_id: u64, table: Arc<DataTable>, col_id: u32) -> Self {
Self::heap_box(HeapValue::ColumnRef {
schema_id,
table,
col_id,
})
}
#[inline]
pub fn from_indexed_table(schema_id: u64, table: Arc<DataTable>, index_col: u32) -> Self {
Self::heap_box(HeapValue::IndexedTable {
schema_id,
table,
index_col,
})
}
#[inline]
pub fn from_range(start: Option<ValueWord>, end: Option<ValueWord>, inclusive: bool) -> Self {
Self::heap_box(HeapValue::Range {
start: start.map(Box::new),
end: end.map(Box::new),
inclusive,
})
}
#[inline]
pub fn from_enum(e: EnumValue) -> Self {
Self::heap_box(HeapValue::Enum(Box::new(e)))
}
#[inline]
pub fn from_some(inner: ValueWord) -> Self {
Self::heap_box(HeapValue::Some(Box::new(inner)))
}
#[inline]
pub fn from_ok(inner: ValueWord) -> Self {
Self::heap_box(HeapValue::Ok(Box::new(inner)))
}
#[inline]
pub fn from_err(inner: ValueWord) -> Self {
Self::heap_box(HeapValue::Err(Box::new(inner)))
}
#[inline]
pub fn from_hashmap(
keys: Vec<ValueWord>,
values: Vec<ValueWord>,
index: HashMap<u64, Vec<usize>>,
) -> ValueWord {
ValueWord::heap_box(HeapValue::HashMap(Box::new(HashMapData {
keys,
values,
index,
shape_id: None,
})))
}
#[inline]
pub fn empty_hashmap() -> ValueWord {
ValueWord::heap_box(HeapValue::HashMap(Box::new(HashMapData {
keys: Vec::new(),
values: Vec::new(),
index: HashMap::new(),
shape_id: None,
})))
}
#[inline]
pub fn from_hashmap_pairs(keys: Vec<ValueWord>, values: Vec<ValueWord>) -> ValueWord {
let index = HashMapData::rebuild_index(&keys);
let shape_id = HashMapData::compute_shape(&keys);
ValueWord::heap_box(HeapValue::HashMap(Box::new(HashMapData {
keys,
values,
index,
shape_id,
})))
}
#[inline]
pub fn from_set(items: Vec<ValueWord>) -> ValueWord {
ValueWord::heap_box(HeapValue::Set(Box::new(SetData::from_items(items))))
}
#[inline]
pub fn empty_set() -> ValueWord {
ValueWord::heap_box(HeapValue::Set(Box::new(SetData {
items: Vec::new(),
index: HashMap::new(),
})))
}
#[inline]
pub fn from_deque(items: Vec<ValueWord>) -> ValueWord {
ValueWord::heap_box(HeapValue::Deque(Box::new(DequeData::from_items(items))))
}
#[inline]
pub fn empty_deque() -> ValueWord {
ValueWord::heap_box(HeapValue::Deque(Box::new(DequeData::new())))
}
#[inline]
pub fn from_priority_queue(items: Vec<ValueWord>) -> ValueWord {
ValueWord::heap_box(HeapValue::PriorityQueue(Box::new(
PriorityQueueData::from_items(items),
)))
}
#[inline]
pub fn empty_priority_queue() -> ValueWord {
ValueWord::heap_box(HeapValue::PriorityQueue(Box::new(PriorityQueueData::new())))
}
#[inline]
pub fn from_content(node: ContentNode) -> ValueWord {
ValueWord::heap_box(HeapValue::Content(Box::new(node)))
}
#[inline]
pub fn from_int_array(a: Arc<crate::typed_buffer::TypedBuffer<i64>>) -> Self {
Self::heap_box(HeapValue::IntArray(a))
}
#[inline]
pub fn from_float_array(a: Arc<crate::typed_buffer::AlignedTypedBuffer>) -> Self {
Self::heap_box(HeapValue::FloatArray(a))
}
#[inline]
pub fn from_bool_array(a: Arc<crate::typed_buffer::TypedBuffer<u8>>) -> Self {
Self::heap_box(HeapValue::BoolArray(a))
}
#[inline]
pub fn from_i8_array(a: Arc<crate::typed_buffer::TypedBuffer<i8>>) -> Self {
Self::heap_box(HeapValue::I8Array(a))
}
#[inline]
pub fn from_i16_array(a: Arc<crate::typed_buffer::TypedBuffer<i16>>) -> Self {
Self::heap_box(HeapValue::I16Array(a))
}
#[inline]
pub fn from_i32_array(a: Arc<crate::typed_buffer::TypedBuffer<i32>>) -> Self {
Self::heap_box(HeapValue::I32Array(a))
}
#[inline]
pub fn from_u8_array(a: Arc<crate::typed_buffer::TypedBuffer<u8>>) -> Self {
Self::heap_box(HeapValue::U8Array(a))
}
#[inline]
pub fn from_u16_array(a: Arc<crate::typed_buffer::TypedBuffer<u16>>) -> Self {
Self::heap_box(HeapValue::U16Array(a))
}
#[inline]
pub fn from_u32_array(a: Arc<crate::typed_buffer::TypedBuffer<u32>>) -> Self {
Self::heap_box(HeapValue::U32Array(a))
}
#[inline]
pub fn from_u64_array(a: Arc<crate::typed_buffer::TypedBuffer<u64>>) -> Self {
Self::heap_box(HeapValue::U64Array(a))
}
#[inline]
pub fn from_f32_array(a: Arc<crate::typed_buffer::TypedBuffer<f32>>) -> Self {
Self::heap_box(HeapValue::F32Array(a))
}
#[inline]
pub fn from_matrix(m: Arc<crate::heap_value::MatrixData>) -> Self {
Self::heap_box(HeapValue::Matrix(m))
}
#[inline]
pub fn from_float_array_slice(
parent: Arc<crate::heap_value::MatrixData>,
offset: u32,
len: u32,
) -> Self {
Self::heap_box(HeapValue::FloatArraySlice {
parent,
offset,
len,
})
}
#[inline]
pub fn from_iterator(state: Box<crate::heap_value::IteratorState>) -> Self {
Self::heap_box(HeapValue::Iterator(state))
}
#[inline]
pub fn from_generator(state: Box<crate::heap_value::GeneratorState>) -> Self {
Self::heap_box(HeapValue::Generator(state))
}
#[inline]
pub fn from_future(id: u64) -> Self {
Self::heap_box(HeapValue::Future(id))
}
#[inline]
pub fn from_task_group(kind: u8, task_ids: Vec<u64>) -> Self {
Self::heap_box(HeapValue::TaskGroup { kind, task_ids })
}
#[inline]
pub fn from_mutex(value: ValueWord) -> Self {
Self::heap_box(HeapValue::Mutex(Box::new(
crate::heap_value::MutexData::new(value),
)))
}
#[inline]
pub fn from_atomic(value: i64) -> Self {
Self::heap_box(HeapValue::Atomic(Box::new(
crate::heap_value::AtomicData::new(value),
)))
}
#[inline]
pub fn from_lazy(initializer: ValueWord) -> Self {
Self::heap_box(HeapValue::Lazy(Box::new(crate::heap_value::LazyData::new(
initializer,
))))
}
#[inline]
pub fn from_channel(data: ChannelData) -> Self {
Self::heap_box(HeapValue::Channel(Box::new(data)))
}
#[inline]
pub fn from_trait_object(value: ValueWord, vtable: Arc<VTable>) -> Self {
Self::heap_box(HeapValue::TraitObject {
value: Box::new(value),
vtable,
})
}
#[inline]
pub fn from_expr_proxy(col_name: Arc<String>) -> Self {
Self::heap_box(HeapValue::ExprProxy(col_name))
}
#[inline]
pub fn from_filter_expr(node: Arc<FilterNode>) -> Self {
Self::heap_box(HeapValue::FilterExpr(node))
}
#[inline]
pub fn from_instant(t: std::time::Instant) -> Self {
Self::heap_box(HeapValue::Instant(Box::new(t)))
}
#[inline]
pub fn from_io_handle(data: crate::heap_value::IoHandleData) -> Self {
Self::heap_box(HeapValue::IoHandle(Box::new(data)))
}
#[inline]
pub fn from_time(t: DateTime<FixedOffset>) -> Self {
Self::heap_box(HeapValue::Time(t))
}
#[inline]
pub fn from_time_utc(t: DateTime<Utc>) -> Self {
Self::heap_box(HeapValue::Time(t.fixed_offset()))
}
#[inline]
pub fn from_duration(d: Duration) -> Self {
Self::heap_box(HeapValue::Duration(d))
}
#[inline]
pub fn from_timespan(ts: chrono::Duration) -> Self {
Self::heap_box(HeapValue::TimeSpan(ts))
}
#[inline]
pub fn from_timeframe(tf: Timeframe) -> Self {
Self::heap_box(HeapValue::Timeframe(tf))
}
#[inline]
pub fn from_host_closure(nc: HostCallable) -> Self {
Self::heap_box(HeapValue::HostClosure(nc))
}
#[inline]
pub fn from_print_result(pr: PrintResult) -> Self {
Self::heap_box(HeapValue::PrintResult(Box::new(pr)))
}
#[inline]
pub fn from_simulation_call(name: String, params: HashMap<String, ValueWord>) -> Self {
Self::heap_box(HeapValue::SimulationCall(Box::new(
crate::heap_value::SimulationCallData { name, params },
)))
}
#[inline]
pub fn from_function_ref(name: String, closure: Option<ValueWord>) -> Self {
Self::heap_box(HeapValue::FunctionRef {
name,
closure: closure.map(Box::new),
})
}
#[inline]
pub fn from_data_reference(
datetime: DateTime<FixedOffset>,
id: String,
timeframe: Timeframe,
) -> Self {
Self::heap_box(HeapValue::DataReference(Box::new(
crate::heap_value::DataReferenceData {
datetime,
id,
timeframe,
},
)))
}
#[inline]
pub fn from_time_reference(tr: TimeReference) -> Self {
Self::heap_box(HeapValue::TimeReference(Box::new(tr)))
}
#[inline]
pub fn from_datetime_expr(de: DateTimeExpr) -> Self {
Self::heap_box(HeapValue::DateTimeExpr(Box::new(de)))
}
#[inline]
pub fn from_data_datetime_ref(dr: DataDateTimeRef) -> Self {
Self::heap_box(HeapValue::DataDateTimeRef(Box::new(dr)))
}
#[inline]
pub fn from_type_annotation(ta: TypeAnnotation) -> Self {
Self::heap_box(HeapValue::TypeAnnotation(Box::new(ta)))
}
#[inline]
pub fn from_type_annotated_value(type_name: String, value: ValueWord) -> Self {
Self::heap_box(HeapValue::TypeAnnotatedValue {
type_name,
value: Box::new(value),
})
}
#[inline(always)]
#[cfg(not(feature = "gc"))]
pub unsafe fn clone_from_bits(bits: u64) -> Self {
if is_tagged(bits) && get_tag(bits) == TAG_HEAP {
let ptr = get_payload(bits) as *const HeapValue;
unsafe { Arc::increment_strong_count(ptr) };
}
Self(bits)
}
#[inline(always)]
#[cfg(feature = "gc")]
pub unsafe fn clone_from_bits(bits: u64) -> Self {
Self(bits)
}
#[inline(always)]
pub fn is_f64(&self) -> bool {
!is_tagged(self.0)
}
#[inline(always)]
pub fn is_i64(&self) -> bool {
is_tagged(self.0) && get_tag(self.0) == TAG_INT
}
#[inline(always)]
pub fn is_bool(&self) -> bool {
is_tagged(self.0) && get_tag(self.0) == TAG_BOOL
}
#[inline(always)]
pub fn is_none(&self) -> bool {
is_tagged(self.0) && get_tag(self.0) == TAG_NONE
}
#[inline(always)]
pub fn is_unit(&self) -> bool {
is_tagged(self.0) && get_tag(self.0) == TAG_UNIT
}
#[inline(always)]
pub fn is_function(&self) -> bool {
is_tagged(self.0) && get_tag(self.0) == TAG_FUNCTION
}
#[inline(always)]
pub fn is_heap(&self) -> bool {
is_tagged(self.0) && get_tag(self.0) == TAG_HEAP
}
#[inline(always)]
pub fn is_ref(&self) -> bool {
if is_tagged(self.0) {
return get_tag(self.0) == TAG_REF;
}
matches!(self.as_heap_ref(), Some(HeapValue::ProjectedRef(_)))
}
#[inline]
pub fn as_ref_target(&self) -> Option<RefTarget> {
if is_tagged(self.0) && get_tag(self.0) == TAG_REF {
let payload = get_payload(self.0);
let idx = (payload & REF_TARGET_INDEX_MASK) as usize;
if payload & REF_TARGET_MODULE_FLAG != 0 {
return Some(RefTarget::ModuleBinding(idx));
}
return Some(RefTarget::Stack(idx));
}
if let Some(HeapValue::ProjectedRef(data)) = self.as_heap_ref() {
return Some(RefTarget::Projected((**data).clone()));
}
None
}
#[inline]
pub fn as_ref_slot(&self) -> Option<usize> {
match self.as_ref_target() {
Some(RefTarget::Stack(slot)) => Some(slot),
_ => None,
}
}
#[inline]
pub fn as_f64(&self) -> Option<f64> {
if self.is_f64() {
Some(f64::from_bits(self.0))
} else {
None
}
}
#[inline]
pub fn as_i64(&self) -> Option<i64> {
if self.is_i64() {
Some(sign_extend_i48(get_payload(self.0)))
} else if let Some(HeapValue::BigInt(v)) = self.as_heap_ref() {
Some(*v)
} else if let Some(HeapValue::NativeScalar(v)) = self.as_heap_ref() {
v.as_i64()
} else {
None
}
}
#[inline]
pub fn as_u64(&self) -> Option<u64> {
if self.is_i64() {
let v = sign_extend_i48(get_payload(self.0));
return u64::try_from(v).ok();
}
if let Some(hv) = self.as_heap_ref() {
return match hv {
HeapValue::BigInt(v) => u64::try_from(*v).ok(),
HeapValue::NativeScalar(v) => v.as_u64(),
_ => None,
};
}
None
}
#[inline]
pub fn as_i128_exact(&self) -> Option<i128> {
if self.is_i64() {
return Some(sign_extend_i48(get_payload(self.0)) as i128);
}
if let Some(hv) = self.as_heap_ref() {
return match hv {
HeapValue::BigInt(v) => Some(*v as i128),
HeapValue::NativeScalar(v) => v.as_i128(),
_ => None,
};
}
None
}
#[inline]
pub fn as_number_strict(&self) -> Option<f64> {
if self.is_f64() {
return Some(f64::from_bits(self.0));
}
if is_tagged(self.0) && get_tag(self.0) == TAG_INT {
return Some(sign_extend_i48(get_payload(self.0)) as f64);
}
if let Some(hv) = self.as_heap_ref() {
return match hv {
HeapValue::NativeScalar(NativeScalar::F32(v)) => Some(*v as f64),
_ => None,
};
}
None
}
#[inline]
pub fn as_bool(&self) -> Option<bool> {
if self.is_bool() {
Some(get_payload(self.0) != 0)
} else {
None
}
}
#[inline]
pub fn as_function(&self) -> Option<u16> {
if self.is_function() {
Some(get_payload(self.0) as u16)
} else {
None
}
}
#[inline(always)]
pub unsafe fn as_f64_unchecked(&self) -> f64 {
if self.is_f64() {
f64::from_bits(self.0)
} else if is_tagged(self.0) && get_tag(self.0) == TAG_INT {
sign_extend_i48(get_payload(self.0)) as f64
} else {
debug_assert!(false, "as_f64_unchecked on non-numeric ValueWord");
0.0
}
}
#[inline(always)]
pub unsafe fn as_i64_unchecked(&self) -> i64 {
if is_tagged(self.0) && get_tag(self.0) == TAG_INT {
sign_extend_i48(get_payload(self.0))
} else if self.is_f64() {
f64::from_bits(self.0) as i64
} else {
debug_assert!(false, "as_i64_unchecked on non-numeric ValueWord");
0
}
}
#[inline(always)]
pub unsafe fn as_bool_unchecked(&self) -> bool {
debug_assert!(self.is_bool(), "as_bool_unchecked on non-bool ValueWord");
get_payload(self.0) != 0
}
#[inline(always)]
pub unsafe fn as_function_unchecked(&self) -> u16 {
debug_assert!(
self.is_function(),
"as_function_unchecked on non-function ValueWord"
);
get_payload(self.0) as u16
}
#[inline]
pub fn as_heap_ref(&self) -> Option<&HeapValue> {
if is_tagged(self.0) && get_tag(self.0) == TAG_HEAP {
let ptr = get_payload(self.0) as *const HeapValue;
Some(unsafe { &*ptr })
} else {
None
}
}
#[inline]
#[cfg(not(feature = "gc"))]
pub fn as_heap_mut(&mut self) -> Option<&mut HeapValue> {
if is_tagged(self.0) && get_tag(self.0) == TAG_HEAP {
let ptr = get_payload(self.0) as *const HeapValue;
let mut arc = unsafe { Arc::from_raw(ptr) };
Arc::make_mut(&mut arc);
let new_ptr = Arc::into_raw(arc) as u64;
self.0 = make_tagged(TAG_HEAP, new_ptr & PAYLOAD_MASK);
let final_ptr = get_payload(self.0) as *mut HeapValue;
Some(unsafe { &mut *final_ptr })
} else {
None
}
}
#[inline]
#[cfg(feature = "gc")]
pub fn as_heap_mut(&mut self) -> Option<&mut HeapValue> {
if is_tagged(self.0) && get_tag(self.0) == TAG_HEAP {
let ptr = get_payload(self.0) as *mut HeapValue;
Some(unsafe { &mut *ptr })
} else {
None
}
}
#[inline]
pub fn is_truthy(&self) -> bool {
if !is_tagged(self.0) {
let f = f64::from_bits(self.0);
return f != 0.0 && !f.is_nan();
}
let tag = get_tag(self.0);
if tag == TAG_HEAP {
let ptr = get_payload(self.0) as *const HeapValue;
return unsafe { (*ptr).is_truthy() };
}
nan_tag_is_truthy(tag, get_payload(self.0))
}
#[inline]
pub fn as_number_coerce(&self) -> Option<f64> {
if let Some(n) = self.as_number_strict() {
Some(n)
} else if is_tagged(self.0) && get_tag(self.0) == TAG_INT {
Some(sign_extend_i48(get_payload(self.0)) as f64)
} else {
None
}
}
#[inline(always)]
pub fn is_module_function(&self) -> bool {
is_tagged(self.0) && get_tag(self.0) == TAG_MODULE_FN
}
#[inline]
pub fn as_module_function(&self) -> Option<usize> {
if self.is_module_function() {
Some(get_payload(self.0) as usize)
} else {
None
}
}
#[inline]
pub fn heap_kind(&self) -> Option<crate::heap_value::HeapKind> {
if let Some(hv) = self.as_heap_ref() {
Some(hv.kind())
} else {
std::option::Option::None
}
}
#[inline]
pub fn tag(&self) -> NanTag {
if !is_tagged(self.0) {
return NanTag::F64;
}
match get_tag(self.0) {
TAG_INT => NanTag::I48,
TAG_BOOL => NanTag::Bool,
TAG_NONE => NanTag::None,
TAG_UNIT => NanTag::Unit,
TAG_FUNCTION => NanTag::Function,
TAG_MODULE_FN => NanTag::ModuleFunction,
TAG_HEAP => NanTag::Heap,
TAG_REF => NanTag::Ref,
_ => unreachable!("invalid ValueWord tag"),
}
}
#[inline]
pub fn as_str(&self) -> Option<&str> {
if let Some(hv) = self.as_heap_ref() {
match hv {
HeapValue::String(s) => Some(s.as_str()),
_ => std::option::Option::None,
}
} else {
std::option::Option::None
}
}
#[inline]
pub fn as_decimal(&self) -> Option<rust_decimal::Decimal> {
if let Some(hv) = self.as_heap_ref() {
match hv {
HeapValue::Decimal(d) => Some(*d),
_ => std::option::Option::None,
}
} else {
std::option::Option::None
}
}
#[inline(always)]
pub unsafe fn as_decimal_unchecked(&self) -> rust_decimal::Decimal {
debug_assert!(matches!(self.as_heap_ref(), Some(HeapValue::Decimal(_))));
match unsafe { self.as_heap_ref().unwrap_unchecked() } {
HeapValue::Decimal(d) => *d,
_ => unsafe { std::hint::unreachable_unchecked() },
}
}
#[inline]
#[deprecated(note = "Use as_any_array() instead for unified typed array dispatch")]
pub fn as_array(&self) -> Option<&VMArray> {
if let Some(hv) = self.as_heap_ref() {
match hv {
HeapValue::Array(arr) => Some(arr),
_ => std::option::Option::None,
}
} else {
std::option::Option::None
}
}
#[inline]
pub fn as_any_array(&self) -> Option<ArrayView<'_>> {
match self.as_heap_ref()? {
HeapValue::Array(a) => Some(ArrayView::Generic(a)),
HeapValue::IntArray(a) => Some(ArrayView::Int(a)),
HeapValue::FloatArray(a) => Some(ArrayView::Float(a)),
HeapValue::BoolArray(a) => Some(ArrayView::Bool(a)),
HeapValue::I8Array(a) => Some(ArrayView::I8(a)),
HeapValue::I16Array(a) => Some(ArrayView::I16(a)),
HeapValue::I32Array(a) => Some(ArrayView::I32(a)),
HeapValue::U8Array(a) => Some(ArrayView::U8(a)),
HeapValue::U16Array(a) => Some(ArrayView::U16(a)),
HeapValue::U32Array(a) => Some(ArrayView::U32(a)),
HeapValue::U64Array(a) => Some(ArrayView::U64(a)),
HeapValue::F32Array(a) => Some(ArrayView::F32(a)),
_ => None,
}
}
#[inline]
pub fn as_any_array_mut(&mut self) -> Option<ArrayViewMut<'_>> {
match self.as_heap_mut()? {
HeapValue::Array(a) => Some(ArrayViewMut::Generic(a)),
HeapValue::IntArray(a) => Some(ArrayViewMut::Int(a)),
HeapValue::FloatArray(a) => Some(ArrayViewMut::Float(a)),
HeapValue::BoolArray(a) => Some(ArrayViewMut::Bool(a)),
_ => None,
}
}
#[inline]
pub fn as_datatable(&self) -> Option<&Arc<DataTable>> {
match self.as_heap_ref()? {
HeapValue::DataTable(dt) => Some(dt),
_ => std::option::Option::None,
}
}
#[inline]
pub fn as_typed_table(&self) -> Option<(u64, &Arc<DataTable>)> {
match self.as_heap_ref()? {
HeapValue::TypedTable { schema_id, table } => Some((*schema_id, table)),
_ => std::option::Option::None,
}
}
#[inline]
pub fn as_row_view(&self) -> Option<(u64, &Arc<DataTable>, usize)> {
match self.as_heap_ref()? {
HeapValue::RowView {
schema_id,
table,
row_idx,
} => Some((*schema_id, table, *row_idx)),
_ => std::option::Option::None,
}
}
#[inline]
pub fn as_column_ref(&self) -> Option<(u64, &Arc<DataTable>, u32)> {
match self.as_heap_ref()? {
HeapValue::ColumnRef {
schema_id,
table,
col_id,
} => Some((*schema_id, table, *col_id)),
_ => std::option::Option::None,
}
}
#[inline]
pub fn as_indexed_table(&self) -> Option<(u64, &Arc<DataTable>, u32)> {
match self.as_heap_ref()? {
HeapValue::IndexedTable {
schema_id,
table,
index_col,
} => Some((*schema_id, table, *index_col)),
_ => std::option::Option::None,
}
}
#[inline]
pub fn as_typed_object(&self) -> Option<(u64, &[ValueSlot], u64)> {
match self.as_heap_ref()? {
HeapValue::TypedObject {
schema_id,
slots,
heap_mask,
} => Some((*schema_id, slots, *heap_mask)),
_ => std::option::Option::None,
}
}
#[inline]
pub fn as_closure(&self) -> Option<(u16, &[crate::value::Upvalue])> {
match self.as_heap_ref()? {
HeapValue::Closure {
function_id,
upvalues,
} => Some((*function_id, upvalues)),
_ => std::option::Option::None,
}
}
#[inline]
pub fn as_some_inner(&self) -> Option<&ValueWord> {
match self.as_heap_ref()? {
HeapValue::Some(inner) => Some(inner),
_ => std::option::Option::None,
}
}
#[inline]
pub fn as_ok_inner(&self) -> Option<&ValueWord> {
match self.as_heap_ref()? {
HeapValue::Ok(inner) => Some(inner),
_ => std::option::Option::None,
}
}
#[inline]
pub fn as_err_inner(&self) -> Option<&ValueWord> {
match self.as_heap_ref()? {
HeapValue::Err(inner) => Some(inner),
_ => std::option::Option::None,
}
}
pub fn as_err_payload(&self) -> Option<ValueWord> {
let inner = match self.as_heap_ref()? {
HeapValue::Err(inner) => inner.as_ref(),
_ => return std::option::Option::None,
};
if let Some(HeapValue::TypedObject {
slots, heap_mask, ..
}) = inner.as_heap_ref()
{
const PAYLOAD_SLOT: usize = 1;
if slots.len() >= 6 && *heap_mask & 1 != 0 {
let cat = slots[0].as_heap_value();
if let HeapValue::String(s) = cat {
if s.as_str() == "AnyError" && PAYLOAD_SLOT < slots.len() {
let is_heap = *heap_mask & (1u64 << PAYLOAD_SLOT) != 0;
return Some(slots[PAYLOAD_SLOT].as_value_word(is_heap));
}
}
}
}
Some(inner.clone())
}
#[inline]
pub fn as_future(&self) -> Option<u64> {
match self.as_heap_ref()? {
HeapValue::Future(id) => Some(*id),
_ => std::option::Option::None,
}
}
#[inline]
pub fn as_trait_object(&self) -> Option<(&ValueWord, &Arc<VTable>)> {
match self.as_heap_ref()? {
HeapValue::TraitObject { value, vtable } => Some((value, vtable)),
_ => std::option::Option::None,
}
}
#[inline]
pub fn as_expr_proxy(&self) -> Option<&Arc<String>> {
match self.as_heap_ref()? {
HeapValue::ExprProxy(name) => Some(name),
_ => std::option::Option::None,
}
}
#[inline]
pub fn as_filter_expr(&self) -> Option<&Arc<FilterNode>> {
match self.as_heap_ref()? {
HeapValue::FilterExpr(node) => Some(node),
_ => std::option::Option::None,
}
}
#[inline]
pub fn as_host_closure(&self) -> Option<&HostCallable> {
match self.as_heap_ref()? {
HeapValue::HostClosure(nc) => Some(nc),
_ => std::option::Option::None,
}
}
#[inline]
pub fn as_duration(&self) -> Option<&shape_ast::ast::Duration> {
match self.as_heap_ref()? {
HeapValue::Duration(d) => Some(d),
_ => std::option::Option::None,
}
}
#[inline]
pub fn as_range(&self) -> Option<(Option<&ValueWord>, Option<&ValueWord>, bool)> {
match self.as_heap_ref()? {
HeapValue::Range {
start,
end,
inclusive,
} => Some((
start.as_ref().map(|b| b.as_ref()),
end.as_ref().map(|b| b.as_ref()),
*inclusive,
)),
_ => std::option::Option::None,
}
}
#[inline]
pub fn as_timespan(&self) -> Option<chrono::Duration> {
match self.as_heap_ref()? {
HeapValue::TimeSpan(ts) => Some(*ts),
_ => std::option::Option::None,
}
}
#[inline]
pub fn as_enum(&self) -> Option<&EnumValue> {
match self.as_heap_ref()? {
HeapValue::Enum(e) => Some(e.as_ref()),
_ => std::option::Option::None,
}
}
#[inline]
pub fn as_timeframe(&self) -> Option<&Timeframe> {
match self.as_heap_ref()? {
HeapValue::Timeframe(tf) => Some(tf),
_ => std::option::Option::None,
}
}
#[inline]
pub fn as_hashmap(
&self,
) -> Option<(&Vec<ValueWord>, &Vec<ValueWord>, &HashMap<u64, Vec<usize>>)> {
match self.as_heap_ref()? {
HeapValue::HashMap(d) => Some((&d.keys, &d.values, &d.index)),
_ => std::option::Option::None,
}
}
#[inline]
pub fn as_hashmap_data(&self) -> Option<&HashMapData> {
match self.as_heap_ref()? {
HeapValue::HashMap(d) => Some(d),
_ => std::option::Option::None,
}
}
#[inline]
pub fn as_hashmap_mut(&mut self) -> Option<&mut HashMapData> {
match self.as_heap_mut()? {
HeapValue::HashMap(d) => Some(d),
_ => std::option::Option::None,
}
}
#[inline]
pub fn as_set(&self) -> Option<&SetData> {
match self.as_heap_ref()? {
HeapValue::Set(d) => Some(d),
_ => std::option::Option::None,
}
}
#[inline]
pub fn as_set_mut(&mut self) -> Option<&mut SetData> {
match self.as_heap_mut()? {
HeapValue::Set(d) => Some(d),
_ => std::option::Option::None,
}
}
#[inline]
pub fn as_deque(&self) -> Option<&DequeData> {
match self.as_heap_ref()? {
HeapValue::Deque(d) => Some(d),
_ => std::option::Option::None,
}
}
#[inline]
pub fn as_deque_mut(&mut self) -> Option<&mut DequeData> {
match self.as_heap_mut()? {
HeapValue::Deque(d) => Some(d),
_ => std::option::Option::None,
}
}
#[inline]
pub fn as_priority_queue(&self) -> Option<&PriorityQueueData> {
match self.as_heap_ref()? {
HeapValue::PriorityQueue(d) => Some(d),
_ => std::option::Option::None,
}
}
#[inline]
pub fn as_priority_queue_mut(&mut self) -> Option<&mut PriorityQueueData> {
match self.as_heap_mut()? {
HeapValue::PriorityQueue(d) => Some(d),
_ => std::option::Option::None,
}
}
#[inline]
pub fn as_content(&self) -> Option<&ContentNode> {
match self.as_heap_ref()? {
HeapValue::Content(node) => Some(node),
_ => std::option::Option::None,
}
}
#[inline]
pub fn as_time(&self) -> Option<DateTime<FixedOffset>> {
match self.as_heap_ref()? {
HeapValue::Time(t) => Some(*t),
_ => std::option::Option::None,
}
}
#[inline]
pub fn as_instant(&self) -> Option<&std::time::Instant> {
match self.as_heap_ref()? {
HeapValue::Instant(t) => Some(t.as_ref()),
_ => std::option::Option::None,
}
}
#[inline]
pub fn as_io_handle(&self) -> Option<&crate::heap_value::IoHandleData> {
match self.as_heap_ref()? {
HeapValue::IoHandle(data) => Some(data.as_ref()),
_ => std::option::Option::None,
}
}
#[inline]
pub fn as_native_scalar(&self) -> Option<NativeScalar> {
match self.as_heap_ref()? {
HeapValue::NativeScalar(v) => Some(*v),
_ => None,
}
}
#[inline]
pub fn as_native_view(&self) -> Option<&NativeViewData> {
match self.as_heap_ref()? {
HeapValue::NativeView(view) => Some(view.as_ref()),
_ => None,
}
}
#[inline]
pub fn as_datetime(&self) -> Option<&DateTime<FixedOffset>> {
match self.as_heap_ref()? {
HeapValue::Time(t) => Some(t),
_ => std::option::Option::None,
}
}
#[inline]
pub fn as_arc_string(&self) -> Option<&Arc<String>> {
match self.as_heap_ref()? {
HeapValue::String(s) => Some(s),
_ => std::option::Option::None,
}
}
#[inline]
pub fn as_int_array(&self) -> Option<&Arc<crate::typed_buffer::TypedBuffer<i64>>> {
match self.as_heap_ref()? {
HeapValue::IntArray(a) => Some(a),
_ => std::option::Option::None,
}
}
#[inline]
pub fn as_float_array(&self) -> Option<&Arc<crate::typed_buffer::AlignedTypedBuffer>> {
match self.as_heap_ref()? {
HeapValue::FloatArray(a) => Some(a),
_ => std::option::Option::None,
}
}
#[inline]
pub fn as_bool_array(&self) -> Option<&Arc<crate::typed_buffer::TypedBuffer<u8>>> {
match self.as_heap_ref()? {
HeapValue::BoolArray(a) => Some(a),
_ => std::option::Option::None,
}
}
#[inline]
pub fn as_matrix(&self) -> Option<&crate::heap_value::MatrixData> {
match self.as_heap_ref()? {
HeapValue::Matrix(m) => Some(m.as_ref()),
_ => std::option::Option::None,
}
}
#[inline]
pub fn as_iterator(&self) -> Option<&crate::heap_value::IteratorState> {
match self.as_heap_ref()? {
HeapValue::Iterator(it) => Some(it.as_ref()),
_ => std::option::Option::None,
}
}
#[inline]
pub fn as_generator(&self) -> Option<&crate::heap_value::GeneratorState> {
match self.as_heap_ref()? {
HeapValue::Generator(g) => Some(g.as_ref()),
_ => std::option::Option::None,
}
}
#[inline]
pub fn typed_array_len(&self) -> Option<usize> {
match self.as_heap_ref()? {
HeapValue::IntArray(a) => Some(a.len()),
HeapValue::FloatArray(a) => Some(a.len()),
HeapValue::BoolArray(a) => Some(a.len()),
HeapValue::I8Array(a) => Some(a.len()),
HeapValue::I16Array(a) => Some(a.len()),
HeapValue::I32Array(a) => Some(a.len()),
HeapValue::U8Array(a) => Some(a.len()),
HeapValue::U16Array(a) => Some(a.len()),
HeapValue::U32Array(a) => Some(a.len()),
HeapValue::U64Array(a) => Some(a.len()),
HeapValue::F32Array(a) => Some(a.len()),
_ => std::option::Option::None,
}
}
pub fn coerce_to_float_array(&self) -> Option<Arc<crate::typed_buffer::AlignedTypedBuffer>> {
match self.as_heap_ref()? {
HeapValue::FloatArray(a) => Some(Arc::clone(a)),
HeapValue::IntArray(a) => {
let mut buf = crate::typed_buffer::AlignedTypedBuffer::with_capacity(a.len());
for &v in a.iter() {
buf.push(v as f64);
}
Some(Arc::new(buf))
}
_ => std::option::Option::None,
}
}
pub fn to_generic_array(&self) -> Option<crate::value::VMArray> {
match self.as_heap_ref()? {
HeapValue::IntArray(a) => Some(Arc::new(
a.iter().map(|&v| ValueWord::from_i64(v)).collect(),
)),
HeapValue::FloatArray(a) => Some(Arc::new(
a.iter().map(|&v| ValueWord::from_f64(v)).collect(),
)),
HeapValue::BoolArray(a) => Some(Arc::new(
a.iter().map(|&v| ValueWord::from_bool(v != 0)).collect(),
)),
_ => std::option::Option::None,
}
}
#[inline]
pub fn vw_equals(&self, other: &ValueWord) -> bool {
if self.0 == other.0 {
if !is_tagged(self.0) {
let f = f64::from_bits(self.0);
return !f.is_nan();
}
return true;
}
if !is_tagged(self.0) || !is_tagged(other.0) {
if let (Some(a), Some(b)) = (self.as_number_coerce(), other.as_number_coerce()) {
return a == b;
}
return false;
}
let tag_a = get_tag(self.0);
let tag_b = get_tag(other.0);
if tag_a != tag_b {
if (tag_a == TAG_INT || !is_tagged(self.0)) && (tag_b == TAG_INT || !is_tagged(other.0))
{
if let (Some(a), Some(b)) = (self.as_number_coerce(), other.as_number_coerce()) {
return a == b;
}
}
return false;
}
if tag_a == TAG_HEAP {
let ptr_a = get_payload(self.0) as *const HeapValue;
let ptr_b = get_payload(other.0) as *const HeapValue;
return unsafe { (*ptr_a).equals(&*ptr_b) };
}
false
}
pub fn vw_hash(&self) -> u64 {
use ahash::AHasher;
use std::hash::{Hash, Hasher};
let tag = self.tag();
match tag {
NanTag::F64 => {
let f = unsafe { self.as_f64_unchecked() };
let bits = if f == 0.0 { 0u64 } else { f.to_bits() };
let mut hasher = AHasher::default();
bits.hash(&mut hasher);
hasher.finish()
}
NanTag::I48 => {
let i = unsafe { self.as_i64_unchecked() };
let mut hasher = AHasher::default();
i.hash(&mut hasher);
hasher.finish()
}
NanTag::Bool => {
if unsafe { self.as_bool_unchecked() } {
1
} else {
0
}
}
NanTag::None => 0x_DEAD_0000,
NanTag::Unit => 0x_DEAD_0001,
NanTag::Heap => {
if let Some(hv) = self.as_heap_ref() {
match hv {
HeapValue::String(s) => {
let mut hasher = AHasher::default();
s.hash(&mut hasher);
hasher.finish()
}
HeapValue::BigInt(i) => {
let mut hasher = AHasher::default();
i.hash(&mut hasher);
hasher.finish()
}
HeapValue::Decimal(d) => {
let mut hasher = AHasher::default();
d.mantissa().hash(&mut hasher);
d.scale().hash(&mut hasher);
hasher.finish()
}
HeapValue::NativeScalar(v) => {
let mut hasher = AHasher::default();
v.type_name().hash(&mut hasher);
v.to_string().hash(&mut hasher);
hasher.finish()
}
HeapValue::NativeView(v) => {
let mut hasher = AHasher::default();
v.ptr.hash(&mut hasher);
v.layout.name.hash(&mut hasher);
v.mutable.hash(&mut hasher);
hasher.finish()
}
_ => {
let mut hasher = AHasher::default();
self.0.hash(&mut hasher);
hasher.finish()
}
}
} else {
let mut hasher = AHasher::default();
self.0.hash(&mut hasher);
hasher.finish()
}
}
_ => {
let mut hasher = AHasher::default();
self.0.hash(&mut hasher);
hasher.finish()
}
}
}
#[inline(always)]
pub unsafe fn add_f64(a: &Self, b: &Self) -> Self {
debug_assert!(a.is_f64() && b.is_f64());
let lhs = unsafe { a.as_f64_unchecked() };
let rhs = unsafe { b.as_f64_unchecked() };
Self::from_f64(lhs + rhs)
}
#[inline(always)]
pub unsafe fn add_i64(a: &Self, b: &Self) -> Self {
debug_assert!(a.is_i64() && b.is_i64());
let lhs = unsafe { a.as_i64_unchecked() };
let rhs = unsafe { b.as_i64_unchecked() };
match lhs.checked_add(rhs) {
Some(result) if result >= I48_MIN && result <= I48_MAX => Self::from_i64(result),
_ => Self::from_f64(lhs as f64 + rhs as f64),
}
}
#[inline(always)]
pub unsafe fn sub_f64(a: &Self, b: &Self) -> Self {
debug_assert!(a.is_f64() && b.is_f64());
let lhs = unsafe { a.as_f64_unchecked() };
let rhs = unsafe { b.as_f64_unchecked() };
Self::from_f64(lhs - rhs)
}
#[inline(always)]
pub unsafe fn sub_i64(a: &Self, b: &Self) -> Self {
debug_assert!(a.is_i64() && b.is_i64());
let lhs = unsafe { a.as_i64_unchecked() };
let rhs = unsafe { b.as_i64_unchecked() };
match lhs.checked_sub(rhs) {
Some(result) if result >= I48_MIN && result <= I48_MAX => Self::from_i64(result),
_ => Self::from_f64(lhs as f64 - rhs as f64),
}
}
#[inline(always)]
pub unsafe fn mul_f64(a: &Self, b: &Self) -> Self {
debug_assert!(a.is_f64() && b.is_f64());
let lhs = unsafe { a.as_f64_unchecked() };
let rhs = unsafe { b.as_f64_unchecked() };
Self::from_f64(lhs * rhs)
}
#[inline(always)]
pub unsafe fn mul_i64(a: &Self, b: &Self) -> Self {
debug_assert!(a.is_i64() && b.is_i64());
let lhs = unsafe { a.as_i64_unchecked() };
let rhs = unsafe { b.as_i64_unchecked() };
match lhs.checked_mul(rhs) {
Some(result) if result >= I48_MIN && result <= I48_MAX => Self::from_i64(result),
_ => Self::from_f64(lhs as f64 * rhs as f64),
}
}
#[inline(always)]
pub unsafe fn div_f64(a: &Self, b: &Self) -> Self {
debug_assert!(a.is_f64() && b.is_f64());
let lhs = unsafe { a.as_f64_unchecked() };
let rhs = unsafe { b.as_f64_unchecked() };
Self::from_f64(lhs / rhs)
}
#[inline(always)]
pub fn binary_int_preserving(
a: &Self,
b: &Self,
a_num: f64,
b_num: f64,
int_op: impl FnOnce(i64, i64) -> Option<i64>,
float_op: impl FnOnce(f64, f64) -> f64,
) -> Self {
if matches!(a.tag(), NanTag::I48) && matches!(b.tag(), NanTag::I48) {
match int_op(unsafe { a.as_i64_unchecked() }, unsafe {
b.as_i64_unchecked()
}) {
Some(result) => Self::from_i64(result),
None => Self::from_f64(float_op(a_num, b_num)),
}
} else {
Self::from_f64(float_op(a_num, b_num))
}
}
#[inline(always)]
pub unsafe fn gt_i64(a: &Self, b: &Self) -> Self {
debug_assert!(a.is_i64() && b.is_i64());
let lhs = unsafe { a.as_i64_unchecked() };
let rhs = unsafe { b.as_i64_unchecked() };
Self::from_bool(lhs > rhs)
}
#[inline(always)]
pub unsafe fn lt_i64(a: &Self, b: &Self) -> Self {
debug_assert!(a.is_i64() && b.is_i64());
let lhs = unsafe { a.as_i64_unchecked() };
let rhs = unsafe { b.as_i64_unchecked() };
Self::from_bool(lhs < rhs)
}
#[inline(always)]
pub fn raw_bits(&self) -> u64 {
self.0
}
#[inline]
pub fn type_name(&self) -> &'static str {
if !is_tagged(self.0) {
return "number";
}
let tag = get_tag(self.0);
if tag == TAG_HEAP {
let ptr = get_payload(self.0) as *const HeapValue;
return unsafe { (*ptr).type_name() };
}
nan_tag_type_name(tag)
}
#[inline]
pub fn to_number(&self) -> Option<f64> {
self.as_number_coerce()
}
#[inline]
pub fn to_bool(&self) -> Option<bool> {
self.as_bool()
}
pub fn as_usize(&self) -> Option<usize> {
if let Some(i) = self.as_i64() {
if i >= 0 {
return Some(i as usize);
}
} else if let Some(n) = self.as_f64() {
if n >= 0.0 && n.is_finite() {
return Some(n as usize);
}
} else if let Some(d) = self.as_decimal() {
use rust_decimal::prelude::ToPrimitive;
if let Some(n) = d.to_f64() {
if n >= 0.0 {
return Some(n as usize);
}
}
} else if let Some(view) = self.as_native_view() {
return Some(view.ptr);
}
None
}
pub fn to_json_value(&self) -> serde_json::Value {
use crate::heap_value::HeapValue;
if let Some(n) = self.as_f64() {
return serde_json::json!(n);
}
if let Some(i) = self.as_i64() {
return serde_json::json!(i);
}
if let Some(b) = self.as_bool() {
return serde_json::json!(b);
}
if self.is_none() || self.is_unit() {
return serde_json::Value::Null;
}
if self.is_function() || self.is_module_function() {
return serde_json::json!(format!("<{}>", self.type_name()));
}
match self.as_heap_ref() {
Some(HeapValue::String(s)) => serde_json::json!(s.as_str()),
Some(HeapValue::Decimal(d)) => {
use rust_decimal::prelude::ToPrimitive;
if let Some(f) = d.to_f64() {
serde_json::json!(f)
} else {
serde_json::json!(d.to_string())
}
}
Some(HeapValue::Array(arr)) => {
let values: Vec<serde_json::Value> =
arr.iter().map(|v| v.to_json_value()).collect();
serde_json::json!(values)
}
Some(HeapValue::Some(v)) => v.to_json_value(),
Some(HeapValue::Ok(v)) => serde_json::json!({
"status": "ok",
"value": v.to_json_value()
}),
Some(HeapValue::Err(v)) => serde_json::json!({
"status": "error",
"value": v.to_json_value()
}),
Some(HeapValue::DataTable(dt)) => serde_json::json!({
"type": "datatable",
"rows": dt.row_count(),
"columns": dt.column_names(),
}),
Some(HeapValue::TypedTable { table, schema_id }) => serde_json::json!({
"type": "typed_table",
"schema_id": schema_id,
"rows": table.row_count(),
"columns": table.column_names(),
}),
Some(HeapValue::RowView {
schema_id, row_idx, ..
}) => serde_json::json!({
"type": "row",
"schema_id": schema_id,
"row_idx": row_idx,
}),
Some(HeapValue::ColumnRef {
schema_id, col_id, ..
}) => serde_json::json!({
"type": "column_ref",
"schema_id": schema_id,
"col_id": col_id,
}),
Some(HeapValue::IndexedTable {
schema_id,
table,
index_col,
}) => serde_json::json!({
"type": "indexed_table",
"schema_id": schema_id,
"rows": table.row_count(),
"columns": table.column_names(),
"index_col": index_col,
}),
Some(HeapValue::HashMap(d)) => {
let mut map = serde_json::Map::new();
for (k, v) in d.keys.iter().zip(d.values.iter()) {
map.insert(format!("{}", k), v.to_json_value());
}
serde_json::Value::Object(map)
}
Some(HeapValue::Set(d)) => {
serde_json::Value::Array(d.items.iter().map(|v| v.to_json_value()).collect())
}
Some(HeapValue::Deque(d)) => {
serde_json::Value::Array(d.items.iter().map(|v| v.to_json_value()).collect())
}
Some(HeapValue::PriorityQueue(d)) => {
serde_json::Value::Array(d.items.iter().map(|v| v.to_json_value()).collect())
}
Some(HeapValue::NativeScalar(v)) => match v {
NativeScalar::I8(n) => serde_json::json!({ "type": "i8", "value": n }),
NativeScalar::U8(n) => serde_json::json!({ "type": "u8", "value": n }),
NativeScalar::I16(n) => serde_json::json!({ "type": "i16", "value": n }),
NativeScalar::U16(n) => serde_json::json!({ "type": "u16", "value": n }),
NativeScalar::I32(n) => serde_json::json!({ "type": "i32", "value": n }),
NativeScalar::U32(n) => serde_json::json!({ "type": "u32", "value": n }),
NativeScalar::I64(n) => {
serde_json::json!({ "type": "i64", "value": n.to_string() })
}
NativeScalar::U64(n) => {
serde_json::json!({ "type": "u64", "value": n.to_string() })
}
NativeScalar::Isize(n) => {
serde_json::json!({ "type": "isize", "value": n.to_string() })
}
NativeScalar::Usize(n) => {
serde_json::json!({ "type": "usize", "value": n.to_string() })
}
NativeScalar::Ptr(n) => {
serde_json::json!({ "type": "ptr", "value": format!("0x{n:x}") })
}
NativeScalar::F32(n) => serde_json::json!({ "type": "f32", "value": n }),
},
Some(HeapValue::NativeView(v)) => serde_json::json!({
"type": if v.mutable { "cmut" } else { "cview" },
"layout": v.layout.name,
"ptr": v.ptr,
}),
Some(HeapValue::IntArray(buf)) => {
serde_json::Value::Array(buf.data.iter().map(|&v| serde_json::json!(v)).collect())
}
Some(HeapValue::FloatArray(buf)) => {
serde_json::Value::Array(buf.data.iter().map(|&v| serde_json::json!(v)).collect())
}
Some(HeapValue::BoolArray(buf)) => serde_json::Value::Array(
buf.data
.iter()
.map(|&v| serde_json::json!(v != 0))
.collect(),
),
Some(HeapValue::I8Array(buf)) => {
serde_json::Value::Array(buf.data.iter().map(|&v| serde_json::json!(v)).collect())
}
Some(HeapValue::I16Array(buf)) => {
serde_json::Value::Array(buf.data.iter().map(|&v| serde_json::json!(v)).collect())
}
Some(HeapValue::I32Array(buf)) => {
serde_json::Value::Array(buf.data.iter().map(|&v| serde_json::json!(v)).collect())
}
Some(HeapValue::U8Array(buf)) => {
serde_json::Value::Array(buf.data.iter().map(|&v| serde_json::json!(v)).collect())
}
Some(HeapValue::U16Array(buf)) => {
serde_json::Value::Array(buf.data.iter().map(|&v| serde_json::json!(v)).collect())
}
Some(HeapValue::U32Array(buf)) => {
serde_json::Value::Array(buf.data.iter().map(|&v| serde_json::json!(v)).collect())
}
Some(HeapValue::U64Array(buf)) => {
serde_json::Value::Array(buf.data.iter().map(|&v| serde_json::json!(v)).collect())
}
Some(HeapValue::F32Array(buf)) => {
serde_json::Value::Array(buf.data.iter().map(|&v| serde_json::json!(v)).collect())
}
_ => serde_json::json!(format!("<{}>", self.type_name())),
}
}
}
impl PartialEq for ValueWord {
fn eq(&self, other: &Self) -> bool {
if self.0 == other.0 {
return true;
}
if is_tagged(self.0)
&& get_tag(self.0) == TAG_HEAP
&& is_tagged(other.0)
&& get_tag(other.0) == TAG_HEAP
{
match (self.as_heap_ref(), other.as_heap_ref()) {
(Some(a), Some(b)) => a.structural_eq(b),
_ => false,
}
} else {
false
}
}
}
impl Eq for ValueWord {}
#[cfg(not(feature = "gc"))]
impl Clone for ValueWord {
#[inline]
fn clone(&self) -> Self {
if is_tagged(self.0) && get_tag(self.0) == TAG_HEAP {
let ptr = get_payload(self.0) as *const HeapValue;
unsafe { Arc::increment_strong_count(ptr) };
Self(self.0)
} else {
Self(self.0)
}
}
}
#[cfg(feature = "gc")]
impl Clone for ValueWord {
#[inline]
fn clone(&self) -> Self {
Self(self.0)
}
}
#[cfg(not(feature = "gc"))]
impl Drop for ValueWord {
fn drop(&mut self) {
if is_tagged(self.0) && get_tag(self.0) == TAG_HEAP {
let ptr = get_payload(self.0) as *const HeapValue;
if !ptr.is_null() {
unsafe {
Arc::decrement_strong_count(ptr);
}
}
}
}
}
#[cfg(feature = "gc")]
impl Drop for ValueWord {
#[inline]
fn drop(&mut self) {
}
}
impl std::fmt::Display for ValueWord {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.is_f64() {
let n = unsafe { self.as_f64_unchecked() };
if n == n.trunc() && n.abs() < 1e15 {
write!(f, "{}.0", n as i64)
} else {
write!(f, "{}", n)
}
} else if self.is_i64() {
write!(f, "{}", unsafe { self.as_i64_unchecked() })
} else if self.is_bool() {
write!(f, "{}", unsafe { self.as_bool_unchecked() })
} else if self.is_none() {
write!(f, "none")
} else if self.is_unit() {
write!(f, "()")
} else if self.is_function() {
write!(f, "<function:{}>", unsafe { self.as_function_unchecked() })
} else if self.is_module_function() {
write!(f, "<module_function>")
} else if let Some(target) = self.as_ref_target() {
match target {
RefTarget::Stack(slot) => write!(f, "&slot_{}", slot),
RefTarget::ModuleBinding(slot) => write!(f, "&module_{}", slot),
RefTarget::Projected(_) => write!(f, "&ref"),
}
} else if let Some(hv) = self.as_heap_ref() {
match hv {
HeapValue::Char(c) => write!(f, "{}", c),
HeapValue::String(s) => write!(f, "{}", s),
HeapValue::Array(arr) => {
write!(f, "[")?;
for (i, elem) in arr.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", elem)?;
}
write!(f, "]")
}
HeapValue::TypedObject { .. } => write!(f, "{{...}}"),
HeapValue::Closure { function_id, .. } => write!(f, "<closure:{}>", function_id),
HeapValue::Decimal(d) => write!(f, "{}", d),
HeapValue::BigInt(i) => write!(f, "{}", i),
HeapValue::HostClosure(_) => write!(f, "<host_closure>"),
HeapValue::DataTable(dt) => {
write!(f, "<datatable:{}x{}>", dt.row_count(), dt.column_count())
}
HeapValue::TypedTable { table, .. } => write!(
f,
"<typed_table:{}x{}>",
table.row_count(),
table.column_count()
),
HeapValue::RowView { row_idx, .. } => write!(f, "<row:{}>", row_idx),
HeapValue::ColumnRef { col_id, .. } => write!(f, "<column:{}>", col_id),
HeapValue::IndexedTable { table, .. } => write!(
f,
"<indexed_table:{}x{}>",
table.row_count(),
table.column_count()
),
HeapValue::HashMap(d) => {
write!(f, "HashMap{{")?;
for (i, (k, v)) in d.keys.iter().zip(d.values.iter()).enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}: {}", k, v)?;
}
write!(f, "}}")
}
HeapValue::Set(d) => {
write!(f, "Set{{")?;
for (i, item) in d.items.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", item)?;
}
write!(f, "}}")
}
HeapValue::Deque(d) => {
write!(f, "Deque[")?;
for (i, item) in d.items.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", item)?;
}
write!(f, "]")
}
HeapValue::PriorityQueue(d) => {
write!(f, "PriorityQueue[")?;
for (i, item) in d.items.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", item)?;
}
write!(f, "]")
}
HeapValue::Content(node) => write!(f, "{}", node),
HeapValue::Instant(t) => write!(f, "<instant:{:?}>", t.elapsed()),
HeapValue::IoHandle(data) => {
let status = if data.is_open() { "open" } else { "closed" };
write!(f, "<io_handle:{}:{}>", data.path, status)
}
HeapValue::Range {
start,
end,
inclusive,
} => {
if let Some(s) = start {
write!(f, "{}", s)?;
}
write!(f, "{}", if *inclusive { "..=" } else { ".." })?;
if let Some(e) = end {
write!(f, "{}", e)?;
}
std::fmt::Result::Ok(())
}
HeapValue::Enum(e) => {
write!(f, "{}", e.variant)?;
match &e.payload {
crate::enums::EnumPayload::Unit => Ok(()),
crate::enums::EnumPayload::Tuple(values) => {
write!(f, "(")?;
for (i, v) in values.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", v)?;
}
write!(f, ")")
}
crate::enums::EnumPayload::Struct(fields) => {
let mut pairs: Vec<_> = fields.iter().collect();
pairs.sort_by_key(|(k, _)| (*k).clone());
write!(f, " {{ ")?;
for (i, (k, v)) in pairs.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}: {}", k, v)?;
}
write!(f, " }}")
}
}
}
HeapValue::Some(v) => write!(f, "some({})", v),
HeapValue::Ok(v) => write!(f, "ok({})", v),
HeapValue::Err(v) => write!(f, "err({})", v),
HeapValue::Future(id) => write!(f, "<future:{}>", id),
HeapValue::TaskGroup { task_ids, .. } => {
write!(f, "<task_group:{}>", task_ids.len())
}
HeapValue::TraitObject { value, .. } => write!(f, "{}", value),
HeapValue::ExprProxy(name) => write!(f, "<expr:{}>", name),
HeapValue::FilterExpr(_) => write!(f, "<filter_expr>"),
HeapValue::Time(t) => write!(f, "{}", t),
HeapValue::Duration(d) => write!(f, "{:?}", d),
HeapValue::TimeSpan(ts) => write!(f, "{}", ts),
HeapValue::Timeframe(tf) => write!(f, "{:?}", tf),
HeapValue::TimeReference(_) => write!(f, "<time_ref>"),
HeapValue::DateTimeExpr(_) => write!(f, "<datetime_expr>"),
HeapValue::DataDateTimeRef(_) => write!(f, "<data_datetime_ref>"),
HeapValue::TypeAnnotation(_) => write!(f, "<type_annotation>"),
HeapValue::TypeAnnotatedValue { type_name, value } => {
write!(f, "{}({})", type_name, value)
}
HeapValue::PrintResult(_) => write!(f, "<print_result>"),
HeapValue::SimulationCall(data) => write!(f, "<simulation:{}>", data.name),
HeapValue::FunctionRef { name, .. } => write!(f, "<fn:{}>", name),
HeapValue::ProjectedRef(_) => write!(f, "&ref"),
HeapValue::DataReference(data) => write!(f, "<data:{}>", data.id),
HeapValue::NativeScalar(v) => write!(f, "{v}"),
HeapValue::NativeView(v) => write!(
f,
"<{}:{}@0x{:x}>",
if v.mutable { "cmut" } else { "cview" },
v.layout.name,
v.ptr
),
HeapValue::SharedCell(arc) => write!(f, "{}", arc.read().unwrap()),
HeapValue::IntArray(a) => {
write!(f, "Vec<int>[")?;
for (i, v) in a.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", v)?;
}
write!(f, "]")
}
HeapValue::FloatArray(a) => {
write!(f, "Vec<number>[")?;
for (i, v) in a.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
if *v == v.trunc() && v.abs() < 1e15 {
write!(f, "{}", *v as i64)?;
} else {
write!(f, "{}", v)?;
}
}
write!(f, "]")
}
HeapValue::BoolArray(a) => {
write!(f, "Vec<bool>[")?;
for (i, v) in a.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", *v != 0)?;
}
write!(f, "]")
}
HeapValue::Matrix(m) => {
write!(f, "<Mat<number>:{}x{}>", m.rows, m.cols)
}
HeapValue::Iterator(it) => {
write!(
f,
"<iterator:pos={},transforms={}>",
it.position,
it.transforms.len()
)
}
HeapValue::Generator(g) => {
write!(f, "<generator:state={}>", g.state)
}
HeapValue::Mutex(_) => write!(f, "<mutex>"),
HeapValue::Atomic(a) => {
write!(
f,
"<atomic:{}>",
a.inner.load(std::sync::atomic::Ordering::Relaxed)
)
}
HeapValue::Channel(c) => {
if c.is_sender() {
write!(f, "<channel:sender>")
} else {
write!(f, "<channel:receiver>")
}
}
HeapValue::Lazy(l) => {
let initialized = l.value.lock().map(|g| g.is_some()).unwrap_or(false);
if initialized {
write!(f, "<lazy:initialized>")
} else {
write!(f, "<lazy:pending>")
}
}
HeapValue::I8Array(a) => {
write!(f, "Vec<i8>[")?;
for (i, v) in a.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", v)?;
}
write!(f, "]")
}
HeapValue::I16Array(a) => {
write!(f, "Vec<i16>[")?;
for (i, v) in a.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", v)?;
}
write!(f, "]")
}
HeapValue::I32Array(a) => {
write!(f, "Vec<i32>[")?;
for (i, v) in a.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", v)?;
}
write!(f, "]")
}
HeapValue::U8Array(a) => {
write!(f, "Vec<u8>[")?;
for (i, v) in a.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", v)?;
}
write!(f, "]")
}
HeapValue::U16Array(a) => {
write!(f, "Vec<u16>[")?;
for (i, v) in a.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", v)?;
}
write!(f, "]")
}
HeapValue::U32Array(a) => {
write!(f, "Vec<u32>[")?;
for (i, v) in a.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", v)?;
}
write!(f, "]")
}
HeapValue::U64Array(a) => {
write!(f, "Vec<u64>[")?;
for (i, v) in a.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", v)?;
}
write!(f, "]")
}
HeapValue::F32Array(a) => {
write!(f, "Vec<f32>[")?;
for (i, v) in a.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", v)?;
}
write!(f, "]")
}
HeapValue::FloatArraySlice {
parent,
offset,
len,
} => {
let slice =
&parent.data[*offset as usize..(*offset + *len) as usize];
write!(f, "Vec<number>[")?;
for (i, v) in slice.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
if *v == v.trunc() && v.abs() < 1e15 {
write!(f, "{}", *v as i64)?;
} else {
write!(f, "{}", v)?;
}
}
write!(f, "]")
}
}
} else {
write!(f, "<unknown>")
}
}
}
impl std::fmt::Debug for ValueWord {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.is_f64() {
write!(f, "ValueWord(f64: {})", unsafe { self.as_f64_unchecked() })
} else if self.is_i64() {
write!(f, "ValueWord(i64: {})", unsafe { self.as_i64_unchecked() })
} else if self.is_bool() {
write!(f, "ValueWord(bool: {})", unsafe {
self.as_bool_unchecked()
})
} else if self.is_none() {
write!(f, "ValueWord(None)")
} else if self.is_unit() {
write!(f, "ValueWord(Unit)")
} else if self.is_function() {
write!(f, "ValueWord(Function({}))", unsafe {
self.as_function_unchecked()
})
} else if let Some(target) = self.as_ref_target() {
write!(f, "ValueWord(Ref({:?}))", target)
} else if self.is_heap() {
let ptr = get_payload(self.0) as *const HeapValue;
let hv = unsafe { &*ptr };
write!(f, "ValueWord(heap: {:?})", hv)
} else {
write!(f, "ValueWord(0x{:016x})", self.0)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Arc;
#[test]
fn test_f64_roundtrip_positive() {
let v = ValueWord::from_f64(3.14);
assert!(v.is_f64());
assert_eq!(v.as_f64(), Some(3.14));
unsafe { assert_eq!(v.as_f64_unchecked(), 3.14) };
}
#[test]
fn test_f64_roundtrip_negative() {
let v = ValueWord::from_f64(-123.456);
assert!(v.is_f64());
assert_eq!(v.as_f64(), Some(-123.456));
}
#[test]
fn test_f64_zero() {
let v = ValueWord::from_f64(0.0);
assert!(v.is_f64());
assert_eq!(v.as_f64(), Some(0.0));
}
#[test]
fn test_f64_negative_zero() {
let v = ValueWord::from_f64(-0.0);
assert!(v.is_f64());
let extracted = v.as_f64().unwrap();
assert_eq!(extracted, 0.0);
assert!(extracted.is_sign_negative());
}
#[test]
fn test_f64_infinity() {
let pos = ValueWord::from_f64(f64::INFINITY);
assert!(pos.is_f64());
assert_eq!(pos.as_f64(), Some(f64::INFINITY));
let neg = ValueWord::from_f64(f64::NEG_INFINITY);
assert!(neg.is_f64());
assert_eq!(neg.as_f64(), Some(f64::NEG_INFINITY));
}
#[test]
fn test_f64_nan_canonicalized() {
let v = ValueWord::from_f64(f64::NAN);
assert!(v.is_f64());
let extracted = v.as_f64().unwrap();
assert!(extracted.is_nan());
}
#[test]
fn test_f64_subnormal() {
let tiny = f64::MIN_POSITIVE / 2.0; let v = ValueWord::from_f64(tiny);
assert!(v.is_f64());
assert_eq!(v.as_f64(), Some(tiny));
}
#[test]
fn test_f64_max_min() {
let max = ValueWord::from_f64(f64::MAX);
assert!(max.is_f64());
assert_eq!(max.as_f64(), Some(f64::MAX));
let min = ValueWord::from_f64(f64::MIN);
assert!(min.is_f64());
assert_eq!(min.as_f64(), Some(f64::MIN));
}
#[test]
fn test_i64_small_positive() {
let v = ValueWord::from_i64(42);
assert!(v.is_i64());
assert_eq!(v.as_i64(), Some(42));
unsafe { assert_eq!(v.as_i64_unchecked(), 42) };
}
#[test]
fn test_i64_small_negative() {
let v = ValueWord::from_i64(-42);
assert!(v.is_i64());
assert_eq!(v.as_i64(), Some(-42));
}
#[test]
fn test_i64_zero() {
let v = ValueWord::from_i64(0);
assert!(v.is_i64());
assert_eq!(v.as_i64(), Some(0));
}
#[test]
fn test_i64_i48_max() {
let max = I48_MAX;
let v = ValueWord::from_i64(max);
assert!(v.is_i64());
assert_eq!(v.as_i64(), Some(max));
}
#[test]
fn test_i64_i48_min() {
let min = I48_MIN;
let v = ValueWord::from_i64(min);
assert!(v.is_i64());
assert_eq!(v.as_i64(), Some(min));
}
#[test]
fn test_i64_large_needs_heap() {
let v = ValueWord::from_i64(i64::MAX);
assert!(v.is_heap());
assert_eq!(v.as_i64(), Some(i64::MAX));
}
#[test]
fn test_i64_min_needs_heap() {
let v = ValueWord::from_i64(i64::MIN);
assert!(v.is_heap());
assert_eq!(v.as_i64(), Some(i64::MIN));
}
#[test]
fn test_i64_just_outside_i48_positive() {
let val = I48_MAX + 1;
let v = ValueWord::from_i64(val);
assert!(v.is_heap());
assert_eq!(v.as_i64(), Some(val));
}
#[test]
fn test_i64_just_outside_i48_negative() {
let val = I48_MIN - 1;
let v = ValueWord::from_i64(val);
assert!(v.is_heap());
assert_eq!(v.as_i64(), Some(val));
}
#[test]
fn test_bool_true() {
let v = ValueWord::from_bool(true);
assert!(v.is_bool());
assert_eq!(v.as_bool(), Some(true));
unsafe { assert_eq!(v.as_bool_unchecked(), true) };
}
#[test]
fn test_bool_false() {
let v = ValueWord::from_bool(false);
assert!(v.is_bool());
assert_eq!(v.as_bool(), Some(false));
unsafe { assert_eq!(v.as_bool_unchecked(), false) };
}
#[test]
fn test_none() {
let v = ValueWord::none();
assert!(v.is_none());
assert!(!v.is_f64());
assert!(!v.is_i64());
assert!(!v.is_bool());
}
#[test]
fn test_unit() {
let v = ValueWord::unit();
assert!(v.is_unit());
}
#[test]
fn test_function() {
let v = ValueWord::from_function(42);
assert!(v.is_function());
assert_eq!(v.as_function(), Some(42));
unsafe { assert_eq!(v.as_function_unchecked(), 42) };
}
#[test]
fn test_function_max_id() {
let v = ValueWord::from_function(u16::MAX);
assert!(v.is_function());
assert_eq!(v.as_function(), Some(u16::MAX));
}
#[test]
fn test_module_function() {
let v = ValueWord::from_module_function(99);
assert!(is_tagged(v.0));
assert_eq!(get_tag(v.0), TAG_MODULE_FN);
assert_eq!(v.as_module_function(), Some(99));
}
#[test]
fn test_heap_string_roundtrip() {
let v = ValueWord::from_string(Arc::new("hello world".to_string()));
assert!(v.is_heap());
assert_eq!(v.as_arc_string().map(|s| s.as_str()), Some("hello world"));
}
#[test]
fn test_heap_array_roundtrip() {
let arr = Arc::new(vec![
ValueWord::from_i64(1),
ValueWord::from_i64(2),
ValueWord::from_i64(3),
]);
let v = ValueWord::from_array(arr.clone());
assert!(v.is_heap());
let extracted = v.as_array().expect("should be array");
assert_eq!(extracted.len(), 3);
}
#[test]
fn test_heap_clone() {
let v = ValueWord::from_string(Arc::new("clone me".to_string()));
let cloned = v.clone();
assert_eq!(v.as_arc_string().map(|s| s.as_str()), Some("clone me"));
assert_eq!(cloned.as_arc_string().map(|s| s.as_str()), Some("clone me"));
assert_eq!(
get_payload(v.0),
get_payload(cloned.0),
"cloned heap pointers should be identical (Arc shared)"
);
}
#[test]
fn test_add_f64() {
let a = ValueWord::from_f64(1.5);
let b = ValueWord::from_f64(2.5);
let result = unsafe { ValueWord::add_f64(&a, &b) };
assert_eq!(result.as_f64(), Some(4.0));
}
#[test]
fn test_add_i64() {
let a = ValueWord::from_i64(100);
let b = ValueWord::from_i64(200);
let result = unsafe { ValueWord::add_i64(&a, &b) };
assert_eq!(result.as_i64(), Some(300));
}
#[test]
fn test_add_i64_negative() {
let a = ValueWord::from_i64(-50);
let b = ValueWord::from_i64(30);
let result = unsafe { ValueWord::add_i64(&a, &b) };
assert_eq!(result.as_i64(), Some(-20));
}
#[test]
fn test_sub_f64() {
let a = ValueWord::from_f64(10.0);
let b = ValueWord::from_f64(3.0);
let result = unsafe { ValueWord::sub_f64(&a, &b) };
assert_eq!(result.as_f64(), Some(7.0));
}
#[test]
fn test_sub_i64() {
let a = ValueWord::from_i64(50);
let b = ValueWord::from_i64(80);
let result = unsafe { ValueWord::sub_i64(&a, &b) };
assert_eq!(result.as_i64(), Some(-30));
}
#[test]
fn test_mul_f64() {
let a = ValueWord::from_f64(3.0);
let b = ValueWord::from_f64(4.0);
let result = unsafe { ValueWord::mul_f64(&a, &b) };
assert_eq!(result.as_f64(), Some(12.0));
}
#[test]
fn test_mul_i64() {
let a = ValueWord::from_i64(7);
let b = ValueWord::from_i64(-6);
let result = unsafe { ValueWord::mul_i64(&a, &b) };
assert_eq!(result.as_i64(), Some(-42));
}
#[test]
fn test_div_f64() {
let a = ValueWord::from_f64(10.0);
let b = ValueWord::from_f64(4.0);
let result = unsafe { ValueWord::div_f64(&a, &b) };
assert_eq!(result.as_f64(), Some(2.5));
}
#[test]
fn test_gt_i64() {
let a = ValueWord::from_i64(10);
let b = ValueWord::from_i64(5);
let result = unsafe { ValueWord::gt_i64(&a, &b) };
assert_eq!(result.as_bool(), Some(true));
let result2 = unsafe { ValueWord::gt_i64(&b, &a) };
assert_eq!(result2.as_bool(), Some(false));
}
#[test]
fn test_lt_i64() {
let a = ValueWord::from_i64(3);
let b = ValueWord::from_i64(7);
let result = unsafe { ValueWord::lt_i64(&a, &b) };
assert_eq!(result.as_bool(), Some(true));
let result2 = unsafe { ValueWord::lt_i64(&b, &a) };
assert_eq!(result2.as_bool(), Some(false));
}
#[test]
fn test_add_i64_overflow_to_heap() {
let a = ValueWord::from_i64(I48_MAX);
let b = ValueWord::from_i64(1);
let result = unsafe { ValueWord::add_i64(&a, &b) };
assert!(result.is_f64());
let expected = (I48_MAX + 1) as f64;
assert_eq!(result.as_f64(), Some(expected));
}
#[test]
fn test_type_checks_exclusive() {
let f = ValueWord::from_f64(1.0);
assert!(f.is_f64());
assert!(!f.is_i64());
assert!(!f.is_bool());
assert!(!f.is_none());
assert!(!f.is_unit());
assert!(!f.is_function());
assert!(!f.is_heap());
let i = ValueWord::from_i64(1);
assert!(!i.is_f64());
assert!(i.is_i64());
assert!(!i.is_bool());
assert!(!i.is_none());
assert!(!i.is_unit());
assert!(!i.is_function());
assert!(!i.is_heap());
let b = ValueWord::from_bool(true);
assert!(!b.is_f64());
assert!(!b.is_i64());
assert!(b.is_bool());
assert!(!b.is_none());
let n = ValueWord::none();
assert!(!n.is_f64());
assert!(!n.is_i64());
assert!(!n.is_bool());
assert!(n.is_none());
let u = ValueWord::unit();
assert!(u.is_unit());
assert!(!u.is_none());
let func = ValueWord::from_function(0);
assert!(func.is_function());
assert!(!func.is_f64());
}
#[test]
fn test_size_is_8_bytes() {
assert_eq!(std::mem::size_of::<ValueWord>(), 8);
}
#[test]
fn test_heap_value_size() {
use crate::heap_value::HeapValue;
let hv_size = std::mem::size_of::<HeapValue>();
assert!(
hv_size <= 48,
"HeapValue grew beyond expected 48 bytes: {} bytes",
hv_size
);
}
#[test]
fn test_debug_format() {
let v = ValueWord::from_f64(3.14);
let dbg = format!("{:?}", v);
assert!(dbg.contains("f64"));
assert!(dbg.contains("3.14"));
let v = ValueWord::from_i64(42);
let dbg = format!("{:?}", v);
assert!(dbg.contains("i64"));
assert!(dbg.contains("42"));
let v = ValueWord::none();
let dbg = format!("{:?}", v);
assert!(dbg.contains("None"));
}
#[test]
fn test_sign_extension_negative_one() {
let v = ValueWord::from_i64(-1);
assert!(v.is_i64());
assert_eq!(v.as_i64(), Some(-1));
}
#[test]
fn test_sign_extension_boundary() {
let v = ValueWord::from_i64(-1);
let payload = get_payload(v.0);
assert_eq!(payload, 0x0000_FFFF_FFFF_FFFF);
assert_eq!(sign_extend_i48(payload), -1);
let v = ValueWord::from_i64(I48_MIN);
let payload = get_payload(v.0);
assert_eq!(payload, 0x0000_8000_0000_0000);
assert_eq!(sign_extend_i48(payload), I48_MIN);
}
#[test]
fn test_drop_non_heap_is_noop() {
let _ = ValueWord::from_f64(1.0);
let _ = ValueWord::from_i64(1);
let _ = ValueWord::from_bool(true);
let _ = ValueWord::none();
let _ = ValueWord::unit();
let _ = ValueWord::from_function(0);
}
#[test]
fn test_drop_heap_frees_memory() {
let _v = ValueWord::from_string(Arc::new("drop test".to_string()));
}
#[test]
fn test_multiple_clones_and_drops() {
let v1 = ValueWord::from_string(Arc::new("multi clone".to_string()));
let v2 = v1.clone();
let v3 = v2.clone();
assert_eq!(v1.as_arc_string().map(|s| s.as_str()), Some("multi clone"));
assert_eq!(v2.as_arc_string().map(|s| s.as_str()), Some("multi clone"));
assert_eq!(v3.as_arc_string().map(|s| s.as_str()), Some("multi clone"));
drop(v2);
assert_eq!(v1.as_arc_string().map(|s| s.as_str()), Some("multi clone"));
assert_eq!(v3.as_arc_string().map(|s| s.as_str()), Some("multi clone"));
}
#[test]
fn test_datetime_fixed_offset_roundtrip() {
use chrono::TimeZone;
let offset = chrono::FixedOffset::east_opt(5 * 3600 + 30 * 60).unwrap(); let dt = offset.with_ymd_and_hms(2024, 6, 15, 14, 30, 0).unwrap();
let v = ValueWord::from_time(dt);
let extracted = v.as_time().unwrap();
assert_eq!(extracted, dt);
assert_eq!(extracted.offset().local_minus_utc(), 5 * 3600 + 30 * 60);
}
#[test]
fn test_datetime_utc_convenience() {
use chrono::TimeZone;
let utc_dt = chrono::Utc.with_ymd_and_hms(2024, 1, 15, 10, 0, 0).unwrap();
let v = ValueWord::from_time_utc(utc_dt);
let extracted = v.as_time().unwrap();
assert_eq!(extracted.offset().local_minus_utc(), 0);
assert_eq!(extracted.timestamp(), utc_dt.timestamp());
}
#[test]
fn test_as_datetime_returns_ref() {
use chrono::TimeZone;
let offset = chrono::FixedOffset::west_opt(4 * 3600).unwrap(); let dt = offset.with_ymd_and_hms(2024, 12, 25, 8, 0, 0).unwrap();
let v = ValueWord::from_time(dt);
let dt_ref = v.as_datetime().unwrap();
assert_eq!(*dt_ref, dt);
assert_eq!(dt_ref.offset().local_minus_utc(), -4 * 3600);
}
#[test]
fn test_datetime_display() {
use chrono::TimeZone;
let utc_dt = chrono::Utc.with_ymd_and_hms(2024, 3, 1, 12, 0, 0).unwrap();
let v = ValueWord::from_time_utc(utc_dt);
let display = format!("{}", v);
assert!(display.contains("2024-03-01"));
}
#[test]
fn test_as_number_coerce_rejects_native_i64_u64() {
let i64_nb = ValueWord::from_native_scalar(NativeScalar::I64(42));
let u64_nb = ValueWord::from_native_u64(u64::MAX);
assert_eq!(i64_nb.as_number_coerce(), None);
assert_eq!(u64_nb.as_number_coerce(), None);
}
#[test]
fn test_as_number_coerce_accepts_native_f32() {
let v = ValueWord::from_native_f32(12.5);
assert_eq!(v.as_number_coerce(), Some(12.5));
}
#[test]
fn test_exact_integer_extractors_cover_u64() {
let v = ValueWord::from_native_u64(u64::MAX);
assert_eq!(v.as_u64(), Some(u64::MAX));
assert_eq!(v.as_i128_exact(), Some(u64::MAX as i128));
}
#[test]
fn test_int_array_construction_and_roundtrip() {
let data = vec![1i64, 2, 3, -100, 0];
let nb = ValueWord::from_int_array(Arc::new(data.clone().into()));
assert!(nb.is_heap());
let arr = nb.as_int_array().unwrap();
assert_eq!(arr.as_slice(), &data);
}
#[test]
fn test_float_array_construction_and_roundtrip() {
use crate::aligned_vec::AlignedVec;
let mut aligned = AlignedVec::with_capacity(4);
aligned.push(1.0);
aligned.push(2.5);
aligned.push(-3.14);
aligned.push(0.0);
let nb = ValueWord::from_float_array(Arc::new(aligned.into()));
assert!(nb.is_heap());
let arr = nb.as_float_array().unwrap();
assert_eq!(arr.len(), 4);
assert_eq!(arr[0], 1.0);
assert_eq!(arr[2], -3.14);
}
#[test]
fn test_bool_array_construction_and_roundtrip() {
let data = vec![1u8, 0, 1, 1, 0];
let nb = ValueWord::from_bool_array(Arc::new(data.clone().into()));
assert!(nb.is_heap());
let arr = nb.as_bool_array().unwrap();
assert_eq!(arr.as_slice(), &data);
}
#[test]
fn test_int_array_type_name() {
let nb = ValueWord::from_int_array(Arc::new(vec![1i64, 2, 3].into()));
assert_eq!(nb.type_name(), "Vec<int>");
}
#[test]
fn test_float_array_type_name() {
use crate::aligned_vec::AlignedVec;
let mut aligned = AlignedVec::with_capacity(1);
aligned.push(1.0);
let nb = ValueWord::from_float_array(Arc::new(aligned.into()));
assert_eq!(nb.type_name(), "Vec<number>");
}
#[test]
fn test_bool_array_type_name() {
let nb = ValueWord::from_bool_array(Arc::new(vec![0u8, 1].into()));
assert_eq!(nb.type_name(), "Vec<bool>");
}
#[test]
fn test_int_array_is_truthy_nonempty() {
let nb = ValueWord::from_int_array(Arc::new(vec![42i64].into()));
assert!(nb.is_truthy());
}
#[test]
fn test_int_array_is_truthy_empty() {
let nb = ValueWord::from_int_array(Arc::new(Vec::<i64>::new().into()));
assert!(!nb.is_truthy());
}
#[test]
fn test_float_array_is_truthy_nonempty() {
use crate::aligned_vec::AlignedVec;
let mut aligned = AlignedVec::with_capacity(1);
aligned.push(0.0);
let nb = ValueWord::from_float_array(Arc::new(aligned.into()));
assert!(nb.is_truthy());
}
#[test]
fn test_float_array_is_truthy_empty() {
use crate::aligned_vec::AlignedVec;
let nb = ValueWord::from_float_array(Arc::new(AlignedVec::new().into()));
assert!(!nb.is_truthy());
}
#[test]
fn test_bool_array_is_truthy_nonempty() {
let nb = ValueWord::from_bool_array(Arc::new(vec![0u8].into()));
assert!(nb.is_truthy());
}
#[test]
fn test_bool_array_is_truthy_empty() {
let nb = ValueWord::from_bool_array(Arc::new(Vec::<u8>::new().into()));
assert!(!nb.is_truthy());
}
#[test]
fn test_int_array_clone_arc_refcount() {
let data: Arc<crate::typed_buffer::TypedBuffer<i64>> = Arc::new(vec![10i64, 20, 30].into());
let nb1 = ValueWord::from_int_array(data.clone());
let nb2 = nb1.clone();
let arr1 = nb1.as_int_array().unwrap();
let arr2 = nb2.as_int_array().unwrap();
assert_eq!(arr1.as_ref(), arr2.as_ref());
assert!(Arc::ptr_eq(arr1, arr2));
}
#[test]
fn test_float_array_clone_arc_refcount() {
use crate::aligned_vec::AlignedVec;
let mut aligned = AlignedVec::with_capacity(2);
aligned.push(1.0);
aligned.push(2.0);
let data: Arc<crate::typed_buffer::AlignedTypedBuffer> = Arc::new(aligned.into());
let nb1 = ValueWord::from_float_array(data.clone());
let nb2 = nb1.clone();
let arr1 = nb1.as_float_array().unwrap();
let arr2 = nb2.as_float_array().unwrap();
assert!(Arc::ptr_eq(arr1, arr2));
}
#[test]
fn test_typed_array_len() {
let int_nb = ValueWord::from_int_array(Arc::new(vec![1i64, 2, 3].into()));
assert_eq!(int_nb.typed_array_len(), Some(3));
use crate::aligned_vec::AlignedVec;
let mut aligned = AlignedVec::with_capacity(2);
aligned.push(1.0);
aligned.push(2.0);
let float_nb = ValueWord::from_float_array(Arc::new(aligned.into()));
assert_eq!(float_nb.typed_array_len(), Some(2));
let bool_nb = ValueWord::from_bool_array(Arc::new(vec![0u8, 1, 1, 0].into()));
assert_eq!(bool_nb.typed_array_len(), Some(4));
let number_nb = ValueWord::from_f64(42.0);
assert_eq!(number_nb.typed_array_len(), None);
}
#[test]
fn test_coerce_to_float_array_from_float() {
use crate::aligned_vec::AlignedVec;
let mut aligned = AlignedVec::with_capacity(3);
aligned.push(1.0);
aligned.push(2.0);
aligned.push(3.0);
let nb = ValueWord::from_float_array(Arc::new(aligned.into()));
let result = nb.coerce_to_float_array().unwrap();
assert_eq!(result.len(), 3);
assert_eq!(result[0], 1.0);
}
#[test]
fn test_coerce_to_float_array_from_int() {
let nb = ValueWord::from_int_array(Arc::new(vec![10i64, 20, 30].into()));
let result = nb.coerce_to_float_array().unwrap();
assert_eq!(result.len(), 3);
assert_eq!(result[0], 10.0);
assert_eq!(result[1], 20.0);
assert_eq!(result[2], 30.0);
}
#[test]
fn test_to_generic_array_int() {
let nb = ValueWord::from_int_array(Arc::new(vec![5i64, 10].into()));
let generic = nb.to_generic_array().unwrap();
assert_eq!(generic.len(), 2);
assert_eq!(generic[0].as_i64(), Some(5));
assert_eq!(generic[1].as_i64(), Some(10));
}
#[test]
fn test_to_generic_array_float() {
use crate::aligned_vec::AlignedVec;
let mut aligned = AlignedVec::with_capacity(2);
aligned.push(1.5);
aligned.push(2.5);
let nb = ValueWord::from_float_array(Arc::new(aligned.into()));
let generic = nb.to_generic_array().unwrap();
assert_eq!(generic.len(), 2);
assert_eq!(generic[0].as_f64(), Some(1.5));
assert_eq!(generic[1].as_f64(), Some(2.5));
}
#[test]
fn test_to_generic_array_bool() {
let nb = ValueWord::from_bool_array(Arc::new(vec![1u8, 0, 1].into()));
let generic = nb.to_generic_array().unwrap();
assert_eq!(generic.len(), 3);
assert_eq!(generic[0].as_bool(), Some(true));
assert_eq!(generic[1].as_bool(), Some(false));
assert_eq!(generic[2].as_bool(), Some(true));
}
#[test]
fn test_int_array_nb_equals() {
let a = ValueWord::from_int_array(Arc::new(vec![1i64, 2, 3].into()));
let b = ValueWord::from_int_array(Arc::new(vec![1i64, 2, 3].into()));
let c = ValueWord::from_int_array(Arc::new(vec![1i64, 2, 4].into()));
assert!(a.vw_equals(&b));
assert!(!a.vw_equals(&c));
}
#[test]
fn test_float_array_nb_equals() {
use crate::aligned_vec::AlignedVec;
let mut a_data = AlignedVec::with_capacity(2);
a_data.push(1.0);
a_data.push(2.0);
let mut b_data = AlignedVec::with_capacity(2);
b_data.push(1.0);
b_data.push(2.0);
let a = ValueWord::from_float_array(Arc::new(a_data.into()));
let b = ValueWord::from_float_array(Arc::new(b_data.into()));
assert!(a.vw_equals(&b));
}
#[test]
fn test_cross_type_accessor_returns_none() {
let int_nb = ValueWord::from_int_array(Arc::new(vec![1i64, 2].into()));
assert!(int_nb.as_float_array().is_none());
assert!(int_nb.as_bool_array().is_none());
assert!(int_nb.as_array().is_none());
use crate::aligned_vec::AlignedVec;
let float_nb = ValueWord::from_float_array(Arc::new(AlignedVec::new().into()));
assert!(float_nb.as_int_array().is_none());
let bool_nb = ValueWord::from_bool_array(Arc::new(Vec::<u8>::new().into()));
assert!(bool_nb.as_int_array().is_none());
assert!(bool_nb.as_float_array().is_none());
}
#[test]
fn test_to_json_value_int_array() {
let buf = crate::typed_buffer::TypedBuffer {
data: vec![1i64, 2, 3],
validity: None,
};
let v = ValueWord::from_int_array(Arc::new(buf));
let json = v.to_json_value();
assert_eq!(json, serde_json::json!([1, 2, 3]));
}
#[test]
fn test_to_json_value_float_array() {
use crate::aligned_vec::AlignedVec;
let mut av = AlignedVec::new();
av.push(1.5);
av.push(2.5);
let buf = crate::typed_buffer::AlignedTypedBuffer {
data: av,
validity: None,
};
let v = ValueWord::from_float_array(Arc::new(buf));
let json = v.to_json_value();
assert_eq!(json, serde_json::json!([1.5, 2.5]));
}
#[test]
fn test_to_json_value_bool_array() {
let buf = crate::typed_buffer::TypedBuffer {
data: vec![1u8, 0, 1],
validity: None,
};
let v = ValueWord::from_bool_array(Arc::new(buf));
let json = v.to_json_value();
assert_eq!(json, serde_json::json!([true, false, true]));
}
#[test]
fn test_to_json_value_empty_int_array() {
let buf = crate::typed_buffer::TypedBuffer::<i64> {
data: vec![],
validity: None,
};
let v = ValueWord::from_int_array(Arc::new(buf));
let json = v.to_json_value();
assert_eq!(json, serde_json::json!([]));
}
#[test]
fn test_to_json_value_i32_array() {
let buf = crate::typed_buffer::TypedBuffer {
data: vec![10i32, 20, 30],
validity: None,
};
let v = ValueWord::heap_box(HeapValue::I32Array(Arc::new(buf)));
let json = v.to_json_value();
assert_eq!(json, serde_json::json!([10, 20, 30]));
}
#[test]
fn test_to_json_value_u64_array() {
let buf = crate::typed_buffer::TypedBuffer {
data: vec![100u64, 200],
validity: None,
};
let v = ValueWord::heap_box(HeapValue::U64Array(Arc::new(buf)));
let json = v.to_json_value();
assert_eq!(json, serde_json::json!([100, 200]));
}
#[test]
fn test_to_json_value_f32_array() {
let buf = crate::typed_buffer::TypedBuffer {
data: vec![1.0f32, 2.0],
validity: None,
};
let v = ValueWord::heap_box(HeapValue::F32Array(Arc::new(buf)));
let json = v.to_json_value();
assert_eq!(json, serde_json::json!([1.0, 2.0]));
}
}