use shape_value::NativeKind;
use shape_value::heap_value::TypedObjectStorage;
use shape_value::v2::decimal_obj::DecimalObj;
use shape_value::v2::heap_element::HeapElement;
use shape_value::v2::heap_header::{HEAP_KIND_V2_TYPED_ARRAY, HeapHeader};
use shape_value::v2::refcount::v2_retain;
use shape_value::v2::string_obj::StringObj;
use shape_value::v2::typed_array::TypedArray;
use shape_value::HeapKind;
pub use shape_value::v2::typed_array::{
ELEM_TYPE_BOOL, ELEM_TYPE_CHAR, ELEM_TYPE_DECIMAL, ELEM_TYPE_F32, ELEM_TYPE_F64,
ELEM_TYPE_I16, ELEM_TYPE_I32, ELEM_TYPE_I64, ELEM_TYPE_I8, ELEM_TYPE_STRING,
ELEM_TYPE_TYPED_OBJECT, ELEM_TYPE_U16, ELEM_TYPE_U32, ELEM_TYPE_U8, ELEM_TYPE_UNKNOWN,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum V2ElemType {
F64,
I64,
I32,
Bool,
I8,
U8,
I16,
U16,
U32,
F32,
Char,
String,
Decimal,
TypedObject,
}
impl V2ElemType {
#[inline]
pub fn from_byte(b: u8) -> Option<Self> {
match b {
ELEM_TYPE_F64 => Some(V2ElemType::F64),
ELEM_TYPE_I64 => Some(V2ElemType::I64),
ELEM_TYPE_I32 => Some(V2ElemType::I32),
ELEM_TYPE_BOOL => Some(V2ElemType::Bool),
ELEM_TYPE_I8 => Some(V2ElemType::I8),
ELEM_TYPE_U8 => Some(V2ElemType::U8),
ELEM_TYPE_I16 => Some(V2ElemType::I16),
ELEM_TYPE_U16 => Some(V2ElemType::U16),
ELEM_TYPE_U32 => Some(V2ElemType::U32),
ELEM_TYPE_F32 => Some(V2ElemType::F32),
ELEM_TYPE_CHAR => Some(V2ElemType::Char),
ELEM_TYPE_STRING => Some(V2ElemType::String),
ELEM_TYPE_DECIMAL => Some(V2ElemType::Decimal),
ELEM_TYPE_TYPED_OBJECT => Some(V2ElemType::TypedObject),
_ => None,
}
}
#[inline]
pub fn elem_kind(self) -> NativeKind {
match self {
V2ElemType::F64 => NativeKind::Float64,
V2ElemType::I64 => NativeKind::Int64,
V2ElemType::I32 => NativeKind::Int32,
V2ElemType::Bool => NativeKind::Bool,
V2ElemType::I8 => NativeKind::Int8,
V2ElemType::U8 => NativeKind::UInt8,
V2ElemType::I16 => NativeKind::Int16,
V2ElemType::U16 => NativeKind::UInt16,
V2ElemType::U32 => NativeKind::UInt32,
V2ElemType::F32 => NativeKind::Float32,
V2ElemType::Char => NativeKind::Char,
V2ElemType::String => NativeKind::StringV2,
V2ElemType::Decimal => NativeKind::DecimalV2,
V2ElemType::TypedObject => {
NativeKind::Ptr(shape_value::HeapKind::TypedObject)
}
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct V2TypedArrayView {
pub ptr: *mut u8,
pub elem_type: V2ElemType,
pub len: u32,
}
#[inline]
pub unsafe fn stamp_elem_type(ptr: *mut u8, elem_type: u8) {
if ptr.is_null() {
return;
}
unsafe {
let pad = ptr.add(7);
*pad = elem_type;
}
}
#[inline]
unsafe fn read_elem_type_byte(ptr: *const u8) -> u8 {
if ptr.is_null() {
return ELEM_TYPE_UNKNOWN;
}
unsafe { *ptr.add(7) }
}
#[inline]
pub fn as_v2_typed_array(bits: u64, kind: NativeKind) -> Option<V2TypedArrayView> {
if !matches!(kind, NativeKind::Ptr(HeapKind::TypedArray)) {
return None;
}
if bits == 0 {
return None;
}
let ptr = bits as usize as *mut u8;
let header = unsafe { &*(ptr as *const HeapHeader) };
if header.kind != HEAP_KIND_V2_TYPED_ARRAY {
return None;
}
let elem_byte = unsafe { read_elem_type_byte(ptr) };
let elem_type = V2ElemType::from_byte(elem_byte)?;
let arr_u8 = ptr as *const TypedArray<u8>;
let len = unsafe { (*arr_u8).len };
Some(V2TypedArrayView {
ptr,
elem_type,
len,
})
}
#[inline]
fn decode_f64(bits: u64, kind: NativeKind) -> Option<f64> {
if matches!(kind, NativeKind::Float64 | NativeKind::NullableFloat64) {
return Some(f64::from_bits(bits));
}
if kind.is_integer_family() {
return Some(decode_i64(bits, kind)? as f64);
}
None
}
#[inline]
fn decode_i64(bits: u64, kind: NativeKind) -> Option<i64> {
match kind {
NativeKind::Int64 | NativeKind::NullableInt64 => Some(bits as i64),
NativeKind::Int32 | NativeKind::NullableInt32 => Some(bits as u32 as i32 as i64),
NativeKind::Int16 | NativeKind::NullableInt16 => Some(bits as u16 as i16 as i64),
NativeKind::Int8 | NativeKind::NullableInt8 => Some(bits as u8 as i8 as i64),
NativeKind::IntSize | NativeKind::NullableIntSize => Some(bits as isize as i64),
NativeKind::UInt64 | NativeKind::NullableUInt64 => Some(bits as i64),
NativeKind::UInt32 | NativeKind::NullableUInt32 => Some(bits as u32 as i64),
NativeKind::UInt16 | NativeKind::NullableUInt16 => Some(bits as u16 as i64),
NativeKind::UInt8 | NativeKind::NullableUInt8 => Some(bits as u8 as i64),
NativeKind::UIntSize | NativeKind::NullableUIntSize => Some(bits as usize as i64),
NativeKind::Float64 | NativeKind::NullableFloat64 => Some(f64::from_bits(bits) as i64),
_ => None,
}
}
#[inline]
fn decode_bool(bits: u64, kind: NativeKind) -> Option<bool> {
if matches!(kind, NativeKind::Bool) {
Some(bits != 0)
} else {
None
}
}
#[inline]
fn decode_f32(bits: u64, kind: NativeKind) -> Option<f32> {
if matches!(kind, NativeKind::Float32) {
return Some(f32::from_bits(bits as u32));
}
if let Some(v) = decode_f64(bits, kind) {
return Some(v as f32);
}
None
}
#[inline]
fn decode_char(bits: u64, kind: NativeKind) -> Option<char> {
if matches!(kind, NativeKind::Char) {
return char::from_u32(bits as u32);
}
if kind.is_integer_family() {
let cp = decode_i64(bits, kind)?;
if cp < 0 {
return None;
}
return char::from_u32(cp as u32);
}
None
}
#[inline]
pub fn read_element(view: &V2TypedArrayView, index: u32) -> Option<(u64, NativeKind)> {
if index >= view.len {
return None;
}
let pair = match view.elem_type {
V2ElemType::F64 => unsafe {
let arr = view.ptr as *const TypedArray<f64>;
let v = TypedArray::<f64>::get_unchecked(arr, index);
(v.to_bits(), NativeKind::Float64)
},
V2ElemType::I64 => unsafe {
let arr = view.ptr as *const TypedArray<i64>;
let v = TypedArray::<i64>::get_unchecked(arr, index);
(v as u64, NativeKind::Int64)
},
V2ElemType::I32 => unsafe {
let arr = view.ptr as *const TypedArray<i32>;
let v = TypedArray::<i32>::get_unchecked(arr, index) as i64;
(v as u64, NativeKind::Int32)
},
V2ElemType::Bool => unsafe {
let arr = view.ptr as *const TypedArray<u8>;
let v = TypedArray::<u8>::get_unchecked(arr, index) != 0;
(v as u64, NativeKind::Bool)
},
V2ElemType::I8 => unsafe {
let arr = view.ptr as *const TypedArray<i8>;
let v = TypedArray::<i8>::get_unchecked(arr, index) as i64;
(v as u64, NativeKind::Int8)
},
V2ElemType::U8 => unsafe {
let arr = view.ptr as *const TypedArray<u8>;
let v = TypedArray::<u8>::get_unchecked(arr, index) as u64;
(v, NativeKind::UInt8)
},
V2ElemType::I16 => unsafe {
let arr = view.ptr as *const TypedArray<i16>;
let v = TypedArray::<i16>::get_unchecked(arr, index) as i64;
(v as u64, NativeKind::Int16)
},
V2ElemType::U16 => unsafe {
let arr = view.ptr as *const TypedArray<u16>;
let v = TypedArray::<u16>::get_unchecked(arr, index) as u64;
(v, NativeKind::UInt16)
},
V2ElemType::U32 => unsafe {
let arr = view.ptr as *const TypedArray<u32>;
let v = TypedArray::<u32>::get_unchecked(arr, index) as u64;
(v, NativeKind::UInt32)
},
V2ElemType::F32 => unsafe {
let arr = view.ptr as *const TypedArray<f32>;
let v = TypedArray::<f32>::get_unchecked(arr, index);
(v.to_bits() as u64, NativeKind::Float32)
},
V2ElemType::Char => unsafe {
let arr = view.ptr as *const TypedArray<char>;
let v = TypedArray::<char>::get_unchecked(arr, index);
(v as u32 as u64, NativeKind::Char)
},
V2ElemType::String => unsafe {
let arr = view.ptr as *const TypedArray<*const StringObj>;
let elem_ptr = TypedArray::<*const StringObj>::get_unchecked(arr, index);
v2_retain(&(*elem_ptr).header);
(elem_ptr as u64, NativeKind::StringV2)
},
V2ElemType::Decimal => unsafe {
let arr = view.ptr as *const TypedArray<*const DecimalObj>;
let elem_ptr = TypedArray::<*const DecimalObj>::get_unchecked(arr, index);
v2_retain(&(*elem_ptr).header);
(elem_ptr as u64, NativeKind::DecimalV2)
},
V2ElemType::TypedObject => unsafe {
let arr = view.ptr as *const TypedArray<*const TypedObjectStorage>;
let elem_ptr =
TypedArray::<*const TypedObjectStorage>::get_unchecked(arr, index);
v2_retain(&(*elem_ptr).header);
(elem_ptr as u64, NativeKind::Ptr(HeapKind::TypedObject))
},
};
Some(pair)
}
#[inline]
pub fn write_element(
view: &V2TypedArrayView,
index: u32,
bits: u64,
kind: NativeKind,
) -> Result<(), &'static str> {
if index >= view.len {
return Err("index out of bounds");
}
match view.elem_type {
V2ElemType::F64 => {
let v = decode_f64(bits, kind).ok_or("expected f64-compatible value")?;
unsafe {
let arr = view.ptr as *mut TypedArray<f64>;
TypedArray::<f64>::set(arr, index, v);
}
}
V2ElemType::I64 => {
let v = decode_i64(bits, kind).ok_or("expected i64-compatible value")?;
unsafe {
let arr = view.ptr as *mut TypedArray<i64>;
TypedArray::<i64>::set(arr, index, v);
}
}
V2ElemType::I32 => {
let v = decode_i64(bits, kind).ok_or("expected i32-compatible value")?;
unsafe {
let arr = view.ptr as *mut TypedArray<i32>;
TypedArray::<i32>::set(arr, index, v as i32);
}
}
V2ElemType::Bool => {
let v = decode_bool(bits, kind).ok_or("expected bool value")?;
unsafe {
let arr = view.ptr as *mut TypedArray<u8>;
TypedArray::<u8>::set(arr, index, if v { 1 } else { 0 });
}
}
V2ElemType::I8 => {
let v = decode_i64(bits, kind).ok_or("expected i8-compatible value")?;
unsafe {
let arr = view.ptr as *mut TypedArray<i8>;
TypedArray::<i8>::set(arr, index, v as i8);
}
}
V2ElemType::U8 => {
let v = decode_i64(bits, kind).ok_or("expected u8-compatible value")?;
unsafe {
let arr = view.ptr as *mut TypedArray<u8>;
TypedArray::<u8>::set(arr, index, v as u8);
}
}
V2ElemType::I16 => {
let v = decode_i64(bits, kind).ok_or("expected i16-compatible value")?;
unsafe {
let arr = view.ptr as *mut TypedArray<i16>;
TypedArray::<i16>::set(arr, index, v as i16);
}
}
V2ElemType::U16 => {
let v = decode_i64(bits, kind).ok_or("expected u16-compatible value")?;
unsafe {
let arr = view.ptr as *mut TypedArray<u16>;
TypedArray::<u16>::set(arr, index, v as u16);
}
}
V2ElemType::U32 => {
let v = decode_i64(bits, kind).ok_or("expected u32-compatible value")?;
unsafe {
let arr = view.ptr as *mut TypedArray<u32>;
TypedArray::<u32>::set(arr, index, v as u32);
}
}
V2ElemType::F32 => {
let v = decode_f32(bits, kind).ok_or("expected f32-compatible value")?;
unsafe {
let arr = view.ptr as *mut TypedArray<f32>;
TypedArray::<f32>::set(arr, index, v);
}
}
V2ElemType::Char => {
let v = decode_char(bits, kind).ok_or("expected char-compatible value")?;
unsafe {
let arr = view.ptr as *mut TypedArray<char>;
TypedArray::<char>::set(arr, index, v);
}
}
V2ElemType::String => {
if kind != NativeKind::StringV2 {
return Err("expected NativeKind::StringV2 for Array<string> write");
}
let new_ptr = bits as usize as *const StringObj;
unsafe {
let arr = view.ptr as *mut TypedArray<*const StringObj>;
let old_ptr = TypedArray::<*const StringObj>::get_unchecked(arr, index);
<StringObj as HeapElement>::release_elem(old_ptr);
TypedArray::<*const StringObj>::set(arr, index, new_ptr);
}
}
V2ElemType::Decimal => {
if kind != NativeKind::DecimalV2 {
return Err("expected NativeKind::DecimalV2 for Array<decimal> write");
}
let new_ptr = bits as usize as *const DecimalObj;
unsafe {
let arr = view.ptr as *mut TypedArray<*const DecimalObj>;
let old_ptr = TypedArray::<*const DecimalObj>::get_unchecked(arr, index);
<DecimalObj as HeapElement>::release_elem(old_ptr);
TypedArray::<*const DecimalObj>::set(arr, index, new_ptr);
}
}
V2ElemType::TypedObject => {
if kind != NativeKind::Ptr(HeapKind::TypedObject) {
return Err(
"expected NativeKind::Ptr(HeapKind::TypedObject) for Array<TypedObject> write",
);
}
let new_ptr = bits as usize as *const TypedObjectStorage;
unsafe {
let arr = view.ptr as *mut TypedArray<*const TypedObjectStorage>;
let old_ptr =
TypedArray::<*const TypedObjectStorage>::get_unchecked(arr, index);
<TypedObjectStorage as HeapElement>::release_elem(old_ptr);
TypedArray::<*const TypedObjectStorage>::set(arr, index, new_ptr);
}
}
}
Ok(())
}
#[inline]
pub fn push_element(
view: &V2TypedArrayView,
bits: u64,
kind: NativeKind,
) -> Result<(), &'static str> {
match view.elem_type {
V2ElemType::F64 => {
let v = decode_f64(bits, kind).ok_or("expected f64-compatible value")?;
unsafe {
let arr = view.ptr as *mut TypedArray<f64>;
TypedArray::<f64>::push(arr, v);
}
}
V2ElemType::I64 => {
let v = decode_i64(bits, kind).ok_or("expected i64-compatible value")?;
unsafe {
let arr = view.ptr as *mut TypedArray<i64>;
TypedArray::<i64>::push(arr, v);
}
}
V2ElemType::I32 => {
let v = decode_i64(bits, kind).ok_or("expected i32-compatible value")?;
unsafe {
let arr = view.ptr as *mut TypedArray<i32>;
TypedArray::<i32>::push(arr, v as i32);
}
}
V2ElemType::Bool => {
let v = decode_bool(bits, kind).ok_or("expected bool value")?;
unsafe {
let arr = view.ptr as *mut TypedArray<u8>;
TypedArray::<u8>::push(arr, if v { 1 } else { 0 });
}
}
V2ElemType::I8 => {
let v = decode_i64(bits, kind).ok_or("expected i8-compatible value")?;
unsafe {
let arr = view.ptr as *mut TypedArray<i8>;
TypedArray::<i8>::push(arr, v as i8);
}
}
V2ElemType::U8 => {
let v = decode_i64(bits, kind).ok_or("expected u8-compatible value")?;
unsafe {
let arr = view.ptr as *mut TypedArray<u8>;
TypedArray::<u8>::push(arr, v as u8);
}
}
V2ElemType::I16 => {
let v = decode_i64(bits, kind).ok_or("expected i16-compatible value")?;
unsafe {
let arr = view.ptr as *mut TypedArray<i16>;
TypedArray::<i16>::push(arr, v as i16);
}
}
V2ElemType::U16 => {
let v = decode_i64(bits, kind).ok_or("expected u16-compatible value")?;
unsafe {
let arr = view.ptr as *mut TypedArray<u16>;
TypedArray::<u16>::push(arr, v as u16);
}
}
V2ElemType::U32 => {
let v = decode_i64(bits, kind).ok_or("expected u32-compatible value")?;
unsafe {
let arr = view.ptr as *mut TypedArray<u32>;
TypedArray::<u32>::push(arr, v as u32);
}
}
V2ElemType::F32 => {
let v = decode_f32(bits, kind).ok_or("expected f32-compatible value")?;
unsafe {
let arr = view.ptr as *mut TypedArray<f32>;
TypedArray::<f32>::push(arr, v);
}
}
V2ElemType::Char => {
let v = decode_char(bits, kind).ok_or("expected char-compatible value")?;
unsafe {
let arr = view.ptr as *mut TypedArray<char>;
TypedArray::<char>::push(arr, v);
}
}
V2ElemType::String => {
if kind != NativeKind::StringV2 {
return Err("expected NativeKind::StringV2 for Array<string> push");
}
let new_ptr = bits as usize as *const StringObj;
unsafe {
let arr = view.ptr as *mut TypedArray<*const StringObj>;
TypedArray::<*const StringObj>::push(arr, new_ptr);
}
}
V2ElemType::Decimal => {
if kind != NativeKind::DecimalV2 {
return Err("expected NativeKind::DecimalV2 for Array<decimal> push");
}
let new_ptr = bits as usize as *const DecimalObj;
unsafe {
let arr = view.ptr as *mut TypedArray<*const DecimalObj>;
TypedArray::<*const DecimalObj>::push(arr, new_ptr);
}
}
V2ElemType::TypedObject => {
if kind != NativeKind::Ptr(HeapKind::TypedObject) {
return Err(
"expected NativeKind::Ptr(HeapKind::TypedObject) for Array<TypedObject> push",
);
}
let new_ptr = bits as usize as *const TypedObjectStorage;
unsafe {
let arr = view.ptr as *mut TypedArray<*const TypedObjectStorage>;
TypedArray::<*const TypedObjectStorage>::push(arr, new_ptr);
}
}
}
Ok(())
}
#[inline]
pub fn pop_element(view: &V2TypedArrayView) -> Option<(u64, NativeKind)> {
match view.elem_type {
V2ElemType::F64 => unsafe {
let arr = view.ptr as *mut TypedArray<f64>;
TypedArray::<f64>::pop(arr).map(|v| (v.to_bits(), NativeKind::Float64))
},
V2ElemType::I64 => unsafe {
let arr = view.ptr as *mut TypedArray<i64>;
TypedArray::<i64>::pop(arr).map(|v| (v as u64, NativeKind::Int64))
},
V2ElemType::I32 => unsafe {
let arr = view.ptr as *mut TypedArray<i32>;
TypedArray::<i32>::pop(arr).map(|v| (v as i64 as u64, NativeKind::Int32))
},
V2ElemType::Bool => unsafe {
let arr = view.ptr as *mut TypedArray<u8>;
TypedArray::<u8>::pop(arr).map(|v| ((v != 0) as u64, NativeKind::Bool))
},
V2ElemType::I8 => unsafe {
let arr = view.ptr as *mut TypedArray<i8>;
TypedArray::<i8>::pop(arr).map(|v| (v as i64 as u64, NativeKind::Int8))
},
V2ElemType::U8 => unsafe {
let arr = view.ptr as *mut TypedArray<u8>;
TypedArray::<u8>::pop(arr).map(|v| (v as u64, NativeKind::UInt8))
},
V2ElemType::I16 => unsafe {
let arr = view.ptr as *mut TypedArray<i16>;
TypedArray::<i16>::pop(arr).map(|v| (v as i64 as u64, NativeKind::Int16))
},
V2ElemType::U16 => unsafe {
let arr = view.ptr as *mut TypedArray<u16>;
TypedArray::<u16>::pop(arr).map(|v| (v as u64, NativeKind::UInt16))
},
V2ElemType::U32 => unsafe {
let arr = view.ptr as *mut TypedArray<u32>;
TypedArray::<u32>::pop(arr).map(|v| (v as u64, NativeKind::UInt32))
},
V2ElemType::F32 => unsafe {
let arr = view.ptr as *mut TypedArray<f32>;
TypedArray::<f32>::pop(arr).map(|v| (v.to_bits() as u64, NativeKind::Float32))
},
V2ElemType::Char => unsafe {
let arr = view.ptr as *mut TypedArray<char>;
TypedArray::<char>::pop(arr).map(|v| (v as u32 as u64, NativeKind::Char))
},
V2ElemType::String => unsafe {
let arr = view.ptr as *mut TypedArray<*const StringObj>;
TypedArray::<*const StringObj>::pop(arr).map(|v| (v as u64, NativeKind::StringV2))
},
V2ElemType::Decimal => unsafe {
let arr = view.ptr as *mut TypedArray<*const DecimalObj>;
TypedArray::<*const DecimalObj>::pop(arr).map(|v| (v as u64, NativeKind::DecimalV2))
},
V2ElemType::TypedObject => unsafe {
let arr = view.ptr as *mut TypedArray<*const TypedObjectStorage>;
TypedArray::<*const TypedObjectStorage>::pop(arr)
.map(|v| (v as u64, NativeKind::Ptr(HeapKind::TypedObject)))
},
}
}
pub fn sum_elements(view: &V2TypedArrayView) -> Option<(u64, NativeKind)> {
const SIMD_SUM_THRESHOLD: u32 = 16;
match view.elem_type {
V2ElemType::F64 => {
let len = view.len;
if len == 0 {
return Some((0.0_f64.to_bits(), NativeKind::Float64));
}
let data = unsafe {
let arr = view.ptr as *const TypedArray<f64>;
(*arr).data as *const f64
};
let s = unsafe { simd_sum_f64(data, len as usize, SIMD_SUM_THRESHOLD as usize) };
Some((s.to_bits(), NativeKind::Float64))
}
V2ElemType::I64 => {
let len = view.len;
if len == 0 {
return Some((0u64, NativeKind::Int64));
}
let data = unsafe {
let arr = view.ptr as *const TypedArray<i64>;
(*arr).data as *const i64
};
let s = unsafe { simd_sum_i64(data, len as usize, SIMD_SUM_THRESHOLD as usize) };
Some((s as u64, NativeKind::Int64))
}
V2ElemType::I32 => {
let mut s: i64 = 0;
for i in 0..view.len {
let val = unsafe {
let arr = view.ptr as *const TypedArray<i32>;
TypedArray::<i32>::get_unchecked(arr, i) as i64
};
s = s.wrapping_add(val);
}
Some((s as u64, NativeKind::Int64))
}
V2ElemType::Bool
| V2ElemType::I8
| V2ElemType::U8
| V2ElemType::I16
| V2ElemType::U16
| V2ElemType::U32
| V2ElemType::F32
| V2ElemType::Char
| V2ElemType::String
| V2ElemType::Decimal
| V2ElemType::TypedObject => None,
}
}
#[inline]
unsafe fn simd_sum_f64(data: *const f64, len: usize, threshold: usize) -> f64 {
use wide::f64x4;
if len < threshold {
let mut s = 0.0_f64;
for i in 0..len {
s += unsafe { *data.add(i) };
}
return s;
}
let chunks = len / 4;
let mut acc = f64x4::splat(0.0);
for i in 0..chunks {
let base = i * 4;
let v = unsafe {
f64x4::from([
*data.add(base),
*data.add(base + 1),
*data.add(base + 2),
*data.add(base + 3),
])
};
acc += v;
}
let parts = acc.to_array();
let mut s = parts[0] + parts[1] + parts[2] + parts[3];
for i in (chunks * 4)..len {
s += unsafe { *data.add(i) };
}
s
}
#[inline]
unsafe fn contains_nan_f64(data: *const f64, len: usize) -> bool {
for i in 0..len {
if unsafe { *data.add(i) }.is_nan() {
return true;
}
}
false
}
#[inline]
unsafe fn simd_min_f64(data: *const f64, len: usize, threshold: usize) -> f64 {
use wide::f64x4;
debug_assert!(len > 0);
if unsafe { contains_nan_f64(data, len) } {
return f64::NAN;
}
if len < threshold {
let mut m = unsafe { *data };
for i in 1..len {
let v = unsafe { *data.add(i) };
if v < m {
m = v;
}
}
return m;
}
let chunks = len / 4;
let mut acc = unsafe {
f64x4::from([
*data,
*data.add(1),
*data.add(2),
*data.add(3),
])
};
for i in 1..chunks {
let base = i * 4;
let v = unsafe {
f64x4::from([
*data.add(base),
*data.add(base + 1),
*data.add(base + 2),
*data.add(base + 3),
])
};
acc = acc.fast_min(v);
}
let parts = acc.to_array();
let mut m = parts[0];
for &p in &parts[1..] {
if p < m {
m = p;
}
}
for i in (chunks * 4)..len {
let v = unsafe { *data.add(i) };
if v < m {
m = v;
}
}
m
}
#[inline]
unsafe fn simd_max_f64(data: *const f64, len: usize, threshold: usize) -> f64 {
use wide::f64x4;
debug_assert!(len > 0);
if unsafe { contains_nan_f64(data, len) } {
return f64::NAN;
}
if len < threshold {
let mut m = unsafe { *data };
for i in 1..len {
let v = unsafe { *data.add(i) };
if v > m {
m = v;
}
}
return m;
}
let chunks = len / 4;
let mut acc = unsafe {
f64x4::from([
*data,
*data.add(1),
*data.add(2),
*data.add(3),
])
};
for i in 1..chunks {
let base = i * 4;
let v = unsafe {
f64x4::from([
*data.add(base),
*data.add(base + 1),
*data.add(base + 2),
*data.add(base + 3),
])
};
acc = acc.fast_max(v);
}
let parts = acc.to_array();
let mut m = parts[0];
for &p in &parts[1..] {
if p > m {
m = p;
}
}
for i in (chunks * 4)..len {
let v = unsafe { *data.add(i) };
if v > m {
m = v;
}
}
m
}
#[inline]
unsafe fn simd_sum_i64(data: *const i64, len: usize, threshold: usize) -> i64 {
use wide::i64x4;
if len < threshold {
let mut s: i64 = 0;
for i in 0..len {
s = s.wrapping_add(unsafe { *data.add(i) });
}
return s;
}
let chunks = len / 4;
let mut acc = i64x4::splat(0);
for i in 0..chunks {
let base = i * 4;
let v = unsafe {
i64x4::from([
*data.add(base),
*data.add(base + 1),
*data.add(base + 2),
*data.add(base + 3),
])
};
acc = acc + v;
}
let parts = acc.to_array();
let mut s = parts[0]
.wrapping_add(parts[1])
.wrapping_add(parts[2])
.wrapping_add(parts[3]);
for i in (chunks * 4)..len {
s = s.wrapping_add(unsafe { *data.add(i) });
}
s
}
pub fn avg_elements(view: &V2TypedArrayView) -> Option<(u64, NativeKind)> {
if view.len == 0 {
return match view.elem_type {
V2ElemType::F64 | V2ElemType::I64 | V2ElemType::I32 => {
Some((f64::NAN.to_bits(), NativeKind::Float64))
}
V2ElemType::Bool
| V2ElemType::I8
| V2ElemType::U8
| V2ElemType::I16
| V2ElemType::U16
| V2ElemType::U32
| V2ElemType::F32
| V2ElemType::Char
| V2ElemType::String
| V2ElemType::Decimal
| V2ElemType::TypedObject => None,
};
}
match view.elem_type {
V2ElemType::F64 => {
let data = unsafe {
let arr = view.ptr as *const TypedArray<f64>;
(*arr).data as *const f64
};
let s = unsafe { simd_sum_f64(data, view.len as usize, 16) };
Some(((s / view.len as f64).to_bits(), NativeKind::Float64))
}
V2ElemType::I64 => {
let mut s = 0.0_f64;
for i in 0..view.len {
s += unsafe {
let arr = view.ptr as *const TypedArray<i64>;
TypedArray::<i64>::get_unchecked(arr, i) as f64
};
}
Some(((s / view.len as f64).to_bits(), NativeKind::Float64))
}
V2ElemType::I32 => {
let mut s = 0.0_f64;
for i in 0..view.len {
s += unsafe {
let arr = view.ptr as *const TypedArray<i32>;
TypedArray::<i32>::get_unchecked(arr, i) as f64
};
}
Some(((s / view.len as f64).to_bits(), NativeKind::Float64))
}
V2ElemType::Bool
| V2ElemType::I8
| V2ElemType::U8
| V2ElemType::I16
| V2ElemType::U16
| V2ElemType::U32
| V2ElemType::F32
| V2ElemType::Char
| V2ElemType::String
| V2ElemType::Decimal
| V2ElemType::TypedObject => None,
}
}
pub fn min_elements(view: &V2TypedArrayView) -> Option<(u64, NativeKind)> {
if view.len == 0 {
return match view.elem_type {
V2ElemType::F64 => Some((f64::NAN.to_bits(), NativeKind::Float64)),
V2ElemType::I64 | V2ElemType::I32 => Some((0u64, NativeKind::Bool)),
V2ElemType::Bool
| V2ElemType::I8
| V2ElemType::U8
| V2ElemType::I16
| V2ElemType::U16
| V2ElemType::U32
| V2ElemType::F32
| V2ElemType::Char
| V2ElemType::String
| V2ElemType::Decimal
| V2ElemType::TypedObject => None,
};
}
match view.elem_type {
V2ElemType::F64 => {
let data = unsafe {
let arr = view.ptr as *const TypedArray<f64>;
(*arr).data as *const f64
};
let min = unsafe { simd_min_f64(data, view.len as usize, 16) };
Some((min.to_bits(), NativeKind::Float64))
}
V2ElemType::I64 => {
let mut min = i64::MAX;
for i in 0..view.len {
let v = unsafe {
let arr = view.ptr as *const TypedArray<i64>;
TypedArray::<i64>::get_unchecked(arr, i)
};
if v < min {
min = v;
}
}
Some((min as u64, NativeKind::Int64))
}
V2ElemType::I32 => {
let mut min = i32::MAX as i64;
for i in 0..view.len {
let v = unsafe {
let arr = view.ptr as *const TypedArray<i32>;
TypedArray::<i32>::get_unchecked(arr, i) as i64
};
if v < min {
min = v;
}
}
Some((min as u64, NativeKind::Int64))
}
V2ElemType::Bool
| V2ElemType::I8
| V2ElemType::U8
| V2ElemType::I16
| V2ElemType::U16
| V2ElemType::U32
| V2ElemType::F32
| V2ElemType::Char
| V2ElemType::String
| V2ElemType::Decimal
| V2ElemType::TypedObject => None,
}
}
pub fn max_elements(view: &V2TypedArrayView) -> Option<(u64, NativeKind)> {
if view.len == 0 {
return match view.elem_type {
V2ElemType::F64 => Some((f64::NAN.to_bits(), NativeKind::Float64)),
V2ElemType::I64 | V2ElemType::I32 => Some((0u64, NativeKind::Bool)),
V2ElemType::Bool
| V2ElemType::I8
| V2ElemType::U8
| V2ElemType::I16
| V2ElemType::U16
| V2ElemType::U32
| V2ElemType::F32
| V2ElemType::Char
| V2ElemType::String
| V2ElemType::Decimal
| V2ElemType::TypedObject => None,
};
}
match view.elem_type {
V2ElemType::F64 => {
let data = unsafe {
let arr = view.ptr as *const TypedArray<f64>;
(*arr).data as *const f64
};
let max = unsafe { simd_max_f64(data, view.len as usize, 16) };
Some((max.to_bits(), NativeKind::Float64))
}
V2ElemType::I64 => {
let mut max = i64::MIN;
for i in 0..view.len {
let v = unsafe {
let arr = view.ptr as *const TypedArray<i64>;
TypedArray::<i64>::get_unchecked(arr, i)
};
if v > max {
max = v;
}
}
Some((max as u64, NativeKind::Int64))
}
V2ElemType::I32 => {
let mut max = i32::MIN as i64;
for i in 0..view.len {
let v = unsafe {
let arr = view.ptr as *const TypedArray<i32>;
TypedArray::<i32>::get_unchecked(arr, i) as i64
};
if v > max {
max = v;
}
}
Some((max as u64, NativeKind::Int64))
}
V2ElemType::Bool
| V2ElemType::I8
| V2ElemType::U8
| V2ElemType::I16
| V2ElemType::U16
| V2ElemType::U32
| V2ElemType::F32
| V2ElemType::Char
| V2ElemType::String
| V2ElemType::Decimal
| V2ElemType::TypedObject => None,
}
}
pub fn variance_elements(view: &V2TypedArrayView) -> Option<(u64, NativeKind)> {
match view.elem_type {
V2ElemType::F64 => {
if view.len < 2 {
return Some((f64::NAN.to_bits(), NativeKind::Float64));
}
let n = view.len as f64;
let mut sum = 0.0_f64;
for i in 0..view.len {
sum += unsafe {
let arr = view.ptr as *const TypedArray<f64>;
TypedArray::<f64>::get_unchecked(arr, i)
};
}
let mean = sum / n;
let mut var_sum = 0.0_f64;
for i in 0..view.len {
let v = unsafe {
let arr = view.ptr as *const TypedArray<f64>;
TypedArray::<f64>::get_unchecked(arr, i)
};
let d = v - mean;
var_sum += d * d;
}
Some(((var_sum / (n - 1.0)).to_bits(), NativeKind::Float64))
}
_ => None,
}
}
pub fn std_elements(view: &V2TypedArrayView) -> Option<(u64, NativeKind)> {
variance_elements(view).map(|(bits, _kind)| {
let v = f64::from_bits(bits);
(v.sqrt().to_bits(), NativeKind::Float64)
})
}
pub fn dot_elements(
view_a: &V2TypedArrayView,
view_b: &V2TypedArrayView,
) -> Option<(u64, NativeKind)> {
if view_a.elem_type != V2ElemType::F64 || view_b.elem_type != V2ElemType::F64 {
return None;
}
if view_a.len != view_b.len {
return None; }
let mut sum = 0.0_f64;
for i in 0..view_a.len {
let a = unsafe {
let arr = view_a.ptr as *const TypedArray<f64>;
TypedArray::<f64>::get_unchecked(arr, i)
};
let b = unsafe {
let arr = view_b.ptr as *const TypedArray<f64>;
TypedArray::<f64>::get_unchecked(arr, i)
};
sum += a * b;
}
Some((sum.to_bits(), NativeKind::Float64))
}
pub fn norm_elements(view: &V2TypedArrayView) -> Option<(u64, NativeKind)> {
match view.elem_type {
V2ElemType::F64 => {
let mut sum_sq = 0.0_f64;
for i in 0..view.len {
let v = unsafe {
let arr = view.ptr as *const TypedArray<f64>;
TypedArray::<f64>::get_unchecked(arr, i)
};
sum_sq += v * v;
}
Some((sum_sq.sqrt().to_bits(), NativeKind::Float64))
}
_ => None,
}
}
pub fn count_true_elements(view: &V2TypedArrayView) -> Option<(u64, NativeKind)> {
match view.elem_type {
V2ElemType::Bool => {
let mut count = 0_i64;
for i in 0..view.len {
let v = unsafe {
let arr = view.ptr as *const TypedArray<u8>;
TypedArray::<u8>::get_unchecked(arr, i)
};
if v != 0 {
count += 1;
}
}
Some((count as u64, NativeKind::Int64))
}
_ => None,
}
}
pub fn any_elements(view: &V2TypedArrayView) -> Option<(u64, NativeKind)> {
match view.elem_type {
V2ElemType::Bool => {
for i in 0..view.len {
let v = unsafe {
let arr = view.ptr as *const TypedArray<u8>;
TypedArray::<u8>::get_unchecked(arr, i)
};
if v != 0 {
return Some((1u64, NativeKind::Bool));
}
}
Some((0u64, NativeKind::Bool))
}
_ => None,
}
}
pub fn all_elements(view: &V2TypedArrayView) -> Option<(u64, NativeKind)> {
match view.elem_type {
V2ElemType::Bool => {
for i in 0..view.len {
let v = unsafe {
let arr = view.ptr as *const TypedArray<u8>;
TypedArray::<u8>::get_unchecked(arr, i)
};
if v == 0 {
return Some((0u64, NativeKind::Bool));
}
}
Some((1u64, NativeKind::Bool))
}
_ => None,
}
}
pub fn clone_array(view: &V2TypedArrayView) -> *mut u8 {
match view.elem_type {
V2ElemType::F64 => {
let new_arr = TypedArray::<f64>::with_capacity(view.len);
unsafe {
let src = view.ptr as *const TypedArray<f64>;
let src_data = (*src).data;
let dst_data = (*new_arr).data;
if view.len > 0 && !src_data.is_null() && !dst_data.is_null() {
std::ptr::copy_nonoverlapping(src_data, dst_data, view.len as usize);
}
(*new_arr).len = view.len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_F64);
p
}
}
V2ElemType::I64 => {
let new_arr = TypedArray::<i64>::with_capacity(view.len);
unsafe {
let src = view.ptr as *const TypedArray<i64>;
let src_data = (*src).data;
let dst_data = (*new_arr).data;
if view.len > 0 && !src_data.is_null() && !dst_data.is_null() {
std::ptr::copy_nonoverlapping(src_data, dst_data, view.len as usize);
}
(*new_arr).len = view.len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_I64);
p
}
}
V2ElemType::I32 => {
let new_arr = TypedArray::<i32>::with_capacity(view.len);
unsafe {
let src = view.ptr as *const TypedArray<i32>;
let src_data = (*src).data;
let dst_data = (*new_arr).data;
if view.len > 0 && !src_data.is_null() && !dst_data.is_null() {
std::ptr::copy_nonoverlapping(src_data, dst_data, view.len as usize);
}
(*new_arr).len = view.len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_I32);
p
}
}
V2ElemType::Bool => {
let new_arr = TypedArray::<u8>::with_capacity(view.len);
unsafe {
let src = view.ptr as *const TypedArray<u8>;
let src_data = (*src).data;
let dst_data = (*new_arr).data;
if view.len > 0 && !src_data.is_null() && !dst_data.is_null() {
std::ptr::copy_nonoverlapping(src_data, dst_data, view.len as usize);
}
(*new_arr).len = view.len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_BOOL);
p
}
}
V2ElemType::I8 => {
let new_arr = TypedArray::<i8>::with_capacity(view.len);
unsafe {
let src = view.ptr as *const TypedArray<i8>;
let src_data = (*src).data;
let dst_data = (*new_arr).data;
if view.len > 0 && !src_data.is_null() && !dst_data.is_null() {
std::ptr::copy_nonoverlapping(src_data, dst_data, view.len as usize);
}
(*new_arr).len = view.len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_I8);
p
}
}
V2ElemType::U8 => {
let new_arr = TypedArray::<u8>::with_capacity(view.len);
unsafe {
let src = view.ptr as *const TypedArray<u8>;
let src_data = (*src).data;
let dst_data = (*new_arr).data;
if view.len > 0 && !src_data.is_null() && !dst_data.is_null() {
std::ptr::copy_nonoverlapping(src_data, dst_data, view.len as usize);
}
(*new_arr).len = view.len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_U8);
p
}
}
V2ElemType::I16 => {
let new_arr = TypedArray::<i16>::with_capacity(view.len);
unsafe {
let src = view.ptr as *const TypedArray<i16>;
let src_data = (*src).data;
let dst_data = (*new_arr).data;
if view.len > 0 && !src_data.is_null() && !dst_data.is_null() {
std::ptr::copy_nonoverlapping(src_data, dst_data, view.len as usize);
}
(*new_arr).len = view.len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_I16);
p
}
}
V2ElemType::U16 => {
let new_arr = TypedArray::<u16>::with_capacity(view.len);
unsafe {
let src = view.ptr as *const TypedArray<u16>;
let src_data = (*src).data;
let dst_data = (*new_arr).data;
if view.len > 0 && !src_data.is_null() && !dst_data.is_null() {
std::ptr::copy_nonoverlapping(src_data, dst_data, view.len as usize);
}
(*new_arr).len = view.len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_U16);
p
}
}
V2ElemType::U32 => {
let new_arr = TypedArray::<u32>::with_capacity(view.len);
unsafe {
let src = view.ptr as *const TypedArray<u32>;
let src_data = (*src).data;
let dst_data = (*new_arr).data;
if view.len > 0 && !src_data.is_null() && !dst_data.is_null() {
std::ptr::copy_nonoverlapping(src_data, dst_data, view.len as usize);
}
(*new_arr).len = view.len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_U32);
p
}
}
V2ElemType::F32 => {
let new_arr = TypedArray::<f32>::with_capacity(view.len);
unsafe {
let src = view.ptr as *const TypedArray<f32>;
let src_data = (*src).data;
let dst_data = (*new_arr).data;
if view.len > 0 && !src_data.is_null() && !dst_data.is_null() {
std::ptr::copy_nonoverlapping(src_data, dst_data, view.len as usize);
}
(*new_arr).len = view.len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_F32);
p
}
}
V2ElemType::Char => {
let new_arr = TypedArray::<char>::with_capacity(view.len);
unsafe {
let src = view.ptr as *const TypedArray<char>;
let src_data = (*src).data;
let dst_data = (*new_arr).data;
if view.len > 0 && !src_data.is_null() && !dst_data.is_null() {
std::ptr::copy_nonoverlapping(src_data, dst_data, view.len as usize);
}
(*new_arr).len = view.len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_CHAR);
p
}
}
V2ElemType::String => {
let new_arr = TypedArray::<*const StringObj>::with_capacity(view.len);
unsafe {
let src = view.ptr as *const TypedArray<*const StringObj>;
let src_data = (*src).data;
let dst_data = (*new_arr).data;
if view.len > 0 && !src_data.is_null() && !dst_data.is_null() {
for i in 0..(view.len as usize) {
let elem = *src_data.add(i);
v2_retain(&(*elem).header);
*dst_data.add(i) = elem;
}
}
(*new_arr).len = view.len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_STRING);
p
}
}
V2ElemType::Decimal => {
let new_arr = TypedArray::<*const DecimalObj>::with_capacity(view.len);
unsafe {
let src = view.ptr as *const TypedArray<*const DecimalObj>;
let src_data = (*src).data;
let dst_data = (*new_arr).data;
if view.len > 0 && !src_data.is_null() && !dst_data.is_null() {
for i in 0..(view.len as usize) {
let elem = *src_data.add(i);
v2_retain(&(*elem).header);
*dst_data.add(i) = elem;
}
}
(*new_arr).len = view.len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_DECIMAL);
p
}
}
V2ElemType::TypedObject => {
let new_arr =
TypedArray::<*const TypedObjectStorage>::with_capacity(view.len);
unsafe {
let src = view.ptr as *const TypedArray<*const TypedObjectStorage>;
let src_data = (*src).data;
let dst_data = (*new_arr).data;
if view.len > 0 && !src_data.is_null() && !dst_data.is_null() {
for i in 0..(view.len as usize) {
let elem = *src_data.add(i);
v2_retain(&(*elem).header);
*dst_data.add(i) = elem;
}
}
(*new_arr).len = view.len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_TYPED_OBJECT);
p
}
}
}
}
const SIMD_UNARY_THRESHOLD: u32 = 16;
pub fn unary_f64_transform(
view: &V2TypedArrayView,
simd_op: fn(wide::f64x4) -> wide::f64x4,
scalar_op: fn(f64) -> f64,
) -> Option<*mut u8> {
use wide::f64x4;
if view.elem_type != V2ElemType::F64 {
return None;
}
let len = view.len;
let out = TypedArray::<f64>::with_capacity(len);
if len == 0 {
unsafe {
(*out).len = 0;
let p = out as *mut u8;
stamp_elem_type(p, ELEM_TYPE_F64);
return Some(p);
}
}
unsafe {
let src_arr = view.ptr as *const TypedArray<f64>;
let src = (*src_arr).data as *const f64;
let dst = (*out).data as *mut f64;
if len >= SIMD_UNARY_THRESHOLD {
let chunks = (len / 4) as usize;
for i in 0..chunks {
let base = i * 4;
let v = f64x4::from([
*src.add(base),
*src.add(base + 1),
*src.add(base + 2),
*src.add(base + 3),
]);
let r = simd_op(v);
let arr = r.to_array();
*dst.add(base) = arr[0];
*dst.add(base + 1) = arr[1];
*dst.add(base + 2) = arr[2];
*dst.add(base + 3) = arr[3];
}
for i in (chunks * 4)..(len as usize) {
*dst.add(i) = scalar_op(*src.add(i));
}
} else {
for i in 0..(len as usize) {
*dst.add(i) = scalar_op(*src.add(i));
}
}
(*out).len = len;
let p = out as *mut u8;
stamp_elem_type(p, ELEM_TYPE_F64);
Some(p)
}
}
pub fn diff_f64(view: &V2TypedArrayView) -> Option<*mut u8> {
use wide::f64x4;
if view.elem_type != V2ElemType::F64 {
return None;
}
let len = view.len;
if len < 2 {
let out = TypedArray::<f64>::with_capacity(0);
unsafe {
(*out).len = 0;
let p = out as *mut u8;
stamp_elem_type(p, ELEM_TYPE_F64);
return Some(p);
}
}
let out_len = len - 1;
let out = TypedArray::<f64>::with_capacity(out_len);
unsafe {
let src_arr = view.ptr as *const TypedArray<f64>;
let src = (*src_arr).data as *const f64;
let dst = (*out).data as *mut f64;
if out_len >= SIMD_UNARY_THRESHOLD {
let mut i: usize = 0;
while i + 4 < (len as usize) {
let prev = f64x4::from([
*src.add(i),
*src.add(i + 1),
*src.add(i + 2),
*src.add(i + 3),
]);
let next = f64x4::from([
*src.add(i + 1),
*src.add(i + 2),
*src.add(i + 3),
*src.add(i + 4),
]);
let d = next - prev;
let arr = d.to_array();
*dst.add(i) = arr[0];
*dst.add(i + 1) = arr[1];
*dst.add(i + 2) = arr[2];
*dst.add(i + 3) = arr[3];
i += 4;
}
for j in i..(out_len as usize) {
*dst.add(j) = *src.add(j + 1) - *src.add(j);
}
} else {
for i in 0..(out_len as usize) {
*dst.add(i) = *src.add(i + 1) - *src.add(i);
}
}
(*out).len = out_len;
let p = out as *mut u8;
stamp_elem_type(p, ELEM_TYPE_F64);
Some(p)
}
}
pub fn reverse_array(view: &V2TypedArrayView) -> *mut u8 {
#[inline]
unsafe fn copy_reverse_scalar<T: Copy>(
src_data: *const T,
dst_data: *mut T,
len: usize,
) {
if len == 0 || src_data.is_null() || dst_data.is_null() {
return;
}
for i in 0..len {
unsafe {
*dst_data.add(i) = *src_data.add(len - 1 - i);
}
}
}
match view.elem_type {
V2ElemType::F64 => {
let new_arr = TypedArray::<f64>::with_capacity(view.len);
unsafe {
let src = view.ptr as *const TypedArray<f64>;
copy_reverse_scalar((*src).data, (*new_arr).data, view.len as usize);
(*new_arr).len = view.len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_F64);
p
}
}
V2ElemType::I64 => {
let new_arr = TypedArray::<i64>::with_capacity(view.len);
unsafe {
let src = view.ptr as *const TypedArray<i64>;
copy_reverse_scalar((*src).data, (*new_arr).data, view.len as usize);
(*new_arr).len = view.len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_I64);
p
}
}
V2ElemType::I32 => {
let new_arr = TypedArray::<i32>::with_capacity(view.len);
unsafe {
let src = view.ptr as *const TypedArray<i32>;
copy_reverse_scalar((*src).data, (*new_arr).data, view.len as usize);
(*new_arr).len = view.len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_I32);
p
}
}
V2ElemType::Bool => {
let new_arr = TypedArray::<u8>::with_capacity(view.len);
unsafe {
let src = view.ptr as *const TypedArray<u8>;
copy_reverse_scalar((*src).data, (*new_arr).data, view.len as usize);
(*new_arr).len = view.len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_BOOL);
p
}
}
V2ElemType::I8 => {
let new_arr = TypedArray::<i8>::with_capacity(view.len);
unsafe {
let src = view.ptr as *const TypedArray<i8>;
copy_reverse_scalar((*src).data, (*new_arr).data, view.len as usize);
(*new_arr).len = view.len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_I8);
p
}
}
V2ElemType::U8 => {
let new_arr = TypedArray::<u8>::with_capacity(view.len);
unsafe {
let src = view.ptr as *const TypedArray<u8>;
copy_reverse_scalar((*src).data, (*new_arr).data, view.len as usize);
(*new_arr).len = view.len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_U8);
p
}
}
V2ElemType::I16 => {
let new_arr = TypedArray::<i16>::with_capacity(view.len);
unsafe {
let src = view.ptr as *const TypedArray<i16>;
copy_reverse_scalar((*src).data, (*new_arr).data, view.len as usize);
(*new_arr).len = view.len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_I16);
p
}
}
V2ElemType::U16 => {
let new_arr = TypedArray::<u16>::with_capacity(view.len);
unsafe {
let src = view.ptr as *const TypedArray<u16>;
copy_reverse_scalar((*src).data, (*new_arr).data, view.len as usize);
(*new_arr).len = view.len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_U16);
p
}
}
V2ElemType::U32 => {
let new_arr = TypedArray::<u32>::with_capacity(view.len);
unsafe {
let src = view.ptr as *const TypedArray<u32>;
copy_reverse_scalar((*src).data, (*new_arr).data, view.len as usize);
(*new_arr).len = view.len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_U32);
p
}
}
V2ElemType::F32 => {
let new_arr = TypedArray::<f32>::with_capacity(view.len);
unsafe {
let src = view.ptr as *const TypedArray<f32>;
copy_reverse_scalar((*src).data, (*new_arr).data, view.len as usize);
(*new_arr).len = view.len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_F32);
p
}
}
V2ElemType::Char => {
let new_arr = TypedArray::<char>::with_capacity(view.len);
unsafe {
let src = view.ptr as *const TypedArray<char>;
copy_reverse_scalar((*src).data, (*new_arr).data, view.len as usize);
(*new_arr).len = view.len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_CHAR);
p
}
}
V2ElemType::String => {
let new_arr = TypedArray::<*const StringObj>::with_capacity(view.len);
unsafe {
let src = view.ptr as *const TypedArray<*const StringObj>;
let src_data = (*src).data;
let dst_data = (*new_arr).data;
let len = view.len as usize;
if len > 0 && !src_data.is_null() && !dst_data.is_null() {
for i in 0..len {
let elem = *src_data.add(len - 1 - i);
v2_retain(&(*elem).header);
*dst_data.add(i) = elem;
}
}
(*new_arr).len = view.len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_STRING);
p
}
}
V2ElemType::Decimal => {
let new_arr = TypedArray::<*const DecimalObj>::with_capacity(view.len);
unsafe {
let src = view.ptr as *const TypedArray<*const DecimalObj>;
let src_data = (*src).data;
let dst_data = (*new_arr).data;
let len = view.len as usize;
if len > 0 && !src_data.is_null() && !dst_data.is_null() {
for i in 0..len {
let elem = *src_data.add(len - 1 - i);
v2_retain(&(*elem).header);
*dst_data.add(i) = elem;
}
}
(*new_arr).len = view.len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_DECIMAL);
p
}
}
V2ElemType::TypedObject => {
let new_arr =
TypedArray::<*const TypedObjectStorage>::with_capacity(view.len);
unsafe {
let src = view.ptr as *const TypedArray<*const TypedObjectStorage>;
let src_data = (*src).data;
let dst_data = (*new_arr).data;
let len = view.len as usize;
if len > 0 && !src_data.is_null() && !dst_data.is_null() {
for i in 0..len {
let elem = *src_data.add(len - 1 - i);
v2_retain(&(*elem).header);
*dst_data.add(i) = elem;
}
}
(*new_arr).len = view.len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_TYPED_OBJECT);
p
}
}
}
}
pub fn concat_arrays(
a: &V2TypedArrayView,
b: &V2TypedArrayView,
) -> Result<*mut u8, &'static str> {
if a.elem_type != b.elem_type {
return Err("concat_arrays: element type mismatch");
}
let total_len = a
.len
.checked_add(b.len)
.ok_or("concat_arrays: result length overflow")?;
#[inline]
unsafe fn copy_two_scalar<T: Copy>(
a_data: *const T,
a_len: usize,
b_data: *const T,
b_len: usize,
dst_data: *mut T,
) {
if dst_data.is_null() {
return;
}
if a_len > 0 && !a_data.is_null() {
unsafe { std::ptr::copy_nonoverlapping(a_data, dst_data, a_len) };
}
if b_len > 0 && !b_data.is_null() {
unsafe { std::ptr::copy_nonoverlapping(b_data, dst_data.add(a_len), b_len) };
}
}
let result = match a.elem_type {
V2ElemType::F64 => unsafe {
let new_arr = TypedArray::<f64>::with_capacity(total_len);
let a_arr = a.ptr as *const TypedArray<f64>;
let b_arr = b.ptr as *const TypedArray<f64>;
copy_two_scalar(
(*a_arr).data,
a.len as usize,
(*b_arr).data,
b.len as usize,
(*new_arr).data,
);
(*new_arr).len = total_len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_F64);
p
},
V2ElemType::I64 => unsafe {
let new_arr = TypedArray::<i64>::with_capacity(total_len);
let a_arr = a.ptr as *const TypedArray<i64>;
let b_arr = b.ptr as *const TypedArray<i64>;
copy_two_scalar(
(*a_arr).data,
a.len as usize,
(*b_arr).data,
b.len as usize,
(*new_arr).data,
);
(*new_arr).len = total_len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_I64);
p
},
V2ElemType::I32 => unsafe {
let new_arr = TypedArray::<i32>::with_capacity(total_len);
let a_arr = a.ptr as *const TypedArray<i32>;
let b_arr = b.ptr as *const TypedArray<i32>;
copy_two_scalar(
(*a_arr).data,
a.len as usize,
(*b_arr).data,
b.len as usize,
(*new_arr).data,
);
(*new_arr).len = total_len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_I32);
p
},
V2ElemType::Bool => unsafe {
let new_arr = TypedArray::<u8>::with_capacity(total_len);
let a_arr = a.ptr as *const TypedArray<u8>;
let b_arr = b.ptr as *const TypedArray<u8>;
copy_two_scalar(
(*a_arr).data,
a.len as usize,
(*b_arr).data,
b.len as usize,
(*new_arr).data,
);
(*new_arr).len = total_len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_BOOL);
p
},
V2ElemType::I8 => unsafe {
let new_arr = TypedArray::<i8>::with_capacity(total_len);
let a_arr = a.ptr as *const TypedArray<i8>;
let b_arr = b.ptr as *const TypedArray<i8>;
copy_two_scalar(
(*a_arr).data,
a.len as usize,
(*b_arr).data,
b.len as usize,
(*new_arr).data,
);
(*new_arr).len = total_len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_I8);
p
},
V2ElemType::U8 => unsafe {
let new_arr = TypedArray::<u8>::with_capacity(total_len);
let a_arr = a.ptr as *const TypedArray<u8>;
let b_arr = b.ptr as *const TypedArray<u8>;
copy_two_scalar(
(*a_arr).data,
a.len as usize,
(*b_arr).data,
b.len as usize,
(*new_arr).data,
);
(*new_arr).len = total_len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_U8);
p
},
V2ElemType::I16 => unsafe {
let new_arr = TypedArray::<i16>::with_capacity(total_len);
let a_arr = a.ptr as *const TypedArray<i16>;
let b_arr = b.ptr as *const TypedArray<i16>;
copy_two_scalar(
(*a_arr).data,
a.len as usize,
(*b_arr).data,
b.len as usize,
(*new_arr).data,
);
(*new_arr).len = total_len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_I16);
p
},
V2ElemType::U16 => unsafe {
let new_arr = TypedArray::<u16>::with_capacity(total_len);
let a_arr = a.ptr as *const TypedArray<u16>;
let b_arr = b.ptr as *const TypedArray<u16>;
copy_two_scalar(
(*a_arr).data,
a.len as usize,
(*b_arr).data,
b.len as usize,
(*new_arr).data,
);
(*new_arr).len = total_len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_U16);
p
},
V2ElemType::U32 => unsafe {
let new_arr = TypedArray::<u32>::with_capacity(total_len);
let a_arr = a.ptr as *const TypedArray<u32>;
let b_arr = b.ptr as *const TypedArray<u32>;
copy_two_scalar(
(*a_arr).data,
a.len as usize,
(*b_arr).data,
b.len as usize,
(*new_arr).data,
);
(*new_arr).len = total_len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_U32);
p
},
V2ElemType::F32 => unsafe {
let new_arr = TypedArray::<f32>::with_capacity(total_len);
let a_arr = a.ptr as *const TypedArray<f32>;
let b_arr = b.ptr as *const TypedArray<f32>;
copy_two_scalar(
(*a_arr).data,
a.len as usize,
(*b_arr).data,
b.len as usize,
(*new_arr).data,
);
(*new_arr).len = total_len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_F32);
p
},
V2ElemType::Char => unsafe {
let new_arr = TypedArray::<char>::with_capacity(total_len);
let a_arr = a.ptr as *const TypedArray<char>;
let b_arr = b.ptr as *const TypedArray<char>;
copy_two_scalar(
(*a_arr).data,
a.len as usize,
(*b_arr).data,
b.len as usize,
(*new_arr).data,
);
(*new_arr).len = total_len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_CHAR);
p
},
V2ElemType::String => unsafe {
let new_arr = TypedArray::<*const StringObj>::with_capacity(total_len);
let a_arr = a.ptr as *const TypedArray<*const StringObj>;
let b_arr = b.ptr as *const TypedArray<*const StringObj>;
let dst_data = (*new_arr).data;
let a_data = (*a_arr).data;
let b_data = (*b_arr).data;
if !dst_data.is_null() {
if a.len > 0 && !a_data.is_null() {
for i in 0..(a.len as usize) {
let elem = *a_data.add(i);
v2_retain(&(*elem).header);
*dst_data.add(i) = elem;
}
}
if b.len > 0 && !b_data.is_null() {
let off = a.len as usize;
for i in 0..(b.len as usize) {
let elem = *b_data.add(i);
v2_retain(&(*elem).header);
*dst_data.add(off + i) = elem;
}
}
}
(*new_arr).len = total_len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_STRING);
p
},
V2ElemType::Decimal => unsafe {
let new_arr = TypedArray::<*const DecimalObj>::with_capacity(total_len);
let a_arr = a.ptr as *const TypedArray<*const DecimalObj>;
let b_arr = b.ptr as *const TypedArray<*const DecimalObj>;
let dst_data = (*new_arr).data;
let a_data = (*a_arr).data;
let b_data = (*b_arr).data;
if !dst_data.is_null() {
if a.len > 0 && !a_data.is_null() {
for i in 0..(a.len as usize) {
let elem = *a_data.add(i);
v2_retain(&(*elem).header);
*dst_data.add(i) = elem;
}
}
if b.len > 0 && !b_data.is_null() {
let off = a.len as usize;
for i in 0..(b.len as usize) {
let elem = *b_data.add(i);
v2_retain(&(*elem).header);
*dst_data.add(off + i) = elem;
}
}
}
(*new_arr).len = total_len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_DECIMAL);
p
},
V2ElemType::TypedObject => unsafe {
let new_arr =
TypedArray::<*const TypedObjectStorage>::with_capacity(total_len);
let a_arr = a.ptr as *const TypedArray<*const TypedObjectStorage>;
let b_arr = b.ptr as *const TypedArray<*const TypedObjectStorage>;
let dst_data = (*new_arr).data;
let a_data = (*a_arr).data;
let b_data = (*b_arr).data;
if !dst_data.is_null() {
if a.len > 0 && !a_data.is_null() {
for i in 0..(a.len as usize) {
let elem = *a_data.add(i);
v2_retain(&(*elem).header);
*dst_data.add(i) = elem;
}
}
if b.len > 0 && !b_data.is_null() {
let off = a.len as usize;
for i in 0..(b.len as usize) {
let elem = *b_data.add(i);
v2_retain(&(*elem).header);
*dst_data.add(off + i) = elem;
}
}
}
(*new_arr).len = total_len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_TYPED_OBJECT);
p
},
};
Ok(result)
}
fn copy_range_to_new_array(
view: &V2TypedArrayView,
start: u32,
end: u32,
) -> *mut u8 {
let start = start.min(view.len);
let end = end.min(view.len);
let out_len = end.saturating_sub(start);
let s = start as usize;
let n = out_len as usize;
#[inline]
unsafe fn copy_scalar_range<T: Copy>(
src_data: *const T,
dst_data: *mut T,
start: usize,
len: usize,
) {
if len == 0 || src_data.is_null() || dst_data.is_null() {
return;
}
unsafe {
std::ptr::copy_nonoverlapping(src_data.add(start), dst_data, len);
}
}
match view.elem_type {
V2ElemType::F64 => unsafe {
let new_arr = TypedArray::<f64>::with_capacity(out_len);
let src = view.ptr as *const TypedArray<f64>;
copy_scalar_range((*src).data, (*new_arr).data, s, n);
(*new_arr).len = out_len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_F64);
p
},
V2ElemType::I64 => unsafe {
let new_arr = TypedArray::<i64>::with_capacity(out_len);
let src = view.ptr as *const TypedArray<i64>;
copy_scalar_range((*src).data, (*new_arr).data, s, n);
(*new_arr).len = out_len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_I64);
p
},
V2ElemType::I32 => unsafe {
let new_arr = TypedArray::<i32>::with_capacity(out_len);
let src = view.ptr as *const TypedArray<i32>;
copy_scalar_range((*src).data, (*new_arr).data, s, n);
(*new_arr).len = out_len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_I32);
p
},
V2ElemType::Bool => unsafe {
let new_arr = TypedArray::<u8>::with_capacity(out_len);
let src = view.ptr as *const TypedArray<u8>;
copy_scalar_range((*src).data, (*new_arr).data, s, n);
(*new_arr).len = out_len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_BOOL);
p
},
V2ElemType::I8 => unsafe {
let new_arr = TypedArray::<i8>::with_capacity(out_len);
let src = view.ptr as *const TypedArray<i8>;
copy_scalar_range((*src).data, (*new_arr).data, s, n);
(*new_arr).len = out_len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_I8);
p
},
V2ElemType::U8 => unsafe {
let new_arr = TypedArray::<u8>::with_capacity(out_len);
let src = view.ptr as *const TypedArray<u8>;
copy_scalar_range((*src).data, (*new_arr).data, s, n);
(*new_arr).len = out_len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_U8);
p
},
V2ElemType::I16 => unsafe {
let new_arr = TypedArray::<i16>::with_capacity(out_len);
let src = view.ptr as *const TypedArray<i16>;
copy_scalar_range((*src).data, (*new_arr).data, s, n);
(*new_arr).len = out_len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_I16);
p
},
V2ElemType::U16 => unsafe {
let new_arr = TypedArray::<u16>::with_capacity(out_len);
let src = view.ptr as *const TypedArray<u16>;
copy_scalar_range((*src).data, (*new_arr).data, s, n);
(*new_arr).len = out_len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_U16);
p
},
V2ElemType::U32 => unsafe {
let new_arr = TypedArray::<u32>::with_capacity(out_len);
let src = view.ptr as *const TypedArray<u32>;
copy_scalar_range((*src).data, (*new_arr).data, s, n);
(*new_arr).len = out_len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_U32);
p
},
V2ElemType::F32 => unsafe {
let new_arr = TypedArray::<f32>::with_capacity(out_len);
let src = view.ptr as *const TypedArray<f32>;
copy_scalar_range((*src).data, (*new_arr).data, s, n);
(*new_arr).len = out_len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_F32);
p
},
V2ElemType::Char => unsafe {
let new_arr = TypedArray::<char>::with_capacity(out_len);
let src = view.ptr as *const TypedArray<char>;
copy_scalar_range((*src).data, (*new_arr).data, s, n);
(*new_arr).len = out_len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_CHAR);
p
},
V2ElemType::String => unsafe {
let new_arr = TypedArray::<*const StringObj>::with_capacity(out_len);
let src = view.ptr as *const TypedArray<*const StringObj>;
let src_data = (*src).data;
let dst_data = (*new_arr).data;
if n > 0 && !src_data.is_null() && !dst_data.is_null() {
for i in 0..n {
let elem = *src_data.add(s + i);
v2_retain(&(*elem).header);
*dst_data.add(i) = elem;
}
}
(*new_arr).len = out_len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_STRING);
p
},
V2ElemType::Decimal => unsafe {
let new_arr = TypedArray::<*const DecimalObj>::with_capacity(out_len);
let src = view.ptr as *const TypedArray<*const DecimalObj>;
let src_data = (*src).data;
let dst_data = (*new_arr).data;
if n > 0 && !src_data.is_null() && !dst_data.is_null() {
for i in 0..n {
let elem = *src_data.add(s + i);
v2_retain(&(*elem).header);
*dst_data.add(i) = elem;
}
}
(*new_arr).len = out_len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_DECIMAL);
p
},
V2ElemType::TypedObject => unsafe {
let new_arr =
TypedArray::<*const TypedObjectStorage>::with_capacity(out_len);
let src = view.ptr as *const TypedArray<*const TypedObjectStorage>;
let src_data = (*src).data;
let dst_data = (*new_arr).data;
if n > 0 && !src_data.is_null() && !dst_data.is_null() {
for i in 0..n {
let elem = *src_data.add(s + i);
v2_retain(&(*elem).header);
*dst_data.add(i) = elem;
}
}
(*new_arr).len = out_len;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_TYPED_OBJECT);
p
},
}
}
#[inline]
pub fn slice_array(view: &V2TypedArrayView, start: u32, end: u32) -> *mut u8 {
copy_range_to_new_array(view, start, end)
}
#[inline]
pub fn take_array(view: &V2TypedArrayView, n: u32) -> *mut u8 {
copy_range_to_new_array(view, 0, n)
}
#[inline]
pub fn drop_array_n(view: &V2TypedArrayView, n: u32) -> *mut u8 {
copy_range_to_new_array(view, n, view.len)
}
#[inline]
pub fn native_kind_to_v2_elem_type(kind: NativeKind) -> Option<V2ElemType> {
match kind {
NativeKind::Float64 => Some(V2ElemType::F64),
NativeKind::Int64 => Some(V2ElemType::I64),
NativeKind::Int32 => Some(V2ElemType::I32),
NativeKind::Bool => Some(V2ElemType::Bool),
NativeKind::Int8 => Some(V2ElemType::I8),
NativeKind::UInt8 => Some(V2ElemType::U8),
NativeKind::Int16 => Some(V2ElemType::I16),
NativeKind::UInt16 => Some(V2ElemType::U16),
NativeKind::UInt32 => Some(V2ElemType::U32),
NativeKind::Float32 => Some(V2ElemType::F32),
NativeKind::Char => Some(V2ElemType::Char),
NativeKind::StringV2 => Some(V2ElemType::String),
NativeKind::DecimalV2 => Some(V2ElemType::Decimal),
NativeKind::Ptr(HeapKind::TypedObject) => Some(V2ElemType::TypedObject),
_ => None,
}
}
pub fn allocate_empty_typed_array(elem_type: V2ElemType, capacity: u32) -> *mut u8 {
unsafe {
let p: *mut u8 = match elem_type {
V2ElemType::F64 => TypedArray::<f64>::with_capacity(capacity) as *mut u8,
V2ElemType::I64 => TypedArray::<i64>::with_capacity(capacity) as *mut u8,
V2ElemType::I32 => TypedArray::<i32>::with_capacity(capacity) as *mut u8,
V2ElemType::Bool => TypedArray::<u8>::with_capacity(capacity) as *mut u8,
V2ElemType::I8 => TypedArray::<i8>::with_capacity(capacity) as *mut u8,
V2ElemType::U8 => TypedArray::<u8>::with_capacity(capacity) as *mut u8,
V2ElemType::I16 => TypedArray::<i16>::with_capacity(capacity) as *mut u8,
V2ElemType::U16 => TypedArray::<u16>::with_capacity(capacity) as *mut u8,
V2ElemType::U32 => TypedArray::<u32>::with_capacity(capacity) as *mut u8,
V2ElemType::F32 => TypedArray::<f32>::with_capacity(capacity) as *mut u8,
V2ElemType::Char => TypedArray::<char>::with_capacity(capacity) as *mut u8,
V2ElemType::String => {
TypedArray::<*const StringObj>::with_capacity(capacity) as *mut u8
}
V2ElemType::Decimal => {
TypedArray::<*const DecimalObj>::with_capacity(capacity) as *mut u8
}
V2ElemType::TypedObject => {
TypedArray::<*const TypedObjectStorage>::with_capacity(capacity) as *mut u8
}
};
let stamp_byte: u8 = match elem_type {
V2ElemType::F64 => ELEM_TYPE_F64,
V2ElemType::I64 => ELEM_TYPE_I64,
V2ElemType::I32 => ELEM_TYPE_I32,
V2ElemType::Bool => ELEM_TYPE_BOOL,
V2ElemType::I8 => ELEM_TYPE_I8,
V2ElemType::U8 => ELEM_TYPE_U8,
V2ElemType::I16 => ELEM_TYPE_I16,
V2ElemType::U16 => ELEM_TYPE_U16,
V2ElemType::U32 => ELEM_TYPE_U32,
V2ElemType::F32 => ELEM_TYPE_F32,
V2ElemType::Char => ELEM_TYPE_CHAR,
V2ElemType::String => ELEM_TYPE_STRING,
V2ElemType::Decimal => ELEM_TYPE_DECIMAL,
V2ElemType::TypedObject => ELEM_TYPE_TYPED_OBJECT,
};
stamp_elem_type(p, stamp_byte);
p
}
}
#[inline]
pub fn eq_element(a_bits: u64, b_bits: u64, elem_type: V2ElemType) -> bool {
match elem_type {
V2ElemType::F64 => a_bits == b_bits,
V2ElemType::I64 => a_bits == b_bits,
V2ElemType::I32 => a_bits == b_bits,
V2ElemType::Bool => a_bits == b_bits,
V2ElemType::I8 => a_bits == b_bits,
V2ElemType::U8 => a_bits == b_bits,
V2ElemType::I16 => a_bits == b_bits,
V2ElemType::U16 => a_bits == b_bits,
V2ElemType::U32 => a_bits == b_bits,
V2ElemType::F32 => a_bits == b_bits,
V2ElemType::Char => a_bits == b_bits,
V2ElemType::String => {
if a_bits == 0 || b_bits == 0 {
return a_bits == b_bits;
}
let a_ptr = a_bits as usize as *const StringObj;
let b_ptr = b_bits as usize as *const StringObj;
if a_ptr == b_ptr {
return true;
}
unsafe { StringObj::as_str(a_ptr) == StringObj::as_str(b_ptr) }
}
V2ElemType::Decimal => {
if a_bits == 0 || b_bits == 0 {
return a_bits == b_bits;
}
let a_ptr = a_bits as usize as *const DecimalObj;
let b_ptr = b_bits as usize as *const DecimalObj;
if a_ptr == b_ptr {
return true;
}
unsafe { DecimalObj::value(a_ptr) == DecimalObj::value(b_ptr) }
}
V2ElemType::TypedObject => {
if a_bits == 0 || b_bits == 0 {
return a_bits == b_bits;
}
let a_ptr = a_bits as usize as *const TypedObjectStorage;
let b_ptr = b_bits as usize as *const TypedObjectStorage;
if a_ptr == b_ptr {
return true;
}
unsafe { typed_object_deep_eq(&*a_ptr, &*b_ptr) }
}
}
}
unsafe fn typed_object_deep_eq(
a: &TypedObjectStorage,
b: &TypedObjectStorage,
) -> bool {
if a.schema_id != b.schema_id {
return false;
}
if a.slots.len() != b.slots.len() {
return false;
}
if a.field_kinds.len() != b.field_kinds.len() {
return false;
}
for (k1, k2) in a.field_kinds.iter().zip(b.field_kinds.iter()) {
if k1 != k2 {
return false;
}
}
for i in 0..a.slots.len() {
let bits_a = a.slots[i].raw();
let bits_b = b.slots[i].raw();
let kind = a.field_kinds[i];
let field_elem = match kind {
NativeKind::Float64 => Some(V2ElemType::F64),
NativeKind::Int64 => Some(V2ElemType::I64),
NativeKind::Int32 => Some(V2ElemType::I32),
NativeKind::Int16 => Some(V2ElemType::I16),
NativeKind::Int8 => Some(V2ElemType::I8),
NativeKind::UInt8 => Some(V2ElemType::U8),
NativeKind::UInt16 => Some(V2ElemType::U16),
NativeKind::UInt32 => Some(V2ElemType::U32),
NativeKind::Float32 => Some(V2ElemType::F32),
NativeKind::Char => Some(V2ElemType::Char),
NativeKind::Bool => Some(V2ElemType::Bool),
NativeKind::StringV2 => Some(V2ElemType::String),
NativeKind::DecimalV2 => Some(V2ElemType::Decimal),
NativeKind::Ptr(HeapKind::TypedObject) => Some(V2ElemType::TypedObject),
NativeKind::Null => {
if bits_a != bits_b {
return false;
}
continue;
}
NativeKind::String => {
if bits_a == bits_b {
continue;
}
if bits_a == 0 || bits_b == 0 {
return false;
}
let s_a = unsafe { &*(bits_a as usize as *const String) };
let s_b = unsafe { &*(bits_b as usize as *const String) };
if s_a != s_b {
return false;
}
continue;
}
_ => {
if bits_a != bits_b {
return false;
}
continue;
}
};
if let Some(et) = field_elem {
if !eq_element(bits_a, bits_b, et) {
return false;
}
}
}
true
}
#[inline]
pub fn position_of(view: &V2TypedArrayView, needle_bits: u64) -> Option<u32> {
let n = view.len;
if n == 0 {
return None;
}
macro_rules! scan_scalar {
($t:ty, $to_bits:expr) => {{
let arr = view.ptr as *const TypedArray<$t>;
for i in 0..n {
let v = unsafe { TypedArray::<$t>::get_unchecked(arr, i) };
if $to_bits(v) == needle_bits {
return Some(i);
}
}
None
}};
}
match view.elem_type {
V2ElemType::F64 => scan_scalar!(f64, |v: f64| v.to_bits()),
V2ElemType::I64 => scan_scalar!(i64, |v: i64| v as u64),
V2ElemType::I32 => scan_scalar!(i32, |v: i32| (v as i64) as u64),
V2ElemType::Bool => scan_scalar!(u8, |v: u8| (v != 0) as u64),
V2ElemType::I8 => scan_scalar!(i8, |v: i8| (v as i64) as u64),
V2ElemType::U8 => scan_scalar!(u8, |v: u8| v as u64),
V2ElemType::I16 => scan_scalar!(i16, |v: i16| (v as i64) as u64),
V2ElemType::U16 => scan_scalar!(u16, |v: u16| v as u64),
V2ElemType::U32 => scan_scalar!(u32, |v: u32| v as u64),
V2ElemType::F32 => scan_scalar!(f32, |v: f32| v.to_bits() as u64),
V2ElemType::Char => scan_scalar!(char, |v: char| v as u32 as u64),
V2ElemType::String => unsafe {
let arr = view.ptr as *const TypedArray<*const StringObj>;
for i in 0..n {
let elem_ptr = TypedArray::<*const StringObj>::get_unchecked(arr, i);
if eq_element(elem_ptr as u64, needle_bits, V2ElemType::String) {
return Some(i);
}
}
None
},
V2ElemType::Decimal => unsafe {
let arr = view.ptr as *const TypedArray<*const DecimalObj>;
for i in 0..n {
let elem_ptr = TypedArray::<*const DecimalObj>::get_unchecked(arr, i);
if eq_element(elem_ptr as u64, needle_bits, V2ElemType::Decimal) {
return Some(i);
}
}
None
},
V2ElemType::TypedObject => unsafe {
let arr = view.ptr as *const TypedArray<*const TypedObjectStorage>;
for i in 0..n {
let elem_ptr = TypedArray::<*const TypedObjectStorage>::get_unchecked(arr, i);
if eq_element(elem_ptr as u64, needle_bits, V2ElemType::TypedObject) {
return Some(i);
}
}
None
}
}
}
#[inline]
pub fn cmp_element_natural(
view: &V2TypedArrayView,
bits_a: u64,
bits_b: u64,
) -> Option<std::cmp::Ordering> {
use std::cmp::Ordering;
match view.elem_type {
V2ElemType::F64 => Some(f64::from_bits(bits_a).total_cmp(&f64::from_bits(bits_b))),
V2ElemType::F32 => Some(
f32::from_bits(bits_a as u32).total_cmp(&f32::from_bits(bits_b as u32)),
),
V2ElemType::I64 => Some((bits_a as i64).cmp(&(bits_b as i64))),
V2ElemType::I32 => Some((bits_a as u32 as i32).cmp(&(bits_b as u32 as i32))),
V2ElemType::I16 => Some((bits_a as u16 as i16).cmp(&(bits_b as u16 as i16))),
V2ElemType::I8 => Some((bits_a as u8 as i8).cmp(&(bits_b as u8 as i8))),
V2ElemType::U32 => Some((bits_a as u32).cmp(&(bits_b as u32))),
V2ElemType::U16 => Some((bits_a as u16).cmp(&(bits_b as u16))),
V2ElemType::U8 => Some((bits_a as u8).cmp(&(bits_b as u8))),
V2ElemType::Bool => {
let a = bits_a != 0;
let b = bits_b != 0;
Some(a.cmp(&b))
}
V2ElemType::Char => {
Some((bits_a as u32).cmp(&(bits_b as u32)))
}
V2ElemType::String => unsafe {
let a_ptr = bits_a as usize as *const StringObj;
let b_ptr = bits_b as usize as *const StringObj;
if a_ptr.is_null() || b_ptr.is_null() {
return None;
}
let a = StringObj::as_str(a_ptr);
let b = StringObj::as_str(b_ptr);
Some(a.cmp(b))
},
V2ElemType::Decimal => unsafe {
let a_ptr = bits_a as usize as *const DecimalObj;
let b_ptr = bits_b as usize as *const DecimalObj;
if a_ptr.is_null() || b_ptr.is_null() {
return None;
}
let a = DecimalObj::value(a_ptr);
let b = DecimalObj::value(b_ptr);
Some(a.cmp(&b))
},
V2ElemType::TypedObject => {
None
}
_ => {
#[allow(unreachable_patterns)]
None
}
}
}
pub fn permute_array(view: &V2TypedArrayView, indices: &[u32]) -> *mut u8 {
let out_len = indices.len() as u32;
#[inline]
unsafe fn permute_scalar<T: Copy>(
src_data: *const T,
dst_data: *mut T,
src_len: u32,
indices: &[u32],
) -> u32 {
if src_data.is_null() || dst_data.is_null() {
return 0;
}
let mut w: u32 = 0;
for &idx in indices {
if idx >= src_len {
continue;
}
unsafe {
let v = *src_data.add(idx as usize);
*dst_data.add(w as usize) = v;
}
w += 1;
}
w
}
match view.elem_type {
V2ElemType::F64 => unsafe {
let new_arr = TypedArray::<f64>::with_capacity(out_len);
let src = view.ptr as *const TypedArray<f64>;
let written = permute_scalar((*src).data, (*new_arr).data, view.len, indices);
(*new_arr).len = written;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_F64);
p
},
V2ElemType::I64 => unsafe {
let new_arr = TypedArray::<i64>::with_capacity(out_len);
let src = view.ptr as *const TypedArray<i64>;
let written = permute_scalar((*src).data, (*new_arr).data, view.len, indices);
(*new_arr).len = written;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_I64);
p
},
V2ElemType::I32 => unsafe {
let new_arr = TypedArray::<i32>::with_capacity(out_len);
let src = view.ptr as *const TypedArray<i32>;
let written = permute_scalar((*src).data, (*new_arr).data, view.len, indices);
(*new_arr).len = written;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_I32);
p
},
V2ElemType::Bool => unsafe {
let new_arr = TypedArray::<u8>::with_capacity(out_len);
let src = view.ptr as *const TypedArray<u8>;
let written = permute_scalar((*src).data, (*new_arr).data, view.len, indices);
(*new_arr).len = written;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_BOOL);
p
},
V2ElemType::I8 => unsafe {
let new_arr = TypedArray::<i8>::with_capacity(out_len);
let src = view.ptr as *const TypedArray<i8>;
let written = permute_scalar((*src).data, (*new_arr).data, view.len, indices);
(*new_arr).len = written;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_I8);
p
},
V2ElemType::U8 => unsafe {
let new_arr = TypedArray::<u8>::with_capacity(out_len);
let src = view.ptr as *const TypedArray<u8>;
let written = permute_scalar((*src).data, (*new_arr).data, view.len, indices);
(*new_arr).len = written;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_U8);
p
},
V2ElemType::I16 => unsafe {
let new_arr = TypedArray::<i16>::with_capacity(out_len);
let src = view.ptr as *const TypedArray<i16>;
let written = permute_scalar((*src).data, (*new_arr).data, view.len, indices);
(*new_arr).len = written;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_I16);
p
},
V2ElemType::U16 => unsafe {
let new_arr = TypedArray::<u16>::with_capacity(out_len);
let src = view.ptr as *const TypedArray<u16>;
let written = permute_scalar((*src).data, (*new_arr).data, view.len, indices);
(*new_arr).len = written;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_U16);
p
},
V2ElemType::U32 => unsafe {
let new_arr = TypedArray::<u32>::with_capacity(out_len);
let src = view.ptr as *const TypedArray<u32>;
let written = permute_scalar((*src).data, (*new_arr).data, view.len, indices);
(*new_arr).len = written;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_U32);
p
},
V2ElemType::F32 => unsafe {
let new_arr = TypedArray::<f32>::with_capacity(out_len);
let src = view.ptr as *const TypedArray<f32>;
let written = permute_scalar((*src).data, (*new_arr).data, view.len, indices);
(*new_arr).len = written;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_F32);
p
},
V2ElemType::Char => unsafe {
let new_arr = TypedArray::<char>::with_capacity(out_len);
let src = view.ptr as *const TypedArray<char>;
let written = permute_scalar((*src).data, (*new_arr).data, view.len, indices);
(*new_arr).len = written;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_CHAR);
p
},
V2ElemType::String => unsafe {
let new_arr = TypedArray::<*const StringObj>::with_capacity(out_len);
let src = view.ptr as *const TypedArray<*const StringObj>;
let src_data = (*src).data;
let dst_data = (*new_arr).data;
let mut w: u32 = 0;
if !src_data.is_null() && !dst_data.is_null() {
for &idx in indices {
if idx >= view.len {
continue;
}
let elem = *src_data.add(idx as usize);
v2_retain(&(*elem).header);
*dst_data.add(w as usize) = elem;
w += 1;
}
}
(*new_arr).len = w;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_STRING);
p
},
V2ElemType::Decimal => unsafe {
let new_arr = TypedArray::<*const DecimalObj>::with_capacity(out_len);
let src = view.ptr as *const TypedArray<*const DecimalObj>;
let src_data = (*src).data;
let dst_data = (*new_arr).data;
let mut w: u32 = 0;
if !src_data.is_null() && !dst_data.is_null() {
for &idx in indices {
if idx >= view.len {
continue;
}
let elem = *src_data.add(idx as usize);
v2_retain(&(*elem).header);
*dst_data.add(w as usize) = elem;
w += 1;
}
}
(*new_arr).len = w;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_DECIMAL);
p
},
V2ElemType::TypedObject => unsafe {
let new_arr =
TypedArray::<*const TypedObjectStorage>::with_capacity(out_len);
let src = view.ptr as *const TypedArray<*const TypedObjectStorage>;
let src_data = (*src).data;
let dst_data = (*new_arr).data;
let mut w: u32 = 0;
if !src_data.is_null() && !dst_data.is_null() {
for &idx in indices {
if idx >= view.len {
continue;
}
let elem = *src_data.add(idx as usize);
v2_retain(&(*elem).header);
*dst_data.add(w as usize) = elem;
w += 1;
}
}
(*new_arr).len = w;
let p = new_arr as *mut u8;
stamp_elem_type(p, ELEM_TYPE_TYPED_OBJECT);
p
},
}
}
#[inline]
pub fn contains_element(view: &V2TypedArrayView, needle_bits: u64) -> bool {
position_of(view, needle_bits).is_some()
}
#[cfg(test)]
mod tests {
use super::*;
#[inline]
fn ptr_pair(ptr: *mut u8) -> (u64, NativeKind) {
(ptr as usize as u64, NativeKind::Ptr(HeapKind::TypedArray))
}
#[test]
fn test_stamp_and_read_elem_type_f64() {
let arr = TypedArray::<f64>::with_capacity(0);
unsafe {
stamp_elem_type(arr as *mut u8, ELEM_TYPE_F64);
let byte = read_elem_type_byte(arr as *const u8);
assert_eq!(byte, ELEM_TYPE_F64);
TypedArray::drop_array(arr);
}
}
#[test]
fn test_as_v2_typed_array_recognizes_stamped_f64() {
let arr = TypedArray::<f64>::with_capacity(4);
unsafe {
TypedArray::push(arr, 1.5);
TypedArray::push(arr, 2.5);
stamp_elem_type(arr as *mut u8, ELEM_TYPE_F64);
}
let (bits, kind) = ptr_pair(arr as *mut u8);
let view = as_v2_typed_array(bits, kind).expect("should recognize v2 typed array");
assert_eq!(view.elem_type, V2ElemType::F64);
assert_eq!(view.len, 2);
unsafe {
TypedArray::drop_array(arr);
}
}
#[test]
fn test_read_element_i64_indices() {
let arr = TypedArray::<i64>::from_slice(&[10, 20, 30]);
unsafe {
stamp_elem_type(arr as *mut u8, ELEM_TYPE_I64);
}
let (bits, kind) = ptr_pair(arr as *mut u8);
let view = as_v2_typed_array(bits, kind).unwrap();
assert_eq!(read_element(&view, 0), Some((10u64, NativeKind::Int64)));
assert_eq!(read_element(&view, 1), Some((20u64, NativeKind::Int64)));
assert_eq!(read_element(&view, 2), Some((30u64, NativeKind::Int64)));
assert!(read_element(&view, 3).is_none());
unsafe {
TypedArray::drop_array(arr);
}
}
#[test]
fn test_clone_array_i64() {
let arr = TypedArray::<i64>::from_slice(&[100, 200, 300]);
unsafe {
stamp_elem_type(arr as *mut u8, ELEM_TYPE_I64);
}
let (bits, kind) = ptr_pair(arr as *mut u8);
let view = as_v2_typed_array(bits, kind).unwrap();
let cloned_ptr = clone_array(&view);
let (cb, ck) = ptr_pair(cloned_ptr);
let cloned_view = as_v2_typed_array(cb, ck).expect("clone should be detectable");
assert_eq!(cloned_view.elem_type, V2ElemType::I64);
assert_eq!(cloned_view.len, 3);
assert_eq!(read_element(&cloned_view, 0), Some((100u64, NativeKind::Int64)));
unsafe {
TypedArray::<i64>::drop_array(cloned_ptr as *mut TypedArray<i64>);
TypedArray::drop_array(arr);
}
}
#[test]
fn test_stamp_and_read_elem_type_f32_char() {
let arr_f32 = TypedArray::<f32>::with_capacity(0);
let arr_char = TypedArray::<char>::with_capacity(0);
unsafe {
stamp_elem_type(arr_f32 as *mut u8, ELEM_TYPE_F32);
stamp_elem_type(arr_char as *mut u8, ELEM_TYPE_CHAR);
assert_eq!(read_elem_type_byte(arr_f32 as *const u8), ELEM_TYPE_F32);
assert_eq!(read_elem_type_byte(arr_char as *const u8), ELEM_TYPE_CHAR);
TypedArray::drop_array(arr_f32);
TypedArray::drop_array(arr_char);
}
}
#[test]
fn test_as_v2_typed_array_recognizes_stamped_f32() {
let arr = TypedArray::<f32>::with_capacity(4);
unsafe {
TypedArray::push(arr, 1.5_f32);
TypedArray::push(arr, 2.5_f32);
stamp_elem_type(arr as *mut u8, ELEM_TYPE_F32);
}
let (bits, kind) = ptr_pair(arr as *mut u8);
let view = as_v2_typed_array(bits, kind).expect("should recognize v2 typed array");
assert_eq!(view.elem_type, V2ElemType::F32);
assert_eq!(view.len, 2);
unsafe { TypedArray::drop_array(arr); }
}
#[test]
fn test_as_v2_typed_array_recognizes_stamped_char() {
let arr = TypedArray::<char>::with_capacity(4);
unsafe {
TypedArray::push(arr, 'A');
TypedArray::push(arr, '☃');
stamp_elem_type(arr as *mut u8, ELEM_TYPE_CHAR);
}
let (bits, kind) = ptr_pair(arr as *mut u8);
let view = as_v2_typed_array(bits, kind).expect("should recognize v2 typed array");
assert_eq!(view.elem_type, V2ElemType::Char);
assert_eq!(view.len, 2);
unsafe { TypedArray::drop_array(arr); }
}
#[test]
fn test_read_element_f32() {
let arr = TypedArray::<f32>::from_slice(&[1.5_f32, 2.25_f32, 3.0_f32]);
unsafe { stamp_elem_type(arr as *mut u8, ELEM_TYPE_F32); }
let (bits, kind) = ptr_pair(arr as *mut u8);
let view = as_v2_typed_array(bits, kind).unwrap();
let r0 = read_element(&view, 0).unwrap();
let r1 = read_element(&view, 1).unwrap();
let r2 = read_element(&view, 2).unwrap();
assert_eq!(r0.1, NativeKind::Float32);
assert_eq!(f32::from_bits(r0.0 as u32), 1.5_f32);
assert_eq!(f32::from_bits(r1.0 as u32), 2.25_f32);
assert_eq!(f32::from_bits(r2.0 as u32), 3.0_f32);
assert!(read_element(&view, 3).is_none());
unsafe { TypedArray::drop_array(arr); }
}
#[test]
fn test_read_element_char() {
let arr = TypedArray::<char>::from_slice(&['h', 'i', '!']);
unsafe { stamp_elem_type(arr as *mut u8, ELEM_TYPE_CHAR); }
let (bits, kind) = ptr_pair(arr as *mut u8);
let view = as_v2_typed_array(bits, kind).unwrap();
for (i, expected) in ['h', 'i', '!'].iter().enumerate() {
let (b, k) = read_element(&view, i as u32).unwrap();
assert_eq!(k, NativeKind::Char);
assert_eq!(char::from_u32(b as u32).unwrap(), *expected);
}
assert!(read_element(&view, 3).is_none());
unsafe { TypedArray::drop_array(arr); }
}
#[test]
fn test_push_element_f32() {
let arr = TypedArray::<f32>::with_capacity(4);
unsafe { stamp_elem_type(arr as *mut u8, ELEM_TYPE_F32); }
let (bits, kind) = ptr_pair(arr as *mut u8);
let view = as_v2_typed_array(bits, kind).unwrap();
push_element(&view, (1.5_f32).to_bits() as u64, NativeKind::Float32).unwrap();
let view = as_v2_typed_array(bits, kind).unwrap();
let (b, k) = read_element(&view, 0).unwrap();
assert_eq!(k, NativeKind::Float32);
assert_eq!(f32::from_bits(b as u32), 1.5_f32);
unsafe { TypedArray::drop_array(arr); }
}
#[test]
fn test_push_element_char() {
let arr = TypedArray::<char>::with_capacity(4);
unsafe { stamp_elem_type(arr as *mut u8, ELEM_TYPE_CHAR); }
let (bits, kind) = ptr_pair(arr as *mut u8);
let view = as_v2_typed_array(bits, kind).unwrap();
push_element(&view, 'Z' as u32 as u64, NativeKind::Char).unwrap();
let view = as_v2_typed_array(bits, kind).unwrap();
let (b, _) = read_element(&view, 0).unwrap();
assert_eq!(char::from_u32(b as u32).unwrap(), 'Z');
unsafe { TypedArray::drop_array(arr); }
}
#[test]
fn test_clone_array_f32() {
let arr = TypedArray::<f32>::from_slice(&[1.0_f32, 2.0_f32, 3.0_f32]);
unsafe { stamp_elem_type(arr as *mut u8, ELEM_TYPE_F32); }
let (bits, kind) = ptr_pair(arr as *mut u8);
let view = as_v2_typed_array(bits, kind).unwrap();
let cloned = clone_array(&view);
let (cb, ck) = ptr_pair(cloned);
let cv = as_v2_typed_array(cb, ck).unwrap();
assert_eq!(cv.elem_type, V2ElemType::F32);
assert_eq!(cv.len, 3);
unsafe {
TypedArray::<f32>::drop_array(cloned as *mut TypedArray<f32>);
TypedArray::drop_array(arr);
}
}
#[test]
fn test_clone_array_char() {
let arr = TypedArray::<char>::from_slice(&['a', 'b', 'c']);
unsafe { stamp_elem_type(arr as *mut u8, ELEM_TYPE_CHAR); }
let (bits, kind) = ptr_pair(arr as *mut u8);
let view = as_v2_typed_array(bits, kind).unwrap();
let cloned = clone_array(&view);
let (cb, ck) = ptr_pair(cloned);
let cv = as_v2_typed_array(cb, ck).unwrap();
assert_eq!(cv.elem_type, V2ElemType::Char);
assert_eq!(cv.len, 3);
unsafe {
TypedArray::<char>::drop_array(cloned as *mut TypedArray<char>);
TypedArray::drop_array(arr);
}
}
#[test]
fn test_non_pointer_value_returns_none() {
assert!(as_v2_typed_array(42u64, NativeKind::Int64).is_none());
assert!(as_v2_typed_array(3.14_f64.to_bits(), NativeKind::Float64).is_none());
assert!(as_v2_typed_array(1u64, NativeKind::Bool).is_none());
assert!(as_v2_typed_array(u64::MAX, NativeKind::UInt64).is_none());
assert!(as_v2_typed_array(1000u64, NativeKind::UInt64).is_none());
assert!(as_v2_typed_array(0u64, NativeKind::UInt64).is_none());
assert!(
as_v2_typed_array(0u64, NativeKind::Ptr(HeapKind::TypedArray)).is_none()
);
}
#[test]
fn test_kind_track_discriminates_array_carrier_from_scalar() {
let arr = TypedArray::<i64>::from_slice(&[100, 200]);
unsafe { stamp_elem_type(arr as *mut u8, ELEM_TYPE_I64) };
let bits = arr as usize as u64;
let view = as_v2_typed_array(bits, NativeKind::Ptr(HeapKind::TypedArray))
.expect("array carrier kind must detect the typed array");
assert_eq!(view.elem_type, V2ElemType::I64);
assert_eq!(view.len, 2);
assert!(
as_v2_typed_array(bits, NativeKind::UInt64).is_none(),
"a scalar-u64 kind must NOT be treated as the array carrier"
);
unsafe { TypedArray::<i64>::drop_array(arr) };
}
#[test]
fn test_stamp_and_read_elem_type_string_decimal() {
let arr_string = TypedArray::<*const StringObj>::with_capacity(0);
let arr_decimal = TypedArray::<*const DecimalObj>::with_capacity(0);
unsafe {
stamp_elem_type(arr_string as *mut u8, ELEM_TYPE_STRING);
stamp_elem_type(arr_decimal as *mut u8, ELEM_TYPE_DECIMAL);
assert_eq!(read_elem_type_byte(arr_string as *const u8), ELEM_TYPE_STRING);
assert_eq!(read_elem_type_byte(arr_decimal as *const u8), ELEM_TYPE_DECIMAL);
TypedArray::<*const StringObj>::drop_array_heap(arr_string);
TypedArray::<*const DecimalObj>::drop_array_heap(arr_decimal);
}
}
#[test]
fn test_as_v2_typed_array_recognizes_stamped_string() {
let arr = TypedArray::<*const StringObj>::with_capacity(4);
unsafe {
let s = StringObj::new("hello");
TypedArray::push(arr, s as *const StringObj);
stamp_elem_type(arr as *mut u8, ELEM_TYPE_STRING);
}
let (bits, kind) = ptr_pair(arr as *mut u8);
let view = as_v2_typed_array(bits, kind).expect("should recognize v2 typed array");
assert_eq!(view.elem_type, V2ElemType::String);
assert_eq!(view.len, 1);
unsafe { TypedArray::<*const StringObj>::drop_array_heap(arr); }
}
#[test]
fn test_as_v2_typed_array_recognizes_stamped_decimal() {
use rust_decimal::Decimal;
use rust_decimal::prelude::FromPrimitive;
let arr = TypedArray::<*const DecimalObj>::with_capacity(4);
unsafe {
let d = DecimalObj::new(Decimal::from_f64(3.14).unwrap());
TypedArray::push(arr, d as *const DecimalObj);
stamp_elem_type(arr as *mut u8, ELEM_TYPE_DECIMAL);
}
let (bits, kind) = ptr_pair(arr as *mut u8);
let view = as_v2_typed_array(bits, kind).expect("should recognize v2 typed array");
assert_eq!(view.elem_type, V2ElemType::Decimal);
assert_eq!(view.len, 1);
unsafe { TypedArray::<*const DecimalObj>::drop_array_heap(arr); }
}
#[test]
fn test_read_element_string_retains_share() {
use shape_value::v2::refcount::v2_get_refcount;
unsafe {
let arr = TypedArray::<*const StringObj>::with_capacity(4);
let s = StringObj::new("greetings");
assert_eq!(v2_get_refcount(&(*s).header), 1);
TypedArray::push(arr, s as *const StringObj);
stamp_elem_type(arr as *mut u8, ELEM_TYPE_STRING);
let (bits, kind) = ptr_pair(arr as *mut u8);
let view = as_v2_typed_array(bits, kind).unwrap();
let (read_bits, read_kind) = read_element(&view, 0).unwrap();
assert_eq!(read_kind, NativeKind::StringV2);
assert_eq!(read_bits, s as u64);
assert_eq!(v2_get_refcount(&(*s).header), 2);
<StringObj as HeapElement>::release_elem(s);
assert_eq!(v2_get_refcount(&(*s).header), 1);
TypedArray::<*const StringObj>::drop_array_heap(arr);
}
}
#[test]
fn test_read_element_decimal_retains_share() {
use rust_decimal::Decimal;
use rust_decimal::prelude::FromPrimitive;
use shape_value::v2::refcount::v2_get_refcount;
unsafe {
let arr = TypedArray::<*const DecimalObj>::with_capacity(4);
let d = DecimalObj::new(Decimal::from_f64(2.5).unwrap());
assert_eq!(v2_get_refcount(&(*d).header), 1);
TypedArray::push(arr, d as *const DecimalObj);
stamp_elem_type(arr as *mut u8, ELEM_TYPE_DECIMAL);
let (bits, kind) = ptr_pair(arr as *mut u8);
let view = as_v2_typed_array(bits, kind).unwrap();
let (read_bits, read_kind) = read_element(&view, 0).unwrap();
assert_eq!(read_kind, NativeKind::DecimalV2);
assert_eq!(read_bits, d as u64);
assert_eq!(v2_get_refcount(&(*d).header), 2);
<DecimalObj as HeapElement>::release_elem(d);
assert_eq!(v2_get_refcount(&(*d).header), 1);
TypedArray::<*const DecimalObj>::drop_array_heap(arr);
}
}
#[test]
fn test_push_element_string_kind_mismatch_refused() {
let arr = TypedArray::<*const StringObj>::with_capacity(4);
unsafe { stamp_elem_type(arr as *mut u8, ELEM_TYPE_STRING); }
let (bits, kind) = ptr_pair(arr as *mut u8);
let view = as_v2_typed_array(bits, kind).unwrap();
let result = push_element(&view, 0xDEAD_BEEF, NativeKind::String);
assert!(result.is_err());
let err = result.unwrap_err();
assert!(err.contains("StringV2"), "expected error to cite StringV2, got: {}", err);
unsafe { TypedArray::<*const StringObj>::drop_array_heap(arr); }
}
#[test]
fn test_clone_array_string_retains_each_element() {
use shape_value::v2::refcount::v2_get_refcount;
unsafe {
let arr = TypedArray::<*const StringObj>::with_capacity(2);
let s1 = StringObj::new("foo");
let s2 = StringObj::new("bar");
TypedArray::push(arr, s1 as *const StringObj);
TypedArray::push(arr, s2 as *const StringObj);
stamp_elem_type(arr as *mut u8, ELEM_TYPE_STRING);
assert_eq!(v2_get_refcount(&(*s1).header), 1);
assert_eq!(v2_get_refcount(&(*s2).header), 1);
let (bits, kind) = ptr_pair(arr as *mut u8);
let view = as_v2_typed_array(bits, kind).unwrap();
let cloned = clone_array(&view);
assert_eq!(v2_get_refcount(&(*s1).header), 2);
assert_eq!(v2_get_refcount(&(*s2).header), 2);
let (cb, ck) = ptr_pair(cloned);
let cv = as_v2_typed_array(cb, ck).unwrap();
assert_eq!(cv.elem_type, V2ElemType::String);
assert_eq!(cv.len, 2);
TypedArray::<*const StringObj>::drop_array_heap(cloned as *mut TypedArray<*const StringObj>);
assert_eq!(v2_get_refcount(&(*s1).header), 1);
assert_eq!(v2_get_refcount(&(*s2).header), 1);
TypedArray::<*const StringObj>::drop_array_heap(arr);
}
}
#[test]
fn test_pop_element_string_transfers_share() {
use shape_value::v2::refcount::v2_get_refcount;
unsafe {
let arr = TypedArray::<*const StringObj>::with_capacity(2);
let s = StringObj::new("popme");
TypedArray::push(arr, s as *const StringObj);
stamp_elem_type(arr as *mut u8, ELEM_TYPE_STRING);
assert_eq!(v2_get_refcount(&(*s).header), 1);
let (bits, kind) = ptr_pair(arr as *mut u8);
let view = as_v2_typed_array(bits, kind).unwrap();
let (popped_bits, popped_kind) = pop_element(&view).unwrap();
assert_eq!(popped_kind, NativeKind::StringV2);
assert_eq!(popped_bits, s as u64);
assert_eq!(v2_get_refcount(&(*s).header), 1);
<StringObj as HeapElement>::release_elem(s);
TypedArray::<*const StringObj>::drop_array_heap(arr);
}
}
#[test]
fn test_reverse_array_i64() {
unsafe {
let arr = TypedArray::<i64>::from_slice(&[1, 2, 3, 4, 5]);
stamp_elem_type(arr as *mut u8, ELEM_TYPE_I64);
let (bits, kind) = ptr_pair(arr as *mut u8);
let view = as_v2_typed_array(bits, kind).unwrap();
let new_ptr = reverse_array(&view);
let new_view =
as_v2_typed_array(new_ptr as u64, NativeKind::Ptr(HeapKind::TypedArray))
.unwrap();
assert_eq!(new_view.elem_type, V2ElemType::I64);
assert_eq!(new_view.len, 5);
let new_arr = new_ptr as *const TypedArray<i64>;
let data = (*new_arr).data;
assert_eq!(*data.add(0), 5);
assert_eq!(*data.add(1), 4);
assert_eq!(*data.add(2), 3);
assert_eq!(*data.add(3), 2);
assert_eq!(*data.add(4), 1);
TypedArray::<i64>::drop_array(arr);
TypedArray::<i64>::drop_array(new_ptr as *mut TypedArray<i64>);
}
}
#[test]
fn test_reverse_array_empty() {
unsafe {
let arr = TypedArray::<i64>::with_capacity(0);
stamp_elem_type(arr as *mut u8, ELEM_TYPE_I64);
let (bits, kind) = ptr_pair(arr as *mut u8);
let view = as_v2_typed_array(bits, kind).unwrap();
let new_ptr = reverse_array(&view);
let new_view =
as_v2_typed_array(new_ptr as u64, NativeKind::Ptr(HeapKind::TypedArray))
.unwrap();
assert_eq!(new_view.len, 0);
TypedArray::<i64>::drop_array(arr);
TypedArray::<i64>::drop_array(new_ptr as *mut TypedArray<i64>);
}
}
#[test]
fn test_reverse_array_string_retains_each_element() {
use shape_value::v2::refcount::v2_get_refcount;
unsafe {
let s1 = StringObj::new("a");
let s2 = StringObj::new("b");
let arr = TypedArray::<*const StringObj>::with_capacity(2);
TypedArray::push(arr, s1 as *const StringObj);
TypedArray::push(arr, s2 as *const StringObj);
stamp_elem_type(arr as *mut u8, ELEM_TYPE_STRING);
assert_eq!(v2_get_refcount(&(*s1).header), 1);
assert_eq!(v2_get_refcount(&(*s2).header), 1);
let (bits, kind) = ptr_pair(arr as *mut u8);
let view = as_v2_typed_array(bits, kind).unwrap();
let new_ptr = reverse_array(&view);
assert_eq!(v2_get_refcount(&(*s1).header), 2);
assert_eq!(v2_get_refcount(&(*s2).header), 2);
let new_view =
as_v2_typed_array(new_ptr as u64, NativeKind::Ptr(HeapKind::TypedArray))
.unwrap();
assert_eq!(new_view.elem_type, V2ElemType::String);
assert_eq!(new_view.len, 2);
let new_arr = new_ptr as *const TypedArray<*const StringObj>;
let data = (*new_arr).data;
assert_eq!(*data.add(0), s2 as *const StringObj);
assert_eq!(*data.add(1), s1 as *const StringObj);
TypedArray::<*const StringObj>::drop_array_heap(arr);
TypedArray::<*const StringObj>::drop_array_heap(
new_ptr as *mut TypedArray<*const StringObj>,
);
}
}
#[test]
fn test_concat_arrays_i64() {
unsafe {
let a = TypedArray::<i64>::from_slice(&[1, 2]);
let b = TypedArray::<i64>::from_slice(&[3, 4, 5]);
stamp_elem_type(a as *mut u8, ELEM_TYPE_I64);
stamp_elem_type(b as *mut u8, ELEM_TYPE_I64);
let view_a = as_v2_typed_array(a as u64, NativeKind::Ptr(HeapKind::TypedArray))
.unwrap();
let view_b = as_v2_typed_array(b as u64, NativeKind::Ptr(HeapKind::TypedArray))
.unwrap();
let new_ptr = concat_arrays(&view_a, &view_b).unwrap();
let new_view =
as_v2_typed_array(new_ptr as u64, NativeKind::Ptr(HeapKind::TypedArray))
.unwrap();
assert_eq!(new_view.elem_type, V2ElemType::I64);
assert_eq!(new_view.len, 5);
let new_arr = new_ptr as *const TypedArray<i64>;
let data = (*new_arr).data;
assert_eq!(*data.add(0), 1);
assert_eq!(*data.add(1), 2);
assert_eq!(*data.add(2), 3);
assert_eq!(*data.add(3), 4);
assert_eq!(*data.add(4), 5);
TypedArray::<i64>::drop_array(a);
TypedArray::<i64>::drop_array(b);
TypedArray::<i64>::drop_array(new_ptr as *mut TypedArray<i64>);
}
}
#[test]
fn test_concat_arrays_kind_mismatch() {
unsafe {
let a = TypedArray::<i64>::from_slice(&[1, 2]);
let b = TypedArray::<f64>::from_slice(&[3.0, 4.0]);
stamp_elem_type(a as *mut u8, ELEM_TYPE_I64);
stamp_elem_type(b as *mut u8, ELEM_TYPE_F64);
let view_a = as_v2_typed_array(a as u64, NativeKind::Ptr(HeapKind::TypedArray))
.unwrap();
let view_b = as_v2_typed_array(b as u64, NativeKind::Ptr(HeapKind::TypedArray))
.unwrap();
assert!(concat_arrays(&view_a, &view_b).is_err());
TypedArray::<i64>::drop_array(a);
TypedArray::<f64>::drop_array(b);
}
}
#[test]
fn test_slice_array_i64() {
unsafe {
let arr = TypedArray::<i64>::from_slice(&[10, 20, 30, 40, 50]);
stamp_elem_type(arr as *mut u8, ELEM_TYPE_I64);
let view = as_v2_typed_array(arr as u64, NativeKind::Ptr(HeapKind::TypedArray))
.unwrap();
let new_ptr = slice_array(&view, 1, 4);
let new_view =
as_v2_typed_array(new_ptr as u64, NativeKind::Ptr(HeapKind::TypedArray))
.unwrap();
assert_eq!(new_view.elem_type, V2ElemType::I64);
assert_eq!(new_view.len, 3);
let new_arr = new_ptr as *const TypedArray<i64>;
let data = (*new_arr).data;
assert_eq!(*data.add(0), 20);
assert_eq!(*data.add(1), 30);
assert_eq!(*data.add(2), 40);
TypedArray::<i64>::drop_array(arr);
TypedArray::<i64>::drop_array(new_ptr as *mut TypedArray<i64>);
}
}
#[test]
fn test_slice_array_clamps_oversize_end() {
unsafe {
let arr = TypedArray::<i64>::from_slice(&[10, 20, 30]);
stamp_elem_type(arr as *mut u8, ELEM_TYPE_I64);
let view = as_v2_typed_array(arr as u64, NativeKind::Ptr(HeapKind::TypedArray))
.unwrap();
let new_ptr = slice_array(&view, 2, 100);
let new_view =
as_v2_typed_array(new_ptr as u64, NativeKind::Ptr(HeapKind::TypedArray))
.unwrap();
assert_eq!(new_view.len, 1);
let new_arr = new_ptr as *const TypedArray<i64>;
assert_eq!(*(*new_arr).data.add(0), 30);
TypedArray::<i64>::drop_array(arr);
TypedArray::<i64>::drop_array(new_ptr as *mut TypedArray<i64>);
}
}
#[test]
fn test_slice_array_inverted_range_empty() {
unsafe {
let arr = TypedArray::<i64>::from_slice(&[1, 2, 3]);
stamp_elem_type(arr as *mut u8, ELEM_TYPE_I64);
let view = as_v2_typed_array(arr as u64, NativeKind::Ptr(HeapKind::TypedArray))
.unwrap();
let new_ptr = slice_array(&view, 5, 2);
let new_view =
as_v2_typed_array(new_ptr as u64, NativeKind::Ptr(HeapKind::TypedArray))
.unwrap();
assert_eq!(new_view.len, 0);
TypedArray::<i64>::drop_array(arr);
TypedArray::<i64>::drop_array(new_ptr as *mut TypedArray<i64>);
}
}
#[test]
fn test_take_array_i64() {
unsafe {
let arr = TypedArray::<i64>::from_slice(&[1, 2, 3, 4, 5]);
stamp_elem_type(arr as *mut u8, ELEM_TYPE_I64);
let view = as_v2_typed_array(arr as u64, NativeKind::Ptr(HeapKind::TypedArray))
.unwrap();
let new_ptr = take_array(&view, 2);
let new_view =
as_v2_typed_array(new_ptr as u64, NativeKind::Ptr(HeapKind::TypedArray))
.unwrap();
assert_eq!(new_view.len, 2);
let new_arr = new_ptr as *const TypedArray<i64>;
assert_eq!(*(*new_arr).data.add(0), 1);
assert_eq!(*(*new_arr).data.add(1), 2);
TypedArray::<i64>::drop_array(arr);
TypedArray::<i64>::drop_array(new_ptr as *mut TypedArray<i64>);
}
}
#[test]
fn test_take_array_n_exceeds_len() {
unsafe {
let arr = TypedArray::<i64>::from_slice(&[1, 2]);
stamp_elem_type(arr as *mut u8, ELEM_TYPE_I64);
let view = as_v2_typed_array(arr as u64, NativeKind::Ptr(HeapKind::TypedArray))
.unwrap();
let new_ptr = take_array(&view, 100);
let new_view =
as_v2_typed_array(new_ptr as u64, NativeKind::Ptr(HeapKind::TypedArray))
.unwrap();
assert_eq!(new_view.len, 2);
TypedArray::<i64>::drop_array(arr);
TypedArray::<i64>::drop_array(new_ptr as *mut TypedArray<i64>);
}
}
#[test]
fn test_drop_array_n_i64() {
unsafe {
let arr = TypedArray::<i64>::from_slice(&[1, 2, 3, 4, 5]);
stamp_elem_type(arr as *mut u8, ELEM_TYPE_I64);
let view = as_v2_typed_array(arr as u64, NativeKind::Ptr(HeapKind::TypedArray))
.unwrap();
let new_ptr = drop_array_n(&view, 2);
let new_view =
as_v2_typed_array(new_ptr as u64, NativeKind::Ptr(HeapKind::TypedArray))
.unwrap();
assert_eq!(new_view.len, 3);
let new_arr = new_ptr as *const TypedArray<i64>;
assert_eq!(*(*new_arr).data.add(0), 3);
assert_eq!(*(*new_arr).data.add(1), 4);
assert_eq!(*(*new_arr).data.add(2), 5);
TypedArray::<i64>::drop_array(arr);
TypedArray::<i64>::drop_array(new_ptr as *mut TypedArray<i64>);
}
}
#[test]
fn test_drop_array_n_exceeds_len_yields_empty() {
unsafe {
let arr = TypedArray::<i64>::from_slice(&[1, 2]);
stamp_elem_type(arr as *mut u8, ELEM_TYPE_I64);
let view = as_v2_typed_array(arr as u64, NativeKind::Ptr(HeapKind::TypedArray))
.unwrap();
let new_ptr = drop_array_n(&view, 100);
let new_view =
as_v2_typed_array(new_ptr as u64, NativeKind::Ptr(HeapKind::TypedArray))
.unwrap();
assert_eq!(new_view.len, 0);
TypedArray::<i64>::drop_array(arr);
TypedArray::<i64>::drop_array(new_ptr as *mut TypedArray<i64>);
}
}
#[test]
fn test_eq_element_scalar_i64() {
assert!(eq_element(42, 42, V2ElemType::I64));
assert!(!eq_element(42, 43, V2ElemType::I64));
assert!(eq_element((-5i64) as u64, (-5i64) as u64, V2ElemType::I64));
assert!(!eq_element((-5i64) as u64, (5i64) as u64, V2ElemType::I64));
}
#[test]
fn test_eq_element_scalar_f64_bitwise() {
assert!(eq_element(1.5f64.to_bits(), 1.5f64.to_bits(), V2ElemType::F64));
assert!(!eq_element(1.5f64.to_bits(), 2.5f64.to_bits(), V2ElemType::F64));
let nan = f64::NAN.to_bits();
assert!(eq_element(nan, nan, V2ElemType::F64));
}
#[test]
fn test_eq_element_scalar_bool() {
assert!(eq_element(1, 1, V2ElemType::Bool));
assert!(eq_element(0, 0, V2ElemType::Bool));
assert!(!eq_element(1, 0, V2ElemType::Bool));
}
#[test]
fn test_eq_element_string_content() {
unsafe {
let s1 = StringObj::new("hello");
let s2 = StringObj::new("hello"); let s3 = StringObj::new("world");
assert!(eq_element(s1 as u64, s2 as u64, V2ElemType::String));
assert!(!eq_element(s1 as u64, s3 as u64, V2ElemType::String));
assert!(eq_element(0, 0, V2ElemType::String));
assert!(!eq_element(s1 as u64, 0, V2ElemType::String));
assert!(eq_element(s1 as u64, s1 as u64, V2ElemType::String));
StringObj::drop(s1);
StringObj::drop(s2);
StringObj::drop(s3);
}
}
#[test]
fn test_eq_element_decimal_content() {
use rust_decimal::Decimal;
use rust_decimal::prelude::FromPrimitive;
unsafe {
let d1 = DecimalObj::new(Decimal::from_f64(3.14).unwrap());
let d2 = DecimalObj::new(Decimal::from_f64(3.14).unwrap());
let d3 = DecimalObj::new(Decimal::from_f64(2.71).unwrap());
assert!(eq_element(d1 as u64, d2 as u64, V2ElemType::Decimal));
assert!(!eq_element(d1 as u64, d3 as u64, V2ElemType::Decimal));
DecimalObj::drop(d1);
DecimalObj::drop(d2);
DecimalObj::drop(d3);
}
}
#[test]
fn test_position_of_i64() {
unsafe {
let arr = TypedArray::<i64>::from_slice(&[10, 20, 30, 20, 40]);
stamp_elem_type(arr as *mut u8, ELEM_TYPE_I64);
let view = as_v2_typed_array(arr as u64, NativeKind::Ptr(HeapKind::TypedArray))
.unwrap();
assert_eq!(position_of(&view, 10u64), Some(0));
assert_eq!(position_of(&view, 20u64), Some(1)); assert_eq!(position_of(&view, 30u64), Some(2));
assert_eq!(position_of(&view, 99u64), None);
TypedArray::<i64>::drop_array(arr);
}
}
#[test]
fn test_position_of_empty_returns_none() {
unsafe {
let arr = TypedArray::<i64>::with_capacity(0);
stamp_elem_type(arr as *mut u8, ELEM_TYPE_I64);
let view = as_v2_typed_array(arr as u64, NativeKind::Ptr(HeapKind::TypedArray))
.unwrap();
assert_eq!(position_of(&view, 0u64), None);
TypedArray::<i64>::drop_array(arr);
}
}
#[test]
fn test_contains_element_i64() {
unsafe {
let arr = TypedArray::<i64>::from_slice(&[1, 2, 3, 4, 5]);
stamp_elem_type(arr as *mut u8, ELEM_TYPE_I64);
let view = as_v2_typed_array(arr as u64, NativeKind::Ptr(HeapKind::TypedArray))
.unwrap();
assert!(contains_element(&view, 3u64));
assert!(!contains_element(&view, 99u64));
TypedArray::<i64>::drop_array(arr);
}
}
#[test]
fn test_position_of_f64_nan_bitwise() {
unsafe {
let arr = TypedArray::<f64>::from_slice(&[1.0, f64::NAN, 2.0]);
stamp_elem_type(arr as *mut u8, ELEM_TYPE_F64);
let view = as_v2_typed_array(arr as u64, NativeKind::Ptr(HeapKind::TypedArray))
.unwrap();
assert_eq!(position_of(&view, f64::NAN.to_bits()), Some(1));
assert_eq!(position_of(&view, (1.0f64).to_bits()), Some(0));
assert_eq!(position_of(&view, (99.0f64).to_bits()), None);
TypedArray::<f64>::drop_array(arr);
}
}
#[test]
fn test_position_of_string_content() {
unsafe {
let s1 = StringObj::new("a");
let s2 = StringObj::new("b");
let s3 = StringObj::new("c");
let arr = TypedArray::<*const StringObj>::with_capacity(3);
TypedArray::push(arr, s1 as *const StringObj);
TypedArray::push(arr, s2 as *const StringObj);
TypedArray::push(arr, s3 as *const StringObj);
stamp_elem_type(arr as *mut u8, ELEM_TYPE_STRING);
let view = as_v2_typed_array(arr as u64, NativeKind::Ptr(HeapKind::TypedArray))
.unwrap();
let needle = StringObj::new("b");
assert_eq!(position_of(&view, needle as u64), Some(1));
let other = StringObj::new("zzz");
assert_eq!(position_of(&view, other as u64), None);
assert!(contains_element(&view, needle as u64));
assert!(!contains_element(&view, other as u64));
TypedArray::<*const StringObj>::drop_array_heap(arr);
StringObj::drop(needle);
StringObj::drop(other);
}
}
#[test]
fn test_position_of_decimal_content() {
use rust_decimal::Decimal;
use rust_decimal::prelude::FromPrimitive;
unsafe {
let d1 = DecimalObj::new(Decimal::from_f64(1.5).unwrap());
let d2 = DecimalObj::new(Decimal::from_f64(2.5).unwrap());
let arr = TypedArray::<*const DecimalObj>::with_capacity(2);
TypedArray::push(arr, d1 as *const DecimalObj);
TypedArray::push(arr, d2 as *const DecimalObj);
stamp_elem_type(arr as *mut u8, ELEM_TYPE_DECIMAL);
let view = as_v2_typed_array(arr as u64, NativeKind::Ptr(HeapKind::TypedArray))
.unwrap();
let needle = DecimalObj::new(Decimal::from_f64(2.5).unwrap());
assert_eq!(position_of(&view, needle as u64), Some(1));
let other = DecimalObj::new(Decimal::from_f64(9.9).unwrap());
assert_eq!(position_of(&view, other as u64), None);
TypedArray::<*const DecimalObj>::drop_array_heap(arr);
DecimalObj::drop(needle);
DecimalObj::drop(other);
}
}
#[test]
fn test_position_of_typed_object_schema_mismatch() {
use shape_value::slot::ValueSlot;
use std::sync::Arc;
unsafe {
let kinds_a: Arc<[NativeKind]> = Arc::from(vec![].into_boxed_slice());
let kinds_b: Arc<[NativeKind]> = Arc::from(vec![].into_boxed_slice());
let obj_a = TypedObjectStorage::_new(
1,
vec![].into_boxed_slice() as Box<[ValueSlot]>,
0,
kinds_a,
);
let obj_b = TypedObjectStorage::_new(
2,
vec![].into_boxed_slice() as Box<[ValueSlot]>,
0,
kinds_b,
);
assert!(!eq_element(
obj_a as u64,
obj_b as u64,
V2ElemType::TypedObject
));
TypedObjectStorage::_drop(obj_a);
TypedObjectStorage::_drop(obj_b);
}
}
#[test]
fn test_position_of_typed_object_same_schema_equal_fields() {
use shape_value::slot::ValueSlot;
use std::sync::Arc;
unsafe {
let kinds: Arc<[NativeKind]> = Arc::from(vec![NativeKind::Int64].into_boxed_slice());
let obj_a = TypedObjectStorage::_new(
7,
vec![ValueSlot::from_raw(42u64)].into_boxed_slice(),
0,
kinds.clone(),
);
let obj_b = TypedObjectStorage::_new(
7,
vec![ValueSlot::from_raw(42u64)].into_boxed_slice(),
0,
kinds.clone(),
);
let obj_c = TypedObjectStorage::_new(
7,
vec![ValueSlot::from_raw(99u64)].into_boxed_slice(),
0,
kinds.clone(),
);
assert!(eq_element(
obj_a as u64,
obj_b as u64,
V2ElemType::TypedObject
));
assert!(!eq_element(
obj_a as u64,
obj_c as u64,
V2ElemType::TypedObject
));
assert!(eq_element(
obj_a as u64,
obj_a as u64,
V2ElemType::TypedObject
));
assert!(!eq_element(0, obj_a as u64, V2ElemType::TypedObject));
assert!(eq_element(0, 0, V2ElemType::TypedObject));
TypedObjectStorage::_drop(obj_a);
TypedObjectStorage::_drop(obj_b);
TypedObjectStorage::_drop(obj_c);
}
}
}