use std::any::Any;
use std::fmt::Debug;
use std::fmt::Formatter;
use std::marker::PhantomData;
use std::ops::Deref;
use std::ops::DerefMut;
use std::sync::Arc;
use vortex_error::VortexResult;
use crate::ArrayRef;
use crate::ArraySlots;
use crate::ExecutionCtx;
use crate::IntoArray;
use crate::LEGACY_SESSION;
use crate::VortexSessionExecute;
use crate::array::ArrayId;
use crate::array::ArrayView;
use crate::array::VTable;
use crate::dtype::DType;
use crate::stats::ArrayStats;
use crate::stats::StatsSet;
use crate::stats::StatsSetRef;
use crate::validity::Validity;
pub(crate) struct ArrayInner<D: ?Sized> {
pub(crate) len: usize,
pub(crate) encoding_id: ArrayId,
pub(crate) dtype: DType,
pub(crate) slots: ArraySlots,
pub(crate) stats: ArrayStats,
pub(crate) data: D, }
pub struct ArrayParts<V: VTable> {
pub vtable: V,
pub dtype: DType,
pub len: usize,
pub data: V::TypedArrayData,
pub slots: ArraySlots,
}
impl<V: VTable> ArrayParts<V> {
pub fn new(vtable: V, dtype: DType, len: usize, data: V::TypedArrayData) -> Self {
Self {
vtable,
dtype,
len,
data,
slots: ArraySlots::new(),
}
}
pub fn with_slots(mut self, slots: ArraySlots) -> Self {
self.slots = slots;
self
}
}
pub trait TypedArrayRef<V: VTable>: AsRef<ArrayRef> + Deref<Target = V::TypedArrayData> {
fn to_owned(&self) -> Array<V> {
self.as_ref().clone().downcast()
}
}
impl<V: VTable> TypedArrayRef<V> for Array<V> {}
impl<V: VTable> TypedArrayRef<V> for ArrayView<'_, V> {}
#[doc(hidden)]
pub(crate) struct ArrayData<V: VTable> {
pub(crate) vtable: V,
pub(crate) data: V::TypedArrayData,
}
impl<V: VTable> ArrayInner<ArrayData<V>> {
#[doc(hidden)]
pub fn try_new(new: ArrayParts<V>) -> VortexResult<Self> {
new.vtable
.validate(&new.data, &new.dtype, new.len, &new.slots)?;
Ok(ArrayInner {
len: new.len,
encoding_id: new.vtable.id(),
dtype: new.dtype,
slots: new.slots,
stats: ArrayStats::default(),
data: ArrayData {
vtable: new.vtable,
data: new.data,
},
})
}
pub(crate) unsafe fn new_unchecked(
vtable: V,
len: usize,
dtype: DType,
data: V::TypedArrayData,
slots: ArraySlots,
stats: ArrayStats,
) -> Self {
ArrayInner {
len,
encoding_id: vtable.id(),
dtype,
slots,
stats,
data: ArrayData { vtable, data },
}
}
}
impl<V: VTable> Deref for ArrayData<V> {
type Target = V::TypedArrayData;
fn deref(&self) -> &V::TypedArrayData {
&self.data
}
}
impl<V: VTable> DerefMut for ArrayData<V> {
fn deref_mut(&mut self) -> &mut V::TypedArrayData {
&mut self.data
}
}
impl<V: VTable> Clone for ArrayData<V> {
fn clone(&self) -> Self {
Self {
vtable: self.vtable.clone(),
data: self.data.clone(),
}
}
}
impl<V: VTable> Debug for ArrayData<V> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ArrayData")
.field("encoding", &self.vtable.id())
.field("inner", &self.data)
.finish()
}
}
pub struct Array<V: VTable> {
inner: ArrayRef,
_phantom: PhantomData<V>,
}
impl<V: VTable> Array<V> {
pub fn try_from_parts(new: ArrayParts<V>) -> VortexResult<Self> {
let store = ArrayInner::<ArrayData<V>>::try_new(new)?;
let inner = ArrayRef::from_inner(Arc::new(store));
Ok(Self {
inner,
_phantom: PhantomData,
})
}
#[doc(hidden)]
pub unsafe fn from_parts_unchecked(new: ArrayParts<V>) -> Self {
let store = unsafe {
ArrayInner::<ArrayData<V>>::new_unchecked(
new.vtable,
new.len,
new.dtype,
new.data,
new.slots,
ArrayStats::default(),
)
};
let inner = ArrayRef::from_inner(Arc::new(store));
Self {
inner,
_phantom: PhantomData,
}
}
pub(crate) unsafe fn from_array_ref_unchecked(array: ArrayRef) -> Self {
Self {
inner: array,
_phantom: PhantomData,
}
}
pub fn try_from_array_ref(array: ArrayRef) -> Result<Self, ArrayRef> {
if array.is::<V>() {
Ok(Self {
inner: array,
_phantom: PhantomData,
})
} else {
Err(array)
}
}
pub fn dtype(&self) -> &DType {
self.inner.dtype()
}
pub fn len(&self) -> usize {
self.inner.len()
}
pub fn is_empty(&self) -> bool {
self.inner.len() == 0
}
pub fn encoding_id(&self) -> ArrayId {
self.inner.encoding_id()
}
pub fn statistics(&self) -> StatsSetRef<'_> {
self.inner.statistics()
}
pub fn data(&self) -> &V::TypedArrayData {
&self.downcast_inner().data
}
pub fn data_mut(&mut self) -> Option<&mut V::TypedArrayData> {
let store = self.inner.inner_mut()?;
let array_inner = store.data.as_any_mut().downcast_mut::<ArrayData<V>>();
Some(&mut array_inner?.data)
}
pub fn try_into_parts(self) -> Result<ArrayParts<V>, Self> {
let Self { inner, _phantom } = self;
let typed_arc = unsafe { inner.downcast_inner_unchecked::<V>() };
match Arc::try_unwrap(typed_arc) {
Ok(store) => Ok(ArrayParts {
vtable: store.data.vtable,
dtype: store.dtype,
len: store.len,
data: store.data.data,
slots: store.slots,
}),
Err(typed_arc) => Err(Self {
inner: ArrayRef::from_inner(typed_arc),
_phantom: PhantomData,
}),
}
}
pub fn with_stats_set(self, stats: StatsSet) -> Self {
self.statistics().replace(stats);
self
}
pub fn into_data(self) -> V::TypedArrayData {
self.downcast_inner().data.clone()
}
pub fn slots(&self) -> &[Option<ArrayRef>] {
self.inner.slots()
}
#[inline(always)]
pub fn as_array(&self) -> &ArrayRef {
&self.inner
}
pub fn as_view(&self) -> ArrayView<'_, V> {
let inner = self.downcast_inner();
unsafe { ArrayView::new_unchecked(&self.inner, &inner.data) }
}
#[inline(always)]
fn downcast_inner(&self) -> &ArrayData<V> {
let any = self.inner.dyn_array().as_any();
debug_assert!(any.is::<ArrayData<V>>());
unsafe { &*(any as *const dyn Any as *const ArrayData<V>) }
}
}
impl<V: VTable> Array<V> {
pub fn slice(&self, range: std::ops::Range<usize>) -> VortexResult<ArrayRef> {
self.inner.slice(range)
}
#[deprecated(
note = "Use `execute_scalar` instead, which allows passing an execution context for more \
efficient execution when fetching multiple scalars from the same array."
)]
pub fn scalar_at(&self, index: usize) -> VortexResult<crate::scalar::Scalar> {
self.inner
.execute_scalar(index, &mut LEGACY_SESSION.create_execution_ctx())
}
pub fn execute_scalar(
&self,
index: usize,
ctx: &mut ExecutionCtx,
) -> VortexResult<crate::scalar::Scalar> {
self.inner.execute_scalar(index, ctx)
}
pub fn filter(&self, mask: vortex_mask::Mask) -> VortexResult<ArrayRef> {
self.inner.filter(mask)
}
pub fn take(&self, indices: ArrayRef) -> VortexResult<ArrayRef> {
self.inner.take(indices)
}
pub fn validity(&self) -> VortexResult<Validity> {
self.inner.validity()
}
pub fn is_valid(&self, index: usize, ctx: &mut ExecutionCtx) -> VortexResult<bool> {
self.inner.is_valid(index, ctx)
}
pub fn is_invalid(&self, index: usize, ctx: &mut ExecutionCtx) -> VortexResult<bool> {
self.inner.is_invalid(index, ctx)
}
pub fn all_valid(&self, ctx: &mut ExecutionCtx) -> VortexResult<bool> {
self.inner.all_valid(ctx)
}
pub fn all_invalid(&self, ctx: &mut ExecutionCtx) -> VortexResult<bool> {
self.inner.all_invalid(ctx)
}
#[deprecated(note = "Use Array::<V>::execute::<Canonical>() instead")]
pub fn to_canonical(&self) -> VortexResult<crate::Canonical> {
#[expect(deprecated)]
let result = self.inner.to_canonical();
result
}
pub fn nbytes(&self) -> u64 {
self.inner.nbytes()
}
pub fn nbuffers(&self) -> usize {
self.inner.nbuffers()
}
pub fn as_constant(&self) -> Option<crate::scalar::Scalar> {
self.inner.as_constant()
}
pub fn valid_count(&self, ctx: &mut ExecutionCtx) -> VortexResult<usize> {
self.inner.valid_count(ctx)
}
pub fn invalid_count(&self, ctx: &mut ExecutionCtx) -> VortexResult<usize> {
self.inner.invalid_count(ctx)
}
pub fn append_to_builder(
&self,
builder: &mut dyn crate::builders::ArrayBuilder,
ctx: &mut ExecutionCtx,
) -> VortexResult<()> {
self.inner.append_to_builder(builder, ctx)
}
}
impl<V: VTable> Deref for Array<V> {
type Target = V::TypedArrayData;
fn deref(&self) -> &V::TypedArrayData {
self.data()
}
}
impl<V: VTable> Clone for Array<V> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
_phantom: PhantomData,
}
}
}
impl<V: VTable> Debug for Array<V> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Array")
.field("encoding", &self.inner.encoding_id())
.field("dtype", self.inner.dtype())
.field("len", &self.inner.len())
.finish()
}
}
impl<V: VTable> AsRef<ArrayRef> for Array<V> {
fn as_ref(&self) -> &ArrayRef {
&self.inner
}
}
impl<V: VTable> IntoArray for Array<V> {
#[inline(always)]
fn into_array(self) -> ArrayRef {
self.inner
}
}
impl<V: VTable> IntoArray for Arc<Array<V>> {
fn into_array(self) -> ArrayRef {
match Arc::try_unwrap(self) {
Ok(array) => array.inner,
Err(arc) => arc.inner.clone(),
}
}
}
impl<V: VTable> From<Array<V>> for ArrayRef {
fn from(value: Array<V>) -> ArrayRef {
value.inner
}
}
#[cfg(test)]
mod tests {
use vortex_buffer::buffer;
use super::Array;
use crate::arrays::Primitive;
use crate::arrays::PrimitiveArray;
use crate::assert_arrays_eq;
use crate::validity::Validity;
#[test]
fn typed_array_into_parts_roundtrips() {
let array = PrimitiveArray::new(buffer![1i32, 2, 3], Validity::NonNullable);
let expected = PrimitiveArray::new(buffer![1i32, 2, 3], Validity::NonNullable);
let parts = array.try_into_parts().unwrap();
let rebuilt = Array::<Primitive>::try_from_parts(parts).unwrap();
assert_arrays_eq!(rebuilt, expected);
}
#[test]
fn typed_array_try_into_parts_requires_unique_owner() {
let array = PrimitiveArray::new(buffer![1i32, 2, 3], Validity::NonNullable);
let alias = array.clone();
let array = match array.try_into_parts() {
Ok(_) => panic!("aliased arrays should not move out their backing parts"),
Err(array) => array,
};
assert_arrays_eq!(array, alias);
}
}