From 5aa3f3d2a8dcb004084797f878cb091e1f031d96 Mon Sep 17 00:00:00 2001 From: Joe Isaacs Date: Tue, 2 Jun 2026 13:50:17 +0100 Subject: [PATCH 001/115] move towards `BitBuffer/{Mut}View` over owned bit buffers (#8208) ## Summary This PR introduces `BitBufferView<'a>` and `BitBufferMutView<'a>` as borrowing analogues to the owned `BitBuffer` and `BitBufferMut` types. These new types enable zero-copy reading and in-place modification of packed bitsets without cloning the underlying `ByteBuffer`. This View will replace BitBuffer after the migration. --------- Signed-off-by: Joe Isaacs --- .../aggregate_fn/fns/all_non_distinct/bool.rs | 2 +- .../src/aggregate_fn/fns/is_constant/bool.rs | 2 +- .../src/aggregate_fn/fns/is_sorted/bool.rs | 6 +- .../src/aggregate_fn/fns/min_max/bool.rs | 2 +- vortex-array/src/aggregate_fn/fns/sum/bool.rs | 2 +- vortex-array/src/arrays/bool/array.rs | 34 +- vortex-array/src/arrays/bool/compute/take.rs | 12 +- vortex-array/src/arrays/bool/patch.rs | 2 +- vortex-array/src/arrays/bool/vtable/mod.rs | 13 +- .../src/arrays/bool/vtable/operations.rs | 2 +- .../src/arrays/dict/compute/fill_null.rs | 2 +- .../src/arrays/patched/compute/compare.rs | 5 +- .../src/arrays/primitive/array/mod.rs | 2 +- vortex-array/src/canonical.rs | 18 +- .../src/scalar_fn/fns/list_contains/mod.rs | 4 +- vortex-buffer/src/bit/buf_mut.rs | 2 +- vortex-buffer/src/bit/meta.rs | 60 ++ vortex-buffer/src/bit/mod.rs | 4 + vortex-buffer/src/bit/view.rs | 546 ++++++++++++++++++ vortex-cuda/src/arrow/canonical.rs | 15 +- vortex-cuda/src/canonical.rs | 10 +- vortex-duckdb/src/exporter/struct_.rs | 2 +- 22 files changed, 694 insertions(+), 53 deletions(-) create mode 100644 vortex-buffer/src/bit/meta.rs create mode 100644 vortex-buffer/src/bit/view.rs diff --git a/vortex-array/src/aggregate_fn/fns/all_non_distinct/bool.rs b/vortex-array/src/aggregate_fn/fns/all_non_distinct/bool.rs index c06d335e458..f201653b815 100644 --- a/vortex-array/src/aggregate_fn/fns/all_non_distinct/bool.rs +++ b/vortex-array/src/aggregate_fn/fns/all_non_distinct/bool.rs @@ -10,5 +10,5 @@ where L: BoolArrayExt, R: BoolArrayExt, { - Ok(lhs.to_bit_buffer() == rhs.to_bit_buffer()) + Ok(lhs.bit_buffer_view() == rhs.bit_buffer_view()) } diff --git a/vortex-array/src/aggregate_fn/fns/is_constant/bool.rs b/vortex-array/src/aggregate_fn/fns/is_constant/bool.rs index c63025ccd7c..967f19058b6 100644 --- a/vortex-array/src/aggregate_fn/fns/is_constant/bool.rs +++ b/vortex-array/src/aggregate_fn/fns/is_constant/bool.rs @@ -5,6 +5,6 @@ use crate::arrays::BoolArray; use crate::arrays::bool::BoolArrayExt; pub(super) fn check_bool_constant(array: &BoolArray) -> bool { - let true_count = array.to_bit_buffer().true_count(); + let true_count = array.bit_buffer_view().true_count(); true_count == array.len() || true_count == 0 } diff --git a/vortex-array/src/aggregate_fn/fns/is_sorted/bool.rs b/vortex-array/src/aggregate_fn/fns/is_sorted/bool.rs index afd18f15902..69c0aaf6692 100644 --- a/vortex-array/src/aggregate_fn/fns/is_sorted/bool.rs +++ b/vortex-array/src/aggregate_fn/fns/is_sorted/bool.rs @@ -21,7 +21,7 @@ pub(super) fn check_bool_sorted( { Mask::AllFalse(_) => Ok(!strict), Mask::AllTrue(_) => { - let values = array.to_bit_buffer(); + let values = array.bit_buffer_view(); Ok(if strict { values.iter().is_strict_sorted() } else { @@ -31,7 +31,7 @@ pub(super) fn check_bool_sorted( Mask::Values(mask_values) => { if strict { let validity_buffer = mask_values.bit_buffer(); - let values = array.to_bit_buffer(); + let values = array.bit_buffer_view(); Ok(validity_buffer .iter() .zip(values.iter()) @@ -39,7 +39,7 @@ pub(super) fn check_bool_sorted( .is_strict_sorted()) } else { let set_indices = mask_values.bit_buffer().set_indices(); - let values = array.to_bit_buffer(); + let values = array.bit_buffer_view(); let values_iter = set_indices.map(|idx| // Safety: // All idxs are in-bounds for the array. diff --git a/vortex-array/src/aggregate_fn/fns/min_max/bool.rs b/vortex-array/src/aggregate_fn/fns/min_max/bool.rs index 54501409fe5..adca6453862 100644 --- a/vortex-array/src/aggregate_fn/fns/min_max/bool.rs +++ b/vortex-array/src/aggregate_fn/fns/min_max/bool.rs @@ -25,7 +25,7 @@ pub(super) fn accumulate_bool( .execute_mask(array.as_ref().len(), ctx)?; let (true_count, valid_count) = match mask.bit_buffer() { AllOr::None => return Ok(()), - AllOr::All => (array.to_bit_buffer().true_count(), array.as_ref().len()), + AllOr::All => (array.bit_buffer_view().true_count(), array.as_ref().len()), AllOr::Some(validity) => ( array.to_bit_buffer().bitand(validity).true_count(), validity.true_count(), diff --git a/vortex-array/src/aggregate_fn/fns/sum/bool.rs b/vortex-array/src/aggregate_fn/fns/sum/bool.rs index a32f3ba72f7..7728d3f64af 100644 --- a/vortex-array/src/aggregate_fn/fns/sum/bool.rs +++ b/vortex-array/src/aggregate_fn/fns/sum/bool.rs @@ -25,7 +25,7 @@ pub(super) fn accumulate_bool( let mask = b.as_ref().validity()?.execute_mask(b.as_ref().len(), ctx)?; let true_count = match mask.bit_buffer() { AllOr::None => return Ok(false), - AllOr::All => b.to_bit_buffer().true_count() as u64, + AllOr::All => b.bit_buffer_view().true_count() as u64, AllOr::Some(validity) => b.to_bit_buffer().bitand(validity).true_count() as u64, }; diff --git a/vortex-array/src/arrays/bool/array.rs b/vortex-array/src/arrays/bool/array.rs index 6585e705899..9049a8e5faf 100644 --- a/vortex-array/src/arrays/bool/array.rs +++ b/vortex-array/src/arrays/bool/array.rs @@ -7,7 +7,9 @@ use std::fmt::Formatter; use arrow_array::BooleanArray; use smallvec::smallvec; use vortex_buffer::BitBuffer; +use vortex_buffer::BitBufferMeta; use vortex_buffer::BitBufferMut; +use vortex_buffer::BitBufferView; use vortex_error::VortexExpect; use vortex_error::VortexResult; use vortex_error::vortex_ensure; @@ -69,19 +71,18 @@ pub(super) const SLOT_NAMES: [&str; NUM_SLOTS] = ["validity"]; #[derive(Clone, Debug)] pub struct BoolData { pub(super) bits: BufferHandle, - pub(super) offset: usize, + pub(super) meta: BitBufferMeta, } impl Display for BoolData { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "offset: {}", self.offset) + write!(f, "offset: {}", self.meta.offset()) } } pub struct BoolDataParts { pub bits: BufferHandle, - pub offset: usize, - pub len: usize, + pub meta: BitBufferMeta, } pub trait BoolArrayExt: TypedArrayRef { @@ -101,7 +102,12 @@ pub trait BoolArrayExt: TypedArrayRef { fn to_bit_buffer(&self) -> BitBuffer { let buffer = self.bits.as_host().clone(); - BitBuffer::new_with_offset(buffer, self.as_ref().len(), self.offset) + BitBuffer::new_with_offset(buffer, self.meta.len(), self.meta.offset()) + } + + /// Borrow the array's packed bits as a [`BitBufferView`] without cloning the backing buffer. + fn bit_buffer_view(&self) -> BitBufferView<'_> { + BitBufferView::from_meta(self.bits.as_host().as_slice(), self.meta) } fn maybe_execute_mask(&self, ctx: &mut ExecutionCtx) -> VortexResult> { @@ -141,8 +147,7 @@ impl BoolData { pub fn into_parts(self, len: usize) -> BoolDataParts { BoolDataParts { bits: self.bits, - offset: self.offset, - len, + meta: BitBufferMeta::new(self.meta.offset(), len), } } @@ -242,7 +247,7 @@ impl Array { let len = self.len(); let data = self.into_data(); let buffer = data.bits.unwrap_host(); - BitBuffer::new_with_offset(buffer, len, data.offset) + BitBuffer::new_with_offset(buffer, len, data.meta.offset()) } } @@ -252,11 +257,11 @@ impl BoolData { let bits = bits.shrink_offset(); Self::validate(&bits, &validity)?; - let (offset, _len, buffer) = bits.into_inner(); + let (offset, len, buffer) = bits.into_inner(); Ok(Self { bits: BufferHandle::new_host(buffer), - offset, + meta: BitBufferMeta::new(offset, len), }) } @@ -281,18 +286,21 @@ impl BoolData { bits.len() * 8, ); - Ok(Self { bits, offset }) + Ok(Self { + bits, + meta: BitBufferMeta::new(offset, len), + }) } pub(super) unsafe fn new_unchecked(bits: BitBuffer, validity: Validity) -> Self { if cfg!(debug_assertions) { Self::try_new(bits, validity).vortex_expect("Failed to create BoolData") } else { - let (offset, _len, buffer) = bits.into_inner(); + let (offset, len, buffer) = bits.into_inner(); Self { bits: BufferHandle::new_host(buffer), - offset, + meta: BitBufferMeta::new(offset, len), } } } diff --git a/vortex-array/src/arrays/bool/compute/take.rs b/vortex-array/src/arrays/bool/compute/take.rs index 1f679445254..d70579136a4 100644 --- a/vortex-array/src/arrays/bool/compute/take.rs +++ b/vortex-array/src/arrays/bool/compute/take.rs @@ -4,6 +4,7 @@ use itertools::Itertools as _; use num_traits::AsPrimitive; use vortex_buffer::BitBuffer; +use vortex_buffer::BitBufferView; use vortex_buffer::get_bit; use vortex_error::VortexResult; use vortex_mask::Mask; @@ -42,7 +43,10 @@ impl TakeExecute for Bool { }; let indices_nulls_zeroed = indices_nulls_zeroed.execute::(ctx)?; let buffer = match_each_integer_ptype!(indices_nulls_zeroed.ptype(), |I| { - take_valid_indices(&array.to_bit_buffer(), indices_nulls_zeroed.as_slice::()) + take_valid_indices( + array.bit_buffer_view(), + indices_nulls_zeroed.as_slice::(), + ) }); Ok(Some( @@ -51,7 +55,7 @@ impl TakeExecute for Bool { } } -fn take_valid_indices>(bools: &BitBuffer, indices: &[I]) -> BitBuffer { +fn take_valid_indices>(bools: BitBufferView<'_>, indices: &[I]) -> BitBuffer { // For boolean arrays that roughly fit into a single page (at least, on Linux), it's worth // the overhead to convert to a Vec. if bools.len() <= 4096 { @@ -68,9 +72,9 @@ fn take_byte_bool>(bools: Vec, indices: &[I]) -> Bit }) } -fn take_bool_impl>(bools: &BitBuffer, indices: &[I]) -> BitBuffer { +fn take_bool_impl>(bools: BitBufferView<'_>, indices: &[I]) -> BitBuffer { // We dereference to underlying buffer to avoid access cost on every index. - let buffer = bools.inner().as_ref(); + let buffer = bools.inner(); BitBuffer::collect_bool(indices.len(), |idx| { // SAFETY: we can take from the indices unchecked since collect_bool just iterates len. let idx = unsafe { indices.get_unchecked(idx).as_() }; diff --git a/vortex-array/src/arrays/bool/patch.rs b/vortex-array/src/arrays/bool/patch.rs index fef03bc30fb..5efdac30231 100644 --- a/vortex-array/src/arrays/bool/patch.rs +++ b/vortex-array/src/arrays/bool/patch.rs @@ -31,7 +31,7 @@ impl BoolArray { for (idx, value) in indices .as_slice::() .iter() - .zip_eq(values.to_bit_buffer().iter()) + .zip_eq(values.bit_buffer_view().iter()) { #[allow(clippy::cast_possible_truncation)] own_values.set_to(*idx as usize - offset, value); diff --git a/vortex-array/src/arrays/bool/vtable/mod.rs b/vortex-array/src/arrays/bool/vtable/mod.rs index 35261fa8d89..899d6d3ab9a 100644 --- a/vortex-array/src/arrays/bool/vtable/mod.rs +++ b/vortex-array/src/arrays/bool/vtable/mod.rs @@ -53,13 +53,13 @@ pub struct BoolMetadata { impl ArrayHash for BoolData { fn array_hash(&self, state: &mut H, precision: Precision) { self.bits.array_hash(state, precision); - self.offset.hash(state); + self.meta.offset().hash(state); } } impl ArrayEq for BoolData { fn array_eq(&self, other: &Self, precision: Precision) -> bool { - self.offset == other.offset && self.bits.array_eq(&other.bits, precision) + self.meta.offset() == other.meta.offset() && self.bits.array_eq(&other.bits, precision) } } @@ -96,10 +96,11 @@ impl VTable for Bool { array: ArrayView<'_, Self>, _session: &VortexSession, ) -> VortexResult>> { - assert!(array.offset < 8, "Offset must be <8, got {}", array.offset); + let offset = array.meta.offset(); + assert!(offset < 8, "Offset must be <8, got {offset}"); Ok(Some( BoolMetadata { - offset: u32::try_from(array.offset).vortex_expect("checked"), + offset: u32::try_from(offset).vortex_expect("checked"), } .encode_to_vec(), )) @@ -116,9 +117,9 @@ impl VTable for Bool { vortex_bail!("Expected bool dtype, got {dtype:?}"); }; vortex_ensure!( - data.bits.len() * 8 >= data.offset + len, + data.bits.len() * 8 >= data.meta.offset() + len, "BoolArray buffer with offset {} cannot back outer length {} (buffer bits = {})", - data.offset, + data.meta.offset(), len, data.bits.len() * 8 ); diff --git a/vortex-array/src/arrays/bool/vtable/operations.rs b/vortex-array/src/arrays/bool/vtable/operations.rs index 11147b810b2..b4ac4682d23 100644 --- a/vortex-array/src/arrays/bool/vtable/operations.rs +++ b/vortex-array/src/arrays/bool/vtable/operations.rs @@ -17,7 +17,7 @@ impl OperationsVTable for Bool { _ctx: &mut ExecutionCtx, ) -> VortexResult { Ok(Scalar::bool( - array.to_bit_buffer().value(index), + array.bit_buffer_view().value(index), array.dtype().nullability(), )) } diff --git a/vortex-array/src/arrays/dict/compute/fill_null.rs b/vortex-array/src/arrays/dict/compute/fill_null.rs index 189d8240050..8f146d728a3 100644 --- a/vortex-array/src/arrays/dict/compute/fill_null.rs +++ b/vortex-array/src/arrays/dict/compute/fill_null.rs @@ -41,7 +41,7 @@ impl FillNullKernel for Dict { // We found the fill value already in the values at this given index. let Some(existing_fill_value_index) = - found_fill_values.to_bit_buffer().set_indices().next() + found_fill_values.bit_buffer_view().set_indices().next() else { // No fill values found, so we must canonicalize and fill_null. return Ok(Some( diff --git a/vortex-array/src/arrays/patched/compute/compare.rs b/vortex-array/src/arrays/patched/compute/compare.rs index c7d879323cb..0f1e6add00f 100644 --- a/vortex-array/src/arrays/patched/compute/compare.rs +++ b/vortex-array/src/arrays/patched/compute/compare.rs @@ -55,9 +55,10 @@ impl CompareKernel for Patched { let validity = child_to_validity(result.slots()[0].as_ref(), result.dtype().nullability()); let len = result.len(); - let BoolDataParts { bits, offset, len } = result.into_data().into_parts(len); + let BoolDataParts { bits, meta } = result.into_data().into_parts(len); - let mut bits = BitBufferMut::from_buffer(bits.unwrap_host().into_mut(), offset, len); + let mut bits = + BitBufferMut::from_buffer(bits.unwrap_host().into_mut(), meta.offset(), meta.len()); let lane_offsets = lhs.lane_offsets().clone().execute::(ctx)?; let indices = lhs.patch_indices().clone().execute::(ctx)?; diff --git a/vortex-array/src/arrays/primitive/array/mod.rs b/vortex-array/src/arrays/primitive/array/mod.rs index 7e3dd9b4ce7..e22fc9a4336 100644 --- a/vortex-array/src/arrays/primitive/array/mod.rs +++ b/vortex-array/src/arrays/primitive/array/mod.rs @@ -549,7 +549,7 @@ impl PrimitiveData { Validity::Array(is_valid) => { #[expect(deprecated)] let bool_array = is_valid.to_bool(); - let bool_buffer = bool_array.to_bit_buffer(); + let bool_buffer = bool_array.bit_buffer_view(); let mut bytes = ByteBufferMut::zeroed_aligned(n_rows * byte_width, alignment); for (i, valid_i) in bool_buffer.set_indices().enumerate() { bytes[valid_i * byte_width..(valid_i + 1) * byte_width] diff --git a/vortex-array/src/canonical.rs b/vortex-array/src/canonical.rs index b1773d453f2..6fbdc154a6c 100644 --- a/vortex-array/src/canonical.rs +++ b/vortex-array/src/canonical.rs @@ -563,9 +563,14 @@ impl Executable for CanonicalValidity { Canonical::Bool(b) => { let validity = child_to_validity(b.slots()[0].as_ref(), b.dtype().nullability()); let len = b.len(); - let BoolDataParts { bits, offset, len } = b.into_data().into_parts(len); + let BoolDataParts { bits, meta } = b.into_data().into_parts(len); Ok(CanonicalValidity(Canonical::Bool( - BoolArray::try_new_from_handle(bits, offset, len, validity.execute(ctx)?)?, + BoolArray::try_new_from_handle( + bits, + meta.offset(), + meta.len(), + validity.execute(ctx)?, + )?, ))) } Canonical::Primitive(p) => { @@ -713,9 +718,14 @@ impl Executable for RecursiveCanonical { Canonical::Bool(b) => { let validity = child_to_validity(b.slots()[0].as_ref(), b.dtype().nullability()); let len = b.len(); - let BoolDataParts { bits, offset, len } = b.into_data().into_parts(len); + let BoolDataParts { bits, meta } = b.into_data().into_parts(len); Ok(RecursiveCanonical(Canonical::Bool( - BoolArray::try_new_from_handle(bits, offset, len, validity.execute(ctx)?)?, + BoolArray::try_new_from_handle( + bits, + meta.offset(), + meta.len(), + validity.execute(ctx)?, + )?, ))) } Canonical::Primitive(p) => { diff --git a/vortex-array/src/scalar_fn/fns/list_contains/mod.rs b/vortex-array/src/scalar_fn/fns/list_contains/mod.rs index e964af75a32..afcd6136d70 100644 --- a/vortex-array/src/scalar_fn/fns/list_contains/mod.rs +++ b/vortex-array/src/scalar_fn/fns/list_contains/mod.rs @@ -356,7 +356,7 @@ where { let offsets_slice = offsets.as_slice::(); let sizes_slice = sizes.as_slice::(); - let bits = matches.to_bit_buffer(); + let bits = matches.bit_buffer_view(); (0..list_array_len) .map(|i| { @@ -365,7 +365,7 @@ where // BitIndexIterator yields indices of true bits only. If `.next()` returns // `Some(_)`, at least one element in this list's range matches. - let mut set_bits = BitIndexIterator::new(bits.inner().as_ref(), offset, size); + let mut set_bits = BitIndexIterator::new(bits.inner(), offset, size); set_bits.next().is_some() }) .collect::() diff --git a/vortex-buffer/src/bit/buf_mut.rs b/vortex-buffer/src/bit/buf_mut.rs index a366eaae9a8..6dac40db8ec 100644 --- a/vortex-buffer/src/bit/buf_mut.rs +++ b/vortex-buffer/src/bit/buf_mut.rs @@ -17,7 +17,7 @@ use crate::buffer_mut; /// Sets all bits in the bit-range `[start_bit, end_bit)` of `slice` to `value`. #[inline(always)] -fn fill_bits(slice: &mut [u8], start_bit: usize, end_bit: usize, value: bool) { +pub(crate) fn fill_bits(slice: &mut [u8], start_bit: usize, end_bit: usize, value: bool) { if start_bit >= end_bit { return; } diff --git a/vortex-buffer/src/bit/meta.rs b/vortex-buffer/src/bit/meta.rs new file mode 100644 index 00000000000..866a308647d --- /dev/null +++ b/vortex-buffer/src/bit/meta.rs @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: Copyright the Vortex contributors + +/// In-memory metadata describing a packed bitset: a normalized bit `offset` (always `< 8`) and a +/// logical bit `len`. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct BitBufferMeta { + offset: usize, + len: usize, +} + +impl BitBufferMeta { + /// Create metadata for a bitset starting at bit `offset` with `len` bits. + /// + /// Panics if `offset >= 8`. Use [`from_raw_offset`](Self::from_raw_offset) to normalize a + /// larger offset. + pub fn new(offset: usize, len: usize) -> Self { + assert!(offset < 8, "BitBufferMeta offset must be < 8, got {offset}"); + Self { offset, len } + } + + /// Normalize a raw bit `offset` into a whole-byte offset plus metadata whose `offset` is + /// `< 8`. + /// + /// Returns `(byte_offset, meta)` so the caller can slice its backing buffer by `byte_offset` + /// and store the remaining sub-byte offset in `meta`. + pub fn from_raw_offset(offset: usize, len: usize) -> (usize, Self) { + ( + offset / 8, + Self { + offset: offset % 8, + len, + }, + ) + } + + /// The sub-byte bit offset. Always `< 8`. + #[inline(always)] + pub fn offset(&self) -> usize { + self.offset + } + + /// The logical length of the bitset in bits. + #[inline(always)] + pub fn len(&self) -> usize { + self.len + } + + /// Returns `true` if the bitset is empty. + #[inline(always)] + pub fn is_empty(&self) -> bool { + self.len == 0 + } + + /// The number of backing bytes required to hold `offset + len` bits. + #[inline] + pub fn byte_len(&self) -> usize { + (self.offset + self.len).div_ceil(8) + } +} diff --git a/vortex-buffer/src/bit/mod.rs b/vortex-buffer/src/bit/mod.rs index a4fe1345e60..41bb5797266 100644 --- a/vortex-buffer/src/bit/mod.rs +++ b/vortex-buffer/src/bit/mod.rs @@ -12,8 +12,10 @@ mod buf; mod buf_mut; mod count_ones; mod macros; +mod meta; mod ops; mod select; +mod view; pub use arrow_buffer::bit_chunk_iterator::BitChunkIterator; pub use arrow_buffer::bit_chunk_iterator::BitChunks; @@ -24,6 +26,8 @@ pub use arrow_buffer::bit_iterator::BitIterator; pub use arrow_buffer::bit_iterator::BitSliceIterator; pub use buf::*; pub use buf_mut::*; +pub use meta::*; +pub use view::*; /// Packs up to 64 boolean values into a little-endian `u64` word. #[inline] diff --git a/vortex-buffer/src/bit/view.rs b/vortex-buffer/src/bit/view.rs new file mode 100644 index 00000000000..722119ee4ab --- /dev/null +++ b/vortex-buffer/src/bit/view.rs @@ -0,0 +1,546 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: Copyright the Vortex contributors + +use std::ops::Bound; +use std::ops::RangeBounds; + +use crate::BitBuffer; +use crate::BitBufferMeta; +use crate::BitBufferMut; +use crate::ByteBuffer; +use crate::bit::BitChunks; +use crate::bit::BitIndexIterator; +use crate::bit::BitIterator; +use crate::bit::BitSliceIterator; +use crate::bit::UnalignedBitChunk; +use crate::bit::buf_mut::fill_bits; +use crate::bit::count_ones::count_ones; +use crate::bit::get_bit_unchecked; +use crate::bit::select::bit_select; +use crate::bit::set_bit_unchecked; +use crate::bit::unset_bit_unchecked; + +/// Resolve `start..end` bounds against a logical length, panicking on invalid ranges. +#[inline] +fn resolve_range(range: impl RangeBounds, len: usize) -> (usize, usize) { + let start = match range.start_bound() { + Bound::Included(&s) => s, + Bound::Excluded(&s) => s + 1, + Bound::Unbounded => 0, + }; + let end = match range.end_bound() { + Bound::Included(&e) => e + 1, + Bound::Excluded(&e) => e, + Bound::Unbounded => len, + }; + + assert!(start <= end); + assert!(start <= len); + assert!(end <= len); + (start, end) +} + +/// Normalize a byte slice and bit offset so that the returned offset is `< 8`. +#[inline] +fn normalize(buffer: &[u8], offset: usize) -> (&[u8], usize) { + let byte_offset = offset / 8; + (&buffer[byte_offset..], offset % 8) +} + +/// An immutable, borrowed view over a packed bitset. +/// +/// This is the borrowing analogue of [`BitBuffer`]: it stores a byte slice together with a bit +/// `offset` (always `< 8`) and a logical bit `len`, without owning or reference-counting the +/// backing allocation. Use it to read a bitset without cloning the underlying [`ByteBuffer`]. +#[derive(Debug, Clone, Copy)] +pub struct BitBufferView<'a> { + buffer: &'a [u8], + offset: usize, + len: usize, +} + +impl<'a> BitBufferView<'a> { + /// Create a new view over `buffer` with `len` bits, starting at bit zero. + /// + /// Panics if the buffer is not large enough to hold `len` bits. + pub fn new(buffer: &'a [u8], len: usize) -> Self { + Self::new_with_offset(buffer, len, 0) + } + + /// Create a new view over `buffer` with `len` bits, starting at the given bit `offset`. + /// + /// Panics if the buffer is not large enough to hold `len` bits after the offset. + pub fn new_with_offset(buffer: &'a [u8], len: usize, offset: usize) -> Self { + assert!( + len.saturating_add(offset) <= buffer.len().saturating_mul(8), + "provided slice (len={}) not large enough to back BitBufferView with offset {offset} len {len}", + buffer.len() + ); + + let (buffer, offset) = normalize(buffer, offset); + Self { + buffer, + offset, + len, + } + } + + /// Create a new view over `buffer` described by `meta`. + pub fn from_meta(buffer: &'a [u8], meta: BitBufferMeta) -> Self { + Self::new_with_offset(buffer, meta.len(), meta.offset()) + } + + /// Returns the [`BitBufferMeta`] (offset and length) describing this view. + pub fn meta(&self) -> BitBufferMeta { + BitBufferMeta::new(self.offset, self.len) + } + + /// Get the logical length of this view in bits. + #[inline] + pub fn len(&self) -> usize { + self.len + } + + /// Returns `true` if the view is empty. + #[inline] + pub fn is_empty(&self) -> bool { + self.len == 0 + } + + /// Offset of the start of the view in bits. Always `< 8`. + #[inline(always)] + pub fn offset(&self) -> usize { + self.offset + } + + /// Get a reference to the underlying byte slice. + #[inline(always)] + pub fn inner(&self) -> &'a [u8] { + self.buffer + } + + /// Retrieve the value at the given index. + /// + /// Panics if the index is out of bounds. + #[inline] + pub fn value(&self, index: usize) -> bool { + assert!(index < self.len); + // SAFETY: checked by assertion + unsafe { self.value_unchecked(index) } + } + + /// Retrieve the value at the given index without bounds checking. + /// + /// # Safety + /// + /// Caller must ensure that `index` is within the range of the view. + #[inline] + pub unsafe fn value_unchecked(&self, index: usize) -> bool { + unsafe { get_bit_unchecked(self.buffer.as_ptr(), index + self.offset) } + } + + /// Create a new view over the range `[start, end)` of this view. + /// + /// Panics if the slice would extend beyond the end of the view. + pub fn slice(&self, range: impl RangeBounds) -> BitBufferView<'a> { + let (start, end) = resolve_range(range, self.len); + BitBufferView::new_with_offset(self.buffer, end - start, self.offset + start) + } + + /// Access chunks of the buffer aligned to an 8 byte boundary as + /// `[prefix, , suffix]`. + pub fn unaligned_chunks(&self) -> UnalignedBitChunk<'a> { + UnalignedBitChunk::new(self.buffer, self.offset, self.len) + } + + /// Access chunks of the underlying buffer as 8 byte chunks with a final trailer. + pub fn chunks(&self) -> BitChunks<'a> { + BitChunks::new(self.buffer, self.offset, self.len) + } + + /// Get the number of set bits in the view. + pub fn true_count(&self) -> usize { + count_ones(self.buffer, self.offset, self.len) + } + + /// Get the number of unset bits in the view. + pub fn false_count(&self) -> usize { + self.len - self.true_count() + } + + /// Returns the position of the `nth` set bit (0-indexed), or `None` if out of range. + pub fn select(&self, nth: usize) -> Option { + bit_select(self.buffer, self.offset, self.len, nth) + } + + /// Iterator over bits in the view. + pub fn iter(&self) -> BitIterator<'a> { + BitIterator::new(self.buffer, self.offset, self.len) + } + + /// Iterator over set indices of the underlying buffer. + pub fn set_indices(&self) -> BitIndexIterator<'a> { + BitIndexIterator::new(self.buffer, self.offset, self.len) + } + + /// Iterator over set slices of the underlying buffer. + pub fn set_slices(&self) -> BitSliceIterator<'a> { + BitSliceIterator::new(self.buffer, self.offset, self.len) + } + + /// Copy this view into an owned [`BitBuffer`]. + pub fn to_bit_buffer(&self) -> BitBuffer { + let bytes = (self.offset + self.len).div_ceil(8); + BitBuffer::new_with_offset( + ByteBuffer::copy_from(&self.buffer[..bytes]), + self.len, + self.offset, + ) + } +} + +impl<'a> IntoIterator for BitBufferView<'a> { + type Item = bool; + type IntoIter = BitIterator<'a>; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl PartialEq for BitBufferView<'_> { + fn eq(&self, other: &Self) -> bool { + if self.len != other.len { + return false; + } + + self.chunks() + .iter_padded() + .zip(other.chunks().iter_padded()) + .all(|(a, b)| a == b) + } +} + +impl Eq for BitBufferView<'_> {} + +/// A mutable, borrowed view over a packed bitset. +/// +/// This is the borrowing analogue of [`BitBufferMut`]: it stores a mutable byte slice together +/// with a bit `offset` (always `< 8`) and a logical bit `len`. Unlike [`BitBufferMut`] it cannot +/// grow or reallocate, so it only supports in-place reads and writes (such as +/// [`set`](Self::set), [`unset`](Self::unset), and [`fill_range`](Self::fill_range)). +#[derive(Debug)] +pub struct BitBufferMutView<'a> { + buffer: &'a mut [u8], + offset: usize, + len: usize, +} + +impl<'a> BitBufferMutView<'a> { + /// Create a new mutable view over `buffer` with `len` bits, starting at bit zero. + /// + /// Panics if the buffer is not large enough to hold `len` bits. + pub fn new(buffer: &'a mut [u8], len: usize) -> Self { + Self::new_with_offset(buffer, len, 0) + } + + /// Create a new mutable view over `buffer` with `len` bits, starting at bit `offset`. + /// + /// Panics if the buffer is not large enough to hold `len` bits after the offset. + pub fn new_with_offset(buffer: &'a mut [u8], len: usize, offset: usize) -> Self { + assert!( + len.saturating_add(offset) <= buffer.len().saturating_mul(8), + "provided slice (len={}) not large enough to back BitBufferMutView with offset {offset} len {len}", + buffer.len() + ); + + let byte_offset = offset / 8; + let offset = offset % 8; + Self { + buffer: &mut buffer[byte_offset..], + offset, + len, + } + } + + /// Borrow this mutable view as an immutable [`BitBufferView`]. + #[inline] + pub fn as_view(&self) -> BitBufferView<'_> { + BitBufferView { + buffer: self.buffer, + offset: self.offset, + len: self.len, + } + } + + /// Get the logical length of this view in bits. + #[inline] + pub fn len(&self) -> usize { + self.len + } + + /// Returns `true` if the view is empty. + #[inline] + pub fn is_empty(&self) -> bool { + self.len == 0 + } + + /// Offset of the start of the view in bits. Always `< 8`. + #[inline(always)] + pub fn offset(&self) -> usize { + self.offset + } + + /// Get the underlying bytes as a slice. + #[inline] + pub fn as_slice(&self) -> &[u8] { + self.buffer + } + + /// Get the underlying bytes as a mutable slice. + #[inline] + pub fn as_mut_slice(&mut self) -> &mut [u8] { + self.buffer + } + + /// Retrieve the value at the given index. + /// + /// Panics if the index is out of bounds. + #[inline] + pub fn value(&self, index: usize) -> bool { + assert!(index < self.len); + // SAFETY: checked by assertion + unsafe { self.value_unchecked(index) } + } + + /// Retrieve the value at the given index without bounds checking. + /// + /// # Safety + /// + /// Caller must ensure that `index` is within the range of the view. + #[inline] + pub unsafe fn value_unchecked(&self, index: usize) -> bool { + unsafe { get_bit_unchecked(self.buffer.as_ptr(), index + self.offset) } + } + + /// Get the number of set bits in the view. + pub fn true_count(&self) -> usize { + self.as_view().true_count() + } + + /// Get the number of unset bits in the view. + pub fn false_count(&self) -> usize { + self.as_view().false_count() + } + + /// Iterator over bits in the view. + pub fn iter(&self) -> BitIterator<'_> { + self.as_view().iter() + } + + /// Set the bit at `index` to the given boolean value. + /// + /// Panics if `index` exceeds the view length. + pub fn set_to(&mut self, index: usize, value: bool) { + if value { + self.set(index); + } else { + self.unset(index); + } + } + + /// Set the bit at `index` to the given boolean value without bounds checking. + /// + /// # Safety + /// + /// Caller must ensure that `index` is within the range of the view. + pub unsafe fn set_to_unchecked(&mut self, index: usize, value: bool) { + if value { + // SAFETY: checked by caller + unsafe { self.set_unchecked(index) } + } else { + // SAFETY: checked by caller + unsafe { self.unset_unchecked(index) } + } + } + + /// Set the bit at `index` to `true`. + /// + /// Panics if `index` exceeds the view length. + pub fn set(&mut self, index: usize) { + assert!(index < self.len, "index {index} exceeds len {}", self.len); + // SAFETY: checked by assertion + unsafe { self.set_unchecked(index) }; + } + + /// Set the bit at `index` to `false`. + /// + /// Panics if `index` exceeds the view length. + pub fn unset(&mut self, index: usize) { + assert!(index < self.len, "index {index} exceeds len {}", self.len); + // SAFETY: checked by assertion + unsafe { self.unset_unchecked(index) }; + } + + /// Set the bit at `index` to `true` without bounds checking. + /// + /// # Safety + /// + /// Caller must ensure that `index` is within the range of the view. + #[inline] + pub unsafe fn set_unchecked(&mut self, index: usize) { + // SAFETY: checked by caller + unsafe { set_bit_unchecked(self.buffer.as_mut_ptr(), self.offset + index) } + } + + /// Set the bit at `index` to `false` without bounds checking. + /// + /// # Safety + /// + /// Caller must ensure that `index` is within the range of the view. + #[inline] + pub unsafe fn unset_unchecked(&mut self, index: usize) { + // SAFETY: checked by caller + unsafe { unset_bit_unchecked(self.buffer.as_mut_ptr(), self.offset + index) } + } + + /// Sets all bits in the range `[start, end)` to `value`. + /// + /// Panics if `end > self.len()` or `start > end`. + #[inline(always)] + pub fn fill_range(&mut self, start: usize, end: usize, value: bool) { + assert!(end <= self.len, "end {end} exceeds len {}", self.len); + assert!(start <= end, "start {start} exceeds end {end}"); + // SAFETY: assertions guarantee start <= end <= self.len. + unsafe { self.fill_range_unchecked(start, end, value) } + } + + /// Sets all bits in the range `[start, end)` to `value` without bounds checking. + /// + /// # Safety + /// + /// Caller must ensure that `start <= end <= self.len()`. + #[inline(always)] + pub unsafe fn fill_range_unchecked(&mut self, start: usize, end: usize, value: bool) { + fill_bits(self.buffer, self.offset + start, self.offset + end, value); + } + + /// Copy this view into an owned [`BitBuffer`]. + pub fn to_bit_buffer(&self) -> BitBuffer { + self.as_view().to_bit_buffer() + } +} + +impl BitBuffer { + /// Borrow this buffer as a [`BitBufferView`] without cloning the backing allocation. + #[inline] + pub fn as_view(&self) -> BitBufferView<'_> { + BitBufferView { + buffer: self.inner().as_slice(), + offset: self.offset(), + len: self.len(), + } + } +} + +impl BitBufferMut { + /// Borrow this buffer as an immutable [`BitBufferView`]. + #[inline] + pub fn as_view(&self) -> BitBufferView<'_> { + BitBufferView { + buffer: self.as_slice(), + offset: self.offset(), + len: self.len(), + } + } + + /// Borrow this buffer as a [`BitBufferMutView`]. + #[inline] + pub fn as_mut_view(&mut self) -> BitBufferMutView<'_> { + let offset = self.offset(); + let len = self.len(); + BitBufferMutView { + buffer: self.as_mut_slice(), + offset, + len, + } + } +} + +#[cfg(test)] +mod tests { + use crate::BitBuffer; + use crate::BitBufferMut; + use crate::bitbuffer; + + #[test] + fn view_reads_match_buffer() { + let buffer = bitbuffer![true, false, true, true, false, true, false, false]; + let view = buffer.as_view(); + + assert_eq!(view.len(), buffer.len()); + assert_eq!(view.true_count(), buffer.true_count()); + assert_eq!(view.false_count(), buffer.false_count()); + for i in 0..buffer.len() { + assert_eq!(view.value(i), buffer.value(i)); + } + assert_eq!( + view.iter().collect::>(), + buffer.iter().collect::>() + ); + } + + #[test] + fn view_slice_preserves_offset() { + let buffer = BitBuffer::new_set(20); + let sliced = buffer.slice(5..17); + let view = buffer.as_view().slice(5..17); + + assert_eq!(view.len(), sliced.len()); + assert_eq!(view.true_count(), sliced.true_count()); + assert_eq!(view.to_bit_buffer(), sliced); + } + + #[test] + fn view_offset_buffer() { + let buffer = BitBuffer::new_set(64).slice(3..40); + let view = buffer.as_view(); + assert_eq!(view.offset(), buffer.offset()); + assert_eq!(view.len(), buffer.len()); + assert_eq!(view.to_bit_buffer(), buffer); + } + + #[test] + fn mut_view_set_unset() { + let mut buffer = BitBufferMut::new_unset(16); + { + let mut view = buffer.as_mut_view(); + view.set(0); + view.set(15); + view.set_to(7, true); + view.fill_range(2, 5, true); + assert!(view.value(0)); + assert_eq!(view.true_count(), 6); + view.unset(0); + } + let frozen = buffer.freeze(); + assert!(!frozen.value(0)); + assert!(frozen.value(2)); + assert!(frozen.value(4)); + assert!(frozen.value(7)); + assert!(frozen.value(15)); + assert_eq!(frozen.true_count(), 5); + } + + #[test] + fn mut_view_with_offset() { + let mut buffer = BitBufferMut::from_buffer(crate::buffer_mut![0u8; 4], 3, 20); + { + let mut view = buffer.as_mut_view(); + assert_eq!(view.offset(), 3); + view.fill_range(0, 20, true); + } + let frozen = buffer.freeze(); + assert_eq!(frozen.true_count(), 20); + } +} diff --git a/vortex-cuda/src/arrow/canonical.rs b/vortex-cuda/src/arrow/canonical.rs index 8d8b3779ec4..b8ad7f40ffa 100644 --- a/vortex-cuda/src/arrow/canonical.rs +++ b/vortex-cuda/src/arrow/canonical.rs @@ -139,15 +139,20 @@ fn export_canonical( Canonical::Bool(bool_array) => { let len = bool_array.len(); let validity = bool_array.validity()?; - let BoolDataParts { - bits, offset, len, .. - } = bool_array.into_data().into_parts(len); + let BoolDataParts { bits, meta } = bool_array.into_data().into_parts(len); let (validity_buffer, null_count) = - export_arrow_validity_buffer(validity, len, offset, ctx).await?; + export_arrow_validity_buffer(validity, len, meta.offset(), ctx).await?; let bits = ctx.ensure_on_device(bits).await?; - export_fixed_size(bits, len, offset, validity_buffer, null_count, ctx) + export_fixed_size( + bits, + meta.len(), + meta.offset(), + validity_buffer, + null_count, + ctx, + ) } Canonical::VarBinView(varbinview) => { let len = varbinview.len(); diff --git a/vortex-cuda/src/canonical.rs b/vortex-cuda/src/canonical.rs index 591f1be404f..4f3d6fd37e3 100644 --- a/vortex-cuda/src/canonical.rs +++ b/vortex-cuda/src/canonical.rs @@ -75,11 +75,13 @@ impl CanonicalCudaExt for Canonical { // Also update other method to copy validity to host. let len = bool.len(); let validity = bool.validity()?; - let BoolDataParts { - bits, offset, len, .. - } = bool.into_data().into_parts(len); + let BoolDataParts { bits, meta } = bool.into_data().into_parts(len); - let bits = BitBuffer::new_with_offset(bits.try_into_host()?.await?, offset, len); + let bits = BitBuffer::new_with_offset( + bits.try_into_host()?.await?, + meta.len(), + meta.offset(), + ); Ok(Canonical::Bool(BoolArray::new(bits, validity))) } Canonical::Primitive(prim) => { diff --git a/vortex-duckdb/src/exporter/struct_.rs b/vortex-duckdb/src/exporter/struct_.rs index f18c5495754..76c07d672a3 100644 --- a/vortex-duckdb/src/exporter/struct_.rs +++ b/vortex-duckdb/src/exporter/struct_.rs @@ -43,7 +43,7 @@ pub(crate) fn new_exporter( let children = fields .iter() .map(|child| { - if validity.to_bit_buffer().true_count() != validity.len() { + if validity.bit_buffer_view().true_count() != validity.len() { // TODO(joe): use new mask. new_array_exporter( child.clone().mask(validity.clone().into_array())?, From b29b2b9b3c66ed9397e8f5249b5c606f31176d7f Mon Sep 17 00:00:00 2001 From: Onur Satici Date: Tue, 2 Jun 2026 14:21:20 +0100 Subject: [PATCH 002/115] layout reader ctx (#8037) ## Summary Introduce `LayoutReaderContext`, a typed-data registry threaded through reader construction so an ancestor layout can publish values that descendant layouts look up by type at construction time. Breaking: Layout::new_reader and VTable::new_reader gain a &LayoutReaderContext parameter. Out-of-tree impls will get a compile error and must add it --------- Signed-off-by: Onur Satici --- vortex-cuda/src/layout.rs | 1 + vortex-file/src/file.rs | 7 +- vortex-file/src/v2/file_stats_reader.rs | 8 +- vortex-layout/public-api.lock | 2203 +++++++++++++++++++ vortex-layout/src/layout.rs | 19 +- vortex-layout/src/layouts/chunked/mod.rs | 3 + vortex-layout/src/layouts/chunked/reader.rs | 12 +- vortex-layout/src/layouts/dict/mod.rs | 2 + vortex-layout/src/layouts/dict/reader.rs | 18 +- vortex-layout/src/layouts/flat/mod.rs | 1 + vortex-layout/src/layouts/flat/reader.rs | 6 +- vortex-layout/src/layouts/flat/writer.rs | 6 +- vortex-layout/src/layouts/foreign/mod.rs | 1 + vortex-layout/src/layouts/row_idx/mod.rs | 12 +- vortex-layout/src/layouts/struct_/mod.rs | 2 + vortex-layout/src/layouts/struct_/reader.rs | 34 +- vortex-layout/src/layouts/zoned/mod.rs | 2 + vortex-layout/src/layouts/zoned/reader.rs | 11 +- vortex-layout/src/lib.rs | 2 + vortex-layout/src/reader.rs | 5 + vortex-layout/src/reader_context.rs | 60 + vortex-layout/src/scan/split_by.rs | 2 +- vortex-layout/src/vtable.rs | 10 + vortex-test/compat-gen/src/adapter.rs | 7 +- vortex-tui/src/browse/app.rs | 7 +- vortex-tui/src/wasm.rs | 7 +- vortex-web/crate/src/wasm.rs | 7 +- 27 files changed, 2415 insertions(+), 40 deletions(-) create mode 100644 vortex-layout/public-api.lock create mode 100644 vortex-layout/src/reader_context.rs diff --git a/vortex-cuda/src/layout.rs b/vortex-cuda/src/layout.rs index 5cc9106c535..a4483a3d8e7 100644 --- a/vortex-cuda/src/layout.rs +++ b/vortex-cuda/src/layout.rs @@ -180,6 +180,7 @@ impl VTable for CudaFlat { name: Arc, segment_source: Arc, session: &VortexSession, + _ctx: &vortex::layout::LayoutReaderContext, ) -> VortexResult { Ok(Arc::new(CudaFlatReader { layout: layout.clone(), diff --git a/vortex-file/src/file.rs b/vortex-file/src/file.rs index cbebe0297bb..4e5a9e99049 100644 --- a/vortex-file/src/file.rs +++ b/vortex-file/src/file.rs @@ -112,7 +112,12 @@ impl VortexFile { .footer .layout() // TODO(ngates): we may want to allow the user pass in a name here? - .new_reader("".into(), segment_source, &self.session)?; + .new_reader( + "".into(), + segment_source, + &self.session, + &Default::default(), + )?; Ok(if let Some(stats) = self.file_stats().cloned() { Arc::new(FileStatsLayoutReader::new( diff --git a/vortex-file/src/v2/file_stats_reader.rs b/vortex-file/src/v2/file_stats_reader.rs index a6ab9ec79df..0121c12b07d 100644 --- a/vortex-file/src/v2/file_stats_reader.rs +++ b/vortex-file/src/v2/file_stats_reader.rs @@ -310,7 +310,7 @@ mod tests { ) .await?; - let child = layout.new_reader("".into(), segments, &SESSION)?; + let child = layout.new_reader("".into(), segments, &SESSION, &Default::default())?; let reader = FileStatsLayoutReader::new(child, test_file_stats(0, 100), SESSION.clone()); @@ -349,7 +349,7 @@ mod tests { ) .await?; - let child = layout.new_reader("".into(), segments, &SESSION)?; + let child = layout.new_reader("".into(), segments, &SESSION, &Default::default())?; let reader = FileStatsLayoutReader::new(child, test_file_stats(0, 100), SESSION.clone()); @@ -400,7 +400,7 @@ mod tests { ) .await?; - let child = layout.new_reader("".into(), segments, &SESSION)?; + let child = layout.new_reader("".into(), segments, &SESSION, &Default::default())?; // File-level stats: 1 null in deleted_at. let mut stats = StatsSet::default(); @@ -449,7 +449,7 @@ mod tests { ) .await?; - let child = layout.new_reader("".into(), segments, &SESSION)?; + let child = layout.new_reader("".into(), segments, &SESSION, &Default::default())?; let reader = FileStatsLayoutReader::new(child, test_file_null_count_stats(5), SESSION.clone()); diff --git a/vortex-layout/public-api.lock b/vortex-layout/public-api.lock new file mode 100644 index 00000000000..c794e93aa41 --- /dev/null +++ b/vortex-layout/public-api.lock @@ -0,0 +1,2203 @@ +pub mod vortex_layout + +pub mod vortex_layout::aliases + +pub mod vortex_layout::aliases::paste + +pub use vortex_layout::aliases::paste::paste + +pub mod vortex_layout::display + +pub struct vortex_layout::display::DisplayLayoutTree + +impl vortex_layout::display::DisplayLayoutTree + +pub fn vortex_layout::display::DisplayLayoutTree::new(vortex_layout::LayoutRef, bool) -> Self + +impl core::fmt::Display for vortex_layout::display::DisplayLayoutTree + +pub fn vortex_layout::display::DisplayLayoutTree::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result + +pub mod vortex_layout::layouts + +pub mod vortex_layout::layouts::buffered + +pub struct vortex_layout::layouts::buffered::BufferedStrategy + +impl vortex_layout::layouts::buffered::BufferedStrategy + +pub fn vortex_layout::layouts::buffered::BufferedStrategy::new(S, u64) -> Self + +impl core::clone::Clone for vortex_layout::layouts::buffered::BufferedStrategy + +pub fn vortex_layout::layouts::buffered::BufferedStrategy::clone(&self) -> vortex_layout::layouts::buffered::BufferedStrategy + +impl vortex_layout::LayoutStrategy for vortex_layout::layouts::buffered::BufferedStrategy + +pub fn vortex_layout::layouts::buffered::BufferedStrategy::buffered_bytes(&self) -> u64 + +pub fn vortex_layout::layouts::buffered::BufferedStrategy::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait + +pub mod vortex_layout::layouts::chunked + +pub mod vortex_layout::layouts::chunked::writer + +pub struct vortex_layout::layouts::chunked::writer::ChunkedLayoutStrategy + +pub vortex_layout::layouts::chunked::writer::ChunkedLayoutStrategy::chunk_strategy: alloc::sync::Arc + +impl vortex_layout::layouts::chunked::writer::ChunkedLayoutStrategy + +pub fn vortex_layout::layouts::chunked::writer::ChunkedLayoutStrategy::new(S) -> Self + +impl core::clone::Clone for vortex_layout::layouts::chunked::writer::ChunkedLayoutStrategy + +pub fn vortex_layout::layouts::chunked::writer::ChunkedLayoutStrategy::clone(&self) -> vortex_layout::layouts::chunked::writer::ChunkedLayoutStrategy + +impl vortex_layout::LayoutStrategy for vortex_layout::layouts::chunked::writer::ChunkedLayoutStrategy + +pub fn vortex_layout::layouts::chunked::writer::ChunkedLayoutStrategy::buffered_bytes(&self) -> u64 + +pub fn vortex_layout::layouts::chunked::writer::ChunkedLayoutStrategy::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait + +pub struct vortex_layout::layouts::chunked::Chunked + +impl core::fmt::Debug for vortex_layout::layouts::chunked::Chunked + +pub fn vortex_layout::layouts::chunked::Chunked::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result + +impl vortex_layout::VTable for vortex_layout::layouts::chunked::Chunked + +pub type vortex_layout::layouts::chunked::Chunked::Encoding = vortex_layout::layouts::chunked::ChunkedLayoutEncoding + +pub type vortex_layout::layouts::chunked::Chunked::Layout = vortex_layout::layouts::chunked::ChunkedLayout + +pub type vortex_layout::layouts::chunked::Chunked::Metadata = vortex_array::metadata::EmptyMetadata + +pub fn vortex_layout::layouts::chunked::Chunked::build(&Self::Encoding, &vortex_array::dtype::DType, u64, &::Output, alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::chunked::Chunked::child(&Self::Layout, usize) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::chunked::Chunked::child_type(&Self::Layout, usize) -> vortex_layout::LayoutChildType + +pub fn vortex_layout::layouts::chunked::Chunked::dtype(&Self::Layout) -> &vortex_array::dtype::DType + +pub fn vortex_layout::layouts::chunked::Chunked::encoding(&Self::Layout) -> vortex_layout::LayoutEncodingRef + +pub fn vortex_layout::layouts::chunked::Chunked::id(&Self::Encoding) -> vortex_layout::LayoutId + +pub fn vortex_layout::layouts::chunked::Chunked::metadata(&Self::Layout) -> Self::Metadata + +pub fn vortex_layout::layouts::chunked::Chunked::nchildren(&Self::Layout) -> usize + +pub fn vortex_layout::layouts::chunked::Chunked::new_reader(&Self::Layout, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::chunked::Chunked::row_count(&Self::Layout) -> u64 + +pub fn vortex_layout::layouts::chunked::Chunked::segment_ids(&Self::Layout) -> alloc::vec::Vec + +pub fn vortex_layout::layouts::chunked::Chunked::with_children(&mut Self::Layout, alloc::vec::Vec) -> vortex_error::VortexResult<()> + +pub struct vortex_layout::layouts::chunked::ChunkedLayout + +impl vortex_layout::layouts::chunked::ChunkedLayout + +pub fn vortex_layout::layouts::chunked::ChunkedLayout::children(&self) -> &alloc::sync::Arc + +pub fn vortex_layout::layouts::chunked::ChunkedLayout::new(u64, vortex_array::dtype::DType, alloc::sync::Arc) -> Self + +impl core::clone::Clone for vortex_layout::layouts::chunked::ChunkedLayout + +pub fn vortex_layout::layouts::chunked::ChunkedLayout::clone(&self) -> vortex_layout::layouts::chunked::ChunkedLayout + +impl core::convert::AsRef for vortex_layout::layouts::chunked::ChunkedLayout + +pub fn vortex_layout::layouts::chunked::ChunkedLayout::as_ref(&self) -> &dyn vortex_layout::Layout + +impl core::convert::From for vortex_layout::LayoutRef + +pub fn vortex_layout::LayoutRef::from(vortex_layout::layouts::chunked::ChunkedLayout) -> vortex_layout::LayoutRef + +impl core::fmt::Debug for vortex_layout::layouts::chunked::ChunkedLayout + +pub fn vortex_layout::layouts::chunked::ChunkedLayout::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result + +impl core::ops::deref::Deref for vortex_layout::layouts::chunked::ChunkedLayout + +pub type vortex_layout::layouts::chunked::ChunkedLayout::Target = dyn vortex_layout::Layout + +pub fn vortex_layout::layouts::chunked::ChunkedLayout::deref(&self) -> &Self::Target + +impl vortex_layout::IntoLayout for vortex_layout::layouts::chunked::ChunkedLayout + +pub fn vortex_layout::layouts::chunked::ChunkedLayout::into_layout(self) -> vortex_layout::LayoutRef + +pub struct vortex_layout::layouts::chunked::ChunkedLayoutEncoding + +impl core::convert::AsRef for vortex_layout::layouts::chunked::ChunkedLayoutEncoding + +pub fn vortex_layout::layouts::chunked::ChunkedLayoutEncoding::as_ref(&self) -> &dyn vortex_layout::LayoutEncoding + +impl core::fmt::Debug for vortex_layout::layouts::chunked::ChunkedLayoutEncoding + +pub fn vortex_layout::layouts::chunked::ChunkedLayoutEncoding::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result + +impl core::ops::deref::Deref for vortex_layout::layouts::chunked::ChunkedLayoutEncoding + +pub type vortex_layout::layouts::chunked::ChunkedLayoutEncoding::Target = dyn vortex_layout::LayoutEncoding + +pub fn vortex_layout::layouts::chunked::ChunkedLayoutEncoding::deref(&self) -> &Self::Target + +pub mod vortex_layout::layouts::collect + +pub struct vortex_layout::layouts::collect::CollectStrategy + +impl vortex_layout::layouts::collect::CollectStrategy + +pub fn vortex_layout::layouts::collect::CollectStrategy::new(S) -> vortex_layout::layouts::collect::CollectStrategy + +impl vortex_layout::LayoutStrategy for vortex_layout::layouts::collect::CollectStrategy + +pub fn vortex_layout::layouts::collect::CollectStrategy::buffered_bytes(&self) -> u64 + +pub fn vortex_layout::layouts::collect::CollectStrategy::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait + +pub mod vortex_layout::layouts::compressed + +pub struct vortex_layout::layouts::compressed::CompressingStrategy + +impl vortex_layout::layouts::compressed::CompressingStrategy + +pub fn vortex_layout::layouts::compressed::CompressingStrategy::new(S, C) -> Self + +pub fn vortex_layout::layouts::compressed::CompressingStrategy::with_concurrency(self, usize) -> Self + +pub fn vortex_layout::layouts::compressed::CompressingStrategy::with_stats(self, &[vortex_array::expr::stats::Stat]) -> Self + +impl core::clone::Clone for vortex_layout::layouts::compressed::CompressingStrategy + +pub fn vortex_layout::layouts::compressed::CompressingStrategy::clone(&self) -> vortex_layout::layouts::compressed::CompressingStrategy + +impl vortex_layout::LayoutStrategy for vortex_layout::layouts::compressed::CompressingStrategy + +pub fn vortex_layout::layouts::compressed::CompressingStrategy::buffered_bytes(&self) -> u64 + +pub fn vortex_layout::layouts::compressed::CompressingStrategy::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait + +pub trait vortex_layout::layouts::compressed::CompressorPlugin: core::marker::Send + core::marker::Sync + 'static + +pub fn vortex_layout::layouts::compressed::CompressorPlugin::compress_chunk(&self, &vortex_array::array::erased::ArrayRef, &mut vortex_array::executor::ExecutionCtx) -> vortex_error::VortexResult + +impl vortex_layout::layouts::compressed::CompressorPlugin for alloc::sync::Arc + +pub fn alloc::sync::Arc::compress_chunk(&self, &vortex_array::array::erased::ArrayRef, &mut vortex_array::executor::ExecutionCtx) -> vortex_error::VortexResult + +impl vortex_layout::layouts::compressed::CompressorPlugin for vortex_btrblocks::canonical_compressor::BtrBlocksCompressor + +pub fn vortex_btrblocks::canonical_compressor::BtrBlocksCompressor::compress_chunk(&self, &vortex_array::array::erased::ArrayRef, &mut vortex_array::executor::ExecutionCtx) -> vortex_error::VortexResult + +impl vortex_layout::layouts::compressed::CompressorPlugin for F where F: core::ops::function::Fn(&vortex_array::array::erased::ArrayRef, &mut vortex_array::executor::ExecutionCtx) -> vortex_error::VortexResult + core::marker::Send + core::marker::Sync + 'static + +pub fn F::compress_chunk(&self, &vortex_array::array::erased::ArrayRef, &mut vortex_array::executor::ExecutionCtx) -> vortex_error::VortexResult + +pub mod vortex_layout::layouts::dict + +pub mod vortex_layout::layouts::dict::writer + +pub struct vortex_layout::layouts::dict::writer::DictLayoutConstraints + +pub vortex_layout::layouts::dict::writer::DictLayoutConstraints::max_bytes: usize + +pub vortex_layout::layouts::dict::writer::DictLayoutConstraints::max_len: u16 + +impl core::clone::Clone for vortex_layout::layouts::dict::writer::DictLayoutConstraints + +pub fn vortex_layout::layouts::dict::writer::DictLayoutConstraints::clone(&self) -> vortex_layout::layouts::dict::writer::DictLayoutConstraints + +impl core::convert::From for vortex_array::builders::dict::DictConstraints + +pub fn vortex_array::builders::dict::DictConstraints::from(vortex_layout::layouts::dict::writer::DictLayoutConstraints) -> Self + +impl core::default::Default for vortex_layout::layouts::dict::writer::DictLayoutConstraints + +pub fn vortex_layout::layouts::dict::writer::DictLayoutConstraints::default() -> Self + +pub struct vortex_layout::layouts::dict::writer::DictLayoutMetadata + +impl vortex_layout::layouts::dict::writer::DictLayoutMetadata + +pub fn vortex_layout::layouts::dict::writer::DictLayoutMetadata::codes_ptype(&self) -> vortex_array::dtype::ptype::PType + +pub fn vortex_layout::layouts::dict::writer::DictLayoutMetadata::set_codes_ptype(&mut self, vortex_array::dtype::ptype::PType) + +impl vortex_layout::layouts::dict::writer::DictLayoutMetadata + +pub fn vortex_layout::layouts::dict::writer::DictLayoutMetadata::new(vortex_array::dtype::ptype::PType) -> Self + +impl core::default::Default for vortex_layout::layouts::dict::writer::DictLayoutMetadata + +pub fn vortex_layout::layouts::dict::writer::DictLayoutMetadata::default() -> Self + +impl core::fmt::Debug for vortex_layout::layouts::dict::writer::DictLayoutMetadata + +pub fn vortex_layout::layouts::dict::writer::DictLayoutMetadata::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result + +impl prost::message::Message for vortex_layout::layouts::dict::writer::DictLayoutMetadata + +pub fn vortex_layout::layouts::dict::writer::DictLayoutMetadata::clear(&mut self) + +pub fn vortex_layout::layouts::dict::writer::DictLayoutMetadata::encoded_len(&self) -> usize + +pub struct vortex_layout::layouts::dict::writer::DictLayoutOptions + +pub vortex_layout::layouts::dict::writer::DictLayoutOptions::constraints: vortex_layout::layouts::dict::writer::DictLayoutConstraints + +impl core::clone::Clone for vortex_layout::layouts::dict::writer::DictLayoutOptions + +pub fn vortex_layout::layouts::dict::writer::DictLayoutOptions::clone(&self) -> vortex_layout::layouts::dict::writer::DictLayoutOptions + +impl core::default::Default for vortex_layout::layouts::dict::writer::DictLayoutOptions + +pub fn vortex_layout::layouts::dict::writer::DictLayoutOptions::default() -> vortex_layout::layouts::dict::writer::DictLayoutOptions + +pub struct vortex_layout::layouts::dict::writer::DictStrategy + +impl vortex_layout::layouts::dict::writer::DictStrategy + +pub fn vortex_layout::layouts::dict::writer::DictStrategy::new(Codes, Values, Fallback, vortex_layout::layouts::dict::writer::DictLayoutOptions) -> Self + +impl core::clone::Clone for vortex_layout::layouts::dict::writer::DictStrategy + +pub fn vortex_layout::layouts::dict::writer::DictStrategy::clone(&self) -> vortex_layout::layouts::dict::writer::DictStrategy + +impl vortex_layout::LayoutStrategy for vortex_layout::layouts::dict::writer::DictStrategy + +pub fn vortex_layout::layouts::dict::writer::DictStrategy::buffered_bytes(&self) -> u64 + +pub fn vortex_layout::layouts::dict::writer::DictStrategy::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait + +pub fn vortex_layout::layouts::dict::writer::dict_layout_supported(&vortex_array::dtype::DType) -> bool + +pub struct vortex_layout::layouts::dict::Dict + +impl core::fmt::Debug for vortex_layout::layouts::dict::Dict + +pub fn vortex_layout::layouts::dict::Dict::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result + +impl vortex_layout::VTable for vortex_layout::layouts::dict::Dict + +pub type vortex_layout::layouts::dict::Dict::Encoding = vortex_layout::layouts::dict::DictLayoutEncoding + +pub type vortex_layout::layouts::dict::Dict::Layout = vortex_layout::layouts::dict::DictLayout + +pub type vortex_layout::layouts::dict::Dict::Metadata = vortex_array::metadata::ProstMetadata + +pub fn vortex_layout::layouts::dict::Dict::build(&Self::Encoding, &vortex_array::dtype::DType, u64, &::Output, alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::dict::Dict::child(&Self::Layout, usize) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::dict::Dict::child_type(&Self::Layout, usize) -> vortex_layout::LayoutChildType + +pub fn vortex_layout::layouts::dict::Dict::dtype(&Self::Layout) -> &vortex_array::dtype::DType + +pub fn vortex_layout::layouts::dict::Dict::encoding(&Self::Layout) -> vortex_layout::LayoutEncodingRef + +pub fn vortex_layout::layouts::dict::Dict::id(&Self::Encoding) -> vortex_layout::LayoutId + +pub fn vortex_layout::layouts::dict::Dict::metadata(&Self::Layout) -> Self::Metadata + +pub fn vortex_layout::layouts::dict::Dict::nchildren(&Self::Layout) -> usize + +pub fn vortex_layout::layouts::dict::Dict::new_reader(&Self::Layout, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::dict::Dict::row_count(&Self::Layout) -> u64 + +pub fn vortex_layout::layouts::dict::Dict::segment_ids(&Self::Layout) -> alloc::vec::Vec + +pub fn vortex_layout::layouts::dict::Dict::with_children(&mut Self::Layout, alloc::vec::Vec) -> vortex_error::VortexResult<()> + +pub struct vortex_layout::layouts::dict::DictLayout + +impl vortex_layout::layouts::dict::DictLayout + +pub fn vortex_layout::layouts::dict::DictLayout::has_all_values_referenced(&self) -> bool + +pub unsafe fn vortex_layout::layouts::dict::DictLayout::set_all_values_referenced(self, bool) -> Self + +impl core::clone::Clone for vortex_layout::layouts::dict::DictLayout + +pub fn vortex_layout::layouts::dict::DictLayout::clone(&self) -> vortex_layout::layouts::dict::DictLayout + +impl core::convert::AsRef for vortex_layout::layouts::dict::DictLayout + +pub fn vortex_layout::layouts::dict::DictLayout::as_ref(&self) -> &dyn vortex_layout::Layout + +impl core::convert::From for vortex_layout::LayoutRef + +pub fn vortex_layout::LayoutRef::from(vortex_layout::layouts::dict::DictLayout) -> vortex_layout::LayoutRef + +impl core::fmt::Debug for vortex_layout::layouts::dict::DictLayout + +pub fn vortex_layout::layouts::dict::DictLayout::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result + +impl core::ops::deref::Deref for vortex_layout::layouts::dict::DictLayout + +pub type vortex_layout::layouts::dict::DictLayout::Target = dyn vortex_layout::Layout + +pub fn vortex_layout::layouts::dict::DictLayout::deref(&self) -> &Self::Target + +impl vortex_layout::IntoLayout for vortex_layout::layouts::dict::DictLayout + +pub fn vortex_layout::layouts::dict::DictLayout::into_layout(self) -> vortex_layout::LayoutRef + +pub struct vortex_layout::layouts::dict::DictLayoutEncoding + +impl core::convert::AsRef for vortex_layout::layouts::dict::DictLayoutEncoding + +pub fn vortex_layout::layouts::dict::DictLayoutEncoding::as_ref(&self) -> &dyn vortex_layout::LayoutEncoding + +impl core::fmt::Debug for vortex_layout::layouts::dict::DictLayoutEncoding + +pub fn vortex_layout::layouts::dict::DictLayoutEncoding::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result + +impl core::ops::deref::Deref for vortex_layout::layouts::dict::DictLayoutEncoding + +pub type vortex_layout::layouts::dict::DictLayoutEncoding::Target = dyn vortex_layout::LayoutEncoding + +pub fn vortex_layout::layouts::dict::DictLayoutEncoding::deref(&self) -> &Self::Target + +pub struct vortex_layout::layouts::dict::DictLayoutMetadata + +impl vortex_layout::layouts::dict::DictLayoutMetadata + +pub fn vortex_layout::layouts::dict::DictLayoutMetadata::all_values_referenced(&self) -> bool + +pub fn vortex_layout::layouts::dict::DictLayoutMetadata::codes_ptype(&self) -> vortex_array::dtype::ptype::PType + +pub fn vortex_layout::layouts::dict::DictLayoutMetadata::is_nullable_codes(&self) -> bool + +pub fn vortex_layout::layouts::dict::DictLayoutMetadata::set_codes_ptype(&mut self, vortex_array::dtype::ptype::PType) + +impl vortex_layout::layouts::dict::DictLayoutMetadata + +pub fn vortex_layout::layouts::dict::DictLayoutMetadata::new(vortex_array::dtype::ptype::PType) -> Self + +impl core::default::Default for vortex_layout::layouts::dict::DictLayoutMetadata + +pub fn vortex_layout::layouts::dict::DictLayoutMetadata::default() -> Self + +impl core::fmt::Debug for vortex_layout::layouts::dict::DictLayoutMetadata + +pub fn vortex_layout::layouts::dict::DictLayoutMetadata::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result + +impl prost::message::Message for vortex_layout::layouts::dict::DictLayoutMetadata + +pub fn vortex_layout::layouts::dict::DictLayoutMetadata::clear(&mut self) + +pub fn vortex_layout::layouts::dict::DictLayoutMetadata::encoded_len(&self) -> usize + +pub mod vortex_layout::layouts::file_stats + +pub struct vortex_layout::layouts::file_stats::FileStatsAccumulator + +impl vortex_layout::layouts::file_stats::FileStatsAccumulator + +pub fn vortex_layout::layouts::file_stats::FileStatsAccumulator::stats_sets(&self) -> alloc::vec::Vec + +impl core::clone::Clone for vortex_layout::layouts::file_stats::FileStatsAccumulator + +pub fn vortex_layout::layouts::file_stats::FileStatsAccumulator::clone(&self) -> vortex_layout::layouts::file_stats::FileStatsAccumulator + +pub fn vortex_layout::layouts::file_stats::accumulate_stats(vortex_layout::sequence::SendableSequentialStream, alloc::sync::Arc<[vortex_array::expr::stats::Stat]>, usize, &vortex_session::VortexSession) -> (vortex_layout::layouts::file_stats::FileStatsAccumulator, vortex_layout::sequence::SendableSequentialStream) + +pub mod vortex_layout::layouts::flat + +pub mod vortex_layout::layouts::flat::writer + +pub struct vortex_layout::layouts::flat::writer::FlatLayoutStrategy + +pub vortex_layout::layouts::flat::writer::FlatLayoutStrategy::allowed_encodings: core::option::Option> + +pub vortex_layout::layouts::flat::writer::FlatLayoutStrategy::include_padding: bool + +pub vortex_layout::layouts::flat::writer::FlatLayoutStrategy::max_variable_length_statistics_size: usize + +impl vortex_layout::layouts::flat::writer::FlatLayoutStrategy + +pub fn vortex_layout::layouts::flat::writer::FlatLayoutStrategy::with_allow_encodings(self, vortex_utils::aliases::hash_set::HashSet) -> Self + +pub fn vortex_layout::layouts::flat::writer::FlatLayoutStrategy::with_include_padding(self, bool) -> Self + +pub fn vortex_layout::layouts::flat::writer::FlatLayoutStrategy::with_max_variable_length_statistics_size(self, usize) -> Self + +impl core::clone::Clone for vortex_layout::layouts::flat::writer::FlatLayoutStrategy + +pub fn vortex_layout::layouts::flat::writer::FlatLayoutStrategy::clone(&self) -> vortex_layout::layouts::flat::writer::FlatLayoutStrategy + +impl core::default::Default for vortex_layout::layouts::flat::writer::FlatLayoutStrategy + +pub fn vortex_layout::layouts::flat::writer::FlatLayoutStrategy::default() -> Self + +impl vortex_layout::LayoutStrategy for vortex_layout::layouts::flat::writer::FlatLayoutStrategy + +pub fn vortex_layout::layouts::flat::writer::FlatLayoutStrategy::buffered_bytes(&self) -> u64 + +pub fn vortex_layout::layouts::flat::writer::FlatLayoutStrategy::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait + +pub struct vortex_layout::layouts::flat::Flat + +impl core::fmt::Debug for vortex_layout::layouts::flat::Flat + +pub fn vortex_layout::layouts::flat::Flat::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result + +impl vortex_layout::VTable for vortex_layout::layouts::flat::Flat + +pub type vortex_layout::layouts::flat::Flat::Encoding = vortex_layout::layouts::flat::FlatLayoutEncoding + +pub type vortex_layout::layouts::flat::Flat::Layout = vortex_layout::layouts::flat::FlatLayout + +pub type vortex_layout::layouts::flat::Flat::Metadata = vortex_array::metadata::ProstMetadata + +pub fn vortex_layout::layouts::flat::Flat::build(&Self::Encoding, &vortex_array::dtype::DType, u64, &::Output, alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::flat::Flat::child(&Self::Layout, usize) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::flat::Flat::child_type(&Self::Layout, usize) -> vortex_layout::LayoutChildType + +pub fn vortex_layout::layouts::flat::Flat::dtype(&Self::Layout) -> &vortex_array::dtype::DType + +pub fn vortex_layout::layouts::flat::Flat::encoding(&Self::Layout) -> vortex_layout::LayoutEncodingRef + +pub fn vortex_layout::layouts::flat::Flat::id(&Self::Encoding) -> vortex_layout::LayoutId + +pub fn vortex_layout::layouts::flat::Flat::metadata(&Self::Layout) -> Self::Metadata + +pub fn vortex_layout::layouts::flat::Flat::nchildren(&Self::Layout) -> usize + +pub fn vortex_layout::layouts::flat::Flat::new_reader(&Self::Layout, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::flat::Flat::row_count(&Self::Layout) -> u64 + +pub fn vortex_layout::layouts::flat::Flat::segment_ids(&Self::Layout) -> alloc::vec::Vec + +pub fn vortex_layout::layouts::flat::Flat::with_children(&mut Self::Layout, alloc::vec::Vec) -> vortex_error::VortexResult<()> + +pub struct vortex_layout::layouts::flat::FlatLayout + +impl vortex_layout::layouts::flat::FlatLayout + +pub fn vortex_layout::layouts::flat::FlatLayout::array_ctx(&self) -> &vortex_session::registry::ReadContext + +pub fn vortex_layout::layouts::flat::FlatLayout::array_tree(&self) -> core::option::Option<&vortex_buffer::ByteBuffer> + +pub fn vortex_layout::layouts::flat::FlatLayout::new(u64, vortex_array::dtype::DType, vortex_layout::segments::SegmentId, vortex_session::registry::ReadContext) -> Self + +pub fn vortex_layout::layouts::flat::FlatLayout::new_with_metadata(u64, vortex_array::dtype::DType, vortex_layout::segments::SegmentId, vortex_session::registry::ReadContext, core::option::Option) -> Self + +pub fn vortex_layout::layouts::flat::FlatLayout::segment_id(&self) -> vortex_layout::segments::SegmentId + +impl core::clone::Clone for vortex_layout::layouts::flat::FlatLayout + +pub fn vortex_layout::layouts::flat::FlatLayout::clone(&self) -> vortex_layout::layouts::flat::FlatLayout + +impl core::convert::AsRef for vortex_layout::layouts::flat::FlatLayout + +pub fn vortex_layout::layouts::flat::FlatLayout::as_ref(&self) -> &dyn vortex_layout::Layout + +impl core::convert::From for vortex_layout::LayoutRef + +pub fn vortex_layout::LayoutRef::from(vortex_layout::layouts::flat::FlatLayout) -> vortex_layout::LayoutRef + +impl core::fmt::Debug for vortex_layout::layouts::flat::FlatLayout + +pub fn vortex_layout::layouts::flat::FlatLayout::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result + +impl core::ops::deref::Deref for vortex_layout::layouts::flat::FlatLayout + +pub type vortex_layout::layouts::flat::FlatLayout::Target = dyn vortex_layout::Layout + +pub fn vortex_layout::layouts::flat::FlatLayout::deref(&self) -> &Self::Target + +impl vortex_layout::IntoLayout for vortex_layout::layouts::flat::FlatLayout + +pub fn vortex_layout::layouts::flat::FlatLayout::into_layout(self) -> vortex_layout::LayoutRef + +pub struct vortex_layout::layouts::flat::FlatLayoutEncoding + +impl core::convert::AsRef for vortex_layout::layouts::flat::FlatLayoutEncoding + +pub fn vortex_layout::layouts::flat::FlatLayoutEncoding::as_ref(&self) -> &dyn vortex_layout::LayoutEncoding + +impl core::fmt::Debug for vortex_layout::layouts::flat::FlatLayoutEncoding + +pub fn vortex_layout::layouts::flat::FlatLayoutEncoding::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result + +impl core::ops::deref::Deref for vortex_layout::layouts::flat::FlatLayoutEncoding + +pub type vortex_layout::layouts::flat::FlatLayoutEncoding::Target = dyn vortex_layout::LayoutEncoding + +pub fn vortex_layout::layouts::flat::FlatLayoutEncoding::deref(&self) -> &Self::Target + +pub struct vortex_layout::layouts::flat::FlatLayoutMetadata + +pub vortex_layout::layouts::flat::FlatLayoutMetadata::array_encoding_tree: core::option::Option> + +impl vortex_layout::layouts::flat::FlatLayoutMetadata + +pub fn vortex_layout::layouts::flat::FlatLayoutMetadata::array_encoding_tree(&self) -> &[u8] + +impl core::default::Default for vortex_layout::layouts::flat::FlatLayoutMetadata + +pub fn vortex_layout::layouts::flat::FlatLayoutMetadata::default() -> Self + +impl core::fmt::Debug for vortex_layout::layouts::flat::FlatLayoutMetadata + +pub fn vortex_layout::layouts::flat::FlatLayoutMetadata::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result + +impl prost::message::Message for vortex_layout::layouts::flat::FlatLayoutMetadata + +pub fn vortex_layout::layouts::flat::FlatLayoutMetadata::clear(&mut self) + +pub fn vortex_layout::layouts::flat::FlatLayoutMetadata::encoded_len(&self) -> usize + +pub mod vortex_layout::layouts::repartition + +pub struct vortex_layout::layouts::repartition::RepartitionStrategy + +impl vortex_layout::layouts::repartition::RepartitionStrategy + +pub fn vortex_layout::layouts::repartition::RepartitionStrategy::new(S, vortex_layout::layouts::repartition::RepartitionWriterOptions) -> Self + +impl core::clone::Clone for vortex_layout::layouts::repartition::RepartitionStrategy + +pub fn vortex_layout::layouts::repartition::RepartitionStrategy::clone(&self) -> vortex_layout::layouts::repartition::RepartitionStrategy + +impl vortex_layout::LayoutStrategy for vortex_layout::layouts::repartition::RepartitionStrategy + +pub fn vortex_layout::layouts::repartition::RepartitionStrategy::buffered_bytes(&self) -> u64 + +pub fn vortex_layout::layouts::repartition::RepartitionStrategy::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait + +pub struct vortex_layout::layouts::repartition::RepartitionWriterOptions + +pub vortex_layout::layouts::repartition::RepartitionWriterOptions::block_len_multiple: usize + +pub vortex_layout::layouts::repartition::RepartitionWriterOptions::block_size_minimum: u64 + +pub vortex_layout::layouts::repartition::RepartitionWriterOptions::block_size_target: core::option::Option + +pub vortex_layout::layouts::repartition::RepartitionWriterOptions::canonicalize: bool + +impl core::clone::Clone for vortex_layout::layouts::repartition::RepartitionWriterOptions + +pub fn vortex_layout::layouts::repartition::RepartitionWriterOptions::clone(&self) -> vortex_layout::layouts::repartition::RepartitionWriterOptions + +pub mod vortex_layout::layouts::row_idx + +pub struct vortex_layout::layouts::row_idx::RowIdx + +impl core::clone::Clone for vortex_layout::layouts::row_idx::RowIdx + +pub fn vortex_layout::layouts::row_idx::RowIdx::clone(&self) -> vortex_layout::layouts::row_idx::RowIdx + +impl vortex_array::scalar_fn::vtable::ScalarFnVTable for vortex_layout::layouts::row_idx::RowIdx + +pub type vortex_layout::layouts::row_idx::RowIdx::Options = vortex_array::scalar_fn::vtable::EmptyOptions + +pub fn vortex_layout::layouts::row_idx::RowIdx::arity(&self, &Self::Options) -> vortex_array::scalar_fn::vtable::Arity + +pub fn vortex_layout::layouts::row_idx::RowIdx::child_name(&self, &Self::Options, usize) -> vortex_array::scalar_fn::vtable::ChildName + +pub fn vortex_layout::layouts::row_idx::RowIdx::execute(&self, &Self::Options, &dyn vortex_array::scalar_fn::vtable::ExecutionArgs, &mut vortex_array::executor::ExecutionCtx) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::row_idx::RowIdx::fmt_sql(&self, &Self::Options, &vortex_array::expr::expression::Expression, &mut core::fmt::Formatter<'_>) -> core::fmt::Result + +pub fn vortex_layout::layouts::row_idx::RowIdx::id(&self) -> vortex_array::scalar_fn::ScalarFnId + +pub fn vortex_layout::layouts::row_idx::RowIdx::return_dtype(&self, &Self::Options, &[vortex_array::dtype::DType]) -> vortex_error::VortexResult + +pub struct vortex_layout::layouts::row_idx::RowIdxLayoutReader + +impl vortex_layout::layouts::row_idx::RowIdxLayoutReader + +pub fn vortex_layout::layouts::row_idx::RowIdxLayoutReader::new(u64, alloc::sync::Arc, vortex_session::VortexSession) -> Self + +impl vortex_layout::LayoutReader for vortex_layout::layouts::row_idx::RowIdxLayoutReader + +pub fn vortex_layout::layouts::row_idx::RowIdxLayoutReader::as_any(&self) -> &dyn core::any::Any + +pub fn vortex_layout::layouts::row_idx::RowIdxLayoutReader::dtype(&self) -> &vortex_array::dtype::DType + +pub fn vortex_layout::layouts::row_idx::RowIdxLayoutReader::filter_evaluation(&self, &core::ops::range::Range, &vortex_array::expr::expression::Expression, vortex_array::mask_future::MaskFuture) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::row_idx::RowIdxLayoutReader::name(&self) -> &alloc::sync::Arc + +pub fn vortex_layout::layouts::row_idx::RowIdxLayoutReader::projection_evaluation(&self, &core::ops::range::Range, &vortex_array::expr::expression::Expression, vortex_array::mask_future::MaskFuture) -> vortex_error::VortexResult>> + +pub fn vortex_layout::layouts::row_idx::RowIdxLayoutReader::pruning_evaluation(&self, &core::ops::range::Range, &vortex_array::expr::expression::Expression, vortex_mask::Mask) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::row_idx::RowIdxLayoutReader::register_splits(&self, &[vortex_array::dtype::field_mask::FieldMask], &vortex_layout::SplitRange, &mut alloc::collections::btree::set::BTreeSet) -> vortex_error::VortexResult<()> + +pub fn vortex_layout::layouts::row_idx::RowIdxLayoutReader::row_count(&self) -> u64 + +pub fn vortex_layout::layouts::row_idx::row_idx() -> vortex_array::expr::expression::Expression + +pub mod vortex_layout::layouts::struct_ + +pub struct vortex_layout::layouts::struct_::Struct + +impl core::fmt::Debug for vortex_layout::layouts::struct_::Struct + +pub fn vortex_layout::layouts::struct_::Struct::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result + +impl vortex_layout::VTable for vortex_layout::layouts::struct_::Struct + +pub type vortex_layout::layouts::struct_::Struct::Encoding = vortex_layout::layouts::struct_::StructLayoutEncoding + +pub type vortex_layout::layouts::struct_::Struct::Layout = vortex_layout::layouts::struct_::StructLayout + +pub type vortex_layout::layouts::struct_::Struct::Metadata = vortex_array::metadata::EmptyMetadata + +pub fn vortex_layout::layouts::struct_::Struct::build(&Self::Encoding, &vortex_array::dtype::DType, u64, &::Output, alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::struct_::Struct::child(&Self::Layout, usize) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::struct_::Struct::child_type(&Self::Layout, usize) -> vortex_layout::LayoutChildType + +pub fn vortex_layout::layouts::struct_::Struct::dtype(&Self::Layout) -> &vortex_array::dtype::DType + +pub fn vortex_layout::layouts::struct_::Struct::encoding(&Self::Layout) -> vortex_layout::LayoutEncodingRef + +pub fn vortex_layout::layouts::struct_::Struct::id(&Self::Encoding) -> vortex_layout::LayoutId + +pub fn vortex_layout::layouts::struct_::Struct::metadata(&Self::Layout) -> Self::Metadata + +pub fn vortex_layout::layouts::struct_::Struct::nchildren(&Self::Layout) -> usize + +pub fn vortex_layout::layouts::struct_::Struct::new_reader(&Self::Layout, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::struct_::Struct::row_count(&Self::Layout) -> u64 + +pub fn vortex_layout::layouts::struct_::Struct::segment_ids(&Self::Layout) -> alloc::vec::Vec + +pub fn vortex_layout::layouts::struct_::Struct::with_children(&mut Self::Layout, alloc::vec::Vec) -> vortex_error::VortexResult<()> + +pub struct vortex_layout::layouts::struct_::StructLayout + +impl vortex_layout::layouts::struct_::StructLayout + +pub fn vortex_layout::layouts::struct_::StructLayout::children(&self) -> &alloc::sync::Arc + +pub fn vortex_layout::layouts::struct_::StructLayout::matching_fields(&self, &[vortex_array::dtype::field_mask::FieldMask], F) -> vortex_error::VortexResult<()> where F: core::ops::function::FnMut(vortex_array::dtype::field_mask::FieldMask, usize) -> vortex_error::VortexResult<()> + +pub fn vortex_layout::layouts::struct_::StructLayout::new(u64, vortex_array::dtype::DType, alloc::vec::Vec) -> Self + +pub fn vortex_layout::layouts::struct_::StructLayout::row_count(&self) -> u64 + +pub fn vortex_layout::layouts::struct_::StructLayout::struct_fields(&self) -> &vortex_array::dtype::struct_::StructFields + +impl core::clone::Clone for vortex_layout::layouts::struct_::StructLayout + +pub fn vortex_layout::layouts::struct_::StructLayout::clone(&self) -> vortex_layout::layouts::struct_::StructLayout + +impl core::convert::AsRef for vortex_layout::layouts::struct_::StructLayout + +pub fn vortex_layout::layouts::struct_::StructLayout::as_ref(&self) -> &dyn vortex_layout::Layout + +impl core::convert::From for vortex_layout::LayoutRef + +pub fn vortex_layout::LayoutRef::from(vortex_layout::layouts::struct_::StructLayout) -> vortex_layout::LayoutRef + +impl core::fmt::Debug for vortex_layout::layouts::struct_::StructLayout + +pub fn vortex_layout::layouts::struct_::StructLayout::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result + +impl core::ops::deref::Deref for vortex_layout::layouts::struct_::StructLayout + +pub type vortex_layout::layouts::struct_::StructLayout::Target = dyn vortex_layout::Layout + +pub fn vortex_layout::layouts::struct_::StructLayout::deref(&self) -> &Self::Target + +impl vortex_layout::IntoLayout for vortex_layout::layouts::struct_::StructLayout + +pub fn vortex_layout::layouts::struct_::StructLayout::into_layout(self) -> vortex_layout::LayoutRef + +pub struct vortex_layout::layouts::struct_::StructLayoutEncoding + +impl core::convert::AsRef for vortex_layout::layouts::struct_::StructLayoutEncoding + +pub fn vortex_layout::layouts::struct_::StructLayoutEncoding::as_ref(&self) -> &dyn vortex_layout::LayoutEncoding + +impl core::fmt::Debug for vortex_layout::layouts::struct_::StructLayoutEncoding + +pub fn vortex_layout::layouts::struct_::StructLayoutEncoding::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result + +impl core::ops::deref::Deref for vortex_layout::layouts::struct_::StructLayoutEncoding + +pub type vortex_layout::layouts::struct_::StructLayoutEncoding::Target = dyn vortex_layout::LayoutEncoding + +pub fn vortex_layout::layouts::struct_::StructLayoutEncoding::deref(&self) -> &Self::Target + +pub mod vortex_layout::layouts::table + +pub struct vortex_layout::layouts::table::TableStrategy + +impl vortex_layout::layouts::table::TableStrategy + +pub fn vortex_layout::layouts::table::TableStrategy::new(alloc::sync::Arc, alloc::sync::Arc) -> Self + +pub fn vortex_layout::layouts::table::TableStrategy::with_default_strategy(self, alloc::sync::Arc) -> Self + +pub fn vortex_layout::layouts::table::TableStrategy::with_field_writer(self, impl core::convert::Into, alloc::sync::Arc) -> Self + +pub fn vortex_layout::layouts::table::TableStrategy::with_field_writers(self, impl core::iter::traits::collect::IntoIterator)>) -> Self + +pub fn vortex_layout::layouts::table::TableStrategy::with_validity_strategy(self, alloc::sync::Arc) -> Self + +impl vortex_layout::LayoutStrategy for vortex_layout::layouts::table::TableStrategy + +pub fn vortex_layout::layouts::table::TableStrategy::buffered_bytes(&self) -> u64 + +pub fn vortex_layout::layouts::table::TableStrategy::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait + +pub mod vortex_layout::layouts::zoned + +pub mod vortex_layout::layouts::zoned::writer + +pub struct vortex_layout::layouts::zoned::writer::ZonedLayoutOptions + +pub vortex_layout::layouts::zoned::writer::ZonedLayoutOptions::block_size: usize + +pub vortex_layout::layouts::zoned::writer::ZonedLayoutOptions::concurrency: usize + +pub vortex_layout::layouts::zoned::writer::ZonedLayoutOptions::max_variable_length_statistics_size: usize + +pub vortex_layout::layouts::zoned::writer::ZonedLayoutOptions::stats: alloc::sync::Arc<[vortex_array::expr::stats::Stat]> + +impl core::default::Default for vortex_layout::layouts::zoned::writer::ZonedLayoutOptions + +pub fn vortex_layout::layouts::zoned::writer::ZonedLayoutOptions::default() -> Self + +pub struct vortex_layout::layouts::zoned::writer::ZonedStrategy + +impl vortex_layout::layouts::zoned::writer::ZonedStrategy + +pub fn vortex_layout::layouts::zoned::writer::ZonedStrategy::new(Child, Stats, vortex_layout::layouts::zoned::writer::ZonedLayoutOptions) -> Self + +impl vortex_layout::LayoutStrategy for vortex_layout::layouts::zoned::writer::ZonedStrategy + +pub fn vortex_layout::layouts::zoned::writer::ZonedStrategy::buffered_bytes(&self) -> u64 + +pub fn vortex_layout::layouts::zoned::writer::ZonedStrategy::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait + +pub mod vortex_layout::layouts::zoned::zone_map + +pub struct vortex_layout::layouts::zoned::zone_map::ZoneMap + +impl vortex_layout::layouts::zoned::zone_map::ZoneMap + +pub fn vortex_layout::layouts::zoned::zone_map::ZoneMap::dtype_for_stats_table(&vortex_array::dtype::DType, &[vortex_array::expr::stats::Stat]) -> vortex_array::dtype::DType + +pub unsafe fn vortex_layout::layouts::zoned::zone_map::ZoneMap::new_unchecked(vortex_array::arrays::struct_::vtable::StructArray, u64, u64) -> Self + +pub fn vortex_layout::layouts::zoned::zone_map::ZoneMap::prune(&self, &vortex_array::expr::expression::Expression, &vortex_session::VortexSession) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::zoned::zone_map::ZoneMap::try_new(vortex_array::dtype::DType, vortex_array::arrays::struct_::vtable::StructArray, alloc::sync::Arc<[vortex_array::expr::stats::Stat]>, u64, u64) -> vortex_error::VortexResult + +impl core::clone::Clone for vortex_layout::layouts::zoned::zone_map::ZoneMap + +pub fn vortex_layout::layouts::zoned::zone_map::ZoneMap::clone(&self) -> vortex_layout::layouts::zoned::zone_map::ZoneMap + +pub struct vortex_layout::layouts::zoned::Zoned + +impl core::fmt::Debug for vortex_layout::layouts::zoned::Zoned + +pub fn vortex_layout::layouts::zoned::Zoned::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result + +impl vortex_layout::VTable for vortex_layout::layouts::zoned::Zoned + +pub type vortex_layout::layouts::zoned::Zoned::Encoding = vortex_layout::layouts::zoned::ZonedLayoutEncoding + +pub type vortex_layout::layouts::zoned::Zoned::Layout = vortex_layout::layouts::zoned::ZonedLayout + +pub type vortex_layout::layouts::zoned::Zoned::Metadata = vortex_layout::layouts::zoned::ZonedMetadata + +pub fn vortex_layout::layouts::zoned::Zoned::build(&Self::Encoding, &vortex_array::dtype::DType, u64, &vortex_layout::layouts::zoned::ZonedMetadata, alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::zoned::Zoned::child(&Self::Layout, usize) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::zoned::Zoned::child_type(&Self::Layout, usize) -> vortex_layout::LayoutChildType + +pub fn vortex_layout::layouts::zoned::Zoned::dtype(&Self::Layout) -> &vortex_array::dtype::DType + +pub fn vortex_layout::layouts::zoned::Zoned::encoding(&Self::Layout) -> vortex_layout::LayoutEncodingRef + +pub fn vortex_layout::layouts::zoned::Zoned::id(&Self::Encoding) -> vortex_layout::LayoutId + +pub fn vortex_layout::layouts::zoned::Zoned::metadata(&Self::Layout) -> Self::Metadata + +pub fn vortex_layout::layouts::zoned::Zoned::nchildren(&Self::Layout) -> usize + +pub fn vortex_layout::layouts::zoned::Zoned::new_reader(&Self::Layout, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::zoned::Zoned::row_count(&Self::Layout) -> u64 + +pub fn vortex_layout::layouts::zoned::Zoned::segment_ids(&Self::Layout) -> alloc::vec::Vec + +pub fn vortex_layout::layouts::zoned::Zoned::with_children(&mut Self::Layout, alloc::vec::Vec) -> vortex_error::VortexResult<()> + +pub struct vortex_layout::layouts::zoned::ZonedLayout + +impl vortex_layout::layouts::zoned::ZonedLayout + +pub fn vortex_layout::layouts::zoned::ZonedLayout::new(vortex_layout::LayoutRef, vortex_layout::LayoutRef, usize, alloc::sync::Arc<[vortex_array::expr::stats::Stat]>) -> Self + +pub fn vortex_layout::layouts::zoned::ZonedLayout::nzones(&self) -> usize + +pub fn vortex_layout::layouts::zoned::ZonedLayout::present_stats(&self) -> &alloc::sync::Arc<[vortex_array::expr::stats::Stat]> + +pub fn vortex_layout::layouts::zoned::ZonedLayout::zone_len(&self) -> usize + +impl core::clone::Clone for vortex_layout::layouts::zoned::ZonedLayout + +pub fn vortex_layout::layouts::zoned::ZonedLayout::clone(&self) -> vortex_layout::layouts::zoned::ZonedLayout + +impl core::convert::AsRef for vortex_layout::layouts::zoned::ZonedLayout + +pub fn vortex_layout::layouts::zoned::ZonedLayout::as_ref(&self) -> &dyn vortex_layout::Layout + +impl core::convert::From for vortex_layout::LayoutRef + +pub fn vortex_layout::LayoutRef::from(vortex_layout::layouts::zoned::ZonedLayout) -> vortex_layout::LayoutRef + +impl core::fmt::Debug for vortex_layout::layouts::zoned::ZonedLayout + +pub fn vortex_layout::layouts::zoned::ZonedLayout::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result + +impl core::ops::deref::Deref for vortex_layout::layouts::zoned::ZonedLayout + +pub type vortex_layout::layouts::zoned::ZonedLayout::Target = dyn vortex_layout::Layout + +pub fn vortex_layout::layouts::zoned::ZonedLayout::deref(&self) -> &Self::Target + +impl vortex_layout::IntoLayout for vortex_layout::layouts::zoned::ZonedLayout + +pub fn vortex_layout::layouts::zoned::ZonedLayout::into_layout(self) -> vortex_layout::LayoutRef + +pub struct vortex_layout::layouts::zoned::ZonedLayoutEncoding + +impl core::convert::AsRef for vortex_layout::layouts::zoned::ZonedLayoutEncoding + +pub fn vortex_layout::layouts::zoned::ZonedLayoutEncoding::as_ref(&self) -> &dyn vortex_layout::LayoutEncoding + +impl core::fmt::Debug for vortex_layout::layouts::zoned::ZonedLayoutEncoding + +pub fn vortex_layout::layouts::zoned::ZonedLayoutEncoding::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result + +impl core::ops::deref::Deref for vortex_layout::layouts::zoned::ZonedLayoutEncoding + +pub type vortex_layout::layouts::zoned::ZonedLayoutEncoding::Target = dyn vortex_layout::LayoutEncoding + +pub fn vortex_layout::layouts::zoned::ZonedLayoutEncoding::deref(&self) -> &Self::Target + +pub struct vortex_layout::layouts::zoned::ZonedMetadata + +impl core::clone::Clone for vortex_layout::layouts::zoned::ZonedMetadata + +pub fn vortex_layout::layouts::zoned::ZonedMetadata::clone(&self) -> vortex_layout::layouts::zoned::ZonedMetadata + +impl core::cmp::Eq for vortex_layout::layouts::zoned::ZonedMetadata + +impl core::cmp::PartialEq for vortex_layout::layouts::zoned::ZonedMetadata + +pub fn vortex_layout::layouts::zoned::ZonedMetadata::eq(&self, &vortex_layout::layouts::zoned::ZonedMetadata) -> bool + +impl core::fmt::Debug for vortex_layout::layouts::zoned::ZonedMetadata + +pub fn vortex_layout::layouts::zoned::ZonedMetadata::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result + +impl core::marker::StructuralPartialEq for vortex_layout::layouts::zoned::ZonedMetadata + +impl vortex_array::metadata::DeserializeMetadata for vortex_layout::layouts::zoned::ZonedMetadata + +pub type vortex_layout::layouts::zoned::ZonedMetadata::Output = vortex_layout::layouts::zoned::ZonedMetadata + +pub fn vortex_layout::layouts::zoned::ZonedMetadata::deserialize(&[u8]) -> vortex_error::VortexResult + +impl vortex_array::metadata::SerializeMetadata for vortex_layout::layouts::zoned::ZonedMetadata + +pub fn vortex_layout::layouts::zoned::ZonedMetadata::serialize(self) -> alloc::vec::Vec + +pub const vortex_layout::layouts::zoned::MAX_IS_TRUNCATED: &str + +pub const vortex_layout::layouts::zoned::MIN_IS_TRUNCATED: &str + +pub type vortex_layout::layouts::SharedArrayFuture = futures_util::future::future::shared::Shared>> + +pub mod vortex_layout::scan + +pub mod vortex_layout::scan::arrow + +pub struct vortex_layout::scan::arrow::RecordBatchIteratorAdapter + +impl vortex_layout::scan::arrow::RecordBatchIteratorAdapter + +pub fn vortex_layout::scan::arrow::RecordBatchIteratorAdapter::new(I, arrow_schema::schema::SchemaRef) -> Self + +impl core::clone::Clone for vortex_layout::scan::arrow::RecordBatchIteratorAdapter + +pub fn vortex_layout::scan::arrow::RecordBatchIteratorAdapter::clone(&self) -> vortex_layout::scan::arrow::RecordBatchIteratorAdapter + +impl arrow_array::record_batch::RecordBatchReader for vortex_layout::scan::arrow::RecordBatchIteratorAdapter where I: core::iter::traits::iterator::Iterator> + +pub fn vortex_layout::scan::arrow::RecordBatchIteratorAdapter::schema(&self) -> arrow_schema::schema::SchemaRef + +impl core::iter::traits::iterator::Iterator for vortex_layout::scan::arrow::RecordBatchIteratorAdapter where I: core::iter::traits::iterator::Iterator> + +pub type vortex_layout::scan::arrow::RecordBatchIteratorAdapter::Item = core::result::Result + +pub fn vortex_layout::scan::arrow::RecordBatchIteratorAdapter::next(&mut self) -> core::option::Option + +pub mod vortex_layout::scan::layout + +pub struct vortex_layout::scan::layout::LayoutReaderDataSource + +impl vortex_layout::scan::layout::LayoutReaderDataSource + +pub fn vortex_layout::scan::layout::LayoutReaderDataSource::new(vortex_layout::LayoutReaderRef, vortex_session::VortexSession) -> Self + +pub fn vortex_layout::scan::layout::LayoutReaderDataSource::with_metrics_registry(self, alloc::sync::Arc) -> Self + +pub fn vortex_layout::scan::layout::LayoutReaderDataSource::with_some_metrics_registry(self, core::option::Option>) -> Self + +pub fn vortex_layout::scan::layout::LayoutReaderDataSource::with_split_max_row_count(self, u64) -> Self + +impl vortex_scan::DataSource for vortex_layout::scan::layout::LayoutReaderDataSource + +pub fn vortex_layout::scan::layout::LayoutReaderDataSource::byte_size(&self) -> core::option::Option> + +pub fn vortex_layout::scan::layout::LayoutReaderDataSource::deserialize_partition(&self, &[u8], &vortex_session::VortexSession) -> vortex_error::VortexResult + +pub fn vortex_layout::scan::layout::LayoutReaderDataSource::dtype(&self) -> &vortex_array::dtype::DType + +pub fn vortex_layout::scan::layout::LayoutReaderDataSource::field_statistics<'life0, 'life1, 'async_trait>(&'life0 self, &'life1 vortex_array::dtype::field::FieldPath) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait + +pub fn vortex_layout::scan::layout::LayoutReaderDataSource::row_count(&self) -> core::option::Option> + +pub fn vortex_layout::scan::layout::LayoutReaderDataSource::scan<'life0, 'async_trait>(&'life0 self, vortex_scan::ScanRequest) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait + +pub mod vortex_layout::scan::multi + +pub enum vortex_layout::scan::multi::MultiLayoutChild + +pub vortex_layout::scan::multi::MultiLayoutChild::Deferred(alloc::sync::Arc) + +pub vortex_layout::scan::multi::MultiLayoutChild::Opened(vortex_layout::LayoutReaderRef) + +pub struct vortex_layout::scan::multi::MultiLayoutDataSource + +impl vortex_layout::scan::multi::MultiLayoutDataSource + +pub fn vortex_layout::scan::multi::MultiLayoutDataSource::children(&self) -> &alloc::vec::Vec + +pub fn vortex_layout::scan::multi::MultiLayoutDataSource::new_deferred(vortex_array::dtype::DType, alloc::vec::Vec>, &vortex_session::VortexSession) -> Self + +pub fn vortex_layout::scan::multi::MultiLayoutDataSource::new_with_first(vortex_layout::LayoutReaderRef, alloc::vec::Vec>, &vortex_session::VortexSession) -> Self + +pub fn vortex_layout::scan::multi::MultiLayoutDataSource::with_concurrency(self, usize) -> Self + +impl vortex_scan::DataSource for vortex_layout::scan::multi::MultiLayoutDataSource + +pub fn vortex_layout::scan::multi::MultiLayoutDataSource::deserialize_partition(&self, &[u8], &vortex_session::VortexSession) -> vortex_error::VortexResult + +pub fn vortex_layout::scan::multi::MultiLayoutDataSource::dtype(&self) -> &vortex_array::dtype::DType + +pub fn vortex_layout::scan::multi::MultiLayoutDataSource::field_statistics<'life0, 'life1, 'async_trait>(&'life0 self, &'life1 vortex_array::dtype::field::FieldPath) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait + +pub fn vortex_layout::scan::multi::MultiLayoutDataSource::row_count(&self) -> core::option::Option> + +pub fn vortex_layout::scan::multi::MultiLayoutDataSource::scan<'life0, 'async_trait>(&'life0 self, vortex_scan::ScanRequest) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait + +pub trait vortex_layout::scan::multi::LayoutReaderFactory: 'static + core::marker::Send + core::marker::Sync + +pub fn vortex_layout::scan::multi::LayoutReaderFactory::open<'life0, 'async_trait>(&'life0 self) -> core::pin::Pin>> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait + +pub mod vortex_layout::scan::repeated_scan + +pub struct vortex_layout::scan::repeated_scan::RepeatedScan + +impl vortex_layout::scan::repeated_scan::RepeatedScan + +pub fn vortex_layout::scan::repeated_scan::RepeatedScan::dtype(&self) -> &vortex_array::dtype::DType + +pub fn vortex_layout::scan::repeated_scan::RepeatedScan::execute_array_iter(&self, core::option::Option>, &B) -> vortex_error::VortexResult + +pub fn vortex_layout::scan::repeated_scan::RepeatedScan::execute_array_stream(&self, core::option::Option>) -> vortex_error::VortexResult + +impl vortex_layout::scan::repeated_scan::RepeatedScan + +pub fn vortex_layout::scan::repeated_scan::RepeatedScan::execute(&self, core::option::Option>) -> vortex_error::VortexResult>>>> + +pub fn vortex_layout::scan::repeated_scan::RepeatedScan::execute_stream(&self, core::option::Option>) -> vortex_error::VortexResult> + core::marker::Send + 'static + use> + +pub fn vortex_layout::scan::repeated_scan::RepeatedScan::new(vortex_session::VortexSession, vortex_layout::LayoutReaderRef, vortex_array::expr::expression::Expression, core::option::Option, bool, core::option::Option>, vortex_scan::selection::Selection, vortex_layout::scan::splits::Splits, usize, alloc::sync::Arc<(dyn core::ops::function::Fn(vortex_array::array::erased::ArrayRef) -> vortex_error::VortexResult + core::marker::Send + core::marker::Sync)>, core::option::Option, vortex_array::dtype::DType) -> Self + +pub mod vortex_layout::scan::scan_builder + +pub struct vortex_layout::scan::scan_builder::ScanBuilder + +impl vortex_layout::scan::scan_builder::ScanBuilder + +pub fn vortex_layout::scan::scan_builder::ScanBuilder::into_array_iter(self, &B) -> vortex_error::VortexResult + +pub fn vortex_layout::scan::scan_builder::ScanBuilder::into_array_stream(self) -> vortex_error::VortexResult + +pub fn vortex_layout::scan::scan_builder::ScanBuilder::new(vortex_session::VortexSession, alloc::sync::Arc) -> Self + +impl vortex_layout::scan::scan_builder::ScanBuilder + +pub fn vortex_layout::scan::scan_builder::ScanBuilder::into_record_batch_reader(self, arrow_schema::schema::SchemaRef, &B) -> vortex_error::VortexResult + +pub fn vortex_layout::scan::scan_builder::ScanBuilder::into_record_batch_stream(self, arrow_schema::schema::SchemaRef) -> vortex_error::VortexResult> + core::marker::Send + 'static> + +impl vortex_layout::scan::scan_builder::ScanBuilder + +pub fn vortex_layout::scan::scan_builder::ScanBuilder::build(self) -> vortex_error::VortexResult>>>> + +pub fn vortex_layout::scan::scan_builder::ScanBuilder::concurrency(&self) -> usize + +pub fn vortex_layout::scan::scan_builder::ScanBuilder::dtype(&self) -> vortex_error::VortexResult + +pub fn vortex_layout::scan::scan_builder::ScanBuilder::into_iter(self, &B) -> vortex_error::VortexResult> + 'static> + +pub fn vortex_layout::scan::scan_builder::ScanBuilder::into_stream(self) -> vortex_error::VortexResult> + core::marker::Send + 'static + use> + +pub fn vortex_layout::scan::scan_builder::ScanBuilder::map(self, impl core::ops::function::Fn(A) -> vortex_error::VortexResult + 'static + core::marker::Send + core::marker::Sync) -> vortex_layout::scan::scan_builder::ScanBuilder + +pub fn vortex_layout::scan::scan_builder::ScanBuilder::ordered(&self) -> bool + +pub fn vortex_layout::scan::scan_builder::ScanBuilder::prepare(self) -> vortex_error::VortexResult> + +pub fn vortex_layout::scan::scan_builder::ScanBuilder::session(&self) -> &vortex_session::VortexSession + +pub fn vortex_layout::scan::scan_builder::ScanBuilder::with_concurrency(self, usize) -> Self + +pub fn vortex_layout::scan::scan_builder::ScanBuilder::with_filter(self, vortex_array::expr::expression::Expression) -> Self + +pub fn vortex_layout::scan::scan_builder::ScanBuilder::with_limit(self, u64) -> Self + +pub fn vortex_layout::scan::scan_builder::ScanBuilder::with_metrics_registry(self, alloc::sync::Arc) -> Self + +pub fn vortex_layout::scan::scan_builder::ScanBuilder::with_ordered(self, bool) -> Self + +pub fn vortex_layout::scan::scan_builder::ScanBuilder::with_projection(self, vortex_array::expr::expression::Expression) -> Self + +pub fn vortex_layout::scan::scan_builder::ScanBuilder::with_row_indices(self, vortex_buffer::buffer::Buffer) -> Self + +pub fn vortex_layout::scan::scan_builder::ScanBuilder::with_row_offset(self, u64) -> Self + +pub fn vortex_layout::scan::scan_builder::ScanBuilder::with_row_range(self, core::ops::range::Range) -> Self + +pub fn vortex_layout::scan::scan_builder::ScanBuilder::with_selection(self, vortex_scan::selection::Selection) -> Self + +pub fn vortex_layout::scan::scan_builder::ScanBuilder::with_some_filter(self, core::option::Option) -> Self + +pub fn vortex_layout::scan::scan_builder::ScanBuilder::with_some_limit(self, core::option::Option) -> Self + +pub fn vortex_layout::scan::scan_builder::ScanBuilder::with_some_metrics_registry(self, core::option::Option>) -> Self + +pub fn vortex_layout::scan::scan_builder::ScanBuilder::with_split_by(self, vortex_layout::scan::split_by::SplitBy) -> Self + +pub fn vortex_layout::scan::scan_builder::filter_and_projection_masks(&vortex_array::expr::expression::Expression, core::option::Option<&vortex_array::expr::expression::Expression>, &vortex_array::dtype::DType) -> vortex_error::VortexResult<(alloc::vec::Vec, alloc::vec::Vec)> + +pub mod vortex_layout::scan::split_by + +pub enum vortex_layout::scan::split_by::SplitBy + +pub vortex_layout::scan::split_by::SplitBy::Layout + +pub vortex_layout::scan::split_by::SplitBy::RowCount(usize) + +impl vortex_layout::scan::split_by::SplitBy + +pub fn vortex_layout::scan::split_by::SplitBy::splits(&self, &dyn vortex_layout::LayoutReader, &core::ops::range::Range, &[vortex_array::dtype::field_mask::FieldMask]) -> vortex_error::VortexResult> + +impl core::clone::Clone for vortex_layout::scan::split_by::SplitBy + +pub fn vortex_layout::scan::split_by::SplitBy::clone(&self) -> vortex_layout::scan::split_by::SplitBy + +impl core::default::Default for vortex_layout::scan::split_by::SplitBy + +pub fn vortex_layout::scan::split_by::SplitBy::default() -> vortex_layout::scan::split_by::SplitBy + +impl core::fmt::Debug for vortex_layout::scan::split_by::SplitBy + +pub fn vortex_layout::scan::split_by::SplitBy::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result + +impl core::marker::Copy for vortex_layout::scan::split_by::SplitBy + +pub mod vortex_layout::segments + +pub struct vortex_layout::segments::InstrumentedSegmentCache + +impl vortex_layout::segments::InstrumentedSegmentCache + +pub fn vortex_layout::segments::InstrumentedSegmentCache::new(C, &dyn vortex_metrics::MetricsRegistry, alloc::vec::Vec) -> Self + +impl vortex_layout::segments::SegmentCache for vortex_layout::segments::InstrumentedSegmentCache + +pub fn vortex_layout::segments::InstrumentedSegmentCache::get<'life0, 'async_trait>(&'life0 self, vortex_layout::segments::SegmentId) -> core::pin::Pin>> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait + +pub fn vortex_layout::segments::InstrumentedSegmentCache::put<'life0, 'async_trait>(&'life0 self, vortex_layout::segments::SegmentId, vortex_buffer::ByteBuffer) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait + +pub struct vortex_layout::segments::MokaSegmentCache(_) + +impl vortex_layout::segments::MokaSegmentCache + +pub fn vortex_layout::segments::MokaSegmentCache::new(u64) -> Self + +impl vortex_layout::segments::SegmentCache for vortex_layout::segments::MokaSegmentCache + +pub fn vortex_layout::segments::MokaSegmentCache::get<'life0, 'async_trait>(&'life0 self, vortex_layout::segments::SegmentId) -> core::pin::Pin>> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait + +pub fn vortex_layout::segments::MokaSegmentCache::put<'life0, 'async_trait>(&'life0 self, vortex_layout::segments::SegmentId, vortex_buffer::ByteBuffer) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait + +pub struct vortex_layout::segments::NoOpSegmentCache + +impl vortex_layout::segments::SegmentCache for vortex_layout::segments::NoOpSegmentCache + +pub fn vortex_layout::segments::NoOpSegmentCache::get<'life0, 'async_trait>(&'life0 self, vortex_layout::segments::SegmentId) -> core::pin::Pin>> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait + +pub fn vortex_layout::segments::NoOpSegmentCache::put<'life0, 'async_trait>(&'life0 self, vortex_layout::segments::SegmentId, vortex_buffer::ByteBuffer) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait + +pub struct vortex_layout::segments::SegmentCacheSourceAdapter + +impl vortex_layout::segments::SegmentCacheSourceAdapter + +pub fn vortex_layout::segments::SegmentCacheSourceAdapter::new(alloc::sync::Arc, alloc::sync::Arc) -> Self + +impl vortex_layout::segments::SegmentSource for vortex_layout::segments::SegmentCacheSourceAdapter + +pub fn vortex_layout::segments::SegmentCacheSourceAdapter::request(&self, vortex_layout::segments::SegmentId) -> vortex_layout::segments::SegmentFuture + +pub struct vortex_layout::segments::SegmentId(_) + +impl core::clone::Clone for vortex_layout::segments::SegmentId + +pub fn vortex_layout::segments::SegmentId::clone(&self) -> vortex_layout::segments::SegmentId + +impl core::cmp::Eq for vortex_layout::segments::SegmentId + +impl core::cmp::Ord for vortex_layout::segments::SegmentId + +pub fn vortex_layout::segments::SegmentId::cmp(&self, &vortex_layout::segments::SegmentId) -> core::cmp::Ordering + +impl core::cmp::PartialEq for vortex_layout::segments::SegmentId + +pub fn vortex_layout::segments::SegmentId::eq(&self, &vortex_layout::segments::SegmentId) -> bool + +impl core::cmp::PartialOrd for vortex_layout::segments::SegmentId + +pub fn vortex_layout::segments::SegmentId::partial_cmp(&self, &vortex_layout::segments::SegmentId) -> core::option::Option + +impl core::convert::From for vortex_layout::segments::SegmentId + +pub fn vortex_layout::segments::SegmentId::from(u32) -> Self + +impl core::convert::TryFrom for vortex_layout::segments::SegmentId + +pub type vortex_layout::segments::SegmentId::Error = vortex_error::VortexError + +pub fn vortex_layout::segments::SegmentId::try_from(usize) -> core::result::Result + +impl core::default::Default for vortex_layout::segments::SegmentId + +pub fn vortex_layout::segments::SegmentId::default() -> vortex_layout::segments::SegmentId + +impl core::fmt::Debug for vortex_layout::segments::SegmentId + +pub fn vortex_layout::segments::SegmentId::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result + +impl core::fmt::Display for vortex_layout::segments::SegmentId + +pub fn vortex_layout::segments::SegmentId::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result + +impl core::hash::Hash for vortex_layout::segments::SegmentId + +pub fn vortex_layout::segments::SegmentId::hash<__H: core::hash::Hasher>(&self, &mut __H) + +impl core::marker::Copy for vortex_layout::segments::SegmentId + +impl core::marker::StructuralPartialEq for vortex_layout::segments::SegmentId + +impl core::ops::deref::Deref for vortex_layout::segments::SegmentId + +pub type vortex_layout::segments::SegmentId::Target = u32 + +pub fn vortex_layout::segments::SegmentId::deref(&self) -> &Self::Target + +pub struct vortex_layout::segments::SharedSegmentSource + +impl vortex_layout::segments::SharedSegmentSource + +pub fn vortex_layout::segments::SharedSegmentSource::new(S) -> Self + +impl vortex_layout::segments::SegmentSource for vortex_layout::segments::SharedSegmentSource + +pub fn vortex_layout::segments::SharedSegmentSource::request(&self, vortex_layout::segments::SegmentId) -> vortex_layout::segments::SegmentFuture + +pub trait vortex_layout::segments::SegmentCache: core::marker::Send + core::marker::Sync + +pub fn vortex_layout::segments::SegmentCache::get<'life0, 'async_trait>(&'life0 self, vortex_layout::segments::SegmentId) -> core::pin::Pin>> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait + +pub fn vortex_layout::segments::SegmentCache::put<'life0, 'async_trait>(&'life0 self, vortex_layout::segments::SegmentId, vortex_buffer::ByteBuffer) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait + +impl vortex_layout::segments::SegmentCache for vortex_layout::segments::MokaSegmentCache + +pub fn vortex_layout::segments::MokaSegmentCache::get<'life0, 'async_trait>(&'life0 self, vortex_layout::segments::SegmentId) -> core::pin::Pin>> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait + +pub fn vortex_layout::segments::MokaSegmentCache::put<'life0, 'async_trait>(&'life0 self, vortex_layout::segments::SegmentId, vortex_buffer::ByteBuffer) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait + +impl vortex_layout::segments::SegmentCache for vortex_layout::segments::NoOpSegmentCache + +pub fn vortex_layout::segments::NoOpSegmentCache::get<'life0, 'async_trait>(&'life0 self, vortex_layout::segments::SegmentId) -> core::pin::Pin>> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait + +pub fn vortex_layout::segments::NoOpSegmentCache::put<'life0, 'async_trait>(&'life0 self, vortex_layout::segments::SegmentId, vortex_buffer::ByteBuffer) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait + +impl vortex_layout::segments::SegmentCache for vortex_layout::segments::InstrumentedSegmentCache + +pub fn vortex_layout::segments::InstrumentedSegmentCache::get<'life0, 'async_trait>(&'life0 self, vortex_layout::segments::SegmentId) -> core::pin::Pin>> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait + +pub fn vortex_layout::segments::InstrumentedSegmentCache::put<'life0, 'async_trait>(&'life0 self, vortex_layout::segments::SegmentId, vortex_buffer::ByteBuffer) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait + +pub trait vortex_layout::segments::SegmentSink: core::marker::Send + core::marker::Sync + +pub fn vortex_layout::segments::SegmentSink::write<'life0, 'async_trait>(&'life0 self, vortex_layout::sequence::SequenceId, alloc::vec::Vec) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait + +pub trait vortex_layout::segments::SegmentSource: 'static + core::marker::Send + core::marker::Sync + +pub fn vortex_layout::segments::SegmentSource::request(&self, vortex_layout::segments::SegmentId) -> vortex_layout::segments::SegmentFuture + +impl vortex_layout::segments::SegmentSource for vortex_layout::segments::SegmentCacheSourceAdapter + +pub fn vortex_layout::segments::SegmentCacheSourceAdapter::request(&self, vortex_layout::segments::SegmentId) -> vortex_layout::segments::SegmentFuture + +impl vortex_layout::segments::SegmentSource for vortex_layout::segments::SharedSegmentSource + +pub fn vortex_layout::segments::SharedSegmentSource::request(&self, vortex_layout::segments::SegmentId) -> vortex_layout::segments::SegmentFuture + +pub type vortex_layout::segments::SegmentFuture = futures_core::future::BoxFuture<'static, vortex_error::VortexResult> + +pub type vortex_layout::segments::SegmentSinkRef = alloc::sync::Arc + +pub mod vortex_layout::sequence + +pub struct vortex_layout::sequence::SequenceId + +impl vortex_layout::sequence::SequenceId + +pub async fn vortex_layout::sequence::SequenceId::collapse(&mut self) + +pub fn vortex_layout::sequence::SequenceId::descend(self) -> vortex_layout::sequence::SequencePointer + +pub fn vortex_layout::sequence::SequenceId::root() -> vortex_layout::sequence::SequencePointer + +impl core::cmp::Eq for vortex_layout::sequence::SequenceId + +impl core::cmp::Ord for vortex_layout::sequence::SequenceId + +pub fn vortex_layout::sequence::SequenceId::cmp(&self, &Self) -> core::cmp::Ordering + +impl core::cmp::PartialEq for vortex_layout::sequence::SequenceId + +pub fn vortex_layout::sequence::SequenceId::eq(&self, &Self) -> bool + +impl core::cmp::PartialOrd for vortex_layout::sequence::SequenceId + +pub fn vortex_layout::sequence::SequenceId::partial_cmp(&self, &Self) -> core::option::Option + +impl core::fmt::Debug for vortex_layout::sequence::SequenceId + +pub fn vortex_layout::sequence::SequenceId::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result + +impl core::hash::Hash for vortex_layout::sequence::SequenceId + +pub fn vortex_layout::sequence::SequenceId::hash(&self, &mut H) + +impl core::ops::drop::Drop for vortex_layout::sequence::SequenceId + +pub fn vortex_layout::sequence::SequenceId::drop(&mut self) + +pub struct vortex_layout::sequence::SequencePointer(_) + +impl vortex_layout::sequence::SequencePointer + +pub fn vortex_layout::sequence::SequencePointer::advance(&mut self) -> vortex_layout::sequence::SequenceId + +pub fn vortex_layout::sequence::SequencePointer::downgrade(self) -> vortex_layout::sequence::SequenceId + +pub fn vortex_layout::sequence::SequencePointer::split(self) -> (vortex_layout::sequence::SequencePointer, vortex_layout::sequence::SequencePointer) + +pub fn vortex_layout::sequence::SequencePointer::split_off(&mut self) -> vortex_layout::sequence::SequencePointer + +impl core::fmt::Debug for vortex_layout::sequence::SequencePointer + +pub fn vortex_layout::sequence::SequencePointer::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result + +pub struct vortex_layout::sequence::SequentialStreamAdapter + +impl vortex_layout::sequence::SequentialStreamAdapter + +pub fn vortex_layout::sequence::SequentialStreamAdapter::new(vortex_array::dtype::DType, S) -> Self + +impl<'__pin, S> core::marker::Unpin for vortex_layout::sequence::SequentialStreamAdapter where pin_project_lite::__private::PinnedFieldsOf<__Origin<'__pin, S>>: core::marker::Unpin + +impl futures_core::stream::Stream for vortex_layout::sequence::SequentialStreamAdapter where S: futures_core::stream::Stream> + +pub type vortex_layout::sequence::SequentialStreamAdapter::Item = core::result::Result<(vortex_layout::sequence::SequenceId, vortex_array::array::erased::ArrayRef), vortex_error::VortexError> + +pub fn vortex_layout::sequence::SequentialStreamAdapter::poll_next(core::pin::Pin<&mut Self>, &mut core::task::wake::Context<'_>) -> core::task::poll::Poll> + +pub fn vortex_layout::sequence::SequentialStreamAdapter::size_hint(&self) -> (usize, core::option::Option) + +impl vortex_layout::sequence::SequentialStream for vortex_layout::sequence::SequentialStreamAdapter where S: futures_core::stream::Stream> + +pub fn vortex_layout::sequence::SequentialStreamAdapter::dtype(&self) -> &vortex_array::dtype::DType + +pub trait vortex_layout::sequence::SequentialArrayStreamExt: vortex_array::stream::ArrayStream + +pub fn vortex_layout::sequence::SequentialArrayStreamExt::sequenced(self, vortex_layout::sequence::SequencePointer) -> vortex_layout::sequence::SendableSequentialStream where Self: core::marker::Sized + core::marker::Send + 'static + +impl vortex_layout::sequence::SequentialArrayStreamExt for S + +pub fn S::sequenced(self, vortex_layout::sequence::SequencePointer) -> vortex_layout::sequence::SendableSequentialStream where Self: core::marker::Sized + core::marker::Send + 'static + +pub trait vortex_layout::sequence::SequentialStream: futures_core::stream::Stream> + +pub fn vortex_layout::sequence::SequentialStream::dtype(&self) -> &vortex_array::dtype::DType + +impl vortex_layout::sequence::SequentialStream for vortex_layout::sequence::SendableSequentialStream + +pub fn vortex_layout::sequence::SendableSequentialStream::dtype(&self) -> &vortex_array::dtype::DType + +impl vortex_layout::sequence::SequentialStream for vortex_layout::sequence::SequentialStreamAdapter where S: futures_core::stream::Stream> + +pub fn vortex_layout::sequence::SequentialStreamAdapter::dtype(&self) -> &vortex_array::dtype::DType + +pub trait vortex_layout::sequence::SequentialStreamExt: vortex_layout::sequence::SequentialStream + +pub fn vortex_layout::sequence::SequentialStreamExt::sendable(self) -> vortex_layout::sequence::SendableSequentialStream where Self: core::marker::Sized + core::marker::Send + 'static + +impl vortex_layout::sequence::SequentialStreamExt for S + +pub fn S::sendable(self) -> vortex_layout::sequence::SendableSequentialStream where Self: core::marker::Sized + core::marker::Send + 'static + +pub type vortex_layout::sequence::SendableSequentialStream = core::pin::Pin> + +pub mod vortex_layout::session + +pub struct vortex_layout::session::LayoutSession + +impl vortex_layout::session::LayoutSession + +pub fn vortex_layout::session::LayoutSession::register(&self, vortex_layout::LayoutEncodingRef) + +pub fn vortex_layout::session::LayoutSession::register_many(&self, impl core::iter::traits::collect::IntoIterator) + +pub fn vortex_layout::session::LayoutSession::registry(&self) -> &vortex_layout::session::LayoutRegistry + +impl core::default::Default for vortex_layout::session::LayoutSession + +pub fn vortex_layout::session::LayoutSession::default() -> Self + +impl core::fmt::Debug for vortex_layout::session::LayoutSession + +pub fn vortex_layout::session::LayoutSession::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result + +impl vortex_session::SessionVar for vortex_layout::session::LayoutSession + +pub fn vortex_layout::session::LayoutSession::as_any(&self) -> &dyn core::any::Any + +pub fn vortex_layout::session::LayoutSession::as_any_mut(&mut self) -> &mut dyn core::any::Any + +pub trait vortex_layout::session::LayoutSessionExt: vortex_session::SessionExt + +pub fn vortex_layout::session::LayoutSessionExt::layouts(&self) -> vortex_session::Ref<'_, vortex_layout::session::LayoutSession> + +impl vortex_layout::session::LayoutSessionExt for S + +pub fn S::layouts(&self) -> vortex_session::Ref<'_, vortex_layout::session::LayoutSession> + +pub type vortex_layout::session::LayoutRegistry = vortex_session::registry::Registry + +pub mod vortex_layout::vtable + +pub trait vortex_layout::vtable::VTable: 'static + core::marker::Sized + core::marker::Send + core::marker::Sync + core::fmt::Debug + +pub type vortex_layout::vtable::VTable::Encoding: 'static + core::marker::Send + core::marker::Sync + core::ops::deref::Deref + +pub type vortex_layout::vtable::VTable::Layout: 'static + core::marker::Send + core::marker::Sync + core::clone::Clone + core::fmt::Debug + core::ops::deref::Deref + vortex_layout::IntoLayout + +pub type vortex_layout::vtable::VTable::Metadata: vortex_array::metadata::SerializeMetadata + vortex_array::metadata::DeserializeMetadata + core::fmt::Debug + +pub fn vortex_layout::vtable::VTable::build(&Self::Encoding, &vortex_array::dtype::DType, u64, &::Output, alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult + +pub fn vortex_layout::vtable::VTable::child(&Self::Layout, usize) -> vortex_error::VortexResult + +pub fn vortex_layout::vtable::VTable::child_type(&Self::Layout, usize) -> vortex_layout::LayoutChildType + +pub fn vortex_layout::vtable::VTable::dtype(&Self::Layout) -> &vortex_array::dtype::DType + +pub fn vortex_layout::vtable::VTable::encoding(&Self::Layout) -> vortex_layout::LayoutEncodingRef + +pub fn vortex_layout::vtable::VTable::id(&Self::Encoding) -> vortex_layout::LayoutId + +pub fn vortex_layout::vtable::VTable::metadata(&Self::Layout) -> Self::Metadata + +pub fn vortex_layout::vtable::VTable::nchildren(&Self::Layout) -> usize + +pub fn vortex_layout::vtable::VTable::new_reader(&Self::Layout, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult + +pub fn vortex_layout::vtable::VTable::row_count(&Self::Layout) -> u64 + +pub fn vortex_layout::vtable::VTable::segment_ids(&Self::Layout) -> alloc::vec::Vec + +pub fn vortex_layout::vtable::VTable::with_children(&mut Self::Layout, alloc::vec::Vec) -> vortex_error::VortexResult<()> + +impl vortex_layout::VTable for vortex_layout::layouts::chunked::Chunked + +pub type vortex_layout::layouts::chunked::Chunked::Encoding = vortex_layout::layouts::chunked::ChunkedLayoutEncoding + +pub type vortex_layout::layouts::chunked::Chunked::Layout = vortex_layout::layouts::chunked::ChunkedLayout + +pub type vortex_layout::layouts::chunked::Chunked::Metadata = vortex_array::metadata::EmptyMetadata + +pub fn vortex_layout::layouts::chunked::Chunked::build(&Self::Encoding, &vortex_array::dtype::DType, u64, &::Output, alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::chunked::Chunked::child(&Self::Layout, usize) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::chunked::Chunked::child_type(&Self::Layout, usize) -> vortex_layout::LayoutChildType + +pub fn vortex_layout::layouts::chunked::Chunked::dtype(&Self::Layout) -> &vortex_array::dtype::DType + +pub fn vortex_layout::layouts::chunked::Chunked::encoding(&Self::Layout) -> vortex_layout::LayoutEncodingRef + +pub fn vortex_layout::layouts::chunked::Chunked::id(&Self::Encoding) -> vortex_layout::LayoutId + +pub fn vortex_layout::layouts::chunked::Chunked::metadata(&Self::Layout) -> Self::Metadata + +pub fn vortex_layout::layouts::chunked::Chunked::nchildren(&Self::Layout) -> usize + +pub fn vortex_layout::layouts::chunked::Chunked::new_reader(&Self::Layout, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::chunked::Chunked::row_count(&Self::Layout) -> u64 + +pub fn vortex_layout::layouts::chunked::Chunked::segment_ids(&Self::Layout) -> alloc::vec::Vec + +pub fn vortex_layout::layouts::chunked::Chunked::with_children(&mut Self::Layout, alloc::vec::Vec) -> vortex_error::VortexResult<()> + +impl vortex_layout::VTable for vortex_layout::layouts::dict::Dict + +pub type vortex_layout::layouts::dict::Dict::Encoding = vortex_layout::layouts::dict::DictLayoutEncoding + +pub type vortex_layout::layouts::dict::Dict::Layout = vortex_layout::layouts::dict::DictLayout + +pub type vortex_layout::layouts::dict::Dict::Metadata = vortex_array::metadata::ProstMetadata + +pub fn vortex_layout::layouts::dict::Dict::build(&Self::Encoding, &vortex_array::dtype::DType, u64, &::Output, alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::dict::Dict::child(&Self::Layout, usize) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::dict::Dict::child_type(&Self::Layout, usize) -> vortex_layout::LayoutChildType + +pub fn vortex_layout::layouts::dict::Dict::dtype(&Self::Layout) -> &vortex_array::dtype::DType + +pub fn vortex_layout::layouts::dict::Dict::encoding(&Self::Layout) -> vortex_layout::LayoutEncodingRef + +pub fn vortex_layout::layouts::dict::Dict::id(&Self::Encoding) -> vortex_layout::LayoutId + +pub fn vortex_layout::layouts::dict::Dict::metadata(&Self::Layout) -> Self::Metadata + +pub fn vortex_layout::layouts::dict::Dict::nchildren(&Self::Layout) -> usize + +pub fn vortex_layout::layouts::dict::Dict::new_reader(&Self::Layout, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::dict::Dict::row_count(&Self::Layout) -> u64 + +pub fn vortex_layout::layouts::dict::Dict::segment_ids(&Self::Layout) -> alloc::vec::Vec + +pub fn vortex_layout::layouts::dict::Dict::with_children(&mut Self::Layout, alloc::vec::Vec) -> vortex_error::VortexResult<()> + +impl vortex_layout::VTable for vortex_layout::layouts::flat::Flat + +pub type vortex_layout::layouts::flat::Flat::Encoding = vortex_layout::layouts::flat::FlatLayoutEncoding + +pub type vortex_layout::layouts::flat::Flat::Layout = vortex_layout::layouts::flat::FlatLayout + +pub type vortex_layout::layouts::flat::Flat::Metadata = vortex_array::metadata::ProstMetadata + +pub fn vortex_layout::layouts::flat::Flat::build(&Self::Encoding, &vortex_array::dtype::DType, u64, &::Output, alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::flat::Flat::child(&Self::Layout, usize) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::flat::Flat::child_type(&Self::Layout, usize) -> vortex_layout::LayoutChildType + +pub fn vortex_layout::layouts::flat::Flat::dtype(&Self::Layout) -> &vortex_array::dtype::DType + +pub fn vortex_layout::layouts::flat::Flat::encoding(&Self::Layout) -> vortex_layout::LayoutEncodingRef + +pub fn vortex_layout::layouts::flat::Flat::id(&Self::Encoding) -> vortex_layout::LayoutId + +pub fn vortex_layout::layouts::flat::Flat::metadata(&Self::Layout) -> Self::Metadata + +pub fn vortex_layout::layouts::flat::Flat::nchildren(&Self::Layout) -> usize + +pub fn vortex_layout::layouts::flat::Flat::new_reader(&Self::Layout, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::flat::Flat::row_count(&Self::Layout) -> u64 + +pub fn vortex_layout::layouts::flat::Flat::segment_ids(&Self::Layout) -> alloc::vec::Vec + +pub fn vortex_layout::layouts::flat::Flat::with_children(&mut Self::Layout, alloc::vec::Vec) -> vortex_error::VortexResult<()> + +impl vortex_layout::VTable for vortex_layout::layouts::struct_::Struct + +pub type vortex_layout::layouts::struct_::Struct::Encoding = vortex_layout::layouts::struct_::StructLayoutEncoding + +pub type vortex_layout::layouts::struct_::Struct::Layout = vortex_layout::layouts::struct_::StructLayout + +pub type vortex_layout::layouts::struct_::Struct::Metadata = vortex_array::metadata::EmptyMetadata + +pub fn vortex_layout::layouts::struct_::Struct::build(&Self::Encoding, &vortex_array::dtype::DType, u64, &::Output, alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::struct_::Struct::child(&Self::Layout, usize) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::struct_::Struct::child_type(&Self::Layout, usize) -> vortex_layout::LayoutChildType + +pub fn vortex_layout::layouts::struct_::Struct::dtype(&Self::Layout) -> &vortex_array::dtype::DType + +pub fn vortex_layout::layouts::struct_::Struct::encoding(&Self::Layout) -> vortex_layout::LayoutEncodingRef + +pub fn vortex_layout::layouts::struct_::Struct::id(&Self::Encoding) -> vortex_layout::LayoutId + +pub fn vortex_layout::layouts::struct_::Struct::metadata(&Self::Layout) -> Self::Metadata + +pub fn vortex_layout::layouts::struct_::Struct::nchildren(&Self::Layout) -> usize + +pub fn vortex_layout::layouts::struct_::Struct::new_reader(&Self::Layout, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::struct_::Struct::row_count(&Self::Layout) -> u64 + +pub fn vortex_layout::layouts::struct_::Struct::segment_ids(&Self::Layout) -> alloc::vec::Vec + +pub fn vortex_layout::layouts::struct_::Struct::with_children(&mut Self::Layout, alloc::vec::Vec) -> vortex_error::VortexResult<()> + +impl vortex_layout::VTable for vortex_layout::layouts::zoned::Zoned + +pub type vortex_layout::layouts::zoned::Zoned::Encoding = vortex_layout::layouts::zoned::ZonedLayoutEncoding + +pub type vortex_layout::layouts::zoned::Zoned::Layout = vortex_layout::layouts::zoned::ZonedLayout + +pub type vortex_layout::layouts::zoned::Zoned::Metadata = vortex_layout::layouts::zoned::ZonedMetadata + +pub fn vortex_layout::layouts::zoned::Zoned::build(&Self::Encoding, &vortex_array::dtype::DType, u64, &vortex_layout::layouts::zoned::ZonedMetadata, alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::zoned::Zoned::child(&Self::Layout, usize) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::zoned::Zoned::child_type(&Self::Layout, usize) -> vortex_layout::LayoutChildType + +pub fn vortex_layout::layouts::zoned::Zoned::dtype(&Self::Layout) -> &vortex_array::dtype::DType + +pub fn vortex_layout::layouts::zoned::Zoned::encoding(&Self::Layout) -> vortex_layout::LayoutEncodingRef + +pub fn vortex_layout::layouts::zoned::Zoned::id(&Self::Encoding) -> vortex_layout::LayoutId + +pub fn vortex_layout::layouts::zoned::Zoned::metadata(&Self::Layout) -> Self::Metadata + +pub fn vortex_layout::layouts::zoned::Zoned::nchildren(&Self::Layout) -> usize + +pub fn vortex_layout::layouts::zoned::Zoned::new_reader(&Self::Layout, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::zoned::Zoned::row_count(&Self::Layout) -> u64 + +pub fn vortex_layout::layouts::zoned::Zoned::segment_ids(&Self::Layout) -> alloc::vec::Vec + +pub fn vortex_layout::layouts::zoned::Zoned::with_children(&mut Self::Layout, alloc::vec::Vec) -> vortex_error::VortexResult<()> + +pub macro vortex_layout::vtable! + +pub enum vortex_layout::LayoutChildType + +pub vortex_layout::LayoutChildType::Auxiliary(alloc::sync::Arc) + +pub vortex_layout::LayoutChildType::Chunk((usize, u64)) + +pub vortex_layout::LayoutChildType::Field(vortex_array::dtype::field_names::FieldName) + +pub vortex_layout::LayoutChildType::Transparent(alloc::sync::Arc) + +impl vortex_layout::LayoutChildType + +pub fn vortex_layout::LayoutChildType::name(&self) -> alloc::sync::Arc + +pub fn vortex_layout::LayoutChildType::row_offset(&self) -> core::option::Option + +impl core::clone::Clone for vortex_layout::LayoutChildType + +pub fn vortex_layout::LayoutChildType::clone(&self) -> vortex_layout::LayoutChildType + +impl core::cmp::Eq for vortex_layout::LayoutChildType + +impl core::cmp::PartialEq for vortex_layout::LayoutChildType + +pub fn vortex_layout::LayoutChildType::eq(&self, &vortex_layout::LayoutChildType) -> bool + +impl core::fmt::Debug for vortex_layout::LayoutChildType + +pub fn vortex_layout::LayoutChildType::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result + +impl core::marker::StructuralPartialEq for vortex_layout::LayoutChildType + +#[repr(transparent)] pub struct vortex_layout::LayoutAdapter(_) + +impl core::fmt::Debug for vortex_layout::LayoutAdapter + +pub fn vortex_layout::LayoutAdapter::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result + +impl vortex_layout::Layout for vortex_layout::LayoutAdapter + +pub fn vortex_layout::LayoutAdapter::as_any(&self) -> &dyn core::any::Any + +pub fn vortex_layout::LayoutAdapter::as_any_arc(alloc::sync::Arc) -> alloc::sync::Arc<(dyn core::any::Any + core::marker::Send + core::marker::Sync)> + +pub fn vortex_layout::LayoutAdapter::child(&self, usize) -> vortex_error::VortexResult + +pub fn vortex_layout::LayoutAdapter::child_type(&self, usize) -> vortex_layout::LayoutChildType + +pub fn vortex_layout::LayoutAdapter::dtype(&self) -> &vortex_array::dtype::DType + +pub fn vortex_layout::LayoutAdapter::encoding(&self) -> vortex_layout::LayoutEncodingRef + +pub fn vortex_layout::LayoutAdapter::metadata(&self) -> alloc::vec::Vec + +pub fn vortex_layout::LayoutAdapter::nchildren(&self) -> usize + +pub fn vortex_layout::LayoutAdapter::new_reader(&self, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult + +pub fn vortex_layout::LayoutAdapter::row_count(&self) -> u64 + +pub fn vortex_layout::LayoutAdapter::segment_ids(&self) -> alloc::vec::Vec + +pub fn vortex_layout::LayoutAdapter::to_layout(&self) -> vortex_layout::LayoutRef + +#[repr(transparent)] pub struct vortex_layout::LayoutEncodingAdapter(_) + +impl core::fmt::Debug for vortex_layout::LayoutEncodingAdapter + +pub fn vortex_layout::LayoutEncodingAdapter::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result + +impl vortex_layout::LayoutEncoding for vortex_layout::LayoutEncodingAdapter + +pub fn vortex_layout::LayoutEncodingAdapter::as_any(&self) -> &dyn core::any::Any + +pub fn vortex_layout::LayoutEncodingAdapter::build(&self, &vortex_array::dtype::DType, u64, &[u8], alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult + +pub fn vortex_layout::LayoutEncodingAdapter::id(&self) -> vortex_layout::LayoutEncodingId + +pub struct vortex_layout::LayoutReaderContext + +impl vortex_layout::LayoutReaderContext + +pub fn vortex_layout::LayoutReaderContext::get(&self, vortex_session::registry::Id) -> core::option::Option> + +pub fn vortex_layout::LayoutReaderContext::new() -> Self + +pub fn vortex_layout::LayoutReaderContext::with(&self, vortex_session::registry::Id, alloc::sync::Arc) -> Self + +impl core::clone::Clone for vortex_layout::LayoutReaderContext + +pub fn vortex_layout::LayoutReaderContext::clone(&self) -> vortex_layout::LayoutReaderContext + +impl core::default::Default for vortex_layout::LayoutReaderContext + +pub fn vortex_layout::LayoutReaderContext::default() -> vortex_layout::LayoutReaderContext + +impl core::fmt::Debug for vortex_layout::LayoutReaderContext + +pub fn vortex_layout::LayoutReaderContext::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result + +pub struct vortex_layout::LazyReaderChildren + +impl vortex_layout::LazyReaderChildren + +pub fn vortex_layout::LazyReaderChildren::get(&self, usize) -> vortex_error::VortexResult<&vortex_layout::LayoutReaderRef> + +pub fn vortex_layout::LazyReaderChildren::new(alloc::sync::Arc, alloc::vec::Vec, alloc::vec::Vec>, alloc::sync::Arc, vortex_session::VortexSession, vortex_layout::LayoutReaderContext) -> Self + +pub struct vortex_layout::SplitRange + +impl vortex_layout::SplitRange + +pub fn vortex_layout::SplitRange::check_bounds(&self, u64) -> vortex_error::VortexResult<()> + +pub fn vortex_layout::SplitRange::is_empty(&self) -> bool + +pub fn vortex_layout::SplitRange::len(&self) -> u64 + +pub fn vortex_layout::SplitRange::root(core::ops::range::Range) -> vortex_error::VortexResult + +pub fn vortex_layout::SplitRange::root_row_range(&self) -> core::ops::range::Range + +pub fn vortex_layout::SplitRange::row_offset(&self) -> u64 + +pub fn vortex_layout::SplitRange::row_range(&self) -> &core::ops::range::Range + +pub fn vortex_layout::SplitRange::try_new(u64, core::ops::range::Range) -> vortex_error::VortexResult + +impl core::clone::Clone for vortex_layout::SplitRange + +pub fn vortex_layout::SplitRange::clone(&self) -> vortex_layout::SplitRange + +impl core::cmp::Eq for vortex_layout::SplitRange + +impl core::cmp::PartialEq for vortex_layout::SplitRange + +pub fn vortex_layout::SplitRange::eq(&self, &vortex_layout::SplitRange) -> bool + +impl core::fmt::Debug for vortex_layout::SplitRange + +pub fn vortex_layout::SplitRange::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result + +impl core::marker::StructuralPartialEq for vortex_layout::SplitRange + +pub trait vortex_layout::ArrayFutureExt + +pub fn vortex_layout::ArrayFutureExt::masked(self, vortex_array::mask_future::MaskFuture) -> Self + +impl vortex_layout::ArrayFutureExt for vortex_layout::ArrayFuture + +pub fn vortex_layout::ArrayFuture::masked(self, vortex_array::mask_future::MaskFuture) -> Self + +pub trait vortex_layout::IntoLayout + +pub fn vortex_layout::IntoLayout::into_layout(self) -> vortex_layout::LayoutRef + +impl vortex_layout::IntoLayout for vortex_layout::layouts::chunked::ChunkedLayout + +pub fn vortex_layout::layouts::chunked::ChunkedLayout::into_layout(self) -> vortex_layout::LayoutRef + +impl vortex_layout::IntoLayout for vortex_layout::layouts::dict::DictLayout + +pub fn vortex_layout::layouts::dict::DictLayout::into_layout(self) -> vortex_layout::LayoutRef + +impl vortex_layout::IntoLayout for vortex_layout::layouts::flat::FlatLayout + +pub fn vortex_layout::layouts::flat::FlatLayout::into_layout(self) -> vortex_layout::LayoutRef + +impl vortex_layout::IntoLayout for vortex_layout::layouts::struct_::StructLayout + +pub fn vortex_layout::layouts::struct_::StructLayout::into_layout(self) -> vortex_layout::LayoutRef + +impl vortex_layout::IntoLayout for vortex_layout::layouts::zoned::ZonedLayout + +pub fn vortex_layout::layouts::zoned::ZonedLayout::into_layout(self) -> vortex_layout::LayoutRef + +pub trait vortex_layout::Layout: 'static + core::marker::Send + core::marker::Sync + core::fmt::Debug + vortex_layout::layout::private::Sealed + +pub fn vortex_layout::Layout::as_any(&self) -> &dyn core::any::Any + +pub fn vortex_layout::Layout::as_any_arc(alloc::sync::Arc) -> alloc::sync::Arc<(dyn core::any::Any + core::marker::Send + core::marker::Sync)> + +pub fn vortex_layout::Layout::child(&self, usize) -> vortex_error::VortexResult + +pub fn vortex_layout::Layout::child_type(&self, usize) -> vortex_layout::LayoutChildType + +pub fn vortex_layout::Layout::dtype(&self) -> &vortex_array::dtype::DType + +pub fn vortex_layout::Layout::encoding(&self) -> vortex_layout::LayoutEncodingRef + +pub fn vortex_layout::Layout::metadata(&self) -> alloc::vec::Vec + +pub fn vortex_layout::Layout::nchildren(&self) -> usize + +pub fn vortex_layout::Layout::new_reader(&self, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult + +pub fn vortex_layout::Layout::row_count(&self) -> u64 + +pub fn vortex_layout::Layout::segment_ids(&self) -> alloc::vec::Vec + +pub fn vortex_layout::Layout::to_layout(&self) -> vortex_layout::LayoutRef + +impl vortex_layout::Layout for vortex_layout::LayoutAdapter + +pub fn vortex_layout::LayoutAdapter::as_any(&self) -> &dyn core::any::Any + +pub fn vortex_layout::LayoutAdapter::as_any_arc(alloc::sync::Arc) -> alloc::sync::Arc<(dyn core::any::Any + core::marker::Send + core::marker::Sync)> + +pub fn vortex_layout::LayoutAdapter::child(&self, usize) -> vortex_error::VortexResult + +pub fn vortex_layout::LayoutAdapter::child_type(&self, usize) -> vortex_layout::LayoutChildType + +pub fn vortex_layout::LayoutAdapter::dtype(&self) -> &vortex_array::dtype::DType + +pub fn vortex_layout::LayoutAdapter::encoding(&self) -> vortex_layout::LayoutEncodingRef + +pub fn vortex_layout::LayoutAdapter::metadata(&self) -> alloc::vec::Vec + +pub fn vortex_layout::LayoutAdapter::nchildren(&self) -> usize + +pub fn vortex_layout::LayoutAdapter::new_reader(&self, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult + +pub fn vortex_layout::LayoutAdapter::row_count(&self) -> u64 + +pub fn vortex_layout::LayoutAdapter::segment_ids(&self) -> alloc::vec::Vec + +pub fn vortex_layout::LayoutAdapter::to_layout(&self) -> vortex_layout::LayoutRef + +pub trait vortex_layout::LayoutChildren: 'static + core::marker::Send + core::marker::Sync + +pub fn vortex_layout::LayoutChildren::child(&self, usize, &vortex_array::dtype::DType) -> vortex_error::VortexResult + +pub fn vortex_layout::LayoutChildren::child_row_count(&self, usize) -> u64 + +pub fn vortex_layout::LayoutChildren::nchildren(&self) -> usize + +pub fn vortex_layout::LayoutChildren::to_arc(&self) -> alloc::sync::Arc + +impl vortex_layout::LayoutChildren for alloc::sync::Arc + +pub fn alloc::sync::Arc::child(&self, usize, &vortex_array::dtype::DType) -> vortex_error::VortexResult + +pub fn alloc::sync::Arc::child_row_count(&self, usize) -> u64 + +pub fn alloc::sync::Arc::nchildren(&self) -> usize + +pub fn alloc::sync::Arc::to_arc(&self) -> alloc::sync::Arc + +pub trait vortex_layout::LayoutEncoding: 'static + core::marker::Send + core::marker::Sync + core::fmt::Debug + vortex_layout::encoding::private::Sealed + +pub fn vortex_layout::LayoutEncoding::as_any(&self) -> &dyn core::any::Any + +pub fn vortex_layout::LayoutEncoding::build(&self, &vortex_array::dtype::DType, u64, &[u8], alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult + +pub fn vortex_layout::LayoutEncoding::id(&self) -> vortex_layout::LayoutEncodingId + +impl vortex_layout::LayoutEncoding for vortex_layout::LayoutEncodingAdapter + +pub fn vortex_layout::LayoutEncodingAdapter::as_any(&self) -> &dyn core::any::Any + +pub fn vortex_layout::LayoutEncodingAdapter::build(&self, &vortex_array::dtype::DType, u64, &[u8], alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult + +pub fn vortex_layout::LayoutEncodingAdapter::id(&self) -> vortex_layout::LayoutEncodingId + +pub trait vortex_layout::LayoutReader: 'static + core::marker::Send + core::marker::Sync + +pub fn vortex_layout::LayoutReader::as_any(&self) -> &dyn core::any::Any + +pub fn vortex_layout::LayoutReader::dtype(&self) -> &vortex_array::dtype::DType + +pub fn vortex_layout::LayoutReader::filter_evaluation(&self, &core::ops::range::Range, &vortex_array::expr::expression::Expression, vortex_array::mask_future::MaskFuture) -> vortex_error::VortexResult + +pub fn vortex_layout::LayoutReader::name(&self) -> &alloc::sync::Arc + +pub fn vortex_layout::LayoutReader::projection_evaluation(&self, &core::ops::range::Range, &vortex_array::expr::expression::Expression, vortex_array::mask_future::MaskFuture) -> vortex_error::VortexResult + +pub fn vortex_layout::LayoutReader::pruning_evaluation(&self, &core::ops::range::Range, &vortex_array::expr::expression::Expression, vortex_mask::Mask) -> vortex_error::VortexResult + +pub fn vortex_layout::LayoutReader::register_splits(&self, &[vortex_array::dtype::field_mask::FieldMask], &vortex_layout::SplitRange, &mut alloc::collections::btree::set::BTreeSet) -> vortex_error::VortexResult<()> + +pub fn vortex_layout::LayoutReader::row_count(&self) -> u64 + +impl vortex_layout::LayoutReader for vortex_layout::layouts::row_idx::RowIdxLayoutReader + +pub fn vortex_layout::layouts::row_idx::RowIdxLayoutReader::as_any(&self) -> &dyn core::any::Any + +pub fn vortex_layout::layouts::row_idx::RowIdxLayoutReader::dtype(&self) -> &vortex_array::dtype::DType + +pub fn vortex_layout::layouts::row_idx::RowIdxLayoutReader::filter_evaluation(&self, &core::ops::range::Range, &vortex_array::expr::expression::Expression, vortex_array::mask_future::MaskFuture) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::row_idx::RowIdxLayoutReader::name(&self) -> &alloc::sync::Arc + +pub fn vortex_layout::layouts::row_idx::RowIdxLayoutReader::projection_evaluation(&self, &core::ops::range::Range, &vortex_array::expr::expression::Expression, vortex_array::mask_future::MaskFuture) -> vortex_error::VortexResult>> + +pub fn vortex_layout::layouts::row_idx::RowIdxLayoutReader::pruning_evaluation(&self, &core::ops::range::Range, &vortex_array::expr::expression::Expression, vortex_mask::Mask) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::row_idx::RowIdxLayoutReader::register_splits(&self, &[vortex_array::dtype::field_mask::FieldMask], &vortex_layout::SplitRange, &mut alloc::collections::btree::set::BTreeSet) -> vortex_error::VortexResult<()> + +pub fn vortex_layout::layouts::row_idx::RowIdxLayoutReader::row_count(&self) -> u64 + +pub trait vortex_layout::LayoutStrategy: 'static + core::marker::Send + core::marker::Sync + +pub fn vortex_layout::LayoutStrategy::buffered_bytes(&self) -> u64 + +pub fn vortex_layout::LayoutStrategy::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait + +impl vortex_layout::LayoutStrategy for alloc::sync::Arc + +pub fn alloc::sync::Arc::buffered_bytes(&self) -> u64 + +pub fn alloc::sync::Arc::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait + +impl vortex_layout::LayoutStrategy for vortex_layout::layouts::buffered::BufferedStrategy + +pub fn vortex_layout::layouts::buffered::BufferedStrategy::buffered_bytes(&self) -> u64 + +pub fn vortex_layout::layouts::buffered::BufferedStrategy::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait + +impl vortex_layout::LayoutStrategy for vortex_layout::layouts::chunked::writer::ChunkedLayoutStrategy + +pub fn vortex_layout::layouts::chunked::writer::ChunkedLayoutStrategy::buffered_bytes(&self) -> u64 + +pub fn vortex_layout::layouts::chunked::writer::ChunkedLayoutStrategy::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait + +impl vortex_layout::LayoutStrategy for vortex_layout::layouts::collect::CollectStrategy + +pub fn vortex_layout::layouts::collect::CollectStrategy::buffered_bytes(&self) -> u64 + +pub fn vortex_layout::layouts::collect::CollectStrategy::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait + +impl vortex_layout::LayoutStrategy for vortex_layout::layouts::compressed::CompressingStrategy + +pub fn vortex_layout::layouts::compressed::CompressingStrategy::buffered_bytes(&self) -> u64 + +pub fn vortex_layout::layouts::compressed::CompressingStrategy::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait + +impl vortex_layout::LayoutStrategy for vortex_layout::layouts::dict::writer::DictStrategy + +pub fn vortex_layout::layouts::dict::writer::DictStrategy::buffered_bytes(&self) -> u64 + +pub fn vortex_layout::layouts::dict::writer::DictStrategy::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait + +impl vortex_layout::LayoutStrategy for vortex_layout::layouts::flat::writer::FlatLayoutStrategy + +pub fn vortex_layout::layouts::flat::writer::FlatLayoutStrategy::buffered_bytes(&self) -> u64 + +pub fn vortex_layout::layouts::flat::writer::FlatLayoutStrategy::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait + +impl vortex_layout::LayoutStrategy for vortex_layout::layouts::repartition::RepartitionStrategy + +pub fn vortex_layout::layouts::repartition::RepartitionStrategy::buffered_bytes(&self) -> u64 + +pub fn vortex_layout::layouts::repartition::RepartitionStrategy::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait + +impl vortex_layout::LayoutStrategy for vortex_layout::layouts::table::TableStrategy + +pub fn vortex_layout::layouts::table::TableStrategy::buffered_bytes(&self) -> u64 + +pub fn vortex_layout::layouts::table::TableStrategy::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait + +impl vortex_layout::LayoutStrategy for vortex_layout::layouts::zoned::writer::ZonedStrategy + +pub fn vortex_layout::layouts::zoned::writer::ZonedStrategy::buffered_bytes(&self) -> u64 + +pub fn vortex_layout::layouts::zoned::writer::ZonedStrategy::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait + +pub trait vortex_layout::VTable: 'static + core::marker::Sized + core::marker::Send + core::marker::Sync + core::fmt::Debug + +pub type vortex_layout::VTable::Encoding: 'static + core::marker::Send + core::marker::Sync + core::ops::deref::Deref + +pub type vortex_layout::VTable::Layout: 'static + core::marker::Send + core::marker::Sync + core::clone::Clone + core::fmt::Debug + core::ops::deref::Deref + vortex_layout::IntoLayout + +pub type vortex_layout::VTable::Metadata: vortex_array::metadata::SerializeMetadata + vortex_array::metadata::DeserializeMetadata + core::fmt::Debug + +pub fn vortex_layout::VTable::build(&Self::Encoding, &vortex_array::dtype::DType, u64, &::Output, alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult + +pub fn vortex_layout::VTable::child(&Self::Layout, usize) -> vortex_error::VortexResult + +pub fn vortex_layout::VTable::child_type(&Self::Layout, usize) -> vortex_layout::LayoutChildType + +pub fn vortex_layout::VTable::dtype(&Self::Layout) -> &vortex_array::dtype::DType + +pub fn vortex_layout::VTable::encoding(&Self::Layout) -> vortex_layout::LayoutEncodingRef + +pub fn vortex_layout::VTable::id(&Self::Encoding) -> vortex_layout::LayoutId + +pub fn vortex_layout::VTable::metadata(&Self::Layout) -> Self::Metadata + +pub fn vortex_layout::VTable::nchildren(&Self::Layout) -> usize + +pub fn vortex_layout::VTable::new_reader(&Self::Layout, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult + +pub fn vortex_layout::VTable::row_count(&Self::Layout) -> u64 + +pub fn vortex_layout::VTable::segment_ids(&Self::Layout) -> alloc::vec::Vec + +pub fn vortex_layout::VTable::with_children(&mut Self::Layout, alloc::vec::Vec) -> vortex_error::VortexResult<()> + +impl vortex_layout::VTable for vortex_layout::layouts::chunked::Chunked + +pub type vortex_layout::layouts::chunked::Chunked::Encoding = vortex_layout::layouts::chunked::ChunkedLayoutEncoding + +pub type vortex_layout::layouts::chunked::Chunked::Layout = vortex_layout::layouts::chunked::ChunkedLayout + +pub type vortex_layout::layouts::chunked::Chunked::Metadata = vortex_array::metadata::EmptyMetadata + +pub fn vortex_layout::layouts::chunked::Chunked::build(&Self::Encoding, &vortex_array::dtype::DType, u64, &::Output, alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::chunked::Chunked::child(&Self::Layout, usize) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::chunked::Chunked::child_type(&Self::Layout, usize) -> vortex_layout::LayoutChildType + +pub fn vortex_layout::layouts::chunked::Chunked::dtype(&Self::Layout) -> &vortex_array::dtype::DType + +pub fn vortex_layout::layouts::chunked::Chunked::encoding(&Self::Layout) -> vortex_layout::LayoutEncodingRef + +pub fn vortex_layout::layouts::chunked::Chunked::id(&Self::Encoding) -> vortex_layout::LayoutId + +pub fn vortex_layout::layouts::chunked::Chunked::metadata(&Self::Layout) -> Self::Metadata + +pub fn vortex_layout::layouts::chunked::Chunked::nchildren(&Self::Layout) -> usize + +pub fn vortex_layout::layouts::chunked::Chunked::new_reader(&Self::Layout, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::chunked::Chunked::row_count(&Self::Layout) -> u64 + +pub fn vortex_layout::layouts::chunked::Chunked::segment_ids(&Self::Layout) -> alloc::vec::Vec + +pub fn vortex_layout::layouts::chunked::Chunked::with_children(&mut Self::Layout, alloc::vec::Vec) -> vortex_error::VortexResult<()> + +impl vortex_layout::VTable for vortex_layout::layouts::dict::Dict + +pub type vortex_layout::layouts::dict::Dict::Encoding = vortex_layout::layouts::dict::DictLayoutEncoding + +pub type vortex_layout::layouts::dict::Dict::Layout = vortex_layout::layouts::dict::DictLayout + +pub type vortex_layout::layouts::dict::Dict::Metadata = vortex_array::metadata::ProstMetadata + +pub fn vortex_layout::layouts::dict::Dict::build(&Self::Encoding, &vortex_array::dtype::DType, u64, &::Output, alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::dict::Dict::child(&Self::Layout, usize) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::dict::Dict::child_type(&Self::Layout, usize) -> vortex_layout::LayoutChildType + +pub fn vortex_layout::layouts::dict::Dict::dtype(&Self::Layout) -> &vortex_array::dtype::DType + +pub fn vortex_layout::layouts::dict::Dict::encoding(&Self::Layout) -> vortex_layout::LayoutEncodingRef + +pub fn vortex_layout::layouts::dict::Dict::id(&Self::Encoding) -> vortex_layout::LayoutId + +pub fn vortex_layout::layouts::dict::Dict::metadata(&Self::Layout) -> Self::Metadata + +pub fn vortex_layout::layouts::dict::Dict::nchildren(&Self::Layout) -> usize + +pub fn vortex_layout::layouts::dict::Dict::new_reader(&Self::Layout, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::dict::Dict::row_count(&Self::Layout) -> u64 + +pub fn vortex_layout::layouts::dict::Dict::segment_ids(&Self::Layout) -> alloc::vec::Vec + +pub fn vortex_layout::layouts::dict::Dict::with_children(&mut Self::Layout, alloc::vec::Vec) -> vortex_error::VortexResult<()> + +impl vortex_layout::VTable for vortex_layout::layouts::flat::Flat + +pub type vortex_layout::layouts::flat::Flat::Encoding = vortex_layout::layouts::flat::FlatLayoutEncoding + +pub type vortex_layout::layouts::flat::Flat::Layout = vortex_layout::layouts::flat::FlatLayout + +pub type vortex_layout::layouts::flat::Flat::Metadata = vortex_array::metadata::ProstMetadata + +pub fn vortex_layout::layouts::flat::Flat::build(&Self::Encoding, &vortex_array::dtype::DType, u64, &::Output, alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::flat::Flat::child(&Self::Layout, usize) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::flat::Flat::child_type(&Self::Layout, usize) -> vortex_layout::LayoutChildType + +pub fn vortex_layout::layouts::flat::Flat::dtype(&Self::Layout) -> &vortex_array::dtype::DType + +pub fn vortex_layout::layouts::flat::Flat::encoding(&Self::Layout) -> vortex_layout::LayoutEncodingRef + +pub fn vortex_layout::layouts::flat::Flat::id(&Self::Encoding) -> vortex_layout::LayoutId + +pub fn vortex_layout::layouts::flat::Flat::metadata(&Self::Layout) -> Self::Metadata + +pub fn vortex_layout::layouts::flat::Flat::nchildren(&Self::Layout) -> usize + +pub fn vortex_layout::layouts::flat::Flat::new_reader(&Self::Layout, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::flat::Flat::row_count(&Self::Layout) -> u64 + +pub fn vortex_layout::layouts::flat::Flat::segment_ids(&Self::Layout) -> alloc::vec::Vec + +pub fn vortex_layout::layouts::flat::Flat::with_children(&mut Self::Layout, alloc::vec::Vec) -> vortex_error::VortexResult<()> + +impl vortex_layout::VTable for vortex_layout::layouts::struct_::Struct + +pub type vortex_layout::layouts::struct_::Struct::Encoding = vortex_layout::layouts::struct_::StructLayoutEncoding + +pub type vortex_layout::layouts::struct_::Struct::Layout = vortex_layout::layouts::struct_::StructLayout + +pub type vortex_layout::layouts::struct_::Struct::Metadata = vortex_array::metadata::EmptyMetadata + +pub fn vortex_layout::layouts::struct_::Struct::build(&Self::Encoding, &vortex_array::dtype::DType, u64, &::Output, alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::struct_::Struct::child(&Self::Layout, usize) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::struct_::Struct::child_type(&Self::Layout, usize) -> vortex_layout::LayoutChildType + +pub fn vortex_layout::layouts::struct_::Struct::dtype(&Self::Layout) -> &vortex_array::dtype::DType + +pub fn vortex_layout::layouts::struct_::Struct::encoding(&Self::Layout) -> vortex_layout::LayoutEncodingRef + +pub fn vortex_layout::layouts::struct_::Struct::id(&Self::Encoding) -> vortex_layout::LayoutId + +pub fn vortex_layout::layouts::struct_::Struct::metadata(&Self::Layout) -> Self::Metadata + +pub fn vortex_layout::layouts::struct_::Struct::nchildren(&Self::Layout) -> usize + +pub fn vortex_layout::layouts::struct_::Struct::new_reader(&Self::Layout, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::struct_::Struct::row_count(&Self::Layout) -> u64 + +pub fn vortex_layout::layouts::struct_::Struct::segment_ids(&Self::Layout) -> alloc::vec::Vec + +pub fn vortex_layout::layouts::struct_::Struct::with_children(&mut Self::Layout, alloc::vec::Vec) -> vortex_error::VortexResult<()> + +impl vortex_layout::VTable for vortex_layout::layouts::zoned::Zoned + +pub type vortex_layout::layouts::zoned::Zoned::Encoding = vortex_layout::layouts::zoned::ZonedLayoutEncoding + +pub type vortex_layout::layouts::zoned::Zoned::Layout = vortex_layout::layouts::zoned::ZonedLayout + +pub type vortex_layout::layouts::zoned::Zoned::Metadata = vortex_layout::layouts::zoned::ZonedMetadata + +pub fn vortex_layout::layouts::zoned::Zoned::build(&Self::Encoding, &vortex_array::dtype::DType, u64, &vortex_layout::layouts::zoned::ZonedMetadata, alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::zoned::Zoned::child(&Self::Layout, usize) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::zoned::Zoned::child_type(&Self::Layout, usize) -> vortex_layout::LayoutChildType + +pub fn vortex_layout::layouts::zoned::Zoned::dtype(&Self::Layout) -> &vortex_array::dtype::DType + +pub fn vortex_layout::layouts::zoned::Zoned::encoding(&Self::Layout) -> vortex_layout::LayoutEncodingRef + +pub fn vortex_layout::layouts::zoned::Zoned::id(&Self::Encoding) -> vortex_layout::LayoutId + +pub fn vortex_layout::layouts::zoned::Zoned::metadata(&Self::Layout) -> Self::Metadata + +pub fn vortex_layout::layouts::zoned::Zoned::nchildren(&Self::Layout) -> usize + +pub fn vortex_layout::layouts::zoned::Zoned::new_reader(&Self::Layout, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult + +pub fn vortex_layout::layouts::zoned::Zoned::row_count(&Self::Layout) -> u64 + +pub fn vortex_layout::layouts::zoned::Zoned::segment_ids(&Self::Layout) -> alloc::vec::Vec + +pub fn vortex_layout::layouts::zoned::Zoned::with_children(&mut Self::Layout, alloc::vec::Vec) -> vortex_error::VortexResult<()> + +pub fn vortex_layout::layout_from_flatbuffer(vortex_flatbuffers::FlatBuffer, &vortex_array::dtype::DType, &vortex_session::registry::ReadContext, &vortex_session::registry::ReadContext, &vortex_layout::session::LayoutRegistry) -> vortex_error::VortexResult + +pub fn vortex_layout::layout_from_flatbuffer_with_options(vortex_flatbuffers::FlatBuffer, &vortex_array::dtype::DType, &vortex_session::registry::ReadContext, &vortex_session::registry::ReadContext, &vortex_layout::session::LayoutRegistry, bool) -> vortex_error::VortexResult + +pub type vortex_layout::ArrayFuture = futures_core::future::BoxFuture<'static, vortex_error::VortexResult> + +pub type vortex_layout::LayoutContext = vortex_session::registry::Context + +pub type vortex_layout::LayoutEncodingId = vortex_session::registry::Id + +pub type vortex_layout::LayoutEncodingRef = arcref::ArcRef + +pub type vortex_layout::LayoutId = vortex_session::registry::Id + +pub type vortex_layout::LayoutReaderRef = alloc::sync::Arc + +pub type vortex_layout::LayoutRef = alloc::sync::Arc diff --git a/vortex-layout/src/layout.rs b/vortex-layout/src/layout.rs index 11f0afd629c..7e4caaa1168 100644 --- a/vortex-layout/src/layout.rs +++ b/vortex-layout/src/layout.rs @@ -19,6 +19,7 @@ use vortex_session::registry::Id; use crate::LayoutEncodingId; use crate::LayoutEncodingRef; +use crate::LayoutReaderContext; use crate::LayoutReaderRef; use crate::VTable; use crate::display::DisplayLayoutTree; @@ -63,11 +64,26 @@ pub trait Layout: 'static + Send + Sync + Debug + private::Sealed { /// Get the segment IDs for this layout. fn segment_ids(&self) -> Vec; + /// Construct a new reader for this layout. + /// + /// - `name` — human-readable label for this reader, propagated to child readers + /// (typically by appending a path component) and surfaced in tracing/debug output. + /// - `segment_source` — source of segment bytes for this and any descendant readers + /// constructed from the returned reader; recursive callers should pass the same + /// source through. + /// - `session` — the [`VortexSession`] hosting the encoding/scalar/layout registries + /// and execution context the reader needs at evaluation time. + /// - `ctx` — id-keyed dependency registry threaded through reader construction (see + /// [`LayoutReaderContext`]). Top-level callers (file open, tests) typically pass + /// `&LayoutReaderContext::new()`; recursive callers inside layout implementations + /// must propagate the `ctx` they were handed so ancestor-published values reach + /// descendants. fn new_reader( &self, name: Arc, segment_source: Arc, session: &VortexSession, + ctx: &LayoutReaderContext, ) -> VortexResult; } @@ -311,8 +327,9 @@ impl Layout for LayoutAdapter { name: Arc, segment_source: Arc, session: &VortexSession, + ctx: &LayoutReaderContext, ) -> VortexResult { - V::new_reader(&self.0, name, segment_source, session) + V::new_reader(&self.0, name, segment_source, session, ctx) } } diff --git a/vortex-layout/src/layouts/chunked/mod.rs b/vortex-layout/src/layouts/chunked/mod.rs index 82fcb650607..e8dd3c06dc0 100644 --- a/vortex-layout/src/layouts/chunked/mod.rs +++ b/vortex-layout/src/layouts/chunked/mod.rs @@ -16,6 +16,7 @@ use vortex_session::registry::ReadContext; use crate::LayoutChildType; use crate::LayoutEncodingRef; use crate::LayoutId; +use crate::LayoutReaderContext; use crate::LayoutReaderRef; use crate::LayoutRef; use crate::VTable; @@ -74,12 +75,14 @@ impl VTable for Chunked { name: Arc, segment_source: Arc, session: &VortexSession, + ctx: &LayoutReaderContext, ) -> VortexResult { Ok(Arc::new(ChunkedReader::new( layout.clone(), name, segment_source, session, + ctx.clone(), ))) } diff --git a/vortex-layout/src/layouts/chunked/reader.rs b/vortex-layout/src/layouts/chunked/reader.rs index 39bd8b27b7c..51eb4f7a15d 100644 --- a/vortex-layout/src/layouts/chunked/reader.rs +++ b/vortex-layout/src/layouts/chunked/reader.rs @@ -26,6 +26,7 @@ use vortex_error::vortex_panic; use vortex_mask::Mask; use vortex_session::VortexSession; +use crate::LayoutReaderContext; use crate::LayoutReaderRef; use crate::LazyReaderChildren; use crate::layouts::chunked::ChunkedLayout; @@ -51,6 +52,7 @@ impl ChunkedReader { name: Arc, segment_source: Arc, session: &VortexSession, + ctx: LayoutReaderContext, ) -> Self { let nchildren = layout.nchildren(); @@ -78,6 +80,7 @@ impl ChunkedReader { names, segment_source, session.clone(), + ctx, ); Self { @@ -455,7 +458,12 @@ mod test { ) { let layout = nested_chunked_layout(); let reader = layout - .new_reader("".into(), Arc::new(TestSegments::default()), &SESSION) + .new_reader( + "".into(), + Arc::new(TestSegments::default()), + &SESSION, + &Default::default(), + ) .unwrap(); let splits = SplitBy::Layout @@ -471,7 +479,7 @@ mod test { ) { block_on(|_h| async { let result = layout - .new_reader("".into(), segments, &SESSION) + .new_reader("".into(), segments, &SESSION, &Default::default()) .unwrap() .projection_evaluation( &(0..layout.row_count()), diff --git a/vortex-layout/src/layouts/dict/mod.rs b/vortex-layout/src/layouts/dict/mod.rs index 7928b447fa9..5fb4ea080b4 100644 --- a/vortex-layout/src/layouts/dict/mod.rs +++ b/vortex-layout/src/layouts/dict/mod.rs @@ -92,12 +92,14 @@ impl VTable for Dict { name: Arc, segment_source: Arc, session: &VortexSession, + ctx: &crate::LayoutReaderContext, ) -> VortexResult { Ok(Arc::new(DictReader::try_new( layout.clone(), name, segment_source, session.clone(), + ctx.clone(), )?)) } diff --git a/vortex-layout/src/layouts/dict/reader.rs b/vortex-layout/src/layouts/dict/reader.rs index 5687274bd90..002b4b1e902 100644 --- a/vortex-layout/src/layouts/dict/reader.rs +++ b/vortex-layout/src/layouts/dict/reader.rs @@ -58,17 +58,21 @@ impl DictReader { name: Arc, segment_source: Arc, session: VortexSession, + ctx: crate::LayoutReaderContext, ) -> VortexResult { let values_len = usize::try_from(layout.values.row_count())?; let values = layout.values.new_reader( format!("{name}.values").into(), Arc::clone(&segment_source), &session, + &ctx, + )?; + let codes = layout.codes.new_reader( + format!("{name}.codes").into(), + segment_source, + &session, + &ctx, )?; - let codes = - layout - .codes - .new_reader(format!("{name}.codes").into(), segment_source, &session)?; Ok(Self { layout, @@ -370,7 +374,7 @@ mod tests { ); assert!(layout.encoding_id() == LayoutId::new("vortex.dict")); let actual = layout - .new_reader("".into(), segments, &session) + .new_reader("".into(), segments, &session, &Default::default()) .unwrap() .projection_evaluation( &(0..layout.row_count()), @@ -454,7 +458,7 @@ mod tests { )), ); let mask = layout - .new_reader("".into(), segments, &session) + .new_reader("".into(), segments, &session, &Default::default()) .unwrap() .filter_evaluation(&(0..3), &filter, MaskFuture::new_true(3)) .unwrap() @@ -515,7 +519,7 @@ mod tests { let expression = is_not_null(root()); assert_eq!(layout.encoding_id(), LayoutId::new("vortex.dict")); let actual = layout - .new_reader("".into(), segments, &session) + .new_reader("".into(), segments, &session, &Default::default()) .unwrap() .projection_evaluation( &(0..layout.row_count()), diff --git a/vortex-layout/src/layouts/flat/mod.rs b/vortex-layout/src/layouts/flat/mod.rs index b167e633170..a652f82fe61 100644 --- a/vortex-layout/src/layouts/flat/mod.rs +++ b/vortex-layout/src/layouts/flat/mod.rs @@ -87,6 +87,7 @@ impl VTable for Flat { name: Arc, segment_source: Arc, session: &VortexSession, + _ctx: &crate::LayoutReaderContext, ) -> VortexResult { Ok(Arc::new(FlatReader::new( layout.clone(), diff --git a/vortex-layout/src/layouts/flat/reader.rs b/vortex-layout/src/layouts/flat/reader.rs index 954bbe5ab5c..2af7b679a8f 100644 --- a/vortex-layout/src/layouts/flat/reader.rs +++ b/vortex-layout/src/layouts/flat/reader.rs @@ -276,7 +276,7 @@ mod test { ); let result = layout - .new_reader("".into(), segments, &SESSION)? + .new_reader("".into(), segments, &SESSION, &Default::default())? .projection_evaluation( &(0..layout.row_count()), &root(), @@ -313,7 +313,7 @@ mod test { let expr = gt(root(), lit(3i32)); let result = layout - .new_reader("".into(), segments, &SESSION) + .new_reader("".into(), segments, &SESSION, &Default::default()) .unwrap() .projection_evaluation( &(0..layout.row_count()), @@ -350,7 +350,7 @@ mod test { .unwrap(); let result = layout - .new_reader("".into(), segments, &SESSION) + .new_reader("".into(), segments, &SESSION, &Default::default()) .unwrap() .projection_evaluation(&(2..4), &root(), MaskFuture::new_true(2)) .unwrap() diff --git a/vortex-layout/src/layouts/flat/writer.rs b/vortex-layout/src/layouts/flat/writer.rs index a50d8a27416..f390555a4e7 100644 --- a/vortex-layout/src/layouts/flat/writer.rs +++ b/vortex-layout/src/layouts/flat/writer.rs @@ -260,7 +260,7 @@ mod tests { .unwrap(); let result = layout - .new_reader("".into(), segments, &SESSION) + .new_reader("".into(), segments, &SESSION, &Default::default()) .unwrap() .projection_evaluation( &(0..layout.row_count()), @@ -311,7 +311,7 @@ mod tests { .unwrap(); let result = layout - .new_reader("".into(), segments, &SESSION) + .new_reader("".into(), segments, &SESSION, &Default::default()) .unwrap() .projection_evaluation( &(0..layout.row_count()), @@ -384,7 +384,7 @@ mod tests { // We should be able to read the array we just wrote. let result: ArrayRef = layout - .new_reader("".into(), segments, &SESSION) + .new_reader("".into(), segments, &SESSION, &Default::default()) .unwrap() .projection_evaluation( &(0..layout.row_count()), diff --git a/vortex-layout/src/layouts/foreign/mod.rs b/vortex-layout/src/layouts/foreign/mod.rs index 9abacc8b26e..6e0a351b41b 100644 --- a/vortex-layout/src/layouts/foreign/mod.rs +++ b/vortex-layout/src/layouts/foreign/mod.rs @@ -171,6 +171,7 @@ impl Layout for ForeignLayout { _name: Arc, _segment_source: Arc, _session: &VortexSession, + _ctx: &crate::LayoutReaderContext, ) -> VortexResult { vortex_bail!( "Cannot read unknown layout encoding '{}'", diff --git a/vortex-layout/src/layouts/row_idx/mod.rs b/vortex-layout/src/layouts/row_idx/mod.rs index 90570101827..3814c2f8093 100644 --- a/vortex-layout/src/layouts/row_idx/mod.rs +++ b/vortex-layout/src/layouts/row_idx/mod.rs @@ -370,7 +370,9 @@ mod tests { let expr = eq(root(), lit(3i32)); let result = RowIdxLayoutReader::new( 0, - layout.new_reader("".into(), segments, &SESSION).unwrap(), + layout + .new_reader("".into(), segments, &SESSION, &Default::default()) + .unwrap(), SESSION.clone(), ) .projection_evaluation( @@ -411,7 +413,9 @@ mod tests { let expr = gt(row_idx(), lit(3u64)); let result = RowIdxLayoutReader::new( 0, - layout.new_reader("".into(), segments, &SESSION).unwrap(), + layout + .new_reader("".into(), segments, &SESSION, &Default::default()) + .unwrap(), SESSION.clone(), ) .projection_evaluation( @@ -456,7 +460,9 @@ mod tests { let result = RowIdxLayoutReader::new( 0, - layout.new_reader("".into(), segments, &SESSION).unwrap(), + layout + .new_reader("".into(), segments, &SESSION, &Default::default()) + .unwrap(), SESSION.clone(), ) .projection_evaluation( diff --git a/vortex-layout/src/layouts/struct_/mod.rs b/vortex-layout/src/layouts/struct_/mod.rs index 29843f096d8..39ada8ccaec 100644 --- a/vortex-layout/src/layouts/struct_/mod.rs +++ b/vortex-layout/src/layouts/struct_/mod.rs @@ -114,12 +114,14 @@ impl VTable for Struct { name: Arc, segment_source: Arc, session: &VortexSession, + ctx: &crate::LayoutReaderContext, ) -> VortexResult { Ok(Arc::new(StructReader::try_new( layout.clone(), name, segment_source, session.session(), + ctx.clone(), )?)) } diff --git a/vortex-layout/src/layouts/struct_/reader.rs b/vortex-layout/src/layouts/struct_/reader.rs index 6364d40b442..5261f85f9a8 100644 --- a/vortex-layout/src/layouts/struct_/reader.rs +++ b/vortex-layout/src/layouts/struct_/reader.rs @@ -68,6 +68,7 @@ impl StructReader { name: Arc, segment_source: Arc, session: VortexSession, + ctx: crate::LayoutReaderContext, ) -> VortexResult { let struct_dt = layout.struct_fields(); @@ -99,6 +100,7 @@ impl StructReader { names, Arc::clone(&segment_source), session.clone(), + ctx, ); // Create an expanded root expression that contains all fields of the struct. @@ -616,7 +618,9 @@ mod tests { fn test_struct_layout_or( #[from(struct_layout)] (segments, layout): (Arc, LayoutRef), ) { - let reader = layout.new_reader("".into(), segments, &SESSION).unwrap(); + let reader = layout + .new_reader("".into(), segments, &SESSION, &Default::default()) + .unwrap(); let filt = or( eq(col("a"), lit(7)), or(eq(col("b"), lit(5)), eq(col("a"), lit(3))), @@ -634,7 +638,9 @@ mod tests { fn test_struct_layout( #[from(struct_layout)] (segments, layout): (Arc, LayoutRef), ) { - let reader = layout.new_reader("".into(), segments, &SESSION).unwrap(); + let reader = layout + .new_reader("".into(), segments, &SESSION, &Default::default()) + .unwrap(); let expr = gt(get_item("a", root()), get_item("b", root())); let result = block_on(|_| { reader @@ -650,7 +656,9 @@ mod tests { fn test_struct_layout_row_mask( #[from(struct_layout)] (segments, layout): (Arc, LayoutRef), ) { - let reader = layout.new_reader("".into(), segments, &SESSION).unwrap(); + let reader = layout + .new_reader("".into(), segments, &SESSION, &Default::default()) + .unwrap(); let expr = gt(get_item("a", root()), get_item("b", root())); let result = block_on(|_| { reader @@ -672,7 +680,9 @@ mod tests { #[from(struct_layout)] (segments, layout): (Arc, LayoutRef), ) { let mut ctx = LEGACY_SESSION.create_execution_ctx(); - let reader = layout.new_reader("".into(), segments, &SESSION).unwrap(); + let reader = layout + .new_reader("".into(), segments, &SESSION, &Default::default()) + .unwrap(); let expr = pack( [("a", get_item("a", root())), ("b", get_item("b", root()))], Nullability::NonNullable, @@ -711,7 +721,9 @@ mod tests { #[from(null_struct_layout)] (segments, layout): (Arc, LayoutRef), ) { // Read the layout source from the top. - let reader = layout.new_reader("".into(), segments, &SESSION).unwrap(); + let reader = layout + .new_reader("".into(), segments, &SESSION, &Default::default()) + .unwrap(); let expr = get_item("a", root()); let project = reader .projection_evaluation(&(0..3), &expr, MaskFuture::new_true(3)) @@ -742,7 +754,9 @@ mod tests { // Project out the nested struct field. // The projection should preserve the nulls of the `b` struct when we select out the // child column `c`. - let reader = layout.new_reader("".into(), segments, &SESSION).unwrap(); + let reader = layout + .new_reader("".into(), segments, &SESSION, &Default::default()) + .unwrap(); let expr = select( vec![FieldName::from("c")], get_item("b", get_item("a", root())), @@ -803,7 +817,9 @@ mod tests { fn test_empty_struct( #[from(empty_struct)] (segments, layout): (Arc, LayoutRef), ) { - let reader = layout.new_reader("".into(), segments, &SESSION).unwrap(); + let reader = layout + .new_reader("".into(), segments, &SESSION, &Default::default()) + .unwrap(); let expr = pack(Vec::<(String, Expression)>::new(), Nullability::Nullable); let project = reader @@ -854,7 +870,9 @@ mod tests { }) .unwrap(); - let reader = layout.new_reader("".into(), segments, &SESSION).unwrap(); + let reader = layout + .new_reader("".into(), segments, &SESSION, &Default::default()) + .unwrap(); // DType mismatch: "age" is u8 but literal is i32 let filt = eq(col("age"), lit(67i32)); diff --git a/vortex-layout/src/layouts/zoned/mod.rs b/vortex-layout/src/layouts/zoned/mod.rs index 697e55e968c..43f9410e831 100644 --- a/vortex-layout/src/layouts/zoned/mod.rs +++ b/vortex-layout/src/layouts/zoned/mod.rs @@ -115,12 +115,14 @@ impl VTable for Zoned { name: Arc, segment_source: Arc, session: &VortexSession, + ctx: &crate::LayoutReaderContext, ) -> VortexResult { Ok(Arc::new(ZonedReader::try_new( layout.clone(), name, segment_source, session.clone(), + ctx.clone(), )?)) } diff --git a/vortex-layout/src/layouts/zoned/reader.rs b/vortex-layout/src/layouts/zoned/reader.rs index 7c3e7fbd1fe..6666be880ab 100644 --- a/vortex-layout/src/layouts/zoned/reader.rs +++ b/vortex-layout/src/layouts/zoned/reader.rs @@ -42,6 +42,7 @@ impl ZonedReader { name: Arc, segment_source: Arc, session: VortexSession, + ctx: crate::LayoutReaderContext, ) -> VortexResult { let dtypes = vec![ layout.dtype.clone(), @@ -54,6 +55,7 @@ impl ZonedReader { names, Arc::clone(&segment_source), session.clone(), + ctx, )); Ok(Self { @@ -307,7 +309,7 @@ mod test { block_on(|handle| async { let session = session_with_handle(handle); let result = layout - .new_reader("".into(), segments, &session) + .new_reader("".into(), segments, &session, &Default::default()) .unwrap() .projection_evaluation( &(0..layout.row_count()), @@ -330,7 +332,9 @@ mod test { block_on(|handle| async { let row_count = layout.row_count(); let session = session_with_handle(handle); - let reader = layout.new_reader("".into(), segments, &session).unwrap(); + let reader = layout + .new_reader("".into(), segments, &session, &Default::default()) + .unwrap(); // Choose a prune-able expression let expr = gt(root(), lit(7)); @@ -376,7 +380,8 @@ mod test { block_on(|handle| async { let row_count = legacy_layout.row_count(); let session = session_with_handle(handle); - let reader = legacy_layout.new_reader("".into(), segments, &session)?; + let reader = + legacy_layout.new_reader("".into(), segments, &session, &Default::default())?; let result = reader .pruning_evaluation( diff --git a/vortex-layout/src/lib.rs b/vortex-layout/src/lib.rs index be1bad67236..aca3d36c04a 100644 --- a/vortex-layout/src/lib.rs +++ b/vortex-layout/src/lib.rs @@ -8,6 +8,7 @@ pub use encoding::*; pub use flatbuffers::*; pub use layout::*; pub use reader::*; +pub use reader_context::*; pub use strategy::*; use vortex_session::registry::Context; pub use vtable::*; @@ -18,6 +19,7 @@ mod encoding; mod flatbuffers; mod layout; mod reader; +mod reader_context; pub mod scan; pub mod segments; pub mod sequence; diff --git a/vortex-layout/src/reader.rs b/vortex-layout/src/reader.rs index 5706fc0d964..c4f65c1c96c 100644 --- a/vortex-layout/src/reader.rs +++ b/vortex-layout/src/reader.rs @@ -20,6 +20,7 @@ use vortex_error::vortex_bail; use vortex_mask::Mask; use vortex_session::VortexSession; +use crate::LayoutReaderContext; use crate::children::LayoutChildren; use crate::segments::SegmentSource; @@ -207,6 +208,7 @@ pub struct LazyReaderChildren { names: Vec>, segment_source: Arc, session: VortexSession, + ctx: LayoutReaderContext, // TODO(ngates): we may want a hash map of some sort here? cache: Vec>, } @@ -218,6 +220,7 @@ impl LazyReaderChildren { names: Vec>, segment_source: Arc, session: VortexSession, + ctx: LayoutReaderContext, ) -> Self { let nchildren = children.nchildren(); let cache = (0..nchildren).map(|_| OnceCell::new()).collect(); @@ -227,6 +230,7 @@ impl LazyReaderChildren { names, segment_source, session, + ctx, cache, } } @@ -243,6 +247,7 @@ impl LazyReaderChildren { Arc::clone(&self.names[idx]), Arc::clone(&self.segment_source), &self.session, + &self.ctx, ) }) } diff --git a/vortex-layout/src/reader_context.rs b/vortex-layout/src/reader_context.rs new file mode 100644 index 00000000000..8c4cfed5c93 --- /dev/null +++ b/vortex-layout/src/reader_context.rs @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: Copyright the Vortex contributors + +use std::any::Any; +use std::sync::Arc; + +use vortex_session::registry::Id; +use vortex_utils::aliases::hash_map::HashMap; + +/// Per-reader-tree dependency context, threaded through [`crate::VTable::new_reader`]. +/// +/// Holds an [`Id`]-keyed registry of `Arc` values. Ancestors publish via +/// [`Self::with`]; descendants retrieve via [`Self::get`]. This is a *read-only* channel +/// from the descendant's perspective — they can only consume what an ancestor chose to +/// publish. +/// +/// [`Self::with`] returns a derived context that copies the existing map and inserts or +/// replaces one entry — original unchanged, so concurrent reader-tree constructions each +/// derive their own context without races. Contexts are cheap to clone via internal `Arc` +/// and can be captured by lazy children helpers. +#[derive(Clone, Default)] +pub struct LayoutReaderContext { + values: Arc>>, +} + +impl std::fmt::Debug for LayoutReaderContext { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("LayoutReaderContext") + .field("ids", &self.values.keys().collect::>()) + .finish() + } +} + +impl LayoutReaderContext { + /// Creates a new, empty context. + pub fn new() -> Self { + Self::default() + } + + /// Returns a new context that publishes `value` under `id`. + /// + /// The original context is unchanged. If a value was already published under the + /// same `id`, the new one replaces it in the returned context — so two ancestors + /// using the same well-known static id give descendants "nearest ancestor wins". + pub fn with(&self, id: Id, value: Arc) -> Self { + let mut values = HashMap::clone(&self.values); + values.insert(id, value); + Self { + values: Arc::new(values), + } + } + + /// Returns the value published under `id`, downcast to `T`. Returns `None` if no + /// ancestor published under that id, or if the published value is not a `T`. + pub fn get(&self, id: Id) -> Option> { + self.values + .get(&id) + .and_then(|v| Arc::clone(v).downcast::().ok()) + } +} diff --git a/vortex-layout/src/scan/split_by.rs b/vortex-layout/src/scan/split_by.rs index 34b0edbf4c8..cdaf260cdb8 100644 --- a/vortex-layout/src/scan/split_by.rs +++ b/vortex-layout/src/scan/split_by.rs @@ -107,7 +107,7 @@ mod test { .unwrap(); layout - .new_reader("".into(), segments, &SCAN_SESSION) + .new_reader("".into(), segments, &SCAN_SESSION, &Default::default()) .unwrap() } diff --git a/vortex-layout/src/vtable.rs b/vortex-layout/src/vtable.rs index 9bb83fcf643..c48a9d33d1f 100644 --- a/vortex-layout/src/vtable.rs +++ b/vortex-layout/src/vtable.rs @@ -19,6 +19,7 @@ use crate::LayoutChildType; use crate::LayoutEncoding; use crate::LayoutEncodingRef; use crate::LayoutId; +use crate::LayoutReaderContext; use crate::LayoutReaderRef; use crate::LayoutRef; use crate::children::LayoutChildren; @@ -58,11 +59,20 @@ pub trait VTable: 'static + Sized + Send + Sync + Debug { fn child_type(layout: &Self::Layout, idx: usize) -> LayoutChildType; /// Create a new reader for the layout. + /// + /// **Layouts with children MUST propagate `ctx` to descendants** by passing it + /// through `Layout::new_reader` (or `LazyReaderChildren::new`) when constructing + /// child readers. If `ctx` is dropped at any link in the chain, ancestor-published + /// values won't reach affected descendants — a silent runtime regression for any + /// descendant that looked up an ancestor-published value via `ctx.get::()`. + /// There is no compile-time check that catches this; reviewer discipline + the + /// integration tests in `vortex-layout` are the only safety net. fn new_reader( layout: &Self::Layout, name: Arc, segment_source: Arc, session: &VortexSession, + ctx: &LayoutReaderContext, ) -> VortexResult; /// Construct a new [`Layout`] from the provided parts. diff --git a/vortex-test/compat-gen/src/adapter.rs b/vortex-test/compat-gen/src/adapter.rs index a97399dac79..bed1300b531 100644 --- a/vortex-test/compat-gen/src/adapter.rs +++ b/vortex-test/compat-gen/src/adapter.rs @@ -136,7 +136,12 @@ pub fn read_layout_tree(bytes: ByteBuffer) -> VortexResult<()> { if row_count == 0 { continue; } - let reader = layout.new_reader("".into(), Arc::clone(&segment_source), &session)?; + let reader = layout.new_reader( + "".into(), + Arc::clone(&segment_source), + &session, + &Default::default(), + )?; let len = usize::try_from(row_count).map_err(|e| vortex_err!("row count overflow: {e}"))?; reader diff --git a/vortex-tui/src/browse/app.rs b/vortex-tui/src/browse/app.rs index 0a7f13ab277..97e17bac205 100644 --- a/vortex-tui/src/browse/app.rs +++ b/vortex-tui/src/browse/app.rs @@ -360,7 +360,12 @@ impl AppState { // Load the array. let reader = layout - .new_reader("".into(), self.vxf.segment_source(), &self.session) + .new_reader( + "".into(), + self.vxf.segment_source(), + &self.session, + &Default::default(), + ) .vortex_expect("Failed to create reader"); let array = reader .projection_evaluation( diff --git a/vortex-tui/src/wasm.rs b/vortex-tui/src/wasm.rs index 8b643161fe2..5fd7dd9054b 100644 --- a/vortex-tui/src/wasm.rs +++ b/vortex-tui/src/wasm.rs @@ -78,7 +78,12 @@ async fn load_flat_array( row_count: u64, ) -> vortex::array::ArrayRef { let reader = layout - .new_reader("".into(), segment_source.clone(), session) + .new_reader( + "".into(), + segment_source.clone(), + session, + &Default::default(), + ) .vortex_expect("Failed to create reader"); reader .projection_evaluation( diff --git a/vortex-web/crate/src/wasm.rs b/vortex-web/crate/src/wasm.rs index 05e328e5341..f1bb4df791e 100644 --- a/vortex-web/crate/src/wasm.rs +++ b/vortex-web/crate/src/wasm.rs @@ -293,7 +293,12 @@ impl VortexFileHandle { let segment_source = self.vxf.segment_source(); let reader = layout - .new_reader(node_id.into(), segment_source, &self.session) + .new_reader( + node_id.into(), + segment_source, + &self.session, + &Default::default(), + ) .map_err(|e| JsValue::from_str(&e.to_string()))?; let stream = ScanBuilder::new(self.session.clone(), reader) From 81184e7685de46cf77f122c41b9b83afb9107a80 Mon Sep 17 00:00:00 2001 From: Robert Kruszewski Date: Tue, 2 Jun 2026 14:44:08 +0100 Subject: [PATCH 003/115] Add ArcSwapMap and use it throughout our session registries (#8072) ArcSwap is faster than a lock for read. These session are mutable but mutations are rare and retrievals are common --------- Signed-off-by: Robert Kruszewski --- vortex-array/src/aggregate_fn/accumulator.rs | 22 +-- .../src/aggregate_fn/accumulator_grouped.rs | 16 +- vortex-array/src/aggregate_fn/proto.rs | 2 +- vortex-array/src/aggregate_fn/session.rs | 99 +++++++++--- vortex-array/src/arc_swap_map.rs | 151 ++++++++++++++++++ vortex-array/src/arrow/session.rs | 57 ++----- vortex-array/src/lib.rs | 1 + vortex-array/src/optimizer/kernels.rs | 44 ++--- 8 files changed, 266 insertions(+), 126 deletions(-) create mode 100644 vortex-array/src/arc_swap_map.rs diff --git a/vortex-array/src/aggregate_fn/accumulator.rs b/vortex-array/src/aggregate_fn/accumulator.rs index 762d5e00fb0..c89418e67a6 100644 --- a/vortex-array/src/aggregate_fn/accumulator.rs +++ b/vortex-array/src/aggregate_fn/accumulator.rs @@ -142,7 +142,6 @@ impl DynAccumulator for Accumulator { } let session = ctx.session().clone(); - let kernels = &session.aggregate_fns().kernels; // 1. Kernel registry first: a registered `(encoding, aggregate_fn)` kernel is strictly // more specific than the vtable's `try_accumulate` short-circuit. Checking the @@ -150,13 +149,9 @@ impl DynAccumulator for Accumulator { // `Combined::try_accumulate` always returns true, so a later kernel check would be // unreachable. { - let kernels_r = kernels.read(); - let batch_id = batch.encoding_id(); - let kernel = kernels_r - .get(&(batch_id, Some(self.aggregate_fn.id()))) - .or_else(|| kernels_r.get(&(batch_id, None))) - .copied(); - drop(kernels_r); + let kernel = session + .aggregate_fns() + .find_aggregate_kernel(batch.encoding_id(), self.aggregate_fn.id()); if let Some(kernel) = kernel && let Some(result) = kernel.aggregate(&self.aggregate_fn, batch, ctx)? { @@ -187,14 +182,9 @@ impl DynAccumulator for Accumulator { break; } - let kernels_r = kernels.read(); - let batch_id = batch.encoding_id(); - let kernel = kernels_r - .get(&(batch_id, Some(self.aggregate_fn.id()))) - .or_else(|| kernels_r.get(&(batch_id, None))) - .copied(); - drop(kernels_r); - if let Some(kernel) = kernel + if let Some(kernel) = session + .aggregate_fns() + .find_aggregate_kernel(batch.encoding_id(), self.aggregate_fn.id()) && let Some(result) = kernel.aggregate(&self.aggregate_fn, &batch, ctx)? { vortex_ensure!( diff --git a/vortex-array/src/aggregate_fn/accumulator_grouped.rs b/vortex-array/src/aggregate_fn/accumulator_grouped.rs index a751a7c5749..3fae7a85bf3 100644 --- a/vortex-array/src/aggregate_fn/accumulator_grouped.rs +++ b/vortex-array/src/aggregate_fn/accumulator_grouped.rs @@ -163,17 +163,15 @@ impl GroupedAccumulator { let mut elements = groups.elements().clone(); let groups_validity = groups.validity()?; let session = ctx.session().clone(); - let kernels = &session.aggregate_fns().grouped_kernels; for _ in 0..max_iterations() { if elements.is::() { break; } - let kernels_r = kernels.read(); - if let Some(result) = kernels_r - .get(&(elements.encoding_id(), Some(self.aggregate_fn.id()))) - .or_else(|| kernels_r.get(&(elements.encoding_id(), None))) + if let Some(result) = session + .aggregate_fns() + .find_grouped_kernel(elements.encoding_id(), self.aggregate_fn.id()) .and_then(|kernel| { // SAFETY: we assume that elements execution is safe let groups = unsafe { @@ -255,17 +253,15 @@ impl GroupedAccumulator { let mut elements = groups.elements().clone(); let groups_validity = groups.validity()?; let session = ctx.session().clone(); - let kernels = &session.aggregate_fns().grouped_kernels; for _ in 0..64 { if elements.is::() { break; } - let kernels_r = kernels.read(); - if let Some(result) = kernels_r - .get(&(elements.encoding_id(), Some(self.aggregate_fn.id()))) - .or_else(|| kernels_r.get(&(elements.encoding_id(), None))) + if let Some(result) = session + .aggregate_fns() + .find_grouped_kernel(elements.encoding_id(), self.aggregate_fn.id()) .and_then(|kernel| { // SAFETY: we assume that elements execution is safe let groups = unsafe { diff --git a/vortex-array/src/aggregate_fn/proto.rs b/vortex-array/src/aggregate_fn/proto.rs index 9bcfab5818c..bff198b3955 100644 --- a/vortex-array/src/aggregate_fn/proto.rs +++ b/vortex-array/src/aggregate_fn/proto.rs @@ -36,7 +36,7 @@ impl AggregateFnRef { /// Note: the serialization format is not stable and may change between versions. pub fn from_proto(proto: &pb::AggregateFn, session: &VortexSession) -> VortexResult { let agg_fn_id: AggregateFnId = AggregateFnId::new(proto.id.as_str()); - let agg_fn = if let Some(plugin) = session.aggregate_fns().registry().find(&agg_fn_id) { + let agg_fn = if let Some(plugin) = session.aggregate_fns().find_plugin(&agg_fn_id) { plugin.deserialize(proto.metadata(), session)? } else if session.allows_unknown() { new_foreign_aggregate_fn(agg_fn_id, proto.metadata().to_vec()) diff --git a/vortex-array/src/aggregate_fn/session.rs b/vortex-array/src/aggregate_fn/session.rs index d89f9da069d..edbafdf386f 100644 --- a/vortex-array/src/aggregate_fn/session.rs +++ b/vortex-array/src/aggregate_fn/session.rs @@ -4,12 +4,9 @@ use std::any::Any; use std::sync::Arc; -use parking_lot::RwLock; use vortex_session::Ref; use vortex_session::SessionExt; use vortex_session::SessionVar; -use vortex_session::registry::Registry; -use vortex_utils::aliases::hash_map::HashMap; use crate::aggregate_fn::AggregateFnId; use crate::aggregate_fn::AggregateFnPluginRef; @@ -34,6 +31,7 @@ use crate::aggregate_fn::fns::sum::Sum; use crate::aggregate_fn::fns::uncompressed_size_in_bytes::UncompressedSizeInBytes; use crate::aggregate_fn::kernels::DynAggregateKernel; use crate::aggregate_fn::kernels::DynGroupedAggregateKernel; +use crate::arc_swap_map::ArcSwapMap; use crate::array::ArrayId; use crate::array::VTable; use crate::arrays::Chunked; @@ -43,16 +41,17 @@ use crate::arrays::dict::compute::is_constant::DictIsConstantKernel; use crate::arrays::dict::compute::is_sorted::DictIsSortedKernel; use crate::arrays::dict::compute::min_max::DictMinMaxKernel; -/// Registry of aggregate function vtables. -pub type AggregateFnRegistry = Registry; - -/// Session state for aggregate function vtables. +/// Session state for aggregate functions and encoding-specific aggregate kernels. +/// +/// The default session registers the built-in aggregate functions and kernels. Additional +/// aggregate functions and kernels may be registered by extensions when they are added to a +/// [`VortexSession`](vortex_session::VortexSession). #[derive(Debug)] pub struct AggregateFnSession { - registry: AggregateFnRegistry, + registry: ArcSwapMap, - pub(super) kernels: RwLock>, - pub(super) grouped_kernels: RwLock>, + kernels: ArcSwapMap, + grouped_kernels: ArcSwapMap, } impl SessionVar for AggregateFnSession { @@ -70,9 +69,9 @@ type KernelKey = (ArrayId, Option); impl Default for AggregateFnSession { fn default() -> Self { let this = Self { - registry: AggregateFnRegistry::default(), - kernels: RwLock::new(HashMap::default()), - grouped_kernels: RwLock::new(HashMap::default()), + registry: ArcSwapMap::default(), + kernels: ArcSwapMap::default(), + grouped_kernels: ArcSwapMap::default(), }; // Register the built-in aggregate functions @@ -106,34 +105,88 @@ impl Default for AggregateFnSession { } impl AggregateFnSession { - /// Returns the aggregate function registry. - pub fn registry(&self) -> &AggregateFnRegistry { - &self.registry + /// Returns the aggregate function plugin registered for `id`, if any. + pub fn find_plugin(&self, id: &AggregateFnId) -> Option { + self.registry.get(id) } /// Register an aggregate function vtable in the session, replacing any existing vtable with /// the same ID. pub fn register(&self, vtable: V) { - self.registry - .register(vtable.id(), Arc::new(vtable) as AggregateFnPluginRef); + let id = vtable.id(); + let pluginref = Arc::new(vtable) as AggregateFnPluginRef; + self.registry.insert(id, pluginref); } - /// Register an aggregate function kernel for a specific aggregate function and array type. + /// Returns the aggregate kernel registered for `array_id` and `agg_fn_id`, if any. + /// + /// Lookup first checks for a kernel registered for the exact aggregate function, then falls + /// back to a kernel registered for all aggregate functions on the same array encoding. + pub fn find_aggregate_kernel( + &self, + array_id: impl Into, + agg_fn_id: impl Into, + ) -> Option<&'static dyn DynAggregateKernel> { + let id = array_id.into(); + let fn_id = agg_fn_id.into(); + self.kernels.read(|kernels| { + kernels + .get(&(id, Some(fn_id))) + .or_else(|| kernels.get(&(id, None))) + .copied() + }) + } + + /// Registers an aggregate kernel for an array encoding. + /// + /// When `agg_fn_id` is `Some`, the kernel is used only for that aggregate function. When + /// `agg_fn_id` is `None`, the kernel is used as the fallback for aggregate functions on the + /// array encoding that do not have a more specific kernel. pub fn register_aggregate_kernel( &self, array_id: impl Into, agg_fn_id: Option>, kernel: &'static dyn DynAggregateKernel, ) { - self.kernels - .write() - .insert((array_id.into(), agg_fn_id.map(|id| id.into())), kernel); + let id = (array_id.into(), agg_fn_id.map(|id| id.into())); + self.kernels.insert(id, kernel); + } + + /// Returns the grouped aggregate kernel registered for `array_id` and `agg_fn_id`, if any. + /// + /// Lookup first checks for a kernel registered for the exact aggregate function, then falls + /// back to a kernel registered for all aggregate functions on the same array encoding. + pub fn find_grouped_kernel( + &self, + array_id: impl Into, + agg_fn_id: impl Into, + ) -> Option<&'static dyn DynGroupedAggregateKernel> { + let id = array_id.into(); + let fn_id = agg_fn_id.into(); + self.grouped_kernels.read(|kernels| { + kernels + .get(&(id, Some(fn_id))) + .or_else(|| kernels.get(&(id, None))) + .copied() + }) + } + + /// Registers a grouped aggregate kernel for a specific aggregate function and array encoding. + pub fn register_grouped_kernel( + &self, + array_id: impl Into, + agg_fn_id: impl Into, + kernel: &'static dyn DynGroupedAggregateKernel, + ) { + let id = array_id.into(); + let fn_id = agg_fn_id.into(); + self.grouped_kernels.insert((id, Some(fn_id)), kernel) } } /// Extension trait for accessing aggregate function session data. pub trait AggregateFnSessionExt: SessionExt { - /// Returns the aggregate function vtable registry. + /// Returns the aggregate function session data. fn aggregate_fns(&self) -> Ref<'_, AggregateFnSession> { self.get::() } diff --git a/vortex-array/src/arc_swap_map.rs b/vortex-array/src/arc_swap_map.rs new file mode 100644 index 00000000000..aff46c22d73 --- /dev/null +++ b/vortex-array/src/arc_swap_map.rs @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: Copyright the Vortex contributors + +//! A concurrent, copy-on-write map backed by an [`ArcSwap`]. + +use std::borrow::Borrow; +use std::fmt::Debug; +use std::fmt::Formatter; +use std::hash::Hash; +use std::sync::Arc; + +use arc_swap::ArcSwap; +use vortex_utils::aliases::hash_map::HashMap; + +/// A concurrent [`HashMap`] backed by an [`ArcSwap`], offering lock-free reads +/// and copy-on-write writes. +/// +/// Reads load the current snapshot without blocking writers. Writes clone the +/// whole map, apply their change, and atomically publish the new version, so a +/// reader always observes a consistent snapshot and writers never block readers. +/// +/// This is the shared building block behind the session-scoped registries (the +/// optimizer-kernel and aggregate-function registries). Because every write +/// clones the entire map, it is intended for maps that are written rarely +/// (typically only while a session is being configured) and read often. +pub(crate) struct ArcSwapMap { + inner: ArcSwap>, +} + +impl Default for ArcSwapMap { + fn default() -> Self { + Self { + inner: ArcSwap::from_pointee(HashMap::default()), + } + } +} + +impl Debug for ArcSwapMap { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + self.read(|map| f.debug_tuple("ArcSwapMap").field(map).finish()) + } +} + +impl ArcSwapMap { + /// Read the current snapshot, passing it to `f`. + /// + /// Every lookup inside `f` observes the same snapshot, which matters when a + /// single logical read consults more than one key. + pub(crate) fn read(&self, f: impl FnOnce(&HashMap) -> R) -> R { + f(&self.inner.load()) + } + + /// Replace the map with the result of applying `f` to a private copy. + /// + /// Writes are copy-on-write via [`ArcSwap::rcu`], so `f` may run more than + /// once under contention and must not move out of its captures. + fn modify(&self, f: impl Fn(&mut HashMap)) + where + K: Clone, + V: Clone, + { + self.inner.rcu(|existing| { + let mut map = existing.as_ref().clone(); + f(&mut map); + map + }); + } +} + +impl ArcSwapMap { + /// Return a clone of the value stored under `key`, if present. + pub(crate) fn get(&self, key: &Q) -> Option + where + K: Borrow, + Q: Eq + Hash + ?Sized, + { + self.inner.load().get(key).cloned() + } + + /// Insert `value` under `key`, replacing any existing value. + pub(crate) fn insert(&self, key: K, value: V) + where + K: Clone, + { + self.modify(|map| { + map.insert(key.clone(), value.clone()); + }); + } +} + +impl ArcSwapMap> { + /// Append `values` to the list stored under `key`, creating it if absent. + /// + /// Each key maps to an immutable `Arc<[T]>`; appending rebuilds that slice + /// copy-on-write so existing readers keep their previous snapshot. + pub(crate) fn extend(&self, key: K, values: &[T]) { + self.modify(|map| { + let merged: Arc<[T]> = match map.get(&key) { + Some(existing) => existing.iter().chain(values).cloned().collect(), + None => values.into(), + }; + map.insert(key.clone(), merged); + }); + } + + /// Append a single `value` to the list stored under `key`, creating it if + /// absent. + pub(crate) fn push(&self, key: K, value: T) { + self.extend(key, &[value]); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn get_and_insert() { + let map = ArcSwapMap::::default(); + assert_eq!(map.get(&1), None); + map.insert(1, 10); + map.insert(1, 20); + assert_eq!(map.get(&1), Some(20)); + } + + #[test] + fn extend_appends_per_key() { + let map = ArcSwapMap::>::default(); + map.extend(1, &[1, 2]); + map.extend(1, &[3]); + map.extend(2, &[4]); + assert_eq!(map.get(&1).as_deref(), Some([1, 2, 3].as_slice())); + assert_eq!(map.get(&2).as_deref(), Some([4].as_slice())); + } + + #[test] + fn push_appends_single_values() { + let map = ArcSwapMap::>::default(); + map.push(1, 1); + map.push(1, 2); + assert_eq!(map.get(&1).as_deref(), Some([1, 2].as_slice())); + } + + #[test] + fn read_observes_a_single_snapshot() { + let map = ArcSwapMap::::default(); + map.insert(1, 1); + map.insert(2, 2); + assert_eq!(map.read(|m| m.values().sum::()), 3); + } +} diff --git a/vortex-array/src/arrow/session.rs b/vortex-array/src/arrow/session.rs index 9aa83cbbd2e..598a903cfa2 100644 --- a/vortex-array/src/arrow/session.rs +++ b/vortex-array/src/arrow/session.rs @@ -24,7 +24,6 @@ use std::any::Any; use std::fmt::Debug; use std::sync::Arc; -use arc_swap::ArcSwap; use arrow_array::Array as _; use arrow_array::ArrayRef as ArrowArrayRef; use arrow_array::RecordBatch; @@ -44,11 +43,11 @@ use vortex_session::Ref; use vortex_session::SessionExt; use vortex_session::SessionVar; use vortex_session::registry::Id; -use vortex_utils::aliases::hash_map::HashMap; use crate::ArrayRef; use crate::ExecutionCtx; use crate::IntoArray; +use crate::arc_swap_map::ArcSwapMap; use crate::arrays::StructArray; use crate::arrow::FromArrowArray; use crate::arrow::convert::nulls; @@ -61,6 +60,7 @@ use crate::dtype::Nullability; use crate::dtype::StructFields; use crate::dtype::arrow::FromArrowType; use crate::dtype::arrow::to_data_type_naive; +use crate::dtype::extension::ExtId; use crate::extension::datetime::AnyTemporal; use crate::extension::uuid::Uuid; use crate::validity::Validity; @@ -154,10 +154,6 @@ pub trait ArrowImportVTable: 'static + Send + Sync + Debug { pub type ArrowExportVTableRef = Arc; pub type ArrowImportVTableRef = Arc; -type ExportMap = HashMap>; -type ImportMap = HashMap>; -type ExportDTypeMap = HashMap>; - /// Session-scoped registry of Arrow extension plugins. /// /// Exporters are stored in two indices: one keyed by Arrow extension Id (used for @@ -169,17 +165,17 @@ type ExportDTypeMap = HashMap>; /// need plugins. #[derive(Debug)] pub struct ArrowSession { - exporters: ArcSwap, - exporters_by_vortex: ArcSwap, - importers: ArcSwap, + exporters: ArcSwapMap>, + exporters_by_vortex: ArcSwapMap>, + importers: ArcSwapMap>, } impl Default for ArrowSession { fn default() -> Self { let session = Self { - exporters: ArcSwap::from_pointee(ExportMap::default()), - exporters_by_vortex: ArcSwap::from_pointee(ExportDTypeMap::default()), - importers: ArcSwap::from_pointee(ImportMap::default()), + exporters: ArcSwapMap::default(), + exporters_by_vortex: ArcSwapMap::default(), + importers: ArcSwapMap::default(), }; session.register_exporter(Arc::new(Uuid)); @@ -193,56 +189,31 @@ impl ArrowSession { /// Register an [`ArrowExportVTable`] under its target Arrow extension Id (for dispatch) /// and its source Vortex extension Id (for schema inference). pub fn register_exporter(&self, exporter: ArrowExportVTableRef) { - Self::insert( - &self.exporters, + self.exporters.push( exporter.arrow_ext_id(), ArrowExportVTableRef::clone(&exporter), ); - Self::insert(&self.exporters_by_vortex, exporter.vortex_id(), exporter); + self.exporters_by_vortex + .push(exporter.vortex_id(), exporter); } /// Register an [`ArrowImportVTable`] under its source Arrow extension name. pub fn register_importer(&self, importer: ArrowImportVTableRef) { - Self::insert(&self.importers, importer.arrow_ext_id(), importer); - } - - fn insert(slot: &ArcSwap>>, key: K, value: T) - where - K: Clone + Eq + std::hash::Hash, - T: Clone, - { - slot.rcu(move |map| { - let mut next = (**map).clone(); - let entry = next.entry(key.clone()).or_insert_with(|| Arc::from([])); - let mut extended: Vec = entry.iter().cloned().collect(); - extended.push(value.clone()); - *entry = Arc::from(extended); - next - }); + self.importers.push(importer.arrow_ext_id(), importer); } fn exporters(&self, id: &Id) -> Arc<[ArrowExportVTableRef]> { - self.exporters - .load() - .get(id) - .cloned() - .unwrap_or_else(|| Arc::from([])) + self.exporters.get(id).unwrap_or_else(|| Arc::from([])) } fn exporters_by_vortex(&self, id: &Id) -> Arc<[ArrowExportVTableRef]> { self.exporters_by_vortex - .load() .get(id) - .cloned() .unwrap_or_else(|| Arc::from([])) } fn importers(&self, id: &Id) -> Arc<[ArrowImportVTableRef]> { - self.importers - .load() - .get(id) - .cloned() - .unwrap_or_else(|| Arc::from([])) + self.importers.get(id).unwrap_or_else(|| Arc::from([])) } /// Build the Arrow [`Field`] for a Vortex [`DType`]. diff --git a/vortex-array/src/lib.rs b/vortex-array/src/lib.rs index 0a9c5969ecc..1f3189eecbb 100644 --- a/vortex-array/src/lib.rs +++ b/vortex-array/src/lib.rs @@ -33,6 +33,7 @@ pub mod accessor; pub mod aggregate_fn; #[doc(hidden)] pub mod aliases; +mod arc_swap_map; mod array; pub mod arrays; pub mod arrow; diff --git a/vortex-array/src/optimizer/kernels.rs b/vortex-array/src/optimizer/kernels.rs index d38bc9402d1..93407a2c42d 100644 --- a/vortex-array/src/optimizer/kernels.rs +++ b/vortex-array/src/optimizer/kernels.rs @@ -26,21 +26,19 @@ use std::any::Any; use std::borrow::Borrow; use std::hash::BuildHasher; -use std::hash::Hash; use std::sync::Arc; use std::sync::LazyLock; -use arc_swap::ArcSwap; use vortex_error::VortexResult; use vortex_session::Ref; use vortex_session::SessionExt; use vortex_session::SessionVar; use vortex_session::registry::Id; use vortex_utils::aliases::DefaultHashBuilder; -use vortex_utils::aliases::hash_map::HashMap; use crate::ArrayRef; use crate::ExecutionCtx; +use crate::arc_swap_map::ArcSwapMap; use crate::array::VTable; use crate::arrays::Struct; use crate::arrays::struct_::compute::cast::struct_cast_execute_parent; @@ -115,8 +113,8 @@ impl Borrow for ExecuteParentFnId { /// functions for an existing key appends them to that key's ordered list. #[derive(Debug)] pub struct ArrayKernels { - reduce_parent: ArcSwap>>, - execute_parent: ArcSwap>>, + reduce_parent: ArcSwapMap>, + execute_parent: ArcSwapMap>, } impl Default for ArrayKernels { @@ -132,8 +130,8 @@ impl ArrayKernels { /// Create an empty [`ArrayKernels`] with no kernels registered. pub fn empty() -> Self { Self { - reduce_parent: ArcSwap::from_pointee(HashMap::default()), - execute_parent: ArcSwap::from_pointee(HashMap::default()), + reduce_parent: ArcSwapMap::default(), + execute_parent: ArcSwapMap::default(), } } @@ -164,9 +162,8 @@ impl ArrayKernels { /// If functions have already been registered for the same pair, these functions are appended /// after them. pub fn register_reduce_parent(&self, parent: Id, child: Id, fns: &[ReduceParentFn]) { - self.reduce_parent.rcu(move |registry| { - update_fns(registry.as_ref().clone(), hash_fn_id(parent, child), fns) - }); + self.reduce_parent + .extend(hash_fn_id(parent, child).into(), fns); } /// Look up the [`ReduceParentFn`]s registered for `(parent, child)`. @@ -174,8 +171,7 @@ impl ArrayKernels { /// Returns an owned [`Arc`] so the session-variable borrow can be dropped before invoking the /// functions. pub fn find_reduce_parent(&self, parent: Id, child: Id) -> Option> { - let id = hash_fn_id(parent, child); - self.reduce_parent.load().get(&id).cloned() + self.reduce_parent.get(&hash_fn_id(parent, child)) } /// Register [`ExecuteParentFn`]s for `(parent, child)`. @@ -187,9 +183,8 @@ impl ArrayKernels { /// If functions have already been registered for the same pair, these functions are appended /// after them. pub fn register_execute_parent(&self, parent: Id, child: Id, fns: &[ExecuteParentFn]) { - self.execute_parent.rcu(move |registry| { - update_fns(registry.as_ref().clone(), hash_fn_id(parent, child), fns) - }); + self.execute_parent + .extend(hash_fn_id(parent, child).into(), fns); } /// Look up the [`ExecuteParentFn`]s registered for `(parent, child)`. @@ -197,8 +192,7 @@ impl ArrayKernels { /// Returns an owned [`Arc`] so the session-variable borrow can be dropped before invoking the /// functions. pub fn find_execute_parent(&self, parent: Id, child: Id) -> Option> { - let id = hash_fn_id(parent, child); - self.execute_parent.load().get(&id).cloned() + self.execute_parent.get(&hash_fn_id(parent, child)) } } @@ -206,22 +200,6 @@ fn hash_fn_id(parent: Id, child: Id) -> u64 { FN_HASHER.hash_one((parent, child)) } -fn update_fns + Eq + Hash + From>( - mut existing: HashMap>, - id: u64, - fns: &[F], -) -> HashMap> { - if let Some(existing_fns) = existing.remove(&id) { - existing.insert( - id.into(), - existing_fns.as_ref().iter().chain(fns).cloned().collect(), - ); - } else { - existing.insert(id.into(), fns.into()); - } - existing -} - impl SessionVar for ArrayKernels { fn as_any(&self) -> &dyn Any { self From 84a4a3fdf205a6a6bd4ec974a7042b34847f456c Mon Sep 17 00:00:00 2001 From: Onur Satici Date: Tue, 2 Jun 2026 15:05:53 +0100 Subject: [PATCH 004/115] fix session deadlock (#8217) ## Summary `MemorySession` is registered lazily. Because it implements default we don't need to register it to the session and the first call to `ctx.allocator()` would hold the session dashmap shard lock and insert the allocator. The problem is when we are in a aggregate function we are already potentially holding the same dashmap shard's lock, because the aggregate functino registry is also read from the session. because session vars are type id keyed and type id's change on every build, if the aggregate function registry and memory session end up being on the same dashmap shard, we deadlock running an aggregate function. This is fixing with a band aid, I am eagerly registering memory session because that is the only one that we forgot to register, hence we won't ever write lock the session after initialising now. I think the right solution is to not hold the read lock of the dashmap shard while executing kernels, which I will do in a follow up Signed-off-by: Onur Satici --- vortex/src/lib.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vortex/src/lib.rs b/vortex/src/lib.rs index 8668de339cb..3f7016be38f 100644 --- a/vortex/src/lib.rs +++ b/vortex/src/lib.rs @@ -12,6 +12,7 @@ use vortex_array::dtype::session::DTypeSession; // vortex::expr is in the process of having its dependencies inverted, and will eventually be // pulled back out into a vortex_expr crate. pub use vortex_array::expr; +use vortex_array::memory::MemorySession; use vortex_array::optimizer::kernels::ArrayKernels; pub use vortex_array::scalar_fn; use vortex_array::scalar_fn::session::ScalarFnSession; @@ -171,6 +172,7 @@ impl VortexSessionDefault for VortexSession { .with::() .with::() .with::() + .with::() .with::(); #[cfg(feature = "files")] From d1b78c244ed1f7ab5c62ae0172ede8dbf0702f74 Mon Sep 17 00:00:00 2001 From: Adam Gutglick Date: Tue, 2 Jun 2026 20:10:29 +0100 Subject: [PATCH 005/115] Share FSST symbol table state between executed variants of arrays. (#8222) ## Summary This PR has two changes: 1. Bumps the FSST dependency to include 0.5.11 which has faster `Compressor::rebuild_from`. 2. When executing an FSST array through slice/filter/take, we keep the compressor instead of creating new lazy instances, as part of a `FSSTSymbolTable` type. --------- Signed-off-by: Adam Gutglick --- Cargo.lock | 4 +- Cargo.toml | 4 +- encodings/fsst/src/array.rs | 187 ++++++++++++++++++++++----- encodings/fsst/src/compute/cast.rs | 5 +- encodings/fsst/src/compute/filter.rs | 5 +- encodings/fsst/src/compute/mod.rs | 5 +- encodings/fsst/src/slice.rs | 5 +- 7 files changed, 168 insertions(+), 47 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ec89bf1161f..efc977abf08 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3272,9 +3272,9 @@ dependencies = [ [[package]] name = "fsst-rs" -version = "0.5.10" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bf53d7c403a2b76873d4d66ba7d79c54bde2784cdaba6083f223d6e33270708" +checksum = "9b13ac798afc0d9194eb4efefef8b9332efbd80b43f302a968cb8cb23b9d5360" dependencies = [ "rustc-hash", ] diff --git a/Cargo.toml b/Cargo.toml index 9700d8d78ed..e3c3cbae67e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -154,7 +154,7 @@ enum-iterator = "2.0.0" env_logger = "0.11" fastlanes = "0.5" flatbuffers = "25.2.10" -fsst-rs = "0.5.5" +fsst-rs = "0.5.11" futures = { version = "0.3.31", default-features = false } fuzzy-matcher = "0.3" get_dir = "0.5.0" @@ -226,7 +226,7 @@ reqwest = { version = "0.13.0", features = [ roaring = "0.11.0" rstest = "0.26.1" rstest_reuse = "0.7.0" -rustc-hash = "2.1" +rustc-hash = "2.1.1" serde = "1.0.220" serde_json = "1.0.138" serde_test = "1.0.176" diff --git a/encodings/fsst/src/array.rs b/encodings/fsst/src/array.rs index 617908e94dd..2019a6c73d4 100644 --- a/encodings/fsst/src/array.rs +++ b/encodings/fsst/src/array.rs @@ -6,7 +6,7 @@ use std::fmt::Display; use std::fmt::Formatter; use std::hash::Hasher; use std::sync::Arc; -use std::sync::LazyLock; +use std::sync::OnceLock; use fsst::Compressor; use fsst::Decompressor; @@ -81,18 +81,23 @@ impl FSSTMetadata { impl ArrayHash for FSSTData { fn array_hash(&self, state: &mut H, precision: Precision) { - self.symbols.array_hash(state, precision); - self.symbol_lengths.array_hash(state, precision); + self.symbol_table.symbols.array_hash(state, precision); + self.symbol_table + .symbol_lengths + .array_hash(state, precision); self.codes_bytes.as_host().array_hash(state, precision); } } impl ArrayEq for FSSTData { fn array_eq(&self, other: &Self, precision: Precision) -> bool { - self.symbols.array_eq(&other.symbols, precision) + self.symbol_table + .symbols + .array_eq(&other.symbol_table.symbols, precision) && self + .symbol_table .symbol_lengths - .array_eq(&other.symbol_lengths, precision) + .array_eq(&other.symbol_table.symbol_lengths, precision) && self .codes_bytes .as_host() @@ -346,28 +351,29 @@ pub(crate) const SLOT_NAMES: [&str; NUM_SLOTS] = /// [`FSSTArrayExt::codes()`], combining this buffer with the offsets/validity from slots. #[derive(Clone)] pub struct FSSTData { - symbols: Buffer, - symbol_lengths: Buffer, + symbol_table: Arc, /// The raw compressed codes bytes, equivalent to `VarBinData::bytes`. codes_bytes: BufferHandle, /// Cached length (number of elements). len: usize, - - /// Memoized compressor used for push-down of compute by compressing the RHS. - compressor: Arc Compressor + Send>>>, } impl Display for FSSTData { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "len: {}, nsymbols: {}", self.len, self.symbols.len()) + write!( + f, + "len: {}, nsymbols: {}", + self.len, + self.symbol_table.symbols.len() + ) } } impl Debug for FSSTData { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.debug_struct("FSSTArray") - .field("symbols", &self.symbols) - .field("symbol_lengths", &self.symbol_lengths) + .field("symbols", &self.symbol_table.symbols) + .field("symbol_lengths", &self.symbol_table.symbol_lengths) .field("codes_bytes_len", &self.codes_bytes.len()) .field("len", &self.len) .field("uncompressed_lengths", &"") @@ -377,6 +383,29 @@ impl Debug for FSSTData { } } +pub(crate) struct FSSTSymbolTable { + symbols: Buffer, + symbol_lengths: Buffer, + /// Memoized compressor used for push-down of compute by compressing the RHS. + compressor: OnceLock, +} + +impl FSSTSymbolTable { + fn new(symbols: Buffer, symbol_lengths: Buffer) -> Self { + Self { + symbols, + symbol_lengths, + compressor: OnceLock::new(), + } + } + + fn compressor(&self) -> &Compressor { + self.compressor.get_or_init(|| { + Compressor::rebuild_from(self.symbols.as_slice(), self.symbol_lengths.as_slice()) + }) + } +} + #[derive(Clone, Debug)] pub struct FSST; @@ -412,6 +441,32 @@ impl FSST { }) } + pub(crate) fn try_new_with_symbol_table( + dtype: DType, + symbol_table: Arc, + codes: VarBinArray, + uncompressed_lengths: ArrayRef, + ctx: &mut ExecutionCtx, + ) -> VortexResult { + let len = codes.len(); + FSSTData::validate_parts_from_codes( + &symbol_table.symbols, + &symbol_table.symbol_lengths, + &codes, + &uncompressed_lengths, + &dtype, + len, + ctx, + )?; + let slots = FSSTData::make_slots(&codes, &uncompressed_lengths); + let codes_bytes = codes.bytes_handle().clone(); + let data = + unsafe { FSSTData::new_unchecked_with_symbol_table(symbol_table, codes_bytes, len) }; + Ok(unsafe { + Array::from_parts_unchecked(ArrayParts::new(FSST, dtype, len, data).with_slots(slots)) + }) + } + /// Legacy deserialization path (2 buffers): the codes were stored as a full /// `VarBinArray` child. We decompose the VarBinArray into its bytes (stored in /// FSSTData) and offsets/validity (stored in slots). @@ -463,17 +518,17 @@ impl FSST { Ok(ArrayParts::new(self.clone(), dtype.clone(), len, data).with_slots(slots)) } - pub(crate) unsafe fn new_unchecked( + pub(crate) unsafe fn new_unchecked_with_symbol_table( dtype: DType, - symbols: Buffer, - symbol_lengths: Buffer, + symbol_table: Arc, codes: VarBinArray, uncompressed_lengths: ArrayRef, ) -> FSSTArray { let len = codes.len(); let slots = FSSTData::make_slots(&codes, &uncompressed_lengths); let codes_bytes = codes.bytes_handle().clone(); - let data = unsafe { FSSTData::new_unchecked(symbols, symbol_lengths, codes_bytes, len) }; + let data = + unsafe { FSSTData::new_unchecked_with_symbol_table(symbol_table, codes_bytes, len) }; unsafe { Array::from_parts_unchecked(ArrayParts::new(FSST, dtype, len, data).with_slots(slots)) } @@ -534,8 +589,8 @@ impl FSSTData { .as_ref() .vortex_expect("FSSTArray codes_offsets slot"); Self::validate_parts( - &self.symbols, - &self.symbol_lengths, + &self.symbol_table.symbols, + &self.symbol_table.symbol_lengths, &self.codes_bytes, codes_offsets, dtype.nullability(), @@ -567,10 +622,13 @@ impl FSSTData { if symbols.len() > 255 { vortex_bail!(InvalidArgument: "symbols array must have length <= 255"); } + if symbols.len() != symbol_lengths.len() { vortex_bail!(InvalidArgument: "symbols and symbol_lengths arrays must have same length"); } + Self::validate_symbol_lengths(symbol_lengths.as_slice())?; + // codes_offsets.len() - 1 == number of elements let codes_len = codes_offsets.len().saturating_sub(1); if codes_len != len { @@ -612,6 +670,32 @@ impl FSSTData { Ok(()) } + fn validate_symbol_lengths(symbol_lengths: &[u8]) -> VortexResult<()> { + let mut expected = 2; + for (idx, &len) in symbol_lengths.iter().enumerate() { + if len > 8 || len == 0 { + vortex_bail!(InvalidArgument: "symbol length at index {idx} must be between 1 and 8, found {len}"); + } + + if expected == 1 { + if len != 1 { + vortex_bail!(InvalidArgument: "symbol length at index {idx} must be 1 after one-byte symbols begin, found {len}"); + } + } else { + if len == 1 { + expected = 1; + } + + if len < expected { + vortex_bail!(InvalidArgument: "symbol length at index {idx} violates FSST symbol table ordering"); + } + expected = len; + } + } + + Ok(()) + } + /// Validate using a VarBinArray for the codes (convenience for construction paths). fn validate_parts_from_codes( symbols: &Buffer, @@ -641,18 +725,19 @@ impl FSSTData { codes_bytes: BufferHandle, len: usize, ) -> Self { - let symbols2 = symbols.clone(); - let symbol_lengths2 = symbol_lengths.clone(); - let compressor = Arc::new(LazyLock::new(Box::new(move || { - Compressor::rebuild_from(symbols2.as_slice(), symbol_lengths2.as_slice()) - }) - as Box Compressor + Send>)); + let symbol_table = Arc::new(FSSTSymbolTable::new(symbols, symbol_lengths)); + unsafe { Self::new_unchecked_with_symbol_table(symbol_table, codes_bytes, len) } + } + + pub(crate) unsafe fn new_unchecked_with_symbol_table( + symbol_table: Arc, + codes_bytes: BufferHandle, + len: usize, + ) -> Self { Self { - symbols, - symbol_lengths, + symbol_table, codes_bytes, len, - compressor, } } @@ -668,12 +753,16 @@ impl FSSTData { /// Access the symbol table array. pub fn symbols(&self) -> &Buffer { - &self.symbols + &self.symbol_table.symbols } /// Access the symbol lengths array. pub fn symbol_lengths(&self) -> &Buffer { - &self.symbol_lengths + &self.symbol_table.symbol_lengths + } + + pub(crate) fn symbol_table(&self) -> Arc { + Arc::clone(&self.symbol_table) } /// Access the compressed codes bytes buffer handle (may be on host or device). @@ -694,7 +783,7 @@ impl FSSTData { /// Retrieves the FSST compressor. pub fn compressor(&self) -> &Compressor { - self.compressor.as_ref() + self.symbol_table.compressor() } } @@ -771,12 +860,48 @@ mod test { use vortex_array::test_harness::check_metadata; use vortex_buffer::Buffer; use vortex_error::VortexError; + use vortex_error::VortexResult; + use vortex_error::vortex_err; use crate::FSST; use crate::array::FSSTArrayExt; use crate::array::FSSTMetadata; use crate::fsst_compress_iter; + #[test] + fn slice_reuses_initialized_compressor() -> VortexResult<()> { + let symbols = Buffer::::copy_from([ + Symbol::from_slice(b"abc00000"), + Symbol::from_slice(b"defghijk"), + ]); + let symbol_lengths = Buffer::::copy_from([3, 8]); + + let compressor = Compressor::rebuild_from(symbols.as_slice(), symbol_lengths.as_slice()); + let mut ctx = LEGACY_SESSION.create_execution_ctx(); + let fsst_array = fsst_compress_iter( + [ + Some(b"abcabcab".as_ref()), + Some(b"defghijk".as_ref()), + Some(b"abcxyz".as_ref()), + ] + .into_iter(), + 3, + DType::Utf8(Nullability::NonNullable), + &compressor, + &mut ctx, + ); + + let compressor_ptr = fsst_array.compressor() as *const Compressor; + let sliced = fsst_array + .slice(1..3)? + .try_downcast::() + .map_err(|_| vortex_err!("slice must return an FSST array"))?; + let sliced_compressor_ptr = sliced.compressor() as *const Compressor; + + assert_eq!(compressor_ptr, sliced_compressor_ptr); + Ok(()) + } + #[cfg_attr(miri, ignore)] #[test] fn test_fsst_metadata() { diff --git a/encodings/fsst/src/compute/cast.rs b/encodings/fsst/src/compute/cast.rs index a1c96363ba0..47c324fc2a4 100644 --- a/encodings/fsst/src/compute/cast.rs +++ b/encodings/fsst/src/compute/cast.rs @@ -30,10 +30,9 @@ fn build_with_codes_validity( )?; Ok(unsafe { - FSST::new_unchecked( + FSST::new_unchecked_with_symbol_table( dtype.clone(), - array.symbols().clone(), - array.symbol_lengths().clone(), + array.symbol_table(), new_codes, array.uncompressed_lengths().clone(), ) diff --git a/encodings/fsst/src/compute/filter.rs b/encodings/fsst/src/compute/filter.rs index 74e32cfbd02..eae72cac8dd 100644 --- a/encodings/fsst/src/compute/filter.rs +++ b/encodings/fsst/src/compute/filter.rs @@ -31,10 +31,9 @@ impl FilterKernel for FSST { .vortex_expect("must be VarBin"); Ok(Some( - FSST::try_new( + FSST::try_new_with_symbol_table( array.dtype().clone(), - array.symbols().clone(), - array.symbol_lengths().clone(), + array.symbol_table(), filtered_codes, array.uncompressed_lengths().filter(mask.clone())?, ctx, diff --git a/encodings/fsst/src/compute/mod.rs b/encodings/fsst/src/compute/mod.rs index 02efdf7febc..04ce6db4eda 100644 --- a/encodings/fsst/src/compute/mod.rs +++ b/encodings/fsst/src/compute/mod.rs @@ -28,13 +28,12 @@ impl TakeExecute for FSST { ctx: &mut ExecutionCtx, ) -> VortexResult> { Ok(Some( - FSST::try_new( + FSST::try_new_with_symbol_table( array .dtype() .clone() .union_nullability(indices.dtype().nullability()), - array.symbols().clone(), - array.symbol_lengths().clone(), + array.symbol_table(), { let codes = array.codes(); let codes = codes.as_view(); diff --git a/encodings/fsst/src/slice.rs b/encodings/fsst/src/slice.rs index 9b7b2833d85..b98f5fa03b3 100644 --- a/encodings/fsst/src/slice.rs +++ b/encodings/fsst/src/slice.rs @@ -19,10 +19,9 @@ impl SliceReduce for FSST { // SAFETY: slicing the `codes` leaves the symbol table intact Ok(Some( unsafe { - FSST::new_unchecked( + FSST::new_unchecked_with_symbol_table( array.dtype().clone(), - array.symbols().clone(), - array.symbol_lengths().clone(), + array.symbol_table(), array .codes() .slice(range.clone())? From 81046d76ce4336cfb0f7baff5cd375db39e07322 Mon Sep 17 00:00:00 2001 From: Robert Kruszewski Date: Tue, 2 Jun 2026 20:15:12 +0100 Subject: [PATCH 006/115] Implement ZipKernel for ListViewArray (#8218) This can be very cheap with ListViews and ends up being very sad because we use a builder by default --------- Signed-off-by: Robert Kruszewski --- .../src/arrays/listview/compute/mod.rs | 1 + .../src/arrays/listview/compute/zip.rs | 367 ++++++++++++++++++ .../src/arrays/listview/tests/operations.rs | 96 +++++ .../src/arrays/listview/vtable/kernel.rs | 7 +- vortex-array/src/scalar_fn/fns/zip/mod.rs | 32 +- 5 files changed, 484 insertions(+), 19 deletions(-) create mode 100644 vortex-array/src/arrays/listview/compute/zip.rs diff --git a/vortex-array/src/arrays/listview/compute/mod.rs b/vortex-array/src/arrays/listview/compute/mod.rs index 9a43503c4b5..87587495f7a 100644 --- a/vortex-array/src/arrays/listview/compute/mod.rs +++ b/vortex-array/src/arrays/listview/compute/mod.rs @@ -6,3 +6,4 @@ mod mask; pub(crate) mod rules; mod slice; mod take; +mod zip; diff --git a/vortex-array/src/arrays/listview/compute/zip.rs b/vortex-array/src/arrays/listview/compute/zip.rs new file mode 100644 index 00000000000..7093c3e0daa --- /dev/null +++ b/vortex-array/src/arrays/listview/compute/zip.rs @@ -0,0 +1,367 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: Copyright the Vortex contributors + +use std::ops::BitAnd; +use std::ops::BitOr; +use std::ops::Not; + +use vortex_buffer::Buffer; +use vortex_buffer::BufferMut; +use vortex_error::VortexResult; +use vortex_mask::Mask; + +use crate::ArrayRef; +use crate::ExecutionCtx; +use crate::IntoArray; +use crate::array::ArrayView; +use crate::arrays::Chunked; +use crate::arrays::ChunkedArray; +use crate::arrays::ListView; +use crate::arrays::ListViewArray; +use crate::arrays::chunked::ChunkedArrayExt; +use crate::arrays::listview::ListViewArrayExt; +use crate::builtins::ArrayBuiltins; +use crate::dtype::DType; +use crate::dtype::Nullability; +use crate::dtype::PType; +use crate::scalar_fn::fns::zip::ZipKernel; +use crate::validity::Validity; + +/// Zip two [`ListViewArray`]s by selecting whole list views per row. +/// +/// A [`ListViewArray`] addresses each list by an `(offset, size)` pair into a shared `elements` +/// array, and unlike [`ListArray`](crate::arrays::ListArray) it does not require lists to be stored +/// contiguously or in order. Zipping two list views is therefore a metadata-only operation over the +/// `offsets`, `sizes` and `validity` child arrays: we concatenate the two `elements` arrays +/// (without rewriting them) and, for each row, select the `(offset, size)` pair from `if_true` or +/// `if_false` per the mask. `if_false` views are shifted past the end of `if_true`'s elements so +/// they continue to address the correct half of the concatenated elements array. +impl ZipKernel for ListView { + fn zip( + if_true: ArrayView<'_, ListView>, + if_false: &ArrayRef, + mask: &ArrayRef, + ctx: &mut ExecutionCtx, + ) -> VortexResult> { + let Some(if_false) = if_false.as_opt::() else { + return Ok(None); + }; + + // Null mask entries select `if_false`, matching `Zip`'s SQL ELSE semantics. + let mask = mask.try_to_mask_fill_null_false(ctx)?; + match &mask { + // Defer the trivial masks to the generic zip, which just casts one side. + Mask::AllTrue(_) | Mask::AllFalse(_) => return Ok(None), + Mask::Values(_) => {} + } + + let len = if_true.len(); + + let result_elements_dtype = if_true + .elements() + .dtype() + .union_nullability(if_false.elements().dtype().nullability()); + + // `if_false`'s elements share the element dtype up to nullability; normalize so both chunks + // of the concatenated elements array have an identical dtype. + let true_elements = if_true.elements().cast(result_elements_dtype.clone())?; + let false_elements = if_false.elements().cast(result_elements_dtype.clone())?; + + // `if_false` views index into the second half of the concatenated elements. + let false_shift = true_elements.len() as u64; + + // Concatenate the two `elements` arrays without copying. If either side is already a + // `ChunkedArray` (e.g. the result of a previous list-view zip), splice its chunks in + // directly rather than nesting chunked arrays. + let mut chunks = Vec::with_capacity(2); + push_element_chunks(true_elements, &mut chunks); + push_element_chunks(false_elements, &mut chunks); + let elements = ChunkedArray::try_new(chunks, result_elements_dtype)?.into_array(); + + let true_offsets = to_u64(if_true.offsets(), ctx)?; + let true_sizes = to_u64(if_true.sizes(), ctx)?; + let false_offsets = to_u64(if_false.offsets(), ctx)?; + let false_sizes = to_u64(if_false.sizes(), ctx)?; + + let mut offsets = BufferMut::::with_capacity(len); + let mut sizes = BufferMut::::with_capacity(len); + for (idx, (out_offsets, out_sizes)) in offsets + .spare_capacity_mut() + .iter_mut() + .zip(sizes.spare_capacity_mut().iter_mut()) + .take(len) + .enumerate() + { + if mask.value(idx) { + out_offsets.write(true_offsets[idx]); + out_sizes.write(true_sizes[idx]); + } else { + out_offsets.write(false_offsets[idx] + false_shift); + out_sizes.write(false_sizes[idx]); + } + } + + // SAFETY: the loop above initialized exactly `len` slots in both buffers. + unsafe { + offsets.set_len(len); + sizes.set_len(len); + } + + let validity = zip_validity(if_true.validity()?, if_false.validity()?, &mask, ctx)?; + + Ok(Some( + ListViewArray::try_new( + elements, + offsets.freeze().into_array(), + sizes.freeze().into_array(), + validity, + )? + .into_array(), + )) + } +} + +/// Appends `array`'s element chunks to `chunks`, flattening a top-level [`ChunkedArray`] so the +/// concatenated elements never nest chunked arrays. +fn push_element_chunks(array: ArrayRef, chunks: &mut Vec) { + match array.as_opt::() { + Some(chunked) => chunks.extend(chunked.iter_chunks().cloned()), + None => chunks.push(array), + } +} + +/// Read a non-nullable integer array into a `u64` buffer. +fn to_u64(array: &ArrayRef, ctx: &mut ExecutionCtx) -> VortexResult> { + array + .clone() + .cast(DType::Primitive(PType::U64, Nullability::NonNullable))? + .execute::>(ctx) +} + +/// Combine the two list-level validities, taking `if_true`'s validity where `mask` is set and +/// `if_false`'s where it is not. +fn zip_validity( + if_true: Validity, + if_false: Validity, + mask: &Mask, + ctx: &mut ExecutionCtx, +) -> VortexResult { + Ok(match (&if_true, &if_false) { + (Validity::NonNullable, Validity::NonNullable) => Validity::NonNullable, + (Validity::AllValid, Validity::AllValid) => Validity::AllValid, + (Validity::AllInvalid, Validity::AllInvalid) => Validity::AllInvalid, + _ => { + let true_mask = if_true.execute_mask(mask.len(), ctx)?; + let false_mask = if_false.execute_mask(mask.len(), ctx)?; + let combined = true_mask + .bitand(mask) + .bitor(&false_mask.bitand(&mask.not())); + Validity::from_mask(combined, if_true.nullability() | if_false.nullability()) + } + }) +} + +#[cfg(test)] +mod tests { + use vortex_buffer::buffer; + use vortex_error::VortexResult; + use vortex_mask::Mask; + + use crate::ArrayRef; + use crate::IntoArray; + use crate::LEGACY_SESSION; + use crate::VortexSessionExecute; + use crate::arrays::BoolArray; + use crate::arrays::Chunked; + use crate::arrays::ChunkedArray; + use crate::arrays::ListView; + use crate::arrays::ListViewArray; + use crate::arrays::chunked::ChunkedArrayExt; + use crate::arrays::listview::ListViewArrayExt; + use crate::assert_arrays_eq; + use crate::builtins::ArrayBuiltins; + use crate::dtype::DType; + use crate::dtype::Nullability; + use crate::dtype::PType; + use crate::validity::Validity; + + fn list_view( + elements: ArrayRef, + offsets: ArrayRef, + sizes: ArrayRef, + validity: Validity, + ) -> ArrayRef { + ListViewArray::try_new(elements, offsets, sizes, validity) + .unwrap() + .into_array() + } + + /// `zip` of two list views selects whole lists per the mask and keeps the list encoding. + #[test] + fn zip_selects_lists() -> VortexResult<()> { + // [[1, 2], [3], [4, 5, 6]] + let if_true = list_view( + buffer![1i32, 2, 3, 4, 5, 6].into_array(), + buffer![0u32, 2, 3].into_array(), + buffer![2u32, 1, 3].into_array(), + Validity::NonNullable, + ); + // [[10], [20, 21], [30]] + let if_false = list_view( + buffer![10i32, 20, 21, 30].into_array(), + buffer![0u32, 1, 3].into_array(), + buffer![1u32, 2, 1].into_array(), + Validity::NonNullable, + ); + let mask = Mask::from_iter([true, false, true]); + + let mut ctx = LEGACY_SESSION.create_execution_ctx(); + let result = mask + .into_array() + .zip(if_true, if_false)? + .execute::(&mut ctx)?; + + // The kernel should keep the list-view encoding rather than canonicalizing. + assert!(result.is::()); + + // Expected: [[1, 2], [20, 21], [4, 5, 6]] + let expected = list_view( + buffer![1i32, 2, 20, 21, 4, 5, 6].into_array(), + buffer![0u32, 2, 4].into_array(), + buffer![2u32, 2, 3].into_array(), + Validity::NonNullable, + ); + assert_arrays_eq!(result, expected); + Ok(()) + } + + /// `zip` selects list-level validity from the chosen side and widens nullability. + #[test] + fn zip_selects_validity() -> VortexResult<()> { + // [[1], null, [2]] (list-level nulls) + let if_true = list_view( + buffer![1i32, 2].into_array(), + buffer![0u32, 1, 1].into_array(), + buffer![1u32, 0, 1].into_array(), + Validity::Array(BoolArray::from_iter([true, false, true]).into_array()), + ); + // [[10], [20], null] + let if_false = list_view( + buffer![10i32, 20].into_array(), + buffer![0u32, 1, 2].into_array(), + buffer![1u32, 1, 0].into_array(), + Validity::Array(BoolArray::from_iter([true, true, false]).into_array()), + ); + // true -> if_true, false -> if_false + let mask = Mask::from_iter([false, true, true]); + + let mut ctx = LEGACY_SESSION.create_execution_ctx(); + let result = mask + .into_array() + .zip(if_true, if_false)? + .execute::(&mut ctx)?; + + // Row 0 -> if_false[0] = [10]; row 1 -> if_true[1] = null; row 2 -> if_true[2] = [2] + let expected = list_view( + buffer![10i32, 2].into_array(), + buffer![0u32, 1, 1].into_array(), + buffer![1u32, 0, 1].into_array(), + Validity::Array(BoolArray::from_iter([true, false, true]).into_array()), + ); + assert_arrays_eq!(result, expected); + Ok(()) + } + + /// `zip` handles out-of-order/non-contiguous offsets and widens nullability when only one side + /// is nullable. + #[test] + fn zip_out_of_order_offsets_and_widening() -> VortexResult<()> { + // [[5, 6], [7], [8, 9]] expressed with out-of-order offsets. + let if_true = list_view( + buffer![7i32, 8, 9, 5, 6].into_array(), + buffer![3u32, 0, 1].into_array(), + buffer![2u32, 1, 2].into_array(), + Validity::NonNullable, + ); + // [[100], null, [200, 201]] + let if_false = list_view( + buffer![100i32, 200, 201].into_array(), + buffer![0u32, 1, 1].into_array(), + buffer![1u32, 0, 2].into_array(), + Validity::Array(BoolArray::from_iter([true, false, true]).into_array()), + ); + let mask = Mask::from_iter([true, true, false]); + + let mut ctx = LEGACY_SESSION.create_execution_ctx(); + let result = mask + .into_array() + .zip(if_true, if_false)? + .execute::(&mut ctx)?; + assert!(result.is::()); + + // [[5, 6], [7], [200, 201]], all valid but nullable (widened by if_false). + let expected = list_view( + buffer![5i32, 6, 7, 200, 201].into_array(), + buffer![0u32, 2, 3].into_array(), + buffer![2u32, 1, 2].into_array(), + Validity::AllValid, + ); + assert_arrays_eq!(result, expected); + Ok(()) + } + + /// When an input's `elements` is already a [`ChunkedArray`], its chunks are spliced in rather + /// than nesting a chunked array inside the concatenated elements. + #[test] + fn zip_flattens_chunked_elements() -> VortexResult<()> { + // elements [1, 2, 3] stored as two chunks; lists [[1, 2], [3]]. + let chunked_elements = ChunkedArray::try_new( + vec![buffer![1i32, 2].into_array(), buffer![3i32].into_array()], + DType::Primitive(PType::I32, Nullability::NonNullable), + )? + .into_array(); + let if_true = list_view( + chunked_elements, + buffer![0u32, 2].into_array(), + buffer![2u32, 1].into_array(), + Validity::NonNullable, + ); + // [[10], [20]] + let if_false = list_view( + buffer![10i32, 20].into_array(), + buffer![0u32, 1].into_array(), + buffer![1u32, 1].into_array(), + Validity::NonNullable, + ); + let mask = Mask::from_iter([true, false]); + + let mut ctx = LEGACY_SESSION.create_execution_ctx(); + let result = mask + .into_array() + .zip(if_true, if_false)? + .execute::(&mut ctx)?; + + // The concatenated elements are chunked, but no chunk is itself a `ChunkedArray`. + let result_lv = result + .as_opt::() + .expect("zip keeps the list-view encoding"); + let chunked = result_lv + .elements() + .as_opt::() + .expect("zip concatenates elements into a chunked array"); + assert!( + chunked.iter_chunks().all(|chunk| !chunk.is::()), + "chunked elements must be flattened, not nested", + ); + + // [[1, 2], [20]] + let expected = list_view( + buffer![1i32, 2, 20].into_array(), + buffer![0u32, 2].into_array(), + buffer![2u32, 1].into_array(), + Validity::NonNullable, + ); + assert_arrays_eq!(result, expected); + Ok(()) + } +} diff --git a/vortex-array/src/arrays/listview/tests/operations.rs b/vortex-array/src/arrays/listview/tests/operations.rs index 235caf53caa..c911b9ba7a5 100644 --- a/vortex-array/src/arrays/listview/tests/operations.rs +++ b/vortex-array/src/arrays/listview/tests/operations.rs @@ -5,11 +5,13 @@ use std::sync::Arc; use rstest::rstest; use vortex_buffer::buffer; +use vortex_error::VortexResult; use vortex_mask::Mask; use super::common::create_basic_listview; use super::common::create_large_listview; use super::common::create_nullable_listview; +use crate::ArrayRef; use crate::IntoArray; use crate::LEGACY_SESSION; #[expect(deprecated)] @@ -382,6 +384,100 @@ fn test_cast_large_dataset() { } } +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Zip tests +//////////////////////////////////////////////////////////////////////////////////////////////////// + +#[test] +fn test_zip_widens_false_element_nullability() -> VortexResult<()> { + // [[1, 2], [3], [4]] + let if_true = ListViewArray::new( + buffer![1i32, 2, 3, 4].into_array(), + buffer![0u32, 2, 3].into_array(), + buffer![2u32, 1, 1].into_array(), + Validity::NonNullable, + ) + .into_array(); + // [[10, null], [30], [40]] + let if_false = ListViewArray::new( + PrimitiveArray::from_option_iter([Some(10i32), None, Some(30), Some(40)]).into_array(), + buffer![0u32, 2, 3].into_array(), + buffer![2u32, 1, 1].into_array(), + Validity::NonNullable, + ) + .into_array(); + let mask = Mask::from_iter([false, true, false]); + + let result = mask + .into_array() + .zip(if_true, if_false)? + .execute::(&mut LEGACY_SESSION.create_execution_ctx())?; + assert!(result.is::()); + assert_eq!( + result.dtype(), + &DType::List( + Arc::new(DType::Primitive(PType::I32, Nullability::Nullable)), + Nullability::NonNullable, + ) + ); + + // [[10, null], [3], [40]] + let expected = ListViewArray::new( + PrimitiveArray::from_option_iter([Some(10i32), None, Some(3), Some(40)]).into_array(), + buffer![0u32, 2, 3].into_array(), + buffer![2u32, 1, 1].into_array(), + Validity::NonNullable, + ) + .into_array(); + assert_arrays_eq!(result, expected); + Ok(()) +} + +#[test] +fn test_zip_widens_true_element_nullability() -> VortexResult<()> { + // [[1, null], [3], [4]] + let if_true = ListViewArray::new( + PrimitiveArray::from_option_iter([Some(1i32), None, Some(3), Some(4)]).into_array(), + buffer![0u32, 2, 3].into_array(), + buffer![2u32, 1, 1].into_array(), + Validity::NonNullable, + ) + .into_array(); + // [[10], [20], [30]] + let if_false = ListViewArray::new( + buffer![10i32, 20, 30].into_array(), + buffer![0u32, 1, 2].into_array(), + buffer![1u32, 1, 1].into_array(), + Validity::NonNullable, + ) + .into_array(); + let mask = Mask::from_iter([true, false, true]); + + let result = mask + .into_array() + .zip(if_true, if_false)? + .execute::(&mut LEGACY_SESSION.create_execution_ctx())?; + assert!(result.is::()); + assert_eq!( + result.dtype(), + &DType::List( + Arc::new(DType::Primitive(PType::I32, Nullability::Nullable)), + Nullability::NonNullable, + ) + ); + + // [[1, null], [20], [4]] + let expected = ListViewArray::new( + PrimitiveArray::from_option_iter([Some(1i32), None, Some(20), Some(4)]).into_array(), + buffer![0u32, 2, 3].into_array(), + buffer![2u32, 1, 1].into_array(), + Validity::NonNullable, + ) + .into_array(); + assert_arrays_eq!(result, expected); + Ok(()) +} + //////////////////////////////////////////////////////////////////////////////////////////////////// // Constant tests //////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/vortex-array/src/arrays/listview/vtable/kernel.rs b/vortex-array/src/arrays/listview/vtable/kernel.rs index f6ceca284bf..1ad98f62a33 100644 --- a/vortex-array/src/arrays/listview/vtable/kernel.rs +++ b/vortex-array/src/arrays/listview/vtable/kernel.rs @@ -4,6 +4,9 @@ use crate::arrays::ListView; use crate::kernel::ParentKernelSet; use crate::scalar_fn::fns::cast::CastExecuteAdaptor; +use crate::scalar_fn::fns::zip::ZipExecuteAdaptor; -pub(super) const PARENT_KERNELS: ParentKernelSet = - ParentKernelSet::new(&[ParentKernelSet::lift(&CastExecuteAdaptor(ListView))]); +pub(super) const PARENT_KERNELS: ParentKernelSet = ParentKernelSet::new(&[ + ParentKernelSet::lift(&CastExecuteAdaptor(ListView)), + ParentKernelSet::lift(&ZipExecuteAdaptor(ListView)), +]); diff --git a/vortex-array/src/scalar_fn/fns/zip/mod.rs b/vortex-array/src/scalar_fn/fns/zip/mod.rs index 86b5c4f7dc1..b1b17e40bb6 100644 --- a/vortex-array/src/scalar_fn/fns/zip/mod.rs +++ b/vortex-array/src/scalar_fn/fns/zip/mod.rs @@ -90,20 +90,12 @@ impl ScalarFnVTable for Zip { } fn return_dtype(&self, _options: &Self::Options, arg_dtypes: &[DType]) -> VortexResult { - vortex_ensure!( - arg_dtypes[0].eq_ignore_nullability(&arg_dtypes[1]), - "zip requires if_true and if_false to have the same base type, got {} and {}", - arg_dtypes[0], - arg_dtypes[1] - ); vortex_ensure!( matches!(arg_dtypes[2], DType::Bool(_)), "zip requires mask to be a boolean type, got {}", arg_dtypes[2] ); - Ok(arg_dtypes[0] - .clone() - .union_nullability(arg_dtypes[1].nullability())) + zip_return_dtype(&arg_dtypes[0], &arg_dtypes[1]) } fn execute( @@ -120,10 +112,7 @@ impl ScalarFnVTable for Zip { .execute::(ctx)? .to_mask_fill_null_false(ctx); - let return_dtype = if_true - .dtype() - .clone() - .union_nullability(if_false.dtype().nullability()); + let return_dtype = zip_return_dtype(if_true.dtype(), if_false.dtype())?; if mask.all_true() { return if_true.cast(return_dtype)?.execute(ctx); @@ -184,10 +173,7 @@ pub(crate) fn zip_impl( "zip requires arrays to have the same size" ); - let return_type = if_true - .dtype() - .clone() - .union_nullability(if_false.dtype().nullability()); + let return_type = zip_return_dtype(if_true.dtype(), if_false.dtype())?; if mask.all_true() { return if_true.cast(return_type); @@ -211,6 +197,18 @@ pub(crate) fn zip_impl( ) } +fn zip_return_dtype(if_true: &DType, if_false: &DType) -> VortexResult { + vortex_ensure!( + if_true.eq_ignore_nullability(if_false), + "zip requires if_true and if_false to have the same base type, got {} and {}", + if_true, + if_false + ); + Ok(if_true + .least_supertype(if_false) + .vortex_expect("zip inputs with the same base type must have a common dtype")) +} + fn zip_impl_with_builder( if_true: &ArrayRef, if_false: &ArrayRef, From 667e1d78d3111e0b3cdc5c60a86208da645c8c09 Mon Sep 17 00:00:00 2001 From: Robert Kruszewski Date: Wed, 3 Jun 2026 00:41:33 +0100 Subject: [PATCH 007/115] Add RoaringBitmap support to vortex-jni bindings (#8220) I think this is a bit messy but I don't know how to make it better in the C API This is being added to support positional deletes in iceberg Signed-off-by: Robert Kruszewski --- Cargo.lock | 1 + java/gradle/libs.versions.toml | 4 +- java/vortex-jni/build.gradle.kts | 27 ++-- .../main/java/dev/vortex/api/DataSource.java | 33 ++++- .../main/java/dev/vortex/api/ScanOptions.java | 115 +++++++++++++++++- .../main/java/dev/vortex/jni/NativeScan.java | 5 +- .../test/java/dev/vortex/api/TestMinimal.java | 77 ++++++++++++ vortex-jni/Cargo.toml | 1 + vortex-jni/src/scan.rs | 21 ++++ 9 files changed, 262 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index efc977abf08..353069cf9b3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9809,6 +9809,7 @@ dependencies = [ "jni", "object_store 0.13.2", "parking_lot", + "roaring", "thiserror 2.0.18", "tracing", "tracing-subscriber", diff --git a/java/gradle/libs.versions.toml b/java/gradle/libs.versions.toml index 464f6339603..3642a5ea54d 100644 --- a/java/gradle/libs.versions.toml +++ b/java/gradle/libs.versions.toml @@ -10,6 +10,7 @@ junit-jupiter = "6.1.0" logback = "1.5.33" netty = "4.2.14.Final" nopen = "1.0.1" +roaringbitmap = "1.6.14" slf4j = "2.0.18" spark3 = "3.5.8" spark4 = "4.1.1" @@ -30,7 +31,8 @@ logback-classic = { module = "ch.qos.logback:logback-classic", version.ref = "lo netty-bom = { module = "io.netty:netty-bom", version.ref = "netty" } nopen-annotations = { module = "com.jakewharton.nopen:nopen-annotations", version.ref = "nopen" } nopen-checker = { module = "com.jakewharton.nopen:nopen-checker", version.ref = "nopen" } +roaringbitmap = { module = "org.roaringbitmap:RoaringBitmap", version.ref = "roaringbitmap" } slf4j-api = { module = "org.slf4j:slf4j-api", version.ref = "slf4j" } slf4j-simple = { module = "org.slf4j:slf4j-simple", version.ref = "slf4j" } s3mock-testcontainers = { module = "com.adobe.testing:s3mock-testcontainers", version.ref = "s3mock" } -testcontainers-juputer = { module = "org.testcontainers:junit-jupiter", version.ref = "testcontainers-jupiter" } \ No newline at end of file +testcontainers-juputer = { module = "org.testcontainers:junit-jupiter", version.ref = "testcontainers-jupiter" } diff --git a/java/vortex-jni/build.gradle.kts b/java/vortex-jni/build.gradle.kts index 3ffc0f8ef6c..9bf8d562540 100644 --- a/java/vortex-jni/build.gradle.kts +++ b/java/vortex-jni/build.gradle.kts @@ -27,6 +27,7 @@ dependencies { implementation(libs.guava) compileOnly(libs.errorprone.annotations) compileOnly(libs.nopen.annotations) + api(libs.roaringbitmap) // Logging implementation(libs.slf4j.api) @@ -131,18 +132,20 @@ tasks.register("makeTestFiles") { val osName = System.getProperty("os.name").lowercase() val osArch = System.getProperty("os.arch").lowercase() - val osShortName = when { - osName.contains("mac") -> "darwin" - osName.contains("nix") || osName.contains("nux") -> "linux" - osName.contains("win") -> "win" - else -> throw GradleException("Unsupported OS for makeTestFiles: $osName") - } - val libExt = when (osShortName) { - "darwin" -> ".dylib" - "linux" -> ".so" - "win" -> ".dll" - else -> throw GradleException("Unsupported OS short name: $osShortName") - } + val osShortName = + when { + osName.contains("mac") -> "darwin" + osName.contains("nix") || osName.contains("nux") -> "linux" + osName.contains("win") -> "win" + else -> throw GradleException("Unsupported OS for makeTestFiles: $osName") + } + val libExt = + when (osShortName) { + "darwin" -> ".dylib" + "linux" -> ".so" + "win" -> ".dll" + else -> throw GradleException("Unsupported OS short name: $osShortName") + } // Only populate the host-arch directory so cross-compiled libs for other // architectures (placed by the publish workflow) are preserved. diff --git a/java/vortex-jni/src/main/java/dev/vortex/api/DataSource.java b/java/vortex-jni/src/main/java/dev/vortex/api/DataSource.java index d13f7e6acc1..24780785274 100644 --- a/java/vortex-jni/src/main/java/dev/vortex/api/DataSource.java +++ b/java/vortex-jni/src/main/java/dev/vortex/api/DataSource.java @@ -136,13 +136,40 @@ public Scan scan(ScanOptions options) { long filterPtr = options.filter().map(Expression::nativePointer).orElse(0L); long begin = options.rowRangeBegin().orElse(0L); long end = options.rowRangeEnd().orElse(0L); - long[] selectionIndices = options.selectionIndices().orElse(null); - byte selectionMode = options.selectionMode().code(); + ScanOptions.SelectionMode selectionMode = options.selectionMode(); + long[] selectionIndices = selectionIndices(options); + byte[] selectionRoaringBitmap = selectionRoaringBitmap(options); long limit = options.limit().orElse(0L); boolean ordered = options.ordered(); long scanPtr = dev.vortex.jni.NativeScan.create( - pointer, projectionPtr, filterPtr, begin, end, selectionIndices, selectionMode, limit, ordered); + pointer, + projectionPtr, + filterPtr, + begin, + end, + selectionIndices, + selectionRoaringBitmap, + selectionMode.code(), + limit, + ordered); return Scan.fromPointer(session, scanPtr); } + + private static long[] selectionIndices(ScanOptions options) { + return switch (options.selectionMode()) { + case INCLUDE, EXCLUDE -> options.selectionIndices().orElse(null); + default -> null; + }; + } + + private static byte[] selectionRoaringBitmap(ScanOptions options) { + return switch (options.selectionMode()) { + case INCLUDE_ROARING, EXCLUDE_ROARING -> + options.selectionRoaringBitmap() + .orElseThrow(() -> new IllegalArgumentException( + "selection roaring bitmap is required for roaring selection modes")); + default -> null; + }; + } } diff --git a/java/vortex-jni/src/main/java/dev/vortex/api/ScanOptions.java b/java/vortex-jni/src/main/java/dev/vortex/api/ScanOptions.java index 2431f9e5722..e9009582e28 100644 --- a/java/vortex-jni/src/main/java/dev/vortex/api/ScanOptions.java +++ b/java/vortex-jni/src/main/java/dev/vortex/api/ScanOptions.java @@ -3,9 +3,15 @@ package dev.vortex.api; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.util.Objects; import java.util.Optional; import java.util.OptionalLong; import org.immutables.value.Value; +import org.roaringbitmap.longlong.Roaring64NavigableMap; /** * Scan configuration passed to {@link DataSource#scan(ScanOptions)}. @@ -28,12 +34,15 @@ public interface ScanOptions { OptionalLong rowRangeEnd(); /** - * Sorted ascending row indices that should be included in (or excluded from) the scan, depending on + * Sorted ascending, unique row indices that should be included in (or excluded from) the scan, depending on * {@link #selectionMode()}. */ Optional selectionIndices(); - /** Meaning of {@link #selectionIndices()}. */ + /** Portable serialized {@link Roaring64NavigableMap} row selection. */ + Optional selectionRoaringBitmap(); + + /** Meaning of the row selection payload. */ @Value.Default default SelectionMode selectionMode() { return SelectionMode.INCLUDE_ALL; @@ -48,6 +57,54 @@ default boolean ordered() { return false; } + @Value.Check + default void validateSelectionPayload() { + boolean hasIndices = selectionIndices().isPresent(); + boolean hasRoaringBitmap = selectionRoaringBitmap().isPresent(); + if (hasIndices && hasRoaringBitmap) { + throw new IllegalArgumentException("row selection must use either indices or roaring bitmap, not both"); + } + if (hasIndices) { + validateSelectionIndices(selectionIndices().orElseThrow()); + } + + switch (selectionMode()) { + case INCLUDE_ALL -> { + if (hasIndices || hasRoaringBitmap) { + throw new IllegalArgumentException("row selection payload requires a selection mode"); + } + } + case INCLUDE, EXCLUDE -> { + if (!hasIndices) { + throw new IllegalArgumentException("selection indices are required for index selection modes"); + } + } + case INCLUDE_ROARING, EXCLUDE_ROARING -> { + if (!hasRoaringBitmap) { + throw new IllegalArgumentException( + "selection roaring bitmap is required for roaring selection modes"); + } + if (selectionRoaringBitmap().orElseThrow().length == 0) { + throw new IllegalArgumentException("selection roaring bitmap must not be empty"); + } + } + } + } + + private static void validateSelectionIndices(long[] selectionIndices) { + long previous = -1L; + for (int i = 0; i < selectionIndices.length; i++) { + long index = selectionIndices[i]; + if (index < 0) { + throw new IllegalArgumentException("selection indices must be non-negative"); + } + if (i > 0 && index <= previous) { + throw new IllegalArgumentException("selection indices must be sorted ascending and unique"); + } + previous = index; + } + } + static ScanOptions of() { return ImmutableScanOptions.builder().build(); } @@ -56,14 +113,62 @@ static ImmutableScanOptions.Builder builder() { return ImmutableScanOptions.builder(); } - /** How to interpret {@link #selectionIndices()}. */ + /** Scan only the rows at the given sorted ascending, unique row indices. */ + static ScanOptions includeRows(long... rowIndices) { + return builder() + .selectionIndices(rowIndices.clone()) + .selectionMode(SelectionMode.INCLUDE) + .build(); + } + + /** Scan all rows except the given sorted ascending, unique row indices. */ + static ScanOptions excludeRows(long... rowIndices) { + return builder() + .selectionIndices(rowIndices.clone()) + .selectionMode(SelectionMode.EXCLUDE) + .build(); + } + + /** Scan only the rows in the given Roaring bitmap. */ + static ScanOptions includeRows(Roaring64NavigableMap rowSelection) { + return builder() + .selectionRoaringBitmap(serializeRoaringBitmap(rowSelection)) + .selectionMode(SelectionMode.INCLUDE_ROARING) + .build(); + } + + /** Scan all rows except the rows in the given Roaring bitmap. */ + static ScanOptions excludeRows(Roaring64NavigableMap rowSelection) { + return builder() + .selectionRoaringBitmap(serializeRoaringBitmap(rowSelection)) + .selectionMode(SelectionMode.EXCLUDE_ROARING) + .build(); + } + + private static byte[] serializeRoaringBitmap(Roaring64NavigableMap rowSelection) { + Objects.requireNonNull(rowSelection, "rowSelection"); + try (ByteArrayOutputStream output = new ByteArrayOutputStream(); + DataOutputStream dataOutput = new DataOutputStream(output)) { + rowSelection.serializePortable(dataOutput); + dataOutput.flush(); + return output.toByteArray(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + /** How to interpret the row selection payload. */ enum SelectionMode { - /** Ignore {@link #selectionIndices()}. */ + /** Ignore row selection payloads. */ INCLUDE_ALL((byte) 0), /** Return only rows at the indices. */ INCLUDE((byte) 1), /** Return rows except those at the indices. */ - EXCLUDE((byte) 2); + EXCLUDE((byte) 2), + /** Return only rows in the Roaring bitmap. */ + INCLUDE_ROARING((byte) 3), + /** Return rows except those in the Roaring bitmap. */ + EXCLUDE_ROARING((byte) 4); private final byte code; diff --git a/java/vortex-jni/src/main/java/dev/vortex/jni/NativeScan.java b/java/vortex-jni/src/main/java/dev/vortex/jni/NativeScan.java index fe1a2e7a674..86b4b2e3359 100644 --- a/java/vortex-jni/src/main/java/dev/vortex/jni/NativeScan.java +++ b/java/vortex-jni/src/main/java/dev/vortex/jni/NativeScan.java @@ -21,8 +21,10 @@ private NativeScan() {} * @param rowRangeBegin inclusive start of the row range, 0 for "unbounded" * @param rowRangeEnd exclusive end of the row range, 0 for "unbounded" * @param selectionIndices sorted row indices; may be null + * @param selectionRoaringBitmap portable serialized Roaring64 bitmap; may be null * @param selectionInclude {@code 0} (all), {@code 1} (include {@code selectionIndices}), {@code 2} (exclude - * {@code selectionIndices}) + * {@code selectionIndices}), {@code 3} (include {@code selectionRoaringBitmap}), {@code 4} (exclude + * {@code selectionRoaringBitmap}) * @param limit max rows to return, or {@code 0} for "no limit" * @param ordered true to preserve row order across partitions */ @@ -33,6 +35,7 @@ public static native long create( long rowRangeBegin, long rowRangeEnd, long[] selectionIndices, + byte[] selectionRoaringBitmap, byte selectionInclude, long limit, boolean ordered); diff --git a/java/vortex-jni/src/test/java/dev/vortex/api/TestMinimal.java b/java/vortex-jni/src/test/java/dev/vortex/api/TestMinimal.java index b422b767dd3..322dc5522c3 100644 --- a/java/vortex-jni/src/test/java/dev/vortex/api/TestMinimal.java +++ b/java/vortex-jni/src/test/java/dev/vortex/api/TestMinimal.java @@ -5,6 +5,7 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import dev.vortex.arrow.ArrowAllocation; import dev.vortex.jni.NativeLoader; @@ -31,6 +32,7 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; +import org.roaringbitmap.longlong.Roaring64NavigableMap; public final class TestMinimal { static final class Person { @@ -180,6 +182,73 @@ public void testProjectedScanWithFilter() throws Exception { assertEquals(List.of(new Person("John", BigDecimal.valueOf(10_000L, 2), "VA")), people); } + @Test + public void testSelectionIncludesRows() throws Exception { + BufferAllocator allocator = ArrowAllocation.rootAllocator(); + Session session = Session.create(); + DataSource ds = DataSource.open(session, writePath); + + List people = readAll(ds, ScanOptions.includeRows(0, 3, 9), allocator, TestMinimal::readFullBatch); + assertEquals(List.of(MINIMAL_DATA.get(0), MINIMAL_DATA.get(3), MINIMAL_DATA.get(9)), people); + } + + @Test + public void testSelectionExcludesRows() throws Exception { + BufferAllocator allocator = ArrowAllocation.rootAllocator(); + Session session = Session.create(); + DataSource ds = DataSource.open(session, writePath); + + List people = readAll(ds, ScanOptions.excludeRows(0, 9), allocator, TestMinimal::readFullBatch); + assertEquals(MINIMAL_DATA.subList(1, 9), people); + } + + @Test + public void testRoaringSelectionIncludesRows() throws Exception { + BufferAllocator allocator = ArrowAllocation.rootAllocator(); + Session session = Session.create(); + DataSource ds = DataSource.open(session, writePath); + + List people = + readAll(ds, ScanOptions.includeRows(roaringRows(0, 3, 9)), allocator, TestMinimal::readFullBatch); + assertEquals(List.of(MINIMAL_DATA.get(0), MINIMAL_DATA.get(3), MINIMAL_DATA.get(9)), people); + } + + @Test + public void testRoaringSelectionExcludesRows() throws Exception { + BufferAllocator allocator = ArrowAllocation.rootAllocator(); + Session session = Session.create(); + DataSource ds = DataSource.open(session, writePath); + + List people = + readAll(ds, ScanOptions.excludeRows(roaringRows(0, 9)), allocator, TestMinimal::readFullBatch); + assertEquals(MINIMAL_DATA.subList(1, 9), people); + } + + @Test + public void testSelectionIndicesMustBeSortedAndUnique() { + IllegalArgumentException exception = + assertThrows(IllegalArgumentException.class, () -> ScanOptions.includeRows(2, 1)); + assertEquals("selection indices must be sorted ascending and unique", exception.getMessage()); + } + + @Test + public void testSelectionPayloadMustChooseIndicesOrRoaring() { + IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, () -> ScanOptions.builder() + .selectionMode(ScanOptions.SelectionMode.INCLUDE) + .selectionIndices(new long[] {0}) + .selectionRoaringBitmap(new byte[] {1}) + .build()); + assertEquals("row selection must use either indices or roaring bitmap, not both", exception.getMessage()); + } + + @Test + public void testSelectionPayloadRequiresMode() { + IllegalArgumentException exception = assertThrows( + IllegalArgumentException.class, + () -> ScanOptions.builder().selectionIndices(new long[] {0}).build()); + assertEquals("row selection payload requires a selection mode", exception.getMessage()); + } + private interface BatchReader { List read(VectorSchemaRoot root); } @@ -221,4 +290,12 @@ private static List readFullBatch(VectorSchemaRoot root) { } return result; } + + private static Roaring64NavigableMap roaringRows(long... rows) { + Roaring64NavigableMap bitmap = new Roaring64NavigableMap(); + for (long row : rows) { + bitmap.addLong(row); + } + return bitmap; + } } diff --git a/vortex-jni/Cargo.toml b/vortex-jni/Cargo.toml index 628c2c89d43..fd9835268f8 100644 --- a/vortex-jni/Cargo.toml +++ b/vortex-jni/Cargo.toml @@ -24,6 +24,7 @@ futures = { workspace = true } jni = { workspace = true } object_store = { workspace = true, features = ["aws", "azure", "gcp"] } parking_lot = { workspace = true } +roaring = { workspace = true } thiserror = { workspace = true } tracing = { workspace = true, features = ["std", "log"] } tracing-subscriber = { workspace = true, features = ["env-filter"] } diff --git a/vortex-jni/src/scan.rs b/vortex-jni/src/scan.rs index 08dc18342eb..606ec8cd040 100644 --- a/vortex-jni/src/scan.rs +++ b/vortex-jni/src/scan.rs @@ -10,6 +10,7 @@ //! 3. `Java_dev_vortex_jni_NativePartition_scanArrow` → consumes a partition into an //! `FFI_ArrowArrayStream` that Java imports via Arrow's C Data Interface. +use std::io::Cursor; use std::ops::Range; use std::ptr; use std::sync::Arc; @@ -22,6 +23,7 @@ use arrow_schema::DataType; use arrow_schema::Field; use futures::StreamExt; use jni::EnvUnowned; +use jni::objects::JByteArray; use jni::objects::JClass; use jni::objects::JLongArray; use jni::sys::jboolean; @@ -75,6 +77,7 @@ fn build_scan_request( row_range_begin: jlong, row_range_end: jlong, selection_idx: &[u64], + selection_roaring_bitmap: &[u8], selection_include: u8, limit: jlong, ordered: jboolean, @@ -95,6 +98,8 @@ fn build_scan_request( 0 => Selection::All, 1 => Selection::IncludeByIndex(Buffer::copy_from(selection_idx)), 2 => Selection::ExcludeByIndex(Buffer::copy_from(selection_idx)), + 3 => Selection::IncludeRoaring(deserialize_roaring_selection(selection_roaring_bitmap)?), + 4 => Selection::ExcludeRoaring(deserialize_roaring_selection(selection_roaring_bitmap)?), other => vortex_bail!("unknown selection include code: {other}"), }; @@ -117,6 +122,14 @@ fn build_scan_request( }) } +fn deserialize_roaring_selection(bytes: &[u8]) -> VortexResult { + if bytes.is_empty() { + vortex_bail!("serialized roaring row selection must not be empty"); + } + let cursor = Cursor::new(bytes); + Ok(roaring::RoaringTreemap::deserialize_from(cursor)?) +} + #[allow(clippy::too_many_arguments)] #[unsafe(no_mangle)] pub extern "system" fn Java_dev_vortex_jni_NativeScan_create( @@ -128,6 +141,7 @@ pub extern "system" fn Java_dev_vortex_jni_NativeScan_create( row_range_begin: jlong, row_range_end: jlong, selection_indices: JLongArray, + selection_roaring_bitmap: JByteArray, selection_include: jni::sys::jbyte, limit: jlong, ordered: jboolean, @@ -151,12 +165,19 @@ pub extern "system" fn Java_dev_vortex_jni_NativeScan_create( out }; + let selection_roaring_bitmap: Vec = if selection_roaring_bitmap.is_null() { + Vec::new() + } else { + env.convert_byte_array(&selection_roaring_bitmap)? + }; + let request = build_scan_request( projection_ptr, filter_ptr, row_range_begin, row_range_end, &selection_idx, + &selection_roaring_bitmap, selection_include as u8, limit, ordered, From 2d003427c55029009aa2e1093d9a52d6e8b8df14 Mon Sep 17 00:00:00 2001 From: Adam Gutglick Date: Wed, 3 Jun 2026 11:41:19 +0100 Subject: [PATCH 008/115] Add `select_unpredictable` hint to search_sorted (#8225) ## Summary Seems to be the main difference between the impl we have and std, docs - https://doc.rust-lang.org/std/hint/fn.select_unpredictable.html Doesn't seem to make a difference in our benchmarks, probably worth further investigation. Signed-off-by: Adam Gutglick --- vortex-array/src/search_sorted.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vortex-array/src/search_sorted.rs b/vortex-array/src/search_sorted.rs index 14741652330..f3514708aec 100644 --- a/vortex-array/src/search_sorted.rs +++ b/vortex-array/src/search_sorted.rs @@ -237,7 +237,7 @@ fn search_sorted_side_idx VortexResult>( // Binary search interacts poorly with branch prediction, so force // the compiler to use conditional moves if supported by the target // architecture. - base = if cmp == Greater { base } else { mid }; + base = hint::select_unpredictable(cmp == Greater, base, mid); // This is imprecise in the case where `size` is odd and the // comparison returns Greater: the mid element still gets included From 7a53ad5e64a10fb35a7cc3cff4045a0bb25e990c Mon Sep 17 00:00:00 2001 From: Mikhail Kot Date: Wed, 3 Jun 2026 13:26:50 +0100 Subject: [PATCH 009/115] Don't recalculate chunk offsets for ChunkedReader (#8231) Reuse them from parent layout. Signed-off-by: Mikhail Kot --- vortex-layout/src/layouts/chunked/mod.rs | 12 +++++------- vortex-layout/src/layouts/chunked/reader.rs | 19 ++++++------------- 2 files changed, 11 insertions(+), 20 deletions(-) diff --git a/vortex-layout/src/layouts/chunked/mod.rs b/vortex-layout/src/layouts/chunked/mod.rs index e8dd3c06dc0..ee37cf87411 100644 --- a/vortex-layout/src/layouts/chunked/mod.rs +++ b/vortex-layout/src/layouts/chunked/mod.rs @@ -106,10 +106,9 @@ impl VTable for Chunked { let new_children = OwnedLayoutChildren::layout_children(children); // Recalculate chunk offsets based on new children - let mut chunk_offsets = Vec::with_capacity(new_children.nchildren() + 1); - chunk_offsets.push(0); + let mut chunk_offsets = vec![0; new_children.nchildren() + 1]; for i in 0..new_children.nchildren() { - chunk_offsets.push(chunk_offsets[i] + new_children.child_row_count(i)); + chunk_offsets[i + 1] = chunk_offsets[i] + new_children.child_row_count(i); } layout.children = new_children; @@ -135,12 +134,11 @@ pub struct ChunkedLayout { impl ChunkedLayout { pub fn new(row_count: u64, dtype: DType, children: Arc) -> Self { - let mut chunk_offsets = Vec::with_capacity(children.nchildren() + 1); - - chunk_offsets.push(0); + let mut chunk_offsets = vec![0; children.nchildren() + 1]; for i in 0..children.nchildren() { - chunk_offsets.push(chunk_offsets[i] + children.child_row_count(i)); + chunk_offsets[i + 1] = chunk_offsets[i] + children.child_row_count(i); } + assert_eq!( chunk_offsets[children.nchildren()], row_count, diff --git a/vortex-layout/src/layouts/chunked/reader.rs b/vortex-layout/src/layouts/chunked/reader.rs index 51eb4f7a15d..4d095cfee37 100644 --- a/vortex-layout/src/layouts/chunked/reader.rs +++ b/vortex-layout/src/layouts/chunked/reader.rs @@ -40,8 +40,6 @@ pub struct ChunkedReader { layout: ChunkedLayout, name: Arc, lazy_children: LazyReaderChildren, - /// Row offset for each chunk - chunk_offsets: Vec, } static UNKNOWN: LazyLock> = LazyLock::new(|| Arc::from("chunked-child")); @@ -55,13 +53,6 @@ impl ChunkedReader { ctx: LayoutReaderContext, ) -> Self { let nchildren = layout.nchildren(); - - let mut chunk_offsets = vec![0; nchildren + 1]; - for i in 1..nchildren { - chunk_offsets[i] = chunk_offsets[i - 1] + layout.children.child_row_count(i - 1); - } - chunk_offsets[nchildren] = layout.row_count(); - let dtypes = vec![layout.dtype.clone(); nchildren]; // format!() has non-marginal overhead for short queries like random @@ -87,7 +78,6 @@ impl ChunkedReader { layout, name, lazy_children, - chunk_offsets, } } @@ -97,22 +87,25 @@ impl ChunkedReader { } fn chunk_offset(&self, idx: usize) -> u64 { - self.chunk_offsets.get(idx).copied().unwrap_or_else(|| { + if idx >= self.layout.chunk_offsets.len() { vortex_panic!( "Internal error: Chunk offset {idx} out of bounds (num_children: {}, num_offsets: {}). \ This indicates a bug in ChunkedReader initialization or chunk_range calculation.", self.layout.nchildren(), - self.chunk_offsets.len() + self.layout.chunk_offsets.len() ) - }) + } + self.layout.chunk_offsets[idx] } fn chunk_range(&self, row_range: &Range) -> Range { let start_chunk = self + .layout .chunk_offsets .binary_search(&row_range.start) .unwrap_or_else(|x| x.saturating_sub(1)); let end_chunk = self + .layout .chunk_offsets .binary_search(&row_range.end) .unwrap_or_else(|x| x); From 679fc05e6bd0e2284d72082e5846dd19a4d8f566 Mon Sep 17 00:00:00 2001 From: Frederic Branczyk Date: Wed, 3 Jun 2026 12:43:03 +0000 Subject: [PATCH 010/115] vortex-io: Add explicit span on spawn_io (#8232) ## Summary I/O tasks are likely to cause a lot of tracing to be produced. With an explicit span on them, we can control it more precisely./ ## API Changes No API changes. ## Testing No testing, this just allows consumers to configure tracing at a more granular level. @AdamGS Signed-off-by: Frederic Branczyk --- vortex-io/src/runtime/handle.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/vortex-io/src/runtime/handle.rs b/vortex-io/src/runtime/handle.rs index 3fc3203fae1..8675f051f19 100644 --- a/vortex-io/src/runtime/handle.rs +++ b/vortex-io/src/runtime/handle.rs @@ -68,7 +68,13 @@ impl Handle { R: Send + 'static, { let (send, recv) = oneshot::channel(); - let span = tracing::Span::current(); + // Instrument with a dedicated, named span on its own target rather than + // re-entering `Span::current()`. `Instrumented::poll` enters and exits the + // span on every poll, so re-entering the caller's span makes its cost scale + // with poll count. A distinct target lets subscribers opt these spans in or + // out via filtering; when filtered out the span is disabled and enter/exit + // are no-ops, which keeps frequently-polled spawned futures cheap. + let span = tracing::trace_span!(target: "vortex_io::spawn", "spawn"); let abort_handle = self.runtime().spawn( async move { // Task::detach allows the receiver to be dropped, so we ignore send errors. @@ -104,7 +110,11 @@ impl Handle { R: Send + 'static, { let (send, recv) = oneshot::channel(); - let span = tracing::Span::current(); + // See `spawn` above: a dedicated target rather than `Span::current()` so + // subscribers can filter these spans in or out. I/O futures are polled + // frequently, so disabling the span (the default for an unconfigured + // target) avoids per-poll enter/exit cost. + let span = tracing::trace_span!(target: "vortex_io::spawn_io", "spawn_io"); let abort_handle = self.runtime().spawn_io( async move { // Task::detach allows the receiver to be dropped, so we ignore send errors. From 66335d4f33f0029f8ceb36ac80516b92204d2761 Mon Sep 17 00:00:00 2001 From: Adam Gutglick Date: Wed, 3 Jun 2026 14:03:23 +0100 Subject: [PATCH 011/115] Remove the full analysis from benchmark comments (#8233) ## Summary Turns out we ran into the maximum github comment size, so time to make it smaller. Signed-off-by: Adam Gutglick --- scripts/compare-benchmark-jsons.py | 31 ------------------------------ 1 file changed, 31 deletions(-) diff --git a/scripts/compare-benchmark-jsons.py b/scripts/compare-benchmark-jsons.py index 69009a5494a..e864209d011 100644 --- a/scripts/compare-benchmark-jsons.py +++ b/scripts/compare-benchmark-jsons.py @@ -783,22 +783,6 @@ def main() -> None: print("---") print("") - if statistical_analysis is not None: - alpha_rows = statistical_analysis["detail_df"][~statistical_analysis["detail_df"]["is_control"]].copy() - if not alpha_rows.empty: - alpha_rows = alpha_rows.sort_values(["query", "engine", "file_format"]) - alpha_table = pd.DataFrame( - { - "Query": alpha_rows["query"].astype(int), - "Config": alpha_rows["combo"], - "Raw Δ": alpha_rows["ratio"].map(format_ratio_change), - "Control Δ": alpha_rows["beta_ratio"].map(format_ratio_change), - "Attributed α": alpha_rows["alpha_ratio"].map(format_ratio_change), - "Noise floor": alpha_rows["noise_floor_ratio"].map(format_ratio_change), - "Significant?": alpha_rows["signal"].map(format_signal), - } - ) - grouped_tables = df3.groupby(["engine", "file_format"], dropna=False, sort=False) for engine, file_format in sorted(grouped_tables.groups.keys(), key=group_sort_key): group_df = grouped_tables.get_group((engine, file_format)).sort_values("name") @@ -847,21 +831,6 @@ def main() -> None: print("") print(file_size_report) - if statistical_analysis is not None and not alpha_rows.empty: - print("
") - print("Full attributed analysis") - print("") - print("
") - print("") - print( - alpha_table.to_markdown( - index=False, - tablefmt="github", - ) - ) - print("") - print("
") - if __name__ == "__main__": main() From 9daf90f1ea14a1fd09377cc2915a70fd6bb7ef86 Mon Sep 17 00:00:00 2001 From: Mikhail Kot Date: Wed, 3 Jun 2026 15:44:17 +0100 Subject: [PATCH 012/115] ViewedLayoutChildren child layout cache (#8234) Avoid re-parsing flatbuffers when you re-access the same children Signed-off-by: Mikhail Kot --- vortex-layout/src/children.rs | 97 ++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 40 deletions(-) diff --git a/vortex-layout/src/children.rs b/vortex-layout/src/children.rs index 21758d0c100..8f7cb5783b0 100644 --- a/vortex-layout/src/children.rs +++ b/vortex-layout/src/children.rs @@ -7,6 +7,7 @@ use std::sync::Arc; use flatbuffers::Follow; use itertools::Itertools; +use once_cell::sync::OnceCell; use vortex_array::dtype::DType; use vortex_error::VortexResult; use vortex_error::vortex_bail; @@ -105,6 +106,7 @@ pub(crate) struct ViewedLayoutChildren { layout_read_ctx: ReadContext, layouts: LayoutRegistry, allow_unknown: bool, + cache: Arc<[OnceCell]>, } impl ViewedLayoutChildren { @@ -121,6 +123,12 @@ impl ViewedLayoutChildren { layouts: LayoutRegistry, allow_unknown: bool, ) -> Self { + // SAFETY: guaranteed by caller + let nchildren = unsafe { fbl::Layout::follow(flatbuffer.as_ref(), flatbuffer_loc) } + .children() + .unwrap_or_default() + .len(); + let cache = vec![OnceCell::new(); nchildren].into_boxed_slice().into(); Self { flatbuffer, flatbuffer_loc, @@ -128,6 +136,7 @@ impl ViewedLayoutChildren { layout_read_ctx, layouts, allow_unknown, + cache, } } @@ -184,47 +193,55 @@ impl LayoutChildren for ViewedLayoutChildren { if idx >= self.nchildren() { vortex_bail!("Child index out of bounds: {} of {}", idx, self.nchildren()); } - let fb_child = self.flatbuffer().children().unwrap_or_default().get(idx); - let viewed_children = ViewedLayoutChildren { - flatbuffer: self.flatbuffer.clone(), - flatbuffer_loc: fb_child._tab.loc(), - array_read_ctx: self.array_read_ctx.clone(), - layout_read_ctx: self.layout_read_ctx.clone(), - layouts: self.layouts.clone(), - allow_unknown: self.allow_unknown, - }; + let layout_ref = self.cache[idx].get_or_try_init(|| { + let fb_child = self.flatbuffer().children().unwrap_or_default().get(idx); - let encoding_id = self - .layout_read_ctx - .resolve(fb_child.encoding()) - .ok_or_else(|| vortex_err!("Encoding not found: {}", fb_child.encoding()))?; - let Some(encoding) = self.layouts.find(&encoding_id) else { - if self.allow_unknown { - return viewed_children.foreign_layout_from_fb(fb_child, dtype); - } - return Err(vortex_err!( - "Encoding not found in registry: {}", - fb_child.encoding() - )); - }; - - encoding.build( - dtype, - fb_child.row_count(), - fb_child - .metadata() - .map(|m| m.bytes()) - .unwrap_or_else(|| &[]), - fb_child - .segments() - .unwrap_or_default() - .iter() - .map(SegmentId::from) - .collect_vec(), - &viewed_children, - &self.array_read_ctx, - ) + // SAFETY: same validated flatbuffer; fb_child._tab.loc() is a valid offset + // We need this to avoid re-initializing cache here + let viewed_children = unsafe { + ViewedLayoutChildren::new_unchecked( + self.flatbuffer.clone(), + fb_child._tab.loc(), + self.array_read_ctx.clone(), + self.layout_read_ctx.clone(), + self.layouts.clone(), + self.allow_unknown, + ) + }; + + let encoding_id = self + .layout_read_ctx + .resolve(fb_child.encoding()) + .ok_or_else(|| vortex_err!("Encoding not found: {}", fb_child.encoding()))?; + let Some(encoding) = self.layouts.find(&encoding_id) else { + if self.allow_unknown { + return viewed_children.foreign_layout_from_fb(fb_child, dtype); + } + return Err(vortex_err!( + "Encoding not found in registry: {}", + fb_child.encoding() + )); + }; + + encoding.build( + dtype, + fb_child.row_count(), + fb_child + .metadata() + .map(|m| m.bytes()) + .unwrap_or_else(|| &[]), + fb_child + .segments() + .unwrap_or_default() + .iter() + .map(SegmentId::from) + .collect_vec(), + &viewed_children, + &self.array_read_ctx, + ) + })?; + Ok(Arc::clone(layout_ref)) } fn child_row_count(&self, idx: usize) -> u64 { @@ -238,6 +255,6 @@ impl LayoutChildren for ViewedLayoutChildren { } fn nchildren(&self) -> usize { - self.flatbuffer().children().unwrap_or_default().len() + self.cache.len() } } From bd6fc3e057974e4157258f66eb8bc62614a4ea6e Mon Sep 17 00:00:00 2001 From: Joe Isaacs Date: Wed, 3 Jun 2026 15:52:42 +0100 Subject: [PATCH 013/115] perf: optimize varbinview construction (#8146) ## Summary Opptimizes the hot path for constructing binary views when the entire decoded heap fits within a single buffer. ### Changes **Optimized view construction** (`vortex-array/src/arrays/varbinview/build_views.rs`): - Fast path for the common case where the entire decoded heap fits in a single buffer - Eliminates per-element rollover checks and out-of-line `BinaryView::make_view` calls in the hot loop - Constructs reference views inline for long strings (>4 bytes) and inlined views for short strings - Reduces branch mispredictions and improves cache locality during view construction --------- Signed-off-by: Joe Isaacs Signed-off-by: Claude Signed-off-by: Claude Opus 4.8 (1M context) Co-authored-by: Claude --- Cargo.lock | 1 + vortex-array/Cargo.toml | 1 + .../src/arrays/varbinview/build_views.rs | 350 +++++++++++++++++- vortex-array/src/arrays/varbinview/view.rs | 74 +++- 4 files changed, 413 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 353069cf9b3..9189591e620 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9196,6 +9196,7 @@ dependencies = [ "static_assertions", "tabled", "termtree", + "test-with", "tracing", "uuid", "vortex-array", diff --git a/vortex-array/Cargo.toml b/vortex-array/Cargo.toml index e103097f8b6..d5abd7bef44 100644 --- a/vortex-array/Cargo.toml +++ b/vortex-array/Cargo.toml @@ -95,6 +95,7 @@ rand_distr = { workspace = true } rstest = { workspace = true } serde_json = { workspace = true } serde_test = { workspace = true } +test-with = { workspace = true } vortex-array = { path = ".", features = ["_test-harness", "table-display"] } [[bench]] diff --git a/vortex-array/src/arrays/varbinview/build_views.rs b/vortex-array/src/arrays/varbinview/build_views.rs index 87f5c84706b..4c35aab721a 100644 --- a/vortex-array/src/arrays/varbinview/build_views.rs +++ b/vortex-array/src/arrays/varbinview/build_views.rs @@ -24,15 +24,87 @@ pub fn offsets_to_lengths(offsets: &[P]) -> Buffer

{ /// Maximum number of buffer bytes that can be referenced by a single `BinaryView` pub const MAX_BUFFER_LEN: usize = i32::MAX as usize; -/// Split a large buffer of input `bytes` holding string data +/// Split a large buffer of input `bytes` holding string data into `VarBinView` buffers and views. +/// +/// `max_buffer_len` must not exceed [`MAX_BUFFER_LEN`], since every view offset is stored in a +/// `u32` and offsets are bounded by `max_buffer_len`. pub fn build_views>( start_buf_index: u32, max_buffer_len: usize, - mut bytes: ByteBufferMut, + bytes: ByteBufferMut, + lens: &[P], +) -> (Vec, Buffer) { + assert!( + max_buffer_len <= MAX_BUFFER_LEN, + "max_buffer_len cannot exceed MAX_BUFFER_LEN, offsets must fit in u32" + ); + + if bytes.len() <= max_buffer_len { + // Common case: the whole decoded heap fits within a single buffer, so no rollover can occur + // (`bytes.len()` is the total decoded size and therefore an upper bound on every offset). + build_views_single_buffer(start_buf_index, bytes, lens) + } else { + build_views_rolling(start_buf_index, max_buffer_len, bytes, lens) + } +} + +/// Build views when the whole heap fits in a single output buffer. +/// +/// Because no rollover can occur, the hot loop drops the per-element rollover branch and constructs +/// reference views inline, avoiding the out-of-line `BinaryView::make_view` call for the common +/// long-string case. Every offset is bounded by `bytes.len()`, which the caller has guaranteed is +/// at most [`MAX_BUFFER_LEN`], so the `usize -> u32` conversions cannot truncate. +fn build_views_single_buffer>( + start_buf_index: u32, + bytes: ByteBufferMut, lens: &[P], ) -> (Vec, Buffer) { let mut views = BufferMut::::with_capacity(lens.len()); + let data = bytes.as_slice(); + let mut offset = 0usize; + // Write directly into the reserved spare capacity rather than `push_unchecked`. The latter + // advances the backing buffer's length on every call, which the optimizer cannot prove is + // loop-invariant, so it reloads and rewrites the output cursor through the stack each + // iteration. Writing into the spare slice keeps the cursor in a register and the length is + // set once after the loop. + let spare = views.spare_capacity_mut(); + for (slot, &len) in spare.iter_mut().zip(lens) { + let len = len.as_(); + let value = &data[offset..offset + len]; + let view = if len > BinaryView::MAX_INLINED_SIZE { + let mut prefix = [0u8; 4]; + prefix.copy_from_slice(&value[..4]); + BinaryView::new_ref(len.as_(), prefix, start_buf_index, offset.as_()) + } else { + BinaryView::make_view(value, start_buf_index, offset.as_()) + }; + slot.write(view); + offset += len; + } + // SAFETY: the loop initialized exactly `lens.len()` contiguous views (`spare` has at least + // `lens.len()` slots, and `zip` stops at the shorter operand). + unsafe { views.set_len(lens.len()) }; + + let buffers = if bytes.is_empty() { + Vec::new() + } else { + vec![bytes.freeze()] + }; + (buffers, views.freeze()) +} + +/// Build views when the heap exceeds `max_buffer_len` and must be split across multiple buffers. +/// +/// The buffer is rolled over every `max_buffer_len` bytes so that no view offset overflows the +/// `u32` offset field. +fn build_views_rolling>( + start_buf_index: u32, + max_buffer_len: usize, + mut bytes: ByteBufferMut, + lens: &[P], +) -> (Vec, Buffer) { + let mut views = BufferMut::::with_capacity(lens.len()); let mut buffers = Vec::new(); let mut buf_index = start_buf_index; @@ -66,12 +138,275 @@ pub fn build_views>( #[cfg(test)] mod tests { + use rstest::rstest; use vortex_buffer::ByteBuffer; use vortex_buffer::ByteBufferMut; use crate::arrays::varbinview::BinaryView; + use crate::arrays::varbinview::build_views::MAX_BUFFER_LEN; use crate::arrays::varbinview::build_views::build_views; + /// Concatenate `values` into a single byte heap and return it alongside the per-element lengths, + /// matching the `(bytes, lens)` inputs that `build_views` consumes. + fn flatten(values: &[&[u8]]) -> (ByteBufferMut, Vec) { + let mut bytes = ByteBufferMut::empty(); + let mut lens = Vec::with_capacity(values.len()); + for v in values { + bytes.extend_from_slice(v); + lens.push(u32::try_from(v.len()).unwrap()); + } + (bytes, lens) + } + + /// Reconstruct the logical value behind each view by dereferencing it through the output + /// buffers. The first buffer corresponds to `start_buf_index`, so buffer indices are rebased by + /// that amount. This is the core correctness invariant: regardless of which code path built the + /// views, every view must point back at its original bytes. + fn reconstruct( + buffers: &[ByteBuffer], + views: &[BinaryView], + start_buf_index: u32, + ) -> Vec> { + views + .iter() + .map(|view| { + if view.is_inlined() { + view.as_inlined().value().to_vec() + } else { + let r = view.as_view(); + let buf = &buffers[(r.buffer_index - start_buf_index) as usize]; + buf[r.as_range()].to_vec() + } + }) + .collect() + } + + /// The single-buffer fast path (`bytes.len() <= max_buffer_len`) must reproduce every input + /// value exactly, emit a single output buffer holding the untouched heap, and reference only + /// `start_buf_index`. We cover a spread of value sets that mix inlined (<= 12 bytes) and + /// reference (> 12 bytes) lengths, including the 12/13 byte inline boundary, empty values, and a + /// fully-inlined set. + #[rstest] + #[case::mixed(&[b"a".as_slice(), b"this is a long reference value", b"short", b"another long value here!!"])] + #[case::inline_boundary(&[&[b'x'; 12] as &[u8], &[b'y'; 13], &[b'z'; 12], &[b'w'; 13]])] + #[case::all_inlined(&[b"".as_slice(), b"a", b"bb", b"ccc", b"dddddddddddd"])] + #[case::all_reference(&[&[b'a'; 100] as &[u8], &[b'b'; 50], &[b'c'; 4096]])] + #[case::empty_values_interleaved(&[b"".as_slice(), b"a long value that is referenced", b"", b"", b"trailing long reference value"])] + #[case::single_long(&[&[7u8; 1 << 16] as &[u8]])] + fn fast_path_roundtrip(#[case] values: &[&[u8]]) { + let (bytes, lens) = flatten(values); + let total = bytes.len(); + let start_buf_index = 3; + + // `max_buffer_len` strictly greater than the heap forces the single-buffer fast path. + let (buffers, views) = build_views(start_buf_index, total + 1, bytes, &lens); + + assert_eq!(views.len(), values.len()); + if total == 0 { + assert!(buffers.is_empty(), "empty heap must not allocate a buffer"); + } else { + assert_eq!(buffers.len(), 1, "whole heap must stay in one buffer"); + // The fast path freezes the input heap unchanged. + let concatenated: Vec = values.concat(); + assert_eq!(buffers[0].as_slice(), concatenated.as_slice()); + } + for view in views.iter() { + if !view.is_inlined() { + assert_eq!(view.as_view().buffer_index, start_buf_index); + } + } + + let expected: Vec> = values.iter().map(|v| v.to_vec()).collect(); + assert_eq!(reconstruct(&buffers, &views, start_buf_index), expected); + } + + /// Offsets and sizes are written into the `u32` `Ref` fields via `as_` truncation, so we must + /// confirm they stay correct once the running offset grows well past the 16-bit range (i.e. is + /// not narrowed to a smaller width). A ~9 MiB heap pushes offsets above 2^23 while remaining far + /// below `MAX_BUFFER_LEN`; each value encodes its index in its first bytes so a misplaced offset + /// would reconstruct the wrong bytes. + #[test] + fn fast_path_large_offsets() { + const N: usize = 9000; + const LEN: usize = 1000; + // The final offset is (N - 1) * LEN, which must exceed 2^23 to be a meaningful check. + const _: () = assert!((N - 1) * LEN > (1 << 23)); + + let values: Vec> = (0..N) + .map(|i| { + let mut v = vec![0u8; LEN]; + v[..4].copy_from_slice(&u32::try_from(i).unwrap().to_le_bytes()); + v + }) + .collect(); + let refs: Vec<&[u8]> = values.iter().map(|v| v.as_slice()).collect(); + + let (bytes, lens) = flatten(&refs); + let total = bytes.len(); + + let (buffers, views) = build_views(0, total + 1, bytes, &lens); + + assert_eq!(buffers.len(), 1); + // The recorded offset must equal the cumulative byte position, exactly, for every view. + for (i, view) in views.iter().enumerate() { + let r = view.as_view(); + assert_eq!(r.offset as usize, i * LEN, "wrong offset for view {i}"); + assert_eq!(r.size as usize, LEN); + } + assert_eq!(reconstruct(&buffers, &views, 0), values); + } + + /// The fast path is taken when `bytes.len() <= max_buffer_len`, so equality at the boundary must + /// still produce a single buffer (not roll over to the slow path). + #[test] + fn fast_path_taken_at_exact_boundary() { + let (bytes, lens) = + flatten(&[b"this value is definitely long", b"and so is this one here"]); + let total = bytes.len(); + + let (buffers, views) = build_views(0, total, bytes, &lens); + + assert_eq!( + buffers.len(), + 1, + "len == max_buffer_len must stay on fast path" + ); + assert_eq!(views.len(), 2); + } + + /// For the same logical data, the fast path (single buffer) and the slow rollover path must + /// reconstruct identical values. Driving the slow path with a small `max_buffer_len` forces + /// buffer splitting while leaving the recovered values unchanged. + #[test] + fn fast_and_slow_paths_agree() { + let values: &[&[u8]] = &[ + b"first long reference value", + b"tiny", + b"second long reference value!!", + b"third looooong reference value", + ]; + let expected: Vec> = values.iter().map(|v| v.to_vec()).collect(); + + let (fast_bytes, lens) = flatten(values); + let total = fast_bytes.len(); + let (fast_buffers, fast_views) = build_views(0, total + 1, fast_bytes, &lens); + assert_eq!(fast_buffers.len(), 1); + assert_eq!(reconstruct(&fast_buffers, &fast_views, 0), expected); + + // Force the rollover path: a small cap (>= the longest value) that the total heap exceeds. + let longest = values.iter().map(|v| v.len()).max().unwrap(); + let (slow_bytes, _) = flatten(values); + let (slow_buffers, slow_views) = build_views(0, longest, slow_bytes, &lens); + assert!( + slow_buffers.len() > 1, + "small cap should split into many buffers" + ); + assert_eq!(reconstruct(&slow_buffers, &slow_views, 0), expected); + + // Same logical contents regardless of how the heap was partitioned. + assert_eq!( + reconstruct(&fast_buffers, &fast_views, 0), + reconstruct(&slow_buffers, &slow_views, 0) + ); + } + + /// Empty input must yield no buffers and no views, exercising the `bytes.is_empty()` branch. + #[test] + fn fast_path_empty_input() { + let lens: Vec = Vec::new(); + let (buffers, views) = build_views(0, 1024, ByteBufferMut::empty(), &lens); + assert!(buffers.is_empty()); + assert!(views.is_empty()); + } + + /// The fast path must produce views byte-identical to the value-inspecting `make_view`, which is + /// what the slow path uses. This pins the inline/reference decision and field layout. + #[test] + fn fast_path_matches_make_view() { + let values: &[&[u8]] = &[b"inline", b"this is a long reference value", b""]; + let (bytes, lens) = flatten(values); + let total = bytes.len(); + let (_buffers, views) = build_views(0, total + 1, bytes, &lens); + + let expected = [ + BinaryView::make_view(b"inline", 0, 0), + BinaryView::make_view(b"this is a long reference value", 0, 6), + BinaryView::make_view(b"", 0, 36), + ]; + assert_eq!(views.as_slice(), &expected); + } + + // TODO(someone): ideally CI would run this in release mode as well, since debug builds make the + // ~2.25 GiB allocation and fill loop substantially slower. + /// Slow regression for the single-buffer fast-path guard. The fast path is only valid when the + /// whole heap fits in one buffer (`bytes.len() <= max_buffer_len`); once the heap exceeds + /// [`MAX_BUFFER_LEN`] (`i32::MAX`, ~2.0 GiB) `build_views` must roll the heap into multiple + /// buffers, resetting the per-buffer offset, so no view references an offset past the + /// `i32`-bounded buffer limit. + /// + /// We build a heap just past `i32::MAX` and assert it rolls over into more than one buffer, that + /// no buffer exceeds `MAX_BUFFER_LEN`, and that values straddling the rollover boundary (where + /// the second buffer's offsets restart from zero) reconstruct exactly. If the guard regressed and + /// the fast path swallowed the whole heap, it would emit a single >2 GiB buffer with offsets past + /// `i32::MAX`, which the buffer-count and buffer-size assertions catch. + /// + /// Allocates ~2.25 GiB, so it is gated to CI and skipped when `VORTEX_SKIP_SLOW_TESTS` is set: + /// + /// ```text + /// CI=1 cargo test --release -p vortex-array build_views_offsets_overflow + /// ``` + /// + /// [`MAX_BUFFER_LEN`]: super::MAX_BUFFER_LEN + #[test_with::env(CI)] + #[test_with::no_env(VORTEX_SKIP_SLOW_TESTS)] + fn build_views_offsets_overflow_i32() { + const STRING_LEN: usize = 64 * 1024; + // Comfortably past MAX_BUFFER_LEN (`i32::MAX` ~= 2.0 GiB) so the heap must roll over. + const TOTAL_BYTES: usize = (1usize << 31) + (256 << 20); // ~2.25 GiB + const N: usize = TOTAL_BYTES / STRING_LEN; + + // Each value's first 8 bytes encode its row index, so a misrouted offset is detectable. + let nth_string = |i: usize| { + let mut s = vec![b'x'; STRING_LEN]; + s[..8].copy_from_slice(&(i as u64).to_le_bytes()); + s + }; + + let mut bytes = ByteBufferMut::with_capacity(N * STRING_LEN); + let mut value = vec![b'x'; STRING_LEN]; + for i in 0..N { + value[..8].copy_from_slice(&(i as u64).to_le_bytes()); + bytes.extend_from_slice(&value); + } + + let lens = vec![u32::try_from(STRING_LEN).unwrap(); N]; + let (buffers, views) = build_views(0, MAX_BUFFER_LEN, bytes, &lens); + + assert_eq!(views.len(), N); + assert!( + buffers.len() >= 2, + "heap exceeding MAX_BUFFER_LEN must roll over into multiple buffers, got {}", + buffers.len() + ); + for (i, b) in buffers.iter().enumerate() { + assert!( + b.len() <= MAX_BUFFER_LEN, + "buffer {i} of {} bytes exceeds MAX_BUFFER_LEN", + b.len() + ); + } + + // The boundary row is the first whose offset would cross MAX_BUFFER_LEN on the fast path. + let boundary = MAX_BUFFER_LEN / STRING_LEN; + for i in [0, boundary - 1, boundary, boundary + 1, N / 2, N - 1] { + let view = &views[i]; + let r = view.as_view(); + let got = &buffers[r.buffer_index as usize][r.as_range()]; + assert_eq!(got, nth_string(i).as_slice(), "value mismatch at row {i}"); + assert_eq!(r.size as usize, STRING_LEN); + } + } + #[test] fn test_to_canonical_large() { // We are testing generating views for raw data that should look like @@ -107,4 +442,15 @@ mod tests { ] ) } + + #[test] + #[should_panic(expected = "max_buffer_len cannot exceed MAX_BUFFER_LEN")] + fn test_max_buffer_len_too_large_panics() { + build_views( + 0, + MAX_BUFFER_LEN + 1, + ByteBufferMut::copy_from("abc"), + &[3u32], + ); + } } diff --git a/vortex-array/src/arrays/varbinview/view.rs b/vortex-array/src/arrays/varbinview/view.rs index 38cbd2798c6..1b07baa4576 100644 --- a/vortex-array/src/arrays/varbinview/view.rs +++ b/vortex-array/src/arrays/varbinview/view.rs @@ -150,17 +150,15 @@ impl BinaryView { 12 => Self { inlined: Inlined::new::<12>(value), }, - _ => Self { - _ref: Ref { - size: u32::try_from(value.len()).vortex_expect("value length must fit in u32"), - prefix: value[0..4] - .try_into() - .ok() - .vortex_expect("prefix must be exactly 4 bytes"), - buffer_index: block, - offset, - }, - }, + _ => Self::new_ref( + u32::try_from(value.len()).vortex_expect("value length must fit in u32"), + value[0..4] + .try_into() + .ok() + .vortex_expect("prefix must be exactly 4 bytes"), + block, + offset, + ), } } @@ -170,6 +168,27 @@ impl BinaryView { Self { le_bytes: [0; 16] } } + /// Create a reference view directly from its components, without inspecting the value. + /// + /// `size` must be greater than [`MAX_INLINED_SIZE`], and `prefix` must hold the first four + /// bytes of the value. This is the fast path for bulk view construction where the caller has + /// already established that the value is too long to inline; it assembles the 16-byte view as a + /// single `u128` so the compiler can emit one wide store per view. + /// + /// [`MAX_INLINED_SIZE`]: Self::MAX_INLINED_SIZE + #[inline] + pub fn new_ref(size: u32, prefix: [u8; 4], buffer_index: u32, offset: u32) -> Self { + debug_assert!(size as usize > Self::MAX_INLINED_SIZE); + // Matches the little-endian field order of `Ref` (size, prefix, buffer_index, offset), + // consistent with `le_bytes` and the `From`/`as_u128` representation. + Self::from( + u128::from(size) + | (u128::from(u32::from_le_bytes(prefix)) << 32) + | (u128::from(buffer_index) << 64) + | (u128::from(offset) << 96), + ) + } + /// Create a new inlined binary view /// /// # Panics @@ -278,3 +297,36 @@ impl fmt::Debug for BinaryView { s.finish() } } + +#[cfg(test)] +mod tests { + use super::*; + + #[rstest::rstest] + // Just past the inline boundary, typical, and large values. + #[case(13, 7, 42)] + #[case(20, 7, 42)] + #[case(255, 7, 42)] + #[case(4096, 7, 42)] + // Zero buffer index/offset and the `u32` extremes, to confirm the `u128` field assembly does + // not overflow into neighbouring fields. + #[case(13, 0, 0)] + #[case(13, u32::MAX, u32::MAX)] + fn new_ref_matches_make_view(#[case] len: u32, #[case] buffer_index: u32, #[case] offset: u32) { + // `new_ref` assembles the reference view as a `u128`; it must be byte-identical to the + // value-inspecting `make_view` for any value longer than the inline limit. + let value: Vec = (0..len) + .map(|i| u8::try_from(i % 251).vortex_expect("i % 251 fits in u8")) + .collect(); + let prefix = [value[0], value[1], value[2], value[3]]; + let made = BinaryView::make_view(&value, buffer_index, offset); + let built = BinaryView::new_ref(len, prefix, buffer_index, offset); + assert_eq!(made.as_u128(), built.as_u128(), "mismatch at len {len}"); + assert!(!built.is_inlined()); + let r = built.as_view(); + assert_eq!(r.size, len); + assert_eq!(r.prefix, prefix); + assert_eq!(r.buffer_index, buffer_index); + assert_eq!(r.offset, offset); + } +} From b9908315441d753ee8fc28f8f212d2a5e34f12da Mon Sep 17 00:00:00 2001 From: Alexander Droste Date: Wed, 3 Jun 2026 17:49:17 +0100 Subject: [PATCH 014/115] feat: arrow device array list view export (#8219) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary Adds CUDA Arrow Device export for Vortex `ListViewArray` by exporting it as standard Arrow `List` for cuDF. - GPU export path for contiguous list-views using device-built `i32` Arrow offsets - GPU rebuild path for supported non-contiguous primitive list-views - Host fallback via CPU `ListViewArray` → `ListArray` rebuild when GPU export cannot handle the shape - CUB exclusive-scan wrapper used by the rebuild path - New CUDA kernels, tests, e2e coverage, and benchmarks for list-view export --------- Signed-off-by: Alexander Droste --- vortex-cuda/Cargo.toml | 4 + vortex-cuda/benches/list_view_cuda.rs | 155 +++++++ vortex-cuda/cub/build.rs | 1 + vortex-cuda/cub/kernels/filter.cu | 30 ++ vortex-cuda/cub/kernels/filter.h | 18 + vortex-cuda/cub/src/lib.rs | 1 + vortex-cuda/cub/src/scan.rs | 71 +++ vortex-cuda/kernels/src/list_view.cu | 248 +++++++++++ vortex-cuda/src/arrow/canonical.rs | 597 +++++++++++++++++++++++++- vortex-cuda/src/arrow/list_view.rs | 444 +++++++++++++++++++ vortex-cuda/src/arrow/mod.rs | 33 +- vortex-cuda/src/cub.rs | 49 +++ vortex-cuda/src/lib.rs | 1 + vortex-test/e2e-cuda/src/lib.rs | 162 +++++-- 14 files changed, 1781 insertions(+), 33 deletions(-) create mode 100644 vortex-cuda/benches/list_view_cuda.rs create mode 100644 vortex-cuda/cub/src/scan.rs create mode 100644 vortex-cuda/kernels/src/list_view.cu create mode 100644 vortex-cuda/src/arrow/list_view.rs create mode 100644 vortex-cuda/src/cub.rs diff --git a/vortex-cuda/Cargo.toml b/vortex-cuda/Cargo.toml index d540c54b7e6..25241cc9a02 100644 --- a/vortex-cuda/Cargo.toml +++ b/vortex-cuda/Cargo.toml @@ -104,3 +104,7 @@ harness = false [[bench]] name = "fsst_cuda" harness = false + +[[bench]] +name = "list_view_cuda" +harness = false diff --git a/vortex-cuda/benches/list_view_cuda.rs b/vortex-cuda/benches/list_view_cuda.rs new file mode 100644 index 00000000000..0904844b900 --- /dev/null +++ b/vortex-cuda/benches/list_view_cuda.rs @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: Copyright the Vortex contributors + +//! CUDA benchmarks for Arrow Device export of Vortex list-view arrays. + +#![expect(clippy::cast_possible_truncation)] + +#[allow(dead_code)] +mod bench_config; +mod timed_launch_strategy; + +use std::sync::Arc; +use std::sync::atomic::Ordering; +use std::time::Duration; + +use criterion::BenchmarkId; +use criterion::Criterion; +use criterion::Throughput; +use futures::executor::block_on; +use vortex::array::ArrayRef; +use vortex::array::IntoArray; +use vortex::array::arrays::ListViewArray; +use vortex::array::arrays::PrimitiveArray; +use vortex::array::validity::Validity; +use vortex::dtype::PType; +use vortex::error::VortexExpect; +use vortex::error::VortexResult; +use vortex::session::VortexSession; +use vortex_cuda::CudaExecutionCtx; +use vortex_cuda::CudaSession; +use vortex_cuda::arrow::ArrowDeviceArray; +use vortex_cuda::arrow::DeviceArrayExt; +use vortex_cuda_macros::cuda_available; +use vortex_cuda_macros::cuda_not_available; + +use crate::timed_launch_strategy::TimedLaunchStrategy; + +const LIST_VIEW_CONTIGUOUS_BENCH_SIZES: &[(usize, &str)] = &[(10_000_000, "10M")]; +const LIST_VIEW_REBUILD_BENCH_SIZES: &[(usize, &str)] = &[(10_000_000, "10M")]; + +async fn primitive_i32_on_device( + values: impl IntoIterator, + ctx: &mut CudaExecutionCtx, +) -> VortexResult { + let primitive = PrimitiveArray::from_iter(values); + let handle = ctx + .ensure_on_device(primitive.buffer_handle().clone()) + .await?; + Ok(PrimitiveArray::from_buffer_handle(handle, PType::I32, Validity::NonNullable).into_array()) +} + +async fn contiguous_list_view(len: usize, ctx: &mut CudaExecutionCtx) -> VortexResult { + let elements = primitive_i32_on_device((0..len).map(|value| value as i32), ctx).await?; + let offsets = primitive_i32_on_device((0..len).map(|value| value as i32), ctx).await?; + let sizes = primitive_i32_on_device(std::iter::repeat_n(1i32, len), ctx).await?; + + Ok(ListViewArray::new(elements, offsets, sizes, Validity::NonNullable).into_array()) +} + +async fn non_contiguous_primitive_list_view( + len: usize, + ctx: &mut CudaExecutionCtx, +) -> VortexResult { + let elements = primitive_i32_on_device((0..len).map(|value| value as i32), ctx).await?; + let offsets = primitive_i32_on_device((0..len).rev().map(|value| value as i32), ctx).await?; + let sizes = primitive_i32_on_device(std::iter::repeat_n(1i32, len), ctx).await?; + + Ok(ListViewArray::new(elements, offsets, sizes, Validity::NonNullable).into_array()) +} + +unsafe fn release_arrow_device_array(array: &mut ArrowDeviceArray) { + unsafe { + if let Some(release) = array.array.release { + release(&raw mut array.array); + } + } +} + +fn benchmark_list_view_export(c: &mut Criterion) { + let mut group = c.benchmark_group("cuda"); + + for &(len, len_label) in LIST_VIEW_CONTIGUOUS_BENCH_SIZES { + // Contiguous path reads offsets/sizes and writes Arrow offsets. + group.throughput(Throughput::Bytes((len * size_of::() * 3) as u64)); + group.bench_with_input( + BenchmarkId::new("cuda/list_view/contiguous_offsets", len_label), + &len, + |b, &len| { + b.iter_custom(|iters| { + let timed = TimedLaunchStrategy::default(); + let timer = timed.timer(); + + let mut cuda_ctx = CudaSession::create_execution_ctx(&VortexSession::empty()) + .vortex_expect("failed to create execution context") + .with_launch_strategy(Arc::new(timed)); + let array = block_on(contiguous_list_view(len, &mut cuda_ctx)) + .vortex_expect("failed to create list-view fixture"); + + for _ in 0..iters { + let mut exported = + block_on(array.clone().export_device_array(&mut cuda_ctx)) + .vortex_expect("failed to export device array"); + unsafe { release_arrow_device_array(&mut exported) }; + } + + Duration::from_nanos(timer.load(Ordering::Relaxed)) + }); + }, + ); + } + + for &(len, len_label) in LIST_VIEW_REBUILD_BENCH_SIZES { + // Rebuild path scans sizes into Arrow offsets, then gathers primitive child values. + group.throughput(Throughput::Bytes((len * size_of::() * 4) as u64)); + group.bench_with_input( + BenchmarkId::new("cuda/list_view/rebuild_primitive", len_label), + &len, + |b, &len| { + b.iter_custom(|iters| { + let timed = TimedLaunchStrategy::default(); + let timer = timed.timer(); + + let mut cuda_ctx = CudaSession::create_execution_ctx(&VortexSession::empty()) + .vortex_expect("failed to create execution context") + .with_launch_strategy(Arc::new(timed)); + let array = block_on(non_contiguous_primitive_list_view(len, &mut cuda_ctx)) + .vortex_expect("failed to create list-view fixture"); + + for _ in 0..iters { + let mut exported = + block_on(array.clone().export_device_array(&mut cuda_ctx)) + .vortex_expect("failed to export device array"); + unsafe { release_arrow_device_array(&mut exported) }; + } + + Duration::from_nanos(timer.load(Ordering::Relaxed)) + }); + }, + ); + } + + group.finish(); +} + +criterion::criterion_group! { + name = benches; + config = bench_config::cuda_bench_config(); + targets = benchmark_list_view_export +} + +#[cuda_available] +criterion::criterion_main!(benches); + +#[cuda_not_available] +fn main() {} diff --git a/vortex-cuda/cub/build.rs b/vortex-cuda/cub/build.rs index 16b758a57dd..5888d9234dd 100644 --- a/vortex-cuda/cub/build.rs +++ b/vortex-cuda/cub/build.rs @@ -101,6 +101,7 @@ fn generate_rust_bindings(kernels_dir: &Path, out_dir: &Path) { .allowlist_function("filter_temp_size_.*") .allowlist_function("filter_bytemask_.*") .allowlist_function("filter_bitmask_.*") + .allowlist_function("scan_exclusive_sum_.*") // Allow CUDA types .allowlist_type("cudaError_t") // Blocklist cudaStream_t and define it manually as an opaque pointer diff --git a/vortex-cuda/cub/kernels/filter.cu b/vortex-cuda/cub/kernels/filter.cu index 756bbf3c23f..73726b3a4a4 100644 --- a/vortex-cuda/cub/kernels/filter.cu +++ b/vortex-cuda/cub/kernels/filter.cu @@ -188,3 +188,33 @@ DEFINE_FILTER_BITMASK(f32, float) DEFINE_FILTER_BITMASK(f64, double) DEFINE_FILTER_BITMASK(i128, __int128_t) DEFINE_FILTER_BITMASK(i256, __int256_t) + +// Query CUB temporary storage for an exclusive-sum scan. +template +static cudaError_t scan_exclusive_sum_temp_size_impl(size_t *temp_bytes, int64_t num_items) { + size_t bytes = 0; + cudaError_t err = cub::DeviceScan::ExclusiveSum(nullptr, + bytes, + static_cast(nullptr), + static_cast(nullptr), + num_items); + *temp_bytes = bytes; + return err; +} + +// Export one temp-size query and scan launch per supported element type. +#define DEFINE_SCAN_EXCLUSIVE_SUM(suffix, Type) \ + extern "C" cudaError_t scan_exclusive_sum_##suffix##_temp_size(size_t *temp_bytes, int64_t num_items) { \ + return scan_exclusive_sum_temp_size_impl(temp_bytes, num_items); \ + } \ + extern "C" cudaError_t scan_exclusive_sum_##suffix(void *d_temp, \ + size_t temp_bytes, \ + const Type *d_in, \ + Type *d_out, \ + int64_t num_items, \ + cudaStream_t stream) { \ + return cub::DeviceScan::ExclusiveSum(d_temp, temp_bytes, d_in, d_out, num_items, stream); \ + } + +DEFINE_SCAN_EXCLUSIVE_SUM(i32, int32_t) +DEFINE_SCAN_EXCLUSIVE_SUM(i64, int64_t) diff --git a/vortex-cuda/cub/kernels/filter.h b/vortex-cuda/cub/kernels/filter.h index 354b877dc17..c49dc62faed 100644 --- a/vortex-cuda/cub/kernels/filter.h +++ b/vortex-cuda/cub/kernels/filter.h @@ -81,6 +81,24 @@ FILTER_TYPE_TABLE(DECLARE_FILTER_BITMASK) #undef DECLARE_FILTER_BITMASK +cudaError_t scan_exclusive_sum_i32_temp_size(size_t *temp_bytes, int64_t num_items); + +cudaError_t scan_exclusive_sum_i32(void *d_temp, + size_t temp_bytes, + const int32_t *d_in, + int32_t *d_out, + int64_t num_items, + cudaStream_t stream); + +cudaError_t scan_exclusive_sum_i64_temp_size(size_t *temp_bytes, int64_t num_items); + +cudaError_t scan_exclusive_sum_i64(void *d_temp, + size_t temp_bytes, + const int64_t *d_in, + int64_t *d_out, + int64_t num_items, + cudaStream_t stream); + #ifdef __cplusplus } #endif diff --git a/vortex-cuda/cub/src/lib.rs b/vortex-cuda/cub/src/lib.rs index c0532576604..ae8e3b52b0f 100644 --- a/vortex-cuda/cub/src/lib.rs +++ b/vortex-cuda/cub/src/lib.rs @@ -23,6 +23,7 @@ pub mod sys; mod error; pub mod filter; +pub mod scan; pub use error::CubError; diff --git a/vortex-cuda/cub/src/scan.rs b/vortex-cuda/cub/src/scan.rs new file mode 100644 index 00000000000..56b4e64b857 --- /dev/null +++ b/vortex-cuda/cub/src/scan.rs @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: Copyright the Vortex contributors + +//! Rust wrappers around CUB DeviceScan operations used by CUDA kernels. + +use std::ffi::c_void; + +use crate::cub_library; +use crate::error::CubError; +use crate::error::check_cuda_error; +pub use crate::sys::cudaStream_t; + +/// Get temporary storage size for CUB `DeviceScan::ExclusiveSum`. +pub fn exclusive_sum_i32_temp_size(num_items: i64) -> Result { + let lib = cub_library()?; + let mut temp_bytes: usize = 0; + let err = unsafe { (lib.scan_exclusive_sum_i32_temp_size)(&raw mut temp_bytes, num_items) }; + check_cuda_error(err, "scan_exclusive_sum_i32_temp_size")?; + Ok(temp_bytes) +} + +/// Execute CUB `DeviceScan::ExclusiveSum`. +/// +/// # Safety +/// +/// All device pointers must be valid and properly sized: +/// - `d_temp` must have at least `temp_bytes` bytes allocated. +/// - `d_in` and `d_out` must have at least `num_items` `i32` values. +pub unsafe fn exclusive_sum_i32( + d_temp: *mut c_void, + temp_bytes: usize, + d_in: *const i32, + d_out: *mut i32, + num_items: i64, + stream: cudaStream_t, +) -> Result<(), CubError> { + let lib = cub_library()?; + let err = + unsafe { (lib.scan_exclusive_sum_i32)(d_temp, temp_bytes, d_in, d_out, num_items, stream) }; + check_cuda_error(err, "scan_exclusive_sum_i32") +} + +/// Get temporary storage size for CUB `DeviceScan::ExclusiveSum`. +pub fn exclusive_sum_i64_temp_size(num_items: i64) -> Result { + let lib = cub_library()?; + let mut temp_bytes: usize = 0; + let err = unsafe { (lib.scan_exclusive_sum_i64_temp_size)(&raw mut temp_bytes, num_items) }; + check_cuda_error(err, "scan_exclusive_sum_i64_temp_size")?; + Ok(temp_bytes) +} + +/// Execute CUB `DeviceScan::ExclusiveSum`. +/// +/// # Safety +/// +/// All device pointers must be valid and properly sized: +/// - `d_temp` must have at least `temp_bytes` bytes allocated. +/// - `d_in` and `d_out` must have at least `num_items` `i64` values. +pub unsafe fn exclusive_sum_i64( + d_temp: *mut c_void, + temp_bytes: usize, + d_in: *const i64, + d_out: *mut i64, + num_items: i64, + stream: cudaStream_t, +) -> Result<(), CubError> { + let lib = cub_library()?; + let err = + unsafe { (lib.scan_exclusive_sum_i64)(d_temp, temp_bytes, d_in, d_out, num_items, stream) }; + check_cuda_error(err, "scan_exclusive_sum_i64") +} diff --git a/vortex-cuda/kernels/src/list_view.cu b/vortex-cuda/kernels/src/list_view.cu new file mode 100644 index 00000000000..4223bc976f9 --- /dev/null +++ b/vortex-cuda/kernels/src/list_view.cu @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: Copyright the Vortex contributors + +#include "config.cuh" + +#include +#include + +namespace { + +template +__device__ bool non_negative_to_u64(T value, uint64_t *out) { + if constexpr (std::is_signed_v) { + if (value < 0) { + return false; + } + } + + *out = static_cast(value); + return true; +} + +__device__ bool checked_add_u64(uint64_t lhs, uint64_t rhs, uint64_t *out) { + if (rhs > UINT64_MAX - lhs) { + return false; + } + + *out = lhs + rhs; + return true; +} + +// Assumes `ListViewArray` construction invariants for basic metadata validity. This kernel only +// decides whether the views are already contiguous Arrow `List` offsets and fit cuDF's i32 limit. +template +__device__ void list_view_offsets_device(const OffsetT *const offsets, + const SizeT *const sizes, + int32_t *const output, + uint32_t *const status, + uint64_t list_len) { + const uint64_t worker = blockIdx.x * blockDim.x + threadIdx.x; + const uint64_t startElem = start_elem(worker, list_len); + const uint64_t stopElem = stop_elem(worker, list_len); + + for (uint64_t idx = startElem; idx < stopElem; idx++) { + const uint64_t offset = static_cast(offsets[idx]); + const uint64_t end = offset + static_cast(sizes[idx]); + output[idx] = static_cast(offset); + + if (end < offset || end > static_cast(INT32_MAX)) { + atomicMax(status, 2u); + } + if (idx == 0 && offset != 0) { + atomicMax(status, 1u); + } + + if (idx + 1 == list_len) { + output[list_len] = static_cast(end); + } else if (static_cast(offsets[idx + 1]) != end) { + atomicMax(status, 1u); + } + } +} + +template +__device__ void list_view_rebuild_init_scan_device(const SizeT *const sizes, + int32_t *const scan, + uint32_t *const status, + uint64_t list_len, + uint64_t scan_len) { + const uint64_t worker = blockIdx.x * blockDim.x + threadIdx.x; + const uint64_t startElem = start_elem(worker, scan_len); + const uint64_t stopElem = stop_elem(worker, scan_len); + + for (uint64_t idx = startElem; idx < stopElem; idx++) { + if (idx >= list_len) { + scan[idx] = 0; + continue; + } + + uint64_t size = 0; + if (!non_negative_to_u64(sizes[idx], &size)) { + atomicMax(status, 1u); + scan[idx] = 0; + } else if (size > static_cast(INT32_MAX)) { + atomicMax(status, 2u); + scan[idx] = 0; + } else { + scan[idx] = static_cast(size); + } + } +} + +template +__device__ void list_view_rebuild_validate_offsets_device(const SizeT *const sizes, + const int32_t *const output_offsets, + uint32_t *const status, + uint64_t list_len) { + const uint64_t worker = blockIdx.x * blockDim.x + threadIdx.x; + const uint64_t startElem = start_elem(worker, list_len); + const uint64_t stopElem = stop_elem(worker, list_len); + + for (uint64_t idx = startElem; idx < stopElem; idx++) { + const int32_t offset = output_offsets[idx]; + const int32_t next_offset = output_offsets[idx + 1]; + if (offset < 0 || next_offset < 0) { + atomicMax(status, 2u); + continue; + } + + uint64_t size = 0; + if (!non_negative_to_u64(sizes[idx], &size)) { + atomicMax(status, 1u); + continue; + } + + const int64_t expected_next = static_cast(offset) + static_cast(size); + if (size > static_cast(INT32_MAX) || expected_next != static_cast(next_offset)) { + atomicMax(status, 2u); + } + } +} + +template +__device__ void list_view_rebuild_primitive_device(const OffsetT *const offsets, + const SizeT *const sizes, + const int32_t *const output_offsets, + const uint8_t *const input_values, + uint8_t *const output_values, + uint32_t *const status, + uint64_t list_len, + uint64_t elements_len, + uint64_t value_width) { + const uint64_t worker = blockIdx.x * blockDim.x + threadIdx.x; + const uint64_t startElem = start_elem(worker, list_len); + const uint64_t stopElem = stop_elem(worker, list_len); + + for (uint64_t list_idx = startElem; list_idx < stopElem; list_idx++) { + uint64_t input_offset = 0; + uint64_t size = 0; + if (!non_negative_to_u64(offsets[list_idx], &input_offset) || + !non_negative_to_u64(sizes[list_idx], &size)) { + atomicMax(status, 1u); + continue; + } + + uint64_t input_end = 0; + if (!checked_add_u64(input_offset, size, &input_end) || input_end > elements_len) { + atomicMax(status, 1u); + continue; + } + + const uint64_t output_idx = static_cast(output_offsets[list_idx]); + for (uint64_t element_idx = 0; element_idx < size; element_idx++) { + const uint64_t input_byte = (input_offset + element_idx) * value_width; + const uint64_t output_byte = (output_idx + element_idx) * value_width; + for (uint64_t byte_idx = 0; byte_idx < value_width; byte_idx++) { + output_values[output_byte + byte_idx] = input_values[input_byte + byte_idx]; + } + } + } +} + +} // namespace + +#define GENERATE_VALIDATE_OFFSETS(SizeT, size_suffix) \ + extern "C" __global__ void list_view_rebuild_validate_offsets_##size_suffix( \ + const SizeT *const sizes, \ + const int32_t *const output_offsets, \ + uint32_t *const status, \ + uint64_t list_len) { \ + list_view_rebuild_validate_offsets_device(sizes, output_offsets, status, list_len); \ + } + +#define GENERATE_KERNEL(OffsetT, offset_suffix, SizeT, size_suffix) \ + extern "C" __global__ void list_view_offsets_##offset_suffix##_##size_suffix( \ + const OffsetT *const offsets, \ + const SizeT *const sizes, \ + int32_t *const output, \ + uint32_t *const status, \ + uint64_t list_len) { \ + list_view_offsets_device(offsets, sizes, output, status, list_len); \ + } \ + extern "C" __global__ void list_view_rebuild_primitive_##offset_suffix##_##size_suffix( \ + const OffsetT *const offsets, \ + const SizeT *const sizes, \ + const int32_t *const output_offsets, \ + const uint8_t *const input_values, \ + uint8_t *const output_values, \ + uint32_t *const status, \ + uint64_t list_len, \ + uint64_t elements_len, \ + uint64_t value_width) { \ + list_view_rebuild_primitive_device(offsets, \ + sizes, \ + output_offsets, \ + input_values, \ + output_values, \ + status, \ + list_len, \ + elements_len, \ + value_width); \ + } + +#define GENERATE_INIT_SCAN(SizeT, size_suffix) \ + extern "C" __global__ void list_view_rebuild_init_scan_##size_suffix(const SizeT *const sizes, \ + int32_t *const scan, \ + uint32_t *const status, \ + uint64_t list_len, \ + uint64_t scan_len) { \ + list_view_rebuild_init_scan_device(sizes, scan, status, list_len, scan_len); \ + } + +#define GENERATE_SIZE_KERNELS(OffsetT, offset_suffix) \ + GENERATE_KERNEL(OffsetT, offset_suffix, uint8_t, u8) \ + GENERATE_KERNEL(OffsetT, offset_suffix, uint16_t, u16) \ + GENERATE_KERNEL(OffsetT, offset_suffix, uint32_t, u32) \ + GENERATE_KERNEL(OffsetT, offset_suffix, uint64_t, u64) \ + GENERATE_KERNEL(OffsetT, offset_suffix, int8_t, i8) \ + GENERATE_KERNEL(OffsetT, offset_suffix, int16_t, i16) \ + GENERATE_KERNEL(OffsetT, offset_suffix, int32_t, i32) \ + GENERATE_KERNEL(OffsetT, offset_suffix, int64_t, i64) + +GENERATE_INIT_SCAN(uint8_t, u8) +GENERATE_INIT_SCAN(uint16_t, u16) +GENERATE_INIT_SCAN(uint32_t, u32) +GENERATE_INIT_SCAN(uint64_t, u64) +GENERATE_INIT_SCAN(int8_t, i8) +GENERATE_INIT_SCAN(int16_t, i16) +GENERATE_INIT_SCAN(int32_t, i32) +GENERATE_INIT_SCAN(int64_t, i64) + +GENERATE_VALIDATE_OFFSETS(uint8_t, u8) +GENERATE_VALIDATE_OFFSETS(uint16_t, u16) +GENERATE_VALIDATE_OFFSETS(uint32_t, u32) +GENERATE_VALIDATE_OFFSETS(uint64_t, u64) +GENERATE_VALIDATE_OFFSETS(int8_t, i8) +GENERATE_VALIDATE_OFFSETS(int16_t, i16) +GENERATE_VALIDATE_OFFSETS(int32_t, i32) +GENERATE_VALIDATE_OFFSETS(int64_t, i64) + +GENERATE_SIZE_KERNELS(uint8_t, u8) +GENERATE_SIZE_KERNELS(uint16_t, u16) +GENERATE_SIZE_KERNELS(uint32_t, u32) +GENERATE_SIZE_KERNELS(uint64_t, u64) +GENERATE_SIZE_KERNELS(int8_t, i8) +GENERATE_SIZE_KERNELS(int16_t, i16) +GENERATE_SIZE_KERNELS(int32_t, i32) +GENERATE_SIZE_KERNELS(int64_t, i64) diff --git a/vortex-cuda/src/arrow/canonical.rs b/vortex-cuda/src/arrow/canonical.rs index b8ad7f40ffa..b1bee7c018f 100644 --- a/vortex-cuda/src/arrow/canonical.rs +++ b/vortex-cuda/src/arrow/canonical.rs @@ -8,25 +8,37 @@ use async_trait::async_trait; use futures::future::BoxFuture; use vortex::array::ArrayRef; use vortex::array::Canonical; +use vortex::array::arrays::FixedSizeListArray; +use vortex::array::arrays::ListArray; use vortex::array::arrays::PrimitiveArray; use vortex::array::arrays::StructArray; use vortex::array::arrays::bool::BoolDataParts; use vortex::array::arrays::decimal::DecimalDataParts; use vortex::array::arrays::extension::ExtensionArrayExt; +use vortex::array::arrays::fixed_size_list::FixedSizeListArrayExt; +use vortex::array::arrays::fixed_size_list::FixedSizeListDataParts; +use vortex::array::arrays::list::ListDataParts; +use vortex::array::arrays::listview::list_from_list_view; use vortex::array::arrays::primitive::PrimitiveDataParts; use vortex::array::arrays::struct_::StructDataParts; use vortex::array::arrays::varbinview::VarBinViewDataParts; use vortex::array::buffer::BufferHandle; +use vortex::array::builtins::ArrayBuiltins; use vortex::array::validity::Validity; use vortex::buffer::Buffer; use vortex::buffer::ByteBuffer; +use vortex::dtype::DType; use vortex::dtype::DecimalType; +use vortex::dtype::Nullability; +use vortex::dtype::PType; use vortex::error::VortexResult; use vortex::error::vortex_bail; use vortex::error::vortex_ensure; +use vortex::error::vortex_err; use vortex::extension::datetime::AnyTemporal; use vortex::mask::Mask; +use super::list_view::export_device_list_view; use crate::CudaExecutionCtx; use crate::arrow::ARROW_DEVICE_CUDA; use crate::arrow::ArrowArray; @@ -154,6 +166,25 @@ fn export_canonical( ctx, ) } + Canonical::List(listview) => { + // cuDF imports standard Arrow `List`, while Vortex canonical lists are list-views. + // Try the GPU path first; host list-views can fall back to a CPU rebuild. + let is_host = listview.as_ref().is_host(); + let gpu_err = match export_device_list_view(listview.clone(), ctx).await { + Ok(exported) => return Ok(exported), + Err(err) => err, + }; + + // CPU rebuild requires host-resident buffers; device-resident arrays keep the GPU error. + if !is_host { + return Err(gpu_err); + } + + export_list(list_from_list_view(listview)?, ctx).await + } + Canonical::FixedSizeList(fixed_size_list) => { + export_fixed_size_list(fixed_size_list, ctx).await + } Canonical::VarBinView(varbinview) => { let len = varbinview.len(); let VarBinViewDataParts { @@ -214,7 +245,7 @@ fn export_canonical( /// Export Vortex validity as an Arrow validity byte buffer. /// /// Returns `None` for the buffer when Arrow can omit validity because all rows are valid. -async fn export_arrow_validity_buffer( +pub(super) async fn export_arrow_validity_buffer( validity: Validity, len: usize, arrow_offset: usize, @@ -237,6 +268,140 @@ async fn export_arrow_validity_buffer( Ok((Some(validity), null_count)) } +/// Export a standard Vortex list as Arrow `List`: validity, offsets, and one child array. +async fn export_list( + array: ListArray, + ctx: &mut CudaExecutionCtx, +) -> VortexResult<(ArrowArray, SyncEvent)> { + let len = array.len(); + let ListDataParts { + elements, + offsets, + validity, + .. + } = array.into_data_parts(); + + let (validity_buffer, null_count) = export_arrow_validity_buffer(validity, len, 0, ctx).await?; + let offsets_buffer = export_arrow_list_offsets(offsets, ctx).await?; + + export_list_layout( + elements, + len, + validity_buffer, + null_count, + offsets_buffer, + ctx, + ) + .await +} + +/// Build the shared Arrow `List` parent once offsets and validity are ready on device. +pub(super) async fn export_list_layout( + elements: ArrayRef, + len: usize, + validity_buffer: Option, + null_count: i64, + offsets_buffer: BufferHandle, + ctx: &mut CudaExecutionCtx, +) -> VortexResult<(ArrowArray, SyncEvent)> { + let cuda_elements = elements.execute_cuda(ctx).await?; + let (elements_child, _) = export_canonical(cuda_elements, ctx).await?; + + let mut private_data = PrivateData::new( + vec![validity_buffer, Some(offsets_buffer)], + vec![elements_child], + ctx, + )?; + let sync_event = private_data.sync_event(); + + let mut arrow_list = ArrowArray::empty(); + arrow_list.length = len as i64; + arrow_list.null_count = null_count; + arrow_list.n_buffers = 2; + arrow_list.buffers = private_data.buffer_ptrs.as_mut_ptr(); + arrow_list.n_children = 1; + arrow_list.children = private_data.children.as_mut_ptr(); + arrow_list.release = Some(release_array); + arrow_list.private_data = Box::into_raw(private_data).cast(); + + Ok((arrow_list, sync_event)) +} + +/// Export a Vortex fixed-size-list as Arrow `List`. +/// +/// cuDF's Arrow Device import accepts `List`/`LargeList` as cuDF `LIST`, but rejects +/// `FixedSizeList`, so emit equivalent standard Arrow `List` offsets. +async fn export_fixed_size_list( + array: FixedSizeListArray, + ctx: &mut CudaExecutionCtx, +) -> VortexResult<(ArrowArray, SyncEvent)> { + let len = array.len(); + let list_size = array.list_size(); + let FixedSizeListDataParts { + elements, validity, .. + } = array.into_data_parts(); + + let (validity_buffer, null_count) = export_arrow_validity_buffer(validity, len, 0, ctx).await?; + let offsets_buffer = fixed_size_list_offsets(len, list_size, ctx).await?; + + export_list_layout( + elements, + len, + validity_buffer, + null_count, + offsets_buffer, + ctx, + ) + .await +} + +async fn fixed_size_list_offsets( + len: usize, + list_size: u32, + ctx: &mut CudaExecutionCtx, +) -> VortexResult { + let list_size = i32::try_from(list_size).map_err(|_| { + vortex_err!( + "cannot export FixedSizeList with list size {list_size}: Arrow List offsets require i32" + ) + })?; + let offsets = (0..=i32::try_from(len)?) + .map(|idx| { + idx.checked_mul(list_size) + .ok_or_else(|| vortex_err!("FixedSizeList Arrow List offsets exceed i32 range")) + }) + .collect::>>()?; + + ctx.ensure_on_device(BufferHandle::new_host( + Buffer::from(offsets).into_byte_buffer(), + )) + .await +} + +/// Return cuDF-supported Arrow `List` offsets as an `i32` device buffer. +async fn export_arrow_list_offsets( + offsets: ArrayRef, + ctx: &mut CudaExecutionCtx, +) -> VortexResult { + let offsets = if offsets.dtype().as_ptype() == PType::I32 { + offsets + } else { + offsets.cast(DType::Primitive(PType::I32, Nullability::NonNullable))? + }; + let offsets = offsets.execute_cuda(ctx).await?; + let Canonical::Primitive(offsets) = offsets else { + vortex_bail!("list offsets must be primitive, got {}", offsets.dtype()); + }; + + let PrimitiveDataParts { ptype, buffer, .. } = offsets.into_data_parts(); + vortex_ensure!( + ptype == PType::I32, + "list offsets cast to i32 produced {ptype}" + ); + + ctx.ensure_on_device(buffer).await +} + async fn export_struct( array: StructArray, ctx: &mut CudaExecutionCtx, @@ -362,11 +527,15 @@ mod tests { use vortex::array::IntoArray; use vortex::array::arrays::BoolArray; use vortex::array::arrays::DecimalArray; + use vortex::array::arrays::FixedSizeListArray; + use vortex::array::arrays::ListArray; + use vortex::array::arrays::ListViewArray; use vortex::array::arrays::NullArray; use vortex::array::arrays::PrimitiveArray; use vortex::array::arrays::StructArray; use vortex::array::arrays::TemporalArray; use vortex::array::arrays::VarBinViewArray; + use vortex::array::arrays::primitive::PrimitiveArrayExt; use vortex::array::arrays::varbinview::BinaryView; use vortex::array::validity::Validity; use vortex::buffer::Buffer; @@ -374,10 +543,13 @@ mod tests { use vortex::dtype::DType; use vortex::dtype::DecimalDType; use vortex::dtype::FieldNames; + use vortex::dtype::NativePType; use vortex::dtype::Nullability; + use vortex::dtype::PType; use vortex::dtype::half::f16; use vortex::error::VortexExpect; use vortex::error::VortexResult; + use vortex::error::vortex_bail; use vortex::extension::datetime::TimeUnit; use vortex::session::VortexSession; @@ -521,6 +693,74 @@ mod tests { (array, buffer_lengths) } + async fn primitive_on_device( + values: impl IntoIterator, + ctx: &mut CudaExecutionCtx, + ) -> VortexResult { + let primitive = PrimitiveArray::from_iter(values); + let handle = ctx + .ensure_on_device(primitive.buffer_handle().clone()) + .await?; + Ok( + PrimitiveArray::from_buffer_handle(handle, T::PTYPE, Validity::NonNullable) + .into_array(), + ) + } + + async fn primitive_i32_on_device( + values: impl IntoIterator, + ctx: &mut CudaExecutionCtx, + ) -> VortexResult { + primitive_on_device(values, ctx).await + } + + #[expect(clippy::cast_possible_truncation)] + async fn integer_array_on_device( + ptype: PType, + values: &[i64], + ctx: &mut CudaExecutionCtx, + ) -> VortexResult { + match ptype { + PType::U8 => primitive_on_device(values.iter().map(|&value| value as u8), ctx).await, + PType::U16 => primitive_on_device(values.iter().map(|&value| value as u16), ctx).await, + PType::U32 => primitive_on_device(values.iter().map(|&value| value as u32), ctx).await, + PType::U64 => primitive_on_device(values.iter().map(|&value| value as u64), ctx).await, + PType::I8 => primitive_on_device(values.iter().map(|&value| value as i8), ctx).await, + PType::I16 => primitive_on_device(values.iter().map(|&value| value as i16), ctx).await, + PType::I32 => primitive_on_device(values.iter().map(|&value| value as i32), ctx).await, + PType::I64 => primitive_on_device(values.iter().copied(), ctx).await, + ptype => vortex_bail!("test helper only supports integer PTypes, got {ptype}"), + } + } + + async fn nullable_primitive_i32_on_device( + values: impl IntoIterator>, + ctx: &mut CudaExecutionCtx, + ) -> VortexResult { + let primitive = PrimitiveArray::from_option_iter(values); + let handle = ctx + .ensure_on_device(primitive.buffer_handle().clone()) + .await?; + Ok( + PrimitiveArray::from_buffer_handle(handle, PType::I32, primitive.validity()?) + .into_array(), + ) + } + + fn private_data_buffer_i32_values( + array: &ArrowArray, + buffer_idx: usize, + ) -> VortexResult> { + let private_data = unsafe { &*array.private_data.cast::() }; + let buffer = private_data.buffers[buffer_idx] + .as_ref() + .vortex_expect("buffer should be present"); + Ok(Buffer::::from_byte_buffer(buffer.to_host_sync()) + .iter() + .copied() + .collect()) + } + // Build a nested struct fixture with an out-of-line string-view value. fn nested_struct_array() -> ArrayRef { let nested = StructArray::new( @@ -717,6 +957,154 @@ mod tests { Ok(()) } + #[crate::test] + async fn test_export_list() -> VortexResult<()> { + let mut ctx = CudaSession::create_execution_ctx(&VortexSession::empty()) + .vortex_expect("failed to create execution context"); + + let array = ListArray::try_new( + PrimitiveArray::from_iter(0i32..5).into_array(), + PrimitiveArray::from_iter([0i32, 2, 2, 5]).into_array(), + Validity::NonNullable, + )? + .into_array(); + let mut exported = array.export_device_array_with_schema(&mut ctx).await?; + + let field = Field::try_from(&exported.schema)?; + assert_eq!( + field, + Field::new_list( + "", + Field::new(Field::LIST_FIELD_DEFAULT_NAME, DataType::Int32, false), + false, + ) + ); + assert_eq!(exported.array.array.length, 3); + assert_eq!(exported.array.array.null_count, 0); + assert_eq!(exported.array.array.n_buffers, 2); + let buffers = unsafe { std::slice::from_raw_parts(exported.array.array.buffers, 2) }; + assert!(buffers[0].is_null()); + assert!(!buffers[1].is_null()); + assert_eq!(exported.array.array.n_children, 1); + let children = unsafe { std::slice::from_raw_parts(exported.array.array.children, 1) }; + let elements = unsafe { &*children[0] }; + assert_eq!(elements.length, 5); + assert_eq!(elements.n_buffers, 2); + assert_eq!(exported.array.device_type, ARROW_DEVICE_CUDA); + + unsafe { release_exported_array(&raw mut exported.array.array) }; + Ok(()) + } + + #[crate::test] + async fn test_export_host_contiguous_list_view() -> VortexResult<()> { + let mut ctx = CudaSession::create_execution_ctx(&VortexSession::empty()) + .vortex_expect("failed to create execution context"); + + let array = ListViewArray::new( + PrimitiveArray::from_iter(0i32..5).into_array(), + PrimitiveArray::from_iter([0i32, 2, 2]).into_array(), + PrimitiveArray::from_iter([2i32, 0, 3]).into_array(), + Validity::NonNullable, + ) + .into_array(); + let mut exported = array.export_device_array_with_schema(&mut ctx).await?; + + assert_eq!(exported.array.array.length, 3); + assert_eq!(exported.array.array.n_buffers, 2); + assert_eq!( + private_data_buffer_i32_values(&exported.array.array, 1)?, + [0, 2, 2, 5] + ); + assert_eq!(exported.array.device_type, ARROW_DEVICE_CUDA); + + unsafe { release_exported_array(&raw mut exported.array.array) }; + Ok(()) + } + + #[crate::test] + async fn test_export_host_non_contiguous_nested_list_view_falls_back_to_cpu() -> VortexResult<()> + { + let mut ctx = CudaSession::create_execution_ctx(&VortexSession::empty()) + .vortex_expect("failed to create execution context"); + + let elements = StructArray::new( + FieldNames::from_iter(["x"]), + vec![PrimitiveArray::from_iter(0i32..4).into_array()], + 4, + Validity::NonNullable, + ) + .into_array(); + let array = ListViewArray::new( + elements, + PrimitiveArray::from_iter([0i32, 1]).into_array(), + PrimitiveArray::from_iter([3i32, 2]).into_array(), + Validity::NonNullable, + ) + .into_array(); + let mut exported = array.export_device_array_with_schema(&mut ctx).await?; + + assert_eq!(exported.array.array.length, 2); + assert_eq!( + private_data_buffer_i32_values(&exported.array.array, 1)?, + [0, 3, 5] + ); + let list_children = unsafe { std::slice::from_raw_parts(exported.array.array.children, 1) }; + let struct_child = unsafe { &*list_children[0] }; + assert_eq!(struct_child.length, 5); + let struct_children = unsafe { std::slice::from_raw_parts(struct_child.children, 1) }; + let field_child = unsafe { &*struct_children[0] }; + assert_eq!( + private_data_buffer_i32_values(field_child, 1)?, + [0, 1, 2, 1, 2] + ); + assert_eq!(exported.array.device_type, ARROW_DEVICE_CUDA); + + unsafe { release_exported_array(&raw mut exported.array.array) }; + Ok(()) + } + + #[rstest] + #[case::i32_i32(PType::I32, PType::I32)] + #[case::u32_u16(PType::U32, PType::U16)] + #[case::i64_u8(PType::I64, PType::U8)] + #[case::u64_i16(PType::U64, PType::I16)] + #[crate::test] + async fn test_export_device_contiguous_list_view( + #[case] offsets_ptype: PType, + #[case] sizes_ptype: PType, + ) -> VortexResult<()> { + let mut ctx = CudaSession::create_execution_ctx(&VortexSession::empty()) + .vortex_expect("failed to create execution context"); + + let elements = primitive_i32_on_device(0..5, &mut ctx).await?; + let offsets = integer_array_on_device(offsets_ptype, &[0, 2, 2], &mut ctx).await?; + let sizes = integer_array_on_device(sizes_ptype, &[2, 0, 3], &mut ctx).await?; + let array = + ListViewArray::new(elements, offsets, sizes, Validity::NonNullable).into_array(); + let mut exported = array.export_device_array_with_schema(&mut ctx).await?; + + let field = Field::try_from(&exported.schema)?; + assert_eq!( + field, + Field::new_list( + "", + Field::new(Field::LIST_FIELD_DEFAULT_NAME, DataType::Int32, false), + false, + ) + ); + assert_eq!(exported.array.array.length, 3); + assert_eq!(exported.array.array.n_buffers, 2); + assert_eq!( + private_data_buffer_i32_values(&exported.array.array, 1)?, + [0, 2, 2, 5] + ); + assert_eq!(exported.array.device_type, ARROW_DEVICE_CUDA); + + unsafe { release_exported_array(&raw mut exported.array.array) }; + Ok(()) + } + #[rstest] #[case::utf8( multi_buffer_varbinview(DType::Utf8(Nullability::NonNullable)), @@ -746,6 +1134,213 @@ mod tests { Ok(()) } + #[rstest] + #[case::i64(PrimitiveArray::from_iter([0i64, 2, 2, 5]).into_array())] + #[case::u64(PrimitiveArray::from_iter([0u64, 2, 2, 5]).into_array())] + #[crate::test] + async fn test_export_list_with_non_i32_offsets(#[case] offsets: ArrayRef) -> VortexResult<()> { + let mut ctx = CudaSession::create_execution_ctx(&VortexSession::empty()) + .vortex_expect("failed to create execution context"); + + let array = ListArray::try_new( + PrimitiveArray::from_iter(0i32..5).into_array(), + offsets, + Validity::NonNullable, + )? + .into_array(); + let mut exported = array.export_device_array_with_schema(&mut ctx).await?; + + assert_eq!(exported.array.array.length, 3); + assert_eq!(exported.array.array.n_buffers, 2); + assert_eq!( + private_data_buffer_i32_values(&exported.array.array, 1)?, + [0, 2, 2, 5] + ); + + unsafe { release_exported_array(&raw mut exported.array.array) }; + Ok(()) + } + + #[rstest] + #[case::i32_i32(PType::I32, PType::I32)] + #[case::u32_u16(PType::U32, PType::U16)] + #[case::i64_u8(PType::I64, PType::U8)] + #[case::u64_i16(PType::U64, PType::I16)] + #[crate::test] + async fn test_export_device_non_contiguous_primitive_list_view( + #[case] offsets_ptype: PType, + #[case] sizes_ptype: PType, + ) -> VortexResult<()> { + let mut ctx = CudaSession::create_execution_ctx(&VortexSession::empty()) + .vortex_expect("failed to create execution context"); + + let elements = primitive_i32_on_device([10, 11, 12, 13, 14], &mut ctx).await?; + let offsets = integer_array_on_device(offsets_ptype, &[3, 0, 2], &mut ctx).await?; + let sizes = integer_array_on_device(sizes_ptype, &[2, 2, 1], &mut ctx).await?; + let array = + ListViewArray::new(elements, offsets, sizes, Validity::NonNullable).into_array(); + let mut exported = array.export_device_array_with_schema(&mut ctx).await?; + + assert_eq!(exported.array.array.length, 3); + assert_eq!( + private_data_buffer_i32_values(&exported.array.array, 1)?, + [0, 2, 4, 5] + ); + let children = unsafe { std::slice::from_raw_parts(exported.array.array.children, 1) }; + let elements = unsafe { &*children[0] }; + assert_eq!( + private_data_buffer_i32_values(elements, 1)?, + [13, 14, 10, 11, 12] + ); + assert_eq!(exported.array.device_type, ARROW_DEVICE_CUDA); + + unsafe { release_exported_array(&raw mut exported.array.array) }; + Ok(()) + } + + #[rstest] + #[case::out_of_bounds(&[3], &[2], "offsets/sizes are invalid")] + #[case::negative_offset(&[-1], &[1], "offsets exceed i32 range")] + #[crate::test] + async fn test_export_device_invalid_list_view_returns_error( + #[case] offsets_values: &[i64], + #[case] sizes_values: &[i64], + #[case] expected_error: &str, + ) -> VortexResult<()> { + let mut ctx = CudaSession::create_execution_ctx(&VortexSession::empty()) + .vortex_expect("failed to create execution context"); + + let elements = primitive_i32_on_device(0..4, &mut ctx).await?; + let offsets = integer_array_on_device(PType::I32, offsets_values, &mut ctx).await?; + let sizes = integer_array_on_device(PType::I32, sizes_values, &mut ctx).await?; + let array = unsafe { + ListViewArray::new_unchecked(elements, offsets, sizes, Validity::NonNullable) + } + .into_array(); + let err = match array.export_device_array(&mut ctx).await { + Ok(mut exported) => { + unsafe { release_exported_array(&raw mut exported.array) }; + vortex_bail!("invalid device list view should be unsupported") + } + Err(err) => err, + }; + + assert!( + err.to_string().contains(expected_error), + "unexpected error: {err}" + ); + Ok(()) + } + + #[crate::test] + async fn test_export_device_non_contiguous_nested_list_view_returns_error() -> VortexResult<()> + { + let mut ctx = CudaSession::create_execution_ctx(&VortexSession::empty()) + .vortex_expect("failed to create execution context"); + + let field = primitive_i32_on_device(0..4, &mut ctx).await?; + let elements = StructArray::new( + FieldNames::from_iter(["x"]), + vec![field], + 4, + Validity::NonNullable, + ) + .into_array(); + let offsets = primitive_i32_on_device([0, 1], &mut ctx).await?; + let sizes = primitive_i32_on_device([3, 2], &mut ctx).await?; + let array = + ListViewArray::new(elements, offsets, sizes, Validity::NonNullable).into_array(); + let err = match array.export_device_array(&mut ctx).await { + Ok(mut exported) => { + unsafe { release_exported_array(&raw mut exported.array) }; + vortex_bail!("non-contiguous nested list view should be unsupported") + } + Err(err) => err, + }; + + assert!( + err.to_string() + .contains("GPU child rebuild only supports primitive children"), + "unexpected error: {err}" + ); + Ok(()) + } + + #[crate::test] + async fn test_export_device_non_contiguous_nullable_primitive_list_view_returns_error() + -> VortexResult<()> { + let mut ctx = CudaSession::create_execution_ctx(&VortexSession::empty()) + .vortex_expect("failed to create execution context"); + + let elements = nullable_primitive_i32_on_device( + [Some(10), None, Some(12), Some(13), Some(14)], + &mut ctx, + ) + .await?; + let offsets = primitive_i32_on_device([3, 0, 2], &mut ctx).await?; + let sizes = primitive_i32_on_device([2, 2, 1], &mut ctx).await?; + let array = + ListViewArray::new(elements, offsets, sizes, Validity::NonNullable).into_array(); + let err = match array.export_device_array(&mut ctx).await { + Ok(mut exported) => { + unsafe { release_exported_array(&raw mut exported.array) }; + vortex_bail!("non-contiguous nullable primitive list view should be unsupported") + } + Err(err) => err, + }; + + assert!( + err.to_string() + .contains("GPU child validity rebuild is not implemented"), + "unexpected error: {err}" + ); + Ok(()) + } + + #[crate::test] + async fn test_export_fixed_size_list_as_list() -> VortexResult<()> { + let mut ctx = CudaSession::create_execution_ctx(&VortexSession::empty()) + .vortex_expect("failed to create execution context"); + + let array = FixedSizeListArray::new( + PrimitiveArray::from_iter(0i32..6).into_array(), + 2, + Validity::NonNullable, + 3, + ) + .into_array(); + let mut exported = array.export_device_array_with_schema(&mut ctx).await?; + + let field = Field::try_from(&exported.schema)?; + assert_eq!( + field, + Field::new_list( + "", + Field::new(Field::LIST_FIELD_DEFAULT_NAME, DataType::Int32, false), + false, + ) + ); + assert_eq!(exported.array.array.length, 3); + assert_eq!(exported.array.array.null_count, 0); + assert_eq!(exported.array.array.n_buffers, 2); + let buffers = unsafe { std::slice::from_raw_parts(exported.array.array.buffers, 2) }; + assert!(buffers[0].is_null()); + assert!(!buffers[1].is_null()); + assert_eq!( + private_data_buffer_i32_values(&exported.array.array, 1)?, + [0, 2, 4, 6] + ); + assert_eq!(exported.array.array.n_children, 1); + let children = unsafe { std::slice::from_raw_parts(exported.array.array.children, 1) }; + let elements = unsafe { &*children[0] }; + assert_eq!(elements.length, 6); + assert_eq!(elements.n_buffers, 2); + assert_eq!(exported.array.device_type, ARROW_DEVICE_CUDA); + + unsafe { release_exported_array(&raw mut exported.array.array) }; + Ok(()) + } + // Check device metadata for data-bearing and metadata-only exports. #[crate::test] async fn test_export_device_metadata() -> VortexResult<()> { diff --git a/vortex-cuda/src/arrow/list_view.rs b/vortex-cuda/src/arrow/list_view.rs new file mode 100644 index 00000000000..f8a0be2f047 --- /dev/null +++ b/vortex-cuda/src/arrow/list_view.rs @@ -0,0 +1,444 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: Copyright the Vortex contributors + +//! CUDA Arrow Device export helpers for Vortex `ListViewArray`. + +use std::sync::Arc; + +use cudarc::driver::CudaSlice; +use cudarc::driver::DeviceRepr; +use cudarc::driver::PushKernelArg; +use vortex::array::ArrayRef; +use vortex::array::Canonical; +use vortex::array::IntoArray; +use vortex::array::arrays::ListViewArray; +use vortex::array::arrays::PrimitiveArray; +use vortex::array::arrays::listview::ListViewDataParts; +use vortex::array::arrays::primitive::PrimitiveDataParts; +use vortex::array::buffer::BufferHandle; +use vortex::array::match_each_integer_ptype; +use vortex::buffer::Buffer; +use vortex::dtype::NativePType; +use vortex::dtype::PType; +use vortex::error::VortexResult; +use vortex::error::vortex_bail; +use vortex::error::vortex_ensure; +use vortex::error::vortex_err; + +use super::ArrowArray; +use super::SyncEvent; +use super::canonical::export_arrow_validity_buffer; +use super::canonical::export_list_layout; +use crate::CudaBufferExt; +use crate::CudaDeviceBuffer; +use crate::CudaExecutionCtx; +use crate::cub::exclusive_sum_i32; +use crate::executor::CudaArrayExt; + +/// Export a Vortex list-view as Arrow `List` using device kernels. +/// +/// Contiguous list-views reuse their child elements. Non-contiguous list-views are rebuilt on GPU +/// only when the child is primitive and non-nullable/non-null; other child shapes are rejected. +pub(super) async fn export_device_list_view( + array: ListViewArray, + ctx: &mut CudaExecutionCtx, +) -> VortexResult<(ArrowArray, SyncEvent)> { + let len = array.len(); + let ListViewDataParts { + elements, + offsets, + sizes, + validity, + .. + } = array.into_data_parts(); + + let (validity_buffer, null_count) = export_arrow_validity_buffer(validity, len, 0, ctx).await?; + + let (offsets_ptype, offsets_buffer) = + primitive_device_buffer(offsets, "list offsets", ctx).await?; + let (sizes_ptype, sizes_buffer) = primitive_device_buffer(sizes, "list sizes", ctx).await?; + + match export_device_list_view_offsets( + offsets_ptype, + offsets_buffer.clone(), + sizes_ptype, + sizes_buffer.clone(), + len, + ctx, + ) + .await? + { + DeviceListViewOffsets::Contiguous(offsets_buffer) => { + export_list_layout( + elements, + len, + validity_buffer, + null_count, + offsets_buffer, + ctx, + ) + .await + } + DeviceListViewOffsets::RequiresRebuild => { + export_rebuilt_primitive_list_view( + elements, + offsets_ptype, + offsets_buffer, + sizes_ptype, + sizes_buffer, + len, + validity_buffer, + null_count, + ctx, + ) + .await + } + } +} + +enum DeviceListViewOffsets { + Contiguous(BufferHandle), + RequiresRebuild, +} + +/// Build cuDF-supported `i32` Arrow `List` offsets from list-view offset/size device buffers. +#[expect(clippy::cognitive_complexity)] +async fn export_device_list_view_offsets( + offsets_ptype: PType, + offsets_buffer: BufferHandle, + sizes_ptype: PType, + sizes_buffer: BufferHandle, + len: usize, + ctx: &mut CudaExecutionCtx, +) -> VortexResult { + if len == 0 { + let offsets = ctx + .ensure_on_device(BufferHandle::new_host( + Buffer::from(vec![0i32]).into_byte_buffer(), + )) + .await?; + return Ok(DeviceListViewOffsets::Contiguous(offsets)); + } + + match_each_integer_ptype!(offsets_ptype, |O| { + match_each_integer_ptype!(sizes_ptype, |S| { + export_device_list_view_offsets_typed::(offsets_buffer, sizes_buffer, len, ctx) + .await + }) + }) +} + +async fn rebuild_primitive_list_view_typed( + offsets: BufferHandle, + sizes: BufferHandle, + values: BufferHandle, + elements_len: usize, + list_len: usize, + values_ptype: PType, + ctx: &mut CudaExecutionCtx, +) -> VortexResult<(BufferHandle, BufferHandle)> +where + O: NativePType + DeviceRepr + Send + Sync + 'static, + S: NativePType + DeviceRepr + Send + Sync + 'static, +{ + let status = new_list_view_status(ctx).await?; + let scan_len = list_len + 1; + + let scan_input = init_list_view_rebuild_scan::(&sizes, &status, list_len, ctx)?; + let output_offsets = BufferHandle::new_device(Arc::new(CudaDeviceBuffer::new( + exclusive_sum_i32(&scan_input, scan_len, ctx)?, + ))); + + validate_list_view_rebuild_offsets::(&sizes, &output_offsets, &status, list_len, ctx)?; + check_list_view_rebuild_status(&status).await?; + + let total_values = total_values_from_offsets(&output_offsets, list_len).await?; + let value_width = values_ptype.byte_width(); + let output_values_bytes = total_values + .checked_mul(value_width) + .ok_or_else(|| vortex_err!("rebuilt list child byte length overflow"))?; + + let output_values = gather_rebuilt_primitive_values::( + &offsets, + &sizes, + &values, + &output_offsets, + output_values_bytes, + elements_len, + list_len, + value_width, + &status, + ctx, + )?; + check_list_view_rebuild_status(&status).await?; + + let values = BufferHandle::new_device(Arc::new(CudaDeviceBuffer::new(output_values))) + .slice(0..output_values_bytes); + Ok((output_offsets, values)) +} + +async fn new_list_view_status(ctx: &mut CudaExecutionCtx) -> VortexResult { + ctx.ensure_on_device(BufferHandle::new_host( + Buffer::from(vec![0u32]).into_byte_buffer(), + )) + .await +} + +async fn check_list_view_rebuild_status(status: &BufferHandle) -> VortexResult<()> { + match Buffer::::from_byte_buffer(status.try_to_host()?.await?)[0] { + 0 => Ok(()), + 1 => vortex_bail!( + "cannot export device-resident ListViewArray as Arrow List: offsets/sizes are invalid for the child elements" + ), + 2 => vortex_bail!( + "cannot export device-resident ListViewArray as Arrow List: offsets exceed i32 range required by cuDF" + ), + status => vortex_bail!("unexpected list-view rebuild status {status}"), + } +} + +fn init_list_view_rebuild_scan( + sizes: &BufferHandle, + status: &BufferHandle, + list_len: usize, + ctx: &mut CudaExecutionCtx, +) -> VortexResult> +where + S: NativePType + DeviceRepr + Send + Sync + 'static, +{ + let scan_len = list_len + 1; + let sizes_view = sizes.cuda_view::()?; + let status_view = status.cuda_view::()?; + let list_len_u64 = list_len as u64; + let scan_len_u64 = scan_len as u64; + let scan_input = ctx.device_alloc::(scan_len)?; + let init_kernel = ctx + .load_function_with_suffixes("list_view", &["rebuild_init_scan", &S::PTYPE.to_string()])?; + + ctx.launch_kernel(&init_kernel, scan_len, |args| { + args.arg(&sizes_view) + .arg(&scan_input) + .arg(&status_view) + .arg(&list_len_u64) + .arg(&scan_len_u64); + })?; + + Ok(scan_input) +} + +fn validate_list_view_rebuild_offsets( + sizes: &BufferHandle, + output_offsets: &BufferHandle, + status: &BufferHandle, + list_len: usize, + ctx: &mut CudaExecutionCtx, +) -> VortexResult<()> +where + S: NativePType + DeviceRepr + Send + Sync + 'static, +{ + let sizes_view = sizes.cuda_view::()?; + let output_offsets_view = output_offsets.cuda_view::()?; + let status_view = status.cuda_view::()?; + let list_len_u64 = list_len as u64; + let validate_kernel = ctx.load_function_with_suffixes( + "list_view", + &["rebuild_validate_offsets", &S::PTYPE.to_string()], + )?; + + ctx.launch_kernel(&validate_kernel, list_len, |args| { + args.arg(&sizes_view) + .arg(&output_offsets_view) + .arg(&status_view) + .arg(&list_len_u64); + }) +} + +async fn total_values_from_offsets( + output_offsets: &BufferHandle, + list_len: usize, +) -> VortexResult { + let total_values = Buffer::::from_byte_buffer( + output_offsets + .slice_typed::(list_len..list_len + 1) + .try_to_host()? + .await?, + )[0]; + + usize::try_from(total_values).map_err(Into::into) +} + +#[expect(clippy::too_many_arguments)] +fn gather_rebuilt_primitive_values( + offsets: &BufferHandle, + sizes: &BufferHandle, + values: &BufferHandle, + output_offsets: &BufferHandle, + output_values_bytes: usize, + elements_len: usize, + list_len: usize, + value_width: usize, + status: &BufferHandle, + ctx: &mut CudaExecutionCtx, +) -> VortexResult> +where + O: NativePType + DeviceRepr + Send + Sync + 'static, + S: NativePType + DeviceRepr + Send + Sync + 'static, +{ + let offsets_view = offsets.cuda_view::()?; + let sizes_view = sizes.cuda_view::()?; + let values_view = values.cuda_view::()?; + let output_offsets_view = output_offsets.cuda_view::()?; + let output_values = ctx.device_alloc::(output_values_bytes.max(1))?; + let status_view = status.cuda_view::()?; + let list_len_u64 = list_len as u64; + let elements_len_u64 = elements_len as u64; + let value_width_u64 = value_width as u64; + let rebuild_kernel = ctx.load_function_with_suffixes( + "list_view", + &[ + "rebuild_primitive", + &O::PTYPE.to_string(), + &S::PTYPE.to_string(), + ], + )?; + + ctx.launch_kernel(&rebuild_kernel, list_len, |args| { + args.arg(&offsets_view) + .arg(&sizes_view) + .arg(&output_offsets_view) + .arg(&values_view) + .arg(&output_values) + .arg(&status_view) + .arg(&list_len_u64) + .arg(&elements_len_u64) + .arg(&value_width_u64); + })?; + + Ok(output_values) +} + +#[expect(clippy::cognitive_complexity, clippy::too_many_arguments)] +async fn export_rebuilt_primitive_list_view( + elements: ArrayRef, + offsets_ptype: PType, + offsets_buffer: BufferHandle, + sizes_ptype: PType, + sizes_buffer: BufferHandle, + len: usize, + validity_buffer: Option, + null_count: i64, + ctx: &mut CudaExecutionCtx, +) -> VortexResult<(ArrowArray, SyncEvent)> { + let canonical_elements = elements.execute_cuda(ctx).await?; + let Canonical::Primitive(elements) = canonical_elements else { + vortex_bail!( + "cannot export non-contiguous device-resident ListViewArray with {} child: GPU child rebuild only supports primitive children", + canonical_elements.dtype() + ); + }; + let elements_len = elements.len(); + let PrimitiveDataParts { + ptype, + buffer, + validity, + .. + } = elements.into_data_parts(); + + vortex_ensure!( + validity.no_nulls(), + "cannot export non-contiguous device-resident ListViewArray with nullable primitive child: GPU child validity rebuild is not implemented" + ); + + let values_buffer = ctx.ensure_on_device(buffer).await?; + let (offsets_buffer, values_buffer) = match_each_integer_ptype!(offsets_ptype, |O| { + match_each_integer_ptype!(sizes_ptype, |S| { + rebuild_primitive_list_view_typed::( + offsets_buffer, + sizes_buffer, + values_buffer, + elements_len, + len, + ptype, + ctx, + ) + .await + }) + })?; + + let rebuilt_elements = + PrimitiveArray::from_buffer_handle(values_buffer, ptype, validity).into_array(); + + export_list_layout( + rebuilt_elements, + len, + validity_buffer, + null_count, + offsets_buffer, + ctx, + ) + .await +} + +async fn primitive_device_buffer( + array: ArrayRef, + name: &str, + ctx: &mut CudaExecutionCtx, +) -> VortexResult<(PType, BufferHandle)> { + let canonical = array.execute_cuda(ctx).await?; + let Canonical::Primitive(primitive) = canonical else { + vortex_bail!("{name} must be primitive, got {}", canonical.dtype()); + }; + + let PrimitiveDataParts { ptype, buffer, .. } = primitive.into_data_parts(); + vortex_ensure!(ptype.is_int(), "{name} must have integer type, got {ptype}"); + + Ok((ptype, ctx.ensure_on_device(buffer).await?)) +} + +async fn export_device_list_view_offsets_typed( + offsets: BufferHandle, + sizes: BufferHandle, + len: usize, + ctx: &mut CudaExecutionCtx, +) -> VortexResult +where + O: NativePType + DeviceRepr + Send + Sync + 'static, + S: NativePType + DeviceRepr + Send + Sync + 'static, +{ + let output_len = len + 1; + let output = ctx.device_alloc::(output_len)?; + + let status = ctx + .ensure_on_device(BufferHandle::new_host( + Buffer::from(vec![0u32]).into_byte_buffer(), + )) + .await?; + + let offsets_view = offsets.cuda_view::()?; + let sizes_view = sizes.cuda_view::()?; + let status_view = status.cuda_view::()?; + let list_len_u64 = len as u64; + + let kernel = ctx.load_function_with_suffixes( + "list_view", + &["offsets", &O::PTYPE.to_string(), &S::PTYPE.to_string()], + )?; + ctx.launch_kernel(&kernel, len, |args| { + args.arg(&offsets_view) + .arg(&sizes_view) + .arg(&output) + .arg(&status_view) + .arg(&list_len_u64); + })?; + + match Buffer::::from_byte_buffer(status.try_to_host()?.await?)[0] { + 0 => Ok(DeviceListViewOffsets::Contiguous(BufferHandle::new_device( + Arc::new(CudaDeviceBuffer::new(output)), + ))), + 1 => Ok(DeviceListViewOffsets::RequiresRebuild), + 2 => vortex_bail!( + "cannot export device-resident ListViewArray as Arrow List: offsets exceed i32 range required by cuDF" + ), + status => vortex_bail!("unexpected list-view offsets kernel status {status}"), + } +} diff --git a/vortex-cuda/src/arrow/mod.rs b/vortex-cuda/src/arrow/mod.rs index 3739a12e6ec..e21383d96e7 100644 --- a/vortex-cuda/src/arrow/mod.rs +++ b/vortex-cuda/src/arrow/mod.rs @@ -9,6 +9,7 @@ //! More documentation at mod canonical; +mod list_view; use std::ffi::c_void; use std::fmt::Debug; @@ -25,6 +26,7 @@ use vortex::array::ArrayRef; use vortex::array::arrow::ArrowSessionExt; use vortex::array::buffer::BufferHandle; use vortex::dtype::DType; +use vortex::dtype::StructFields; use vortex::error::VortexResult; use vortex::error::vortex_err; @@ -208,16 +210,37 @@ fn arrow_schema_for_array( ctx: &mut CudaExecutionCtx, ) -> VortexResult { let arrow = ctx.execution_ctx().session().arrow(); - match array.dtype() { - DType::Struct(..) => Ok(FFI_ArrowSchema::try_from( - arrow.to_arrow_schema(array.dtype())?, - )?), + let dtype = arrow_device_export_dtype(array.dtype()); + match &dtype { + DType::Struct(..) => Ok(FFI_ArrowSchema::try_from(arrow.to_arrow_schema(&dtype)?)?), _ => Ok(FFI_ArrowSchema::try_from( - arrow.to_arrow_field("", array.dtype())?, + arrow.to_arrow_field("", &dtype)?, )?), } } +fn arrow_device_export_dtype(dtype: &DType) -> DType { + match dtype { + DType::List(element, nullability) => { + DType::List(Arc::new(arrow_device_export_dtype(element)), *nullability) + } + DType::FixedSizeList(element, _, nullability) => { + DType::List(Arc::new(arrow_device_export_dtype(element)), *nullability) + } + DType::Struct(fields, nullability) => DType::Struct( + StructFields::new( + fields.names().clone(), + fields + .fields() + .map(|dtype| arrow_device_export_dtype(&dtype)) + .collect(), + ), + *nullability, + ), + dtype => dtype.clone(), + } +} + /// A type that can convert a Vortex array into an [`ArrowDeviceArray`]. #[async_trait] pub trait ExportDeviceArray: Debug + Send + Sync + 'static { diff --git a/vortex-cuda/src/cub.rs b/vortex-cuda/src/cub.rs new file mode 100644 index 00000000000..4b6009e7cea --- /dev/null +++ b/vortex-cuda/src/cub.rs @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: Copyright the Vortex contributors + +//! CUDA wrappers around CUB scan primitives. + +use std::ffi::c_void; + +use cudarc::driver::CudaSlice; +use cudarc::driver::DevicePtr; +use cudarc::driver::DevicePtrMut; +use vortex::error::VortexResult; +use vortex::error::vortex_err; +use vortex_cub::scan; +use vortex_cub::scan::cudaStream_t; + +use crate::CudaExecutionCtx; + +pub(crate) fn exclusive_sum_i32( + input: &CudaSlice, + len: usize, + ctx: &mut CudaExecutionCtx, +) -> VortexResult> { + let len_i64 = i64::try_from(len)?; + let temp_bytes = scan::exclusive_sum_i32_temp_size(len_i64) + .map_err(|err| vortex_err!("CUB scan_exclusive_sum_i32_temp_size failed: {err}"))?; + + let mut temp = ctx.device_alloc::(temp_bytes.max(1))?; + let mut output = ctx.device_alloc::(len)?; + let stream = ctx.stream(); + let stream_ptr = stream.cu_stream() as cudaStream_t; + let (input_ptr, record_input) = input.device_ptr(stream); + let (output_ptr, record_output) = output.device_ptr_mut(stream); + let (temp_ptr, record_temp) = temp.device_ptr_mut(stream); + + ctx.launch_external(len, || unsafe { + scan::exclusive_sum_i32( + temp_ptr as *mut c_void, + temp_bytes, + input_ptr as *const i32, + output_ptr as *mut i32, + len_i64, + stream_ptr, + ) + .map_err(|err| vortex_err!("CUB scan_exclusive_sum_i32 failed: {err}")) + })?; + drop((record_input, record_output, record_temp)); + + Ok(output) +} diff --git a/vortex-cuda/src/lib.rs b/vortex-cuda/src/lib.rs index d2227579d9f..9e9de33a288 100644 --- a/vortex-cuda/src/lib.rs +++ b/vortex-cuda/src/lib.rs @@ -9,6 +9,7 @@ use tracing::info; pub mod arrow; mod canonical; +mod cub; mod device_buffer; mod device_read_at; pub mod dynamic_dispatch; diff --git a/vortex-test/e2e-cuda/src/lib.rs b/vortex-test/e2e-cuda/src/lib.rs index 0a8ca91f021..11a540ff70e 100644 --- a/vortex-test/e2e-cuda/src/lib.rs +++ b/vortex-test/e2e-cuda/src/lib.rs @@ -1,20 +1,13 @@ // SPDX-License-Identifier: Apache-2.0 // SPDX-FileCopyrightText: Copyright the Vortex contributors -//! This file is a simple C-compatible API that is called from the cudf-test-harness at CI time. -//! -//! The flow is: -//! -//! * test harness calls `dlopen` in this library -//! * invokes the `export_array` function to get back the device array -//! * pass the arrays to `cudf`'s `from_arrow_device_column` -//! * run some operations on the loaded column view -//! * call `array->release()` to drop the data allocated from the Rust side - -#![expect(clippy::unwrap_used, clippy::expect_used)] +//! C ABI used by `cudf-test-harness` to export and validate Arrow Device data in CI. + +#![expect(clippy::expect_used)] use std::env; use std::mem; +use std::panic; use std::sync::Arc; use std::sync::LazyLock; @@ -27,6 +20,7 @@ use arrow_array::cast::AsArray; use arrow_array::ffi::FFI_ArrowArray; use arrow_array::ffi::from_ffi; use arrow_array::make_array; +use arrow_schema::DataType; use arrow_schema::Field; use arrow_schema::Fields; use arrow_schema::ffi::FFI_ArrowSchema; @@ -35,6 +29,8 @@ use vortex::array::ArrayRef as VortexArrayRef; use vortex::array::IntoArray; use vortex::array::VortexSessionExecute; use vortex::array::arrays::DecimalArray; +use vortex::array::arrays::FixedSizeListArray; +use vortex::array::arrays::ListArray; use vortex::array::arrays::PrimitiveArray; use vortex::array::arrays::StructArray; use vortex::array::arrays::TemporalArray; @@ -94,14 +90,54 @@ fn primitive_array() -> Result { }) } +fn list_array() -> VortexArrayRef { + ListArray::try_new( + PrimitiveArray::from_iter([10i32, 11, 12, 13, 14]).into_array(), + PrimitiveArray::from_iter([0i32, 2, 2, 5, 5, 5]).into_array(), + Validity::from_iter([true, false, true, true, false]), + ) + .expect("list array") + .into_array() +} + +fn fixed_size_list_array() -> VortexArrayRef { + FixedSizeListArray::new( + PrimitiveArray::from_iter(20i32..30).into_array(), + 2, + Validity::from_iter([true, false, true, true, false]), + 5, + ) + .into_array() +} + +fn fixed_size_list_as_list_array() -> VortexArrayRef { + ListArray::try_new( + PrimitiveArray::from_iter(20i32..30).into_array(), + PrimitiveArray::from_iter([0i32, 2, 4, 6, 8, 10]).into_array(), + Validity::from_iter([true, false, true, true, false]), + ) + .expect("fixed-size-list as list array") + .into_array() +} + /// # Safety -/// called by C++ code. +/// `schema_ptr` and `array_ptr` must be valid writable pointers. #[unsafe(no_mangle)] pub unsafe extern "C" fn export_array( schema_ptr: &mut FFI_ArrowSchema, array_ptr: &mut ArrowDeviceArray, ) -> i32 { - let mut ctx = CudaSession::create_execution_ctx(&SESSION).unwrap(); + ffi_boundary("export_array", || export_array_inner(schema_ptr, array_ptr)) +} + +fn export_array_inner(schema_ptr: &mut FFI_ArrowSchema, array_ptr: &mut ArrowDeviceArray) -> i32 { + let mut ctx = match CudaSession::create_execution_ctx(&SESSION) { + Ok(ctx) => ctx, + Err(err) => { + eprintln!("error creating CUDA execution context: {err}"); + return 1; + } + }; let primitive = match primitive_array() { Ok(array) => array, @@ -128,12 +164,21 @@ pub unsafe extern "C" fn export_array( ); let array = StructArray::new( - FieldNames::from_iter(["prims", "decimals", "strings", "dates"]), + FieldNames::from_iter([ + "prims", + "decimals", + "strings", + "dates", + "lists", + "fixed_lists", + ]), vec![ primitive, decimal.into_array(), strings.into_array(), dates.into_array(), + list_array(), + fixed_size_list_array(), ], 5, Validity::NonNullable, @@ -154,16 +199,38 @@ pub unsafe extern "C" fn export_array( } /// # Safety -/// called by C++ code. +/// `ffi_schema` and `ffi_array` must describe a valid Arrow C Data array. #[unsafe(no_mangle)] pub unsafe extern "C" fn validate_array( ffi_schema: &FFI_ArrowSchema, ffi_array: &mut FFI_ArrowArray, ) -> i32 { - // SAFETY: the provided pointers must not be null, and must point at valid FFI Arrow types. + ffi_boundary("validate_array", || { + validate_array_inner(ffi_schema, ffi_array) + }) +} + +fn ffi_boundary(name: &str, f: impl FnOnce() -> i32) -> i32 { + match panic::catch_unwind(panic::AssertUnwindSafe(f)) { + Ok(code) => code, + Err(_) => { + eprintln!("panic in {name}"); + 1 + } + } +} + +fn validate_array_inner(ffi_schema: &FFI_ArrowSchema, ffi_array: &mut FFI_ArrowArray) -> i32 { + // SAFETY: guaranteed by the C ABI contract. let array_data = unsafe { let ffi_array = mem::replace(ffi_array, FFI_ArrowArray::empty()); - from_ffi(ffi_array, ffi_schema).expect("from_ffi failed") + match from_ffi(ffi_array, ffi_schema) { + Ok(array_data) => array_data, + Err(err) => { + eprintln!("from_ffi failed: {err}"); + return 1; + } + } }; let array = make_array(array_data); @@ -188,31 +255,72 @@ pub unsafe extern "C" fn validate_array( None, ]); let date = Date32Array::from(vec![Some(100i32), None, Some(300), Some(400), None]); + let list = SESSION + .arrow() + .execute_arrow(list_array(), None, &mut SESSION.create_execution_ctx()) + .expect("expected list Arrow array"); + let fixed_size_list = SESSION + .arrow() + .execute_arrow( + fixed_size_list_as_list_array(), + None, + &mut SESSION.create_execution_ctx(), + ) + .expect("expected fixed-size-list-as-list Arrow array"); let expected_fields = Fields::from_iter([ Field::new("prims", primitive.data_type().clone(), true), Field::new("decimals", decimal.data_type().clone(), true), Field::new("strings", string.data_type().clone(), true), Field::new("dates", date.data_type().clone(), true), + cudf_list_field("lists"), + cudf_list_field("fixed_lists"), ]); + if &expected_fields != struct_array.fields() { + eprintln!("wrong fields for host array"); + return 1; + } - assert_eq!( - &expected_fields, - struct_array.fields(), - "wrong fields for host array: {:?}", - struct_array.fields() - ); - - let expected_fields: [ArrowArrayRef; _] = [ + let expected_arrays: [ArrowArrayRef; 4] = [ primitive, Arc::new(decimal), Arc::new(string), Arc::new(date), ]; - for (expected, actual) in expected_fields.iter().zip(struct_array.columns()) { - assert_eq!(expected.as_ref(), actual.as_ref()); + for (idx, (expected, actual)) in expected_arrays + .iter() + .zip(struct_array.columns()) + .enumerate() + { + if expected.as_ref() != actual.as_ref() { + eprintln!("wrong values for host column {idx}"); + return 1; + } + } + + if !list_values_eq(list.as_ref(), struct_array.column(4).as_ref()) { + eprintln!("wrong values for lists column"); + return 1; + } + if !list_values_eq(fixed_size_list.as_ref(), struct_array.column(5).as_ref()) { + eprintln!("wrong values for fixed_lists column"); + return 1; } 0 } + +fn cudf_list_field(name: &str) -> Field { + Field::new_list(name, Field::new("element", DataType::Int32, false), true) +} + +fn list_values_eq(expected: &dyn Array, actual: &dyn Array) -> bool { + let expected = expected.as_list::(); + let actual = actual.as_list::(); + + expected.len() == actual.len() + && expected.value_offsets() == actual.value_offsets() + && (0..expected.len()).all(|idx| expected.is_null(idx) == actual.is_null(idx)) + && expected.values().as_ref() == actual.values().as_ref() +} From 15521354b34563a6cec85864b638578d92d0c398 Mon Sep 17 00:00:00 2001 From: Mikhail Kot Date: Wed, 3 Jun 2026 18:14:11 +0100 Subject: [PATCH 015/115] CachedIds for functions (#8240) Re-creating interner IDs is an issue for random access Signed-off-by: Mikhail Kot --- vortex-array/src/scalar_fn/fns/between/mod.rs | 4 +++- vortex-array/src/scalar_fn/fns/binary/mod.rs | 4 +++- vortex-array/src/scalar_fn/fns/case_when.rs | 4 +++- vortex-array/src/scalar_fn/fns/cast/mod.rs | 4 +++- vortex-array/src/scalar_fn/fns/dynamic.rs | 4 +++- .../src/scalar_fn/fns/fill_null/mod.rs | 4 +++- vortex-array/src/scalar_fn/fns/get_item.rs | 4 +++- vortex-array/src/scalar_fn/fns/is_not_null.rs | 4 +++- vortex-array/src/scalar_fn/fns/is_null.rs | 4 +++- vortex-array/src/scalar_fn/fns/like/mod.rs | 4 +++- .../src/scalar_fn/fns/list_contains/mod.rs | 4 +++- vortex-array/src/scalar_fn/fns/literal.rs | 4 +++- vortex-array/src/scalar_fn/fns/mask/mod.rs | 4 +++- vortex-array/src/scalar_fn/fns/merge.rs | 4 +++- vortex-array/src/scalar_fn/fns/not/mod.rs | 4 +++- vortex-array/src/scalar_fn/fns/pack.rs | 4 +++- vortex-array/src/scalar_fn/fns/root.rs | 4 +++- vortex-array/src/scalar_fn/fns/select.rs | 4 +++- vortex-array/src/scalar_fn/fns/stat.rs | 4 +++- .../src/scalar_fn/fns/variant_get/mod.rs | 4 +++- vortex-array/src/scalar_fn/fns/zip/mod.rs | 4 +++- .../src/scalar_fn/internal/row_count.rs | 4 +++- vortex-layout/src/layouts/row_idx/expr.rs | 4 +++- vortex-layout/src/layouts/struct_/reader.rs | 22 ++++++++----------- .../src/scalar_fns/cosine_similarity.rs | 4 +++- vortex-tensor/src/scalar_fns/inner_product.rs | 4 +++- vortex-tensor/src/scalar_fns/l2_denorm.rs | 4 +++- vortex-tensor/src/scalar_fns/l2_norm.rs | 4 +++- .../src/scalar_fns/sorf_transform/vtable.rs | 4 +++- vortex-turboquant/src/scalar_fns/decode.rs | 4 +++- vortex-turboquant/src/scalar_fns/encode.rs | 4 +++- 31 files changed, 99 insertions(+), 43 deletions(-) diff --git a/vortex-array/src/scalar_fn/fns/between/mod.rs b/vortex-array/src/scalar_fn/fns/between/mod.rs index 1c3dd0eef4f..0e0d9949195 100644 --- a/vortex-array/src/scalar_fn/fns/between/mod.rs +++ b/vortex-array/src/scalar_fn/fns/between/mod.rs @@ -13,6 +13,7 @@ use vortex_error::VortexResult; use vortex_error::vortex_bail; use vortex_proto::expr as pb; use vortex_session::VortexSession; +use vortex_session::registry::CachedId; use crate::ArrayRef; use crate::Canonical; @@ -176,7 +177,8 @@ impl ScalarFnVTable for Between { type Options = BetweenOptions; fn id(&self) -> ScalarFnId { - ScalarFnId::new("vortex.between") + static ID: CachedId = CachedId::new("vortex.between"); + *ID } fn serialize(&self, instance: &Self::Options) -> VortexResult>> { diff --git a/vortex-array/src/scalar_fn/fns/binary/mod.rs b/vortex-array/src/scalar_fn/fns/binary/mod.rs index 374d34d1ec9..1c860cb75b5 100644 --- a/vortex-array/src/scalar_fn/fns/binary/mod.rs +++ b/vortex-array/src/scalar_fn/fns/binary/mod.rs @@ -12,6 +12,7 @@ use vortex_error::VortexResult; use vortex_error::vortex_bail; use vortex_proto::expr as pb; use vortex_session::VortexSession; +use vortex_session::registry::CachedId; use crate::ArrayRef; use crate::ExecutionCtx; @@ -52,7 +53,8 @@ impl ScalarFnVTable for Binary { type Options = Operator; fn id(&self) -> ScalarFnId { - ScalarFnId::new("vortex.binary") + static ID: CachedId = CachedId::new("vortex.binary"); + *ID } fn serialize(&self, instance: &Self::Options) -> VortexResult>> { diff --git a/vortex-array/src/scalar_fn/fns/case_when.rs b/vortex-array/src/scalar_fn/fns/case_when.rs index e018afee35a..0b14a3eafb7 100644 --- a/vortex-array/src/scalar_fn/fns/case_when.rs +++ b/vortex-array/src/scalar_fn/fns/case_when.rs @@ -22,6 +22,7 @@ use vortex_mask::AllOr; use vortex_mask::Mask; use vortex_proto::expr as pb; use vortex_session::VortexSession; +use vortex_session::registry::CachedId; use crate::ArrayRef; use crate::ExecutionCtx; @@ -79,7 +80,8 @@ impl ScalarFnVTable for CaseWhen { type Options = CaseWhenOptions; fn id(&self) -> ScalarFnId { - ScalarFnId::new("vortex.case_when") + static ID: CachedId = CachedId::new("vortex.case_when"); + *ID } fn serialize(&self, _options: &Self::Options) -> VortexResult>> { diff --git a/vortex-array/src/scalar_fn/fns/cast/mod.rs b/vortex-array/src/scalar_fn/fns/cast/mod.rs index a007788acb2..abc59af2c9a 100644 --- a/vortex-array/src/scalar_fn/fns/cast/mod.rs +++ b/vortex-array/src/scalar_fn/fns/cast/mod.rs @@ -12,6 +12,7 @@ use vortex_error::vortex_bail; use vortex_error::vortex_err; use vortex_proto::expr as pb; use vortex_session::VortexSession; +use vortex_session::registry::CachedId; use crate::AnyColumnar; use crate::ArrayRef; @@ -53,7 +54,8 @@ impl ScalarFnVTable for Cast { type Options = DType; fn id(&self) -> ScalarFnId { - ScalarFnId::new("vortex.cast") + static ID: CachedId = CachedId::new("vortex.cast"); + *ID } fn serialize(&self, dtype: &DType) -> VortexResult>> { diff --git a/vortex-array/src/scalar_fn/fns/dynamic.rs b/vortex-array/src/scalar_fn/fns/dynamic.rs index e25117b36bc..7efebf79220 100644 --- a/vortex-array/src/scalar_fn/fns/dynamic.rs +++ b/vortex-array/src/scalar_fn/fns/dynamic.rs @@ -12,6 +12,7 @@ use parking_lot::Mutex; use vortex_error::VortexExpect; use vortex_error::VortexResult; use vortex_error::vortex_bail; +use vortex_session::registry::CachedId; use crate::ArrayRef; use crate::ExecutionCtx; @@ -46,7 +47,8 @@ impl ScalarFnVTable for DynamicComparison { type Options = DynamicComparisonExpr; fn id(&self) -> ScalarFnId { - ScalarFnId::from("vortex.dynamic") + static ID: CachedId = CachedId::new("vortex.dynamic"); + *ID } fn arity(&self, _options: &Self::Options) -> Arity { diff --git a/vortex-array/src/scalar_fn/fns/fill_null/mod.rs b/vortex-array/src/scalar_fn/fns/fill_null/mod.rs index bde1694fdb5..b2d07b41255 100644 --- a/vortex-array/src/scalar_fn/fns/fill_null/mod.rs +++ b/vortex-array/src/scalar_fn/fns/fill_null/mod.rs @@ -9,6 +9,7 @@ use vortex_error::vortex_bail; use vortex_error::vortex_ensure; use vortex_error::vortex_err; use vortex_session::VortexSession; +use vortex_session::registry::CachedId; use crate::AnyColumnar; use crate::ArrayRef; @@ -37,7 +38,8 @@ impl ScalarFnVTable for FillNull { type Options = EmptyOptions; fn id(&self) -> ScalarFnId { - ScalarFnId::new("vortex.fill_null") + static ID: CachedId = CachedId::new("vortex.fill_null"); + *ID } fn serialize(&self, _options: &Self::Options) -> VortexResult>> { diff --git a/vortex-array/src/scalar_fn/fns/get_item.rs b/vortex-array/src/scalar_fn/fns/get_item.rs index 3f41e9b69c8..de7e45ca9b0 100644 --- a/vortex-array/src/scalar_fn/fns/get_item.rs +++ b/vortex-array/src/scalar_fn/fns/get_item.rs @@ -8,6 +8,7 @@ use vortex_error::VortexResult; use vortex_error::vortex_err; use vortex_proto::expr as pb; use vortex_session::VortexSession; +use vortex_session::registry::CachedId; use crate::ArrayRef; use crate::ExecutionCtx; @@ -44,7 +45,8 @@ impl ScalarFnVTable for GetItem { type Options = FieldName; fn id(&self) -> ScalarFnId { - ScalarFnId::new("vortex.get_item") + static ID: CachedId = CachedId::new("vortex.get_item"); + *ID } fn serialize(&self, instance: &Self::Options) -> VortexResult>> { diff --git a/vortex-array/src/scalar_fn/fns/is_not_null.rs b/vortex-array/src/scalar_fn/fns/is_not_null.rs index 26e474cc447..589333304e2 100644 --- a/vortex-array/src/scalar_fn/fns/is_not_null.rs +++ b/vortex-array/src/scalar_fn/fns/is_not_null.rs @@ -6,6 +6,7 @@ use std::fmt::Formatter; use vortex_array::scalar_fn::internal::row_count::RowCount; use vortex_error::VortexResult; use vortex_session::VortexSession; +use vortex_session::registry::CachedId; use crate::ArrayRef; use crate::ExecutionCtx; @@ -34,7 +35,8 @@ impl ScalarFnVTable for IsNotNull { type Options = EmptyOptions; fn id(&self) -> ScalarFnId { - ScalarFnId::new("vortex.is_not_null") + static ID: CachedId = CachedId::new("vortex.is_not_null"); + *ID } fn serialize(&self, _instance: &Self::Options) -> VortexResult>> { diff --git a/vortex-array/src/scalar_fn/fns/is_null.rs b/vortex-array/src/scalar_fn/fns/is_null.rs index ee0aab280ee..7315fbe8c07 100644 --- a/vortex-array/src/scalar_fn/fns/is_null.rs +++ b/vortex-array/src/scalar_fn/fns/is_null.rs @@ -3,6 +3,7 @@ use vortex_error::VortexResult; use vortex_session::VortexSession; +use vortex_session::registry::CachedId; use crate::ArrayRef; use crate::ExecutionCtx; @@ -32,7 +33,8 @@ impl ScalarFnVTable for IsNull { type Options = EmptyOptions; fn id(&self) -> ScalarFnId { - ScalarFnId::from("vortex.is_null") + static ID: CachedId = CachedId::new("vortex.is_null"); + *ID } fn serialize(&self, _instance: &Self::Options) -> VortexResult>> { diff --git a/vortex-array/src/scalar_fn/fns/like/mod.rs b/vortex-array/src/scalar_fn/fns/like/mod.rs index a99854d7e89..20699efb1ff 100644 --- a/vortex-array/src/scalar_fn/fns/like/mod.rs +++ b/vortex-array/src/scalar_fn/fns/like/mod.rs @@ -13,6 +13,7 @@ use vortex_error::VortexResult; use vortex_error::vortex_bail; use vortex_proto::expr as pb; use vortex_session::VortexSession; +use vortex_session::registry::CachedId; use crate::ArrayRef; use crate::ExecutionCtx; @@ -63,7 +64,8 @@ impl ScalarFnVTable for Like { type Options = LikeOptions; fn id(&self) -> ScalarFnId { - ScalarFnId::new("vortex.like") + static ID: CachedId = CachedId::new("vortex.like"); + *ID } fn serialize(&self, instance: &Self::Options) -> VortexResult>> { diff --git a/vortex-array/src/scalar_fn/fns/list_contains/mod.rs b/vortex-array/src/scalar_fn/fns/list_contains/mod.rs index afcd6136d70..978a1da1caf 100644 --- a/vortex-array/src/scalar_fn/fns/list_contains/mod.rs +++ b/vortex-array/src/scalar_fn/fns/list_contains/mod.rs @@ -14,6 +14,7 @@ use vortex_error::VortexResult; use vortex_error::vortex_bail; use vortex_error::vortex_err; use vortex_session::VortexSession; +use vortex_session::registry::CachedId; use vortex_utils::iter::ReduceBalancedIterExt; use crate::ArrayRef; @@ -61,7 +62,8 @@ impl ScalarFnVTable for ListContains { type Options = EmptyOptions; fn id(&self) -> ScalarFnId { - ScalarFnId::new("vortex.list.contains") + static ID: CachedId = CachedId::new("vortex.list.contains"); + *ID } fn serialize(&self, _instance: &Self::Options) -> VortexResult>> { diff --git a/vortex-array/src/scalar_fn/fns/literal.rs b/vortex-array/src/scalar_fn/fns/literal.rs index 97d8089a5bf..16b112e5a78 100644 --- a/vortex-array/src/scalar_fn/fns/literal.rs +++ b/vortex-array/src/scalar_fn/fns/literal.rs @@ -8,6 +8,7 @@ use vortex_error::VortexResult; use vortex_error::vortex_err; use vortex_proto::expr as pb; use vortex_session::VortexSession; +use vortex_session::registry::CachedId; use crate::ArrayRef; use crate::ExecutionCtx; @@ -38,7 +39,8 @@ impl ScalarFnVTable for Literal { type Options = Scalar; fn id(&self) -> ScalarFnId { - ScalarFnId::from("vortex.literal") + static ID: CachedId = CachedId::new("vortex.literal"); + *ID } fn serialize(&self, instance: &Self::Options) -> VortexResult>> { diff --git a/vortex-array/src/scalar_fn/fns/mask/mod.rs b/vortex-array/src/scalar_fn/fns/mask/mod.rs index 259f4b34e18..4dd55948ec8 100644 --- a/vortex-array/src/scalar_fn/fns/mask/mod.rs +++ b/vortex-array/src/scalar_fn/fns/mask/mod.rs @@ -8,6 +8,7 @@ use vortex_error::VortexExpect; use vortex_error::VortexResult; use vortex_error::vortex_ensure; use vortex_session::VortexSession; +use vortex_session::registry::CachedId; use crate::ArrayRef; use crate::Canonical; @@ -44,7 +45,8 @@ impl ScalarFnVTable for Mask { type Options = EmptyOptions; fn id(&self) -> ScalarFnId { - ScalarFnId::new("vortex.mask") + static ID: CachedId = CachedId::new("vortex.mask"); + *ID } fn serialize(&self, _options: &Self::Options) -> VortexResult>> { diff --git a/vortex-array/src/scalar_fn/fns/merge.rs b/vortex-array/src/scalar_fn/fns/merge.rs index 608ba69a255..390d3a2213e 100644 --- a/vortex-array/src/scalar_fn/fns/merge.rs +++ b/vortex-array/src/scalar_fn/fns/merge.rs @@ -11,6 +11,7 @@ use vortex_error::VortexExpect; use vortex_error::VortexResult; use vortex_error::vortex_bail; use vortex_session::VortexSession; +use vortex_session::registry::CachedId; use vortex_utils::aliases::hash_set::HashSet; use crate::ArrayRef; @@ -51,7 +52,8 @@ impl ScalarFnVTable for Merge { type Options = DuplicateHandling; fn id(&self) -> ScalarFnId { - ScalarFnId::from("vortex.merge") + static ID: CachedId = CachedId::new("vortex.merge"); + *ID } fn serialize(&self, instance: &Self::Options) -> VortexResult>> { diff --git a/vortex-array/src/scalar_fn/fns/not/mod.rs b/vortex-array/src/scalar_fn/fns/not/mod.rs index 4e82d75e790..156a6568df7 100644 --- a/vortex-array/src/scalar_fn/fns/not/mod.rs +++ b/vortex-array/src/scalar_fn/fns/not/mod.rs @@ -7,6 +7,7 @@ pub use kernel::*; use vortex_error::VortexResult; use vortex_error::vortex_bail; use vortex_session::VortexSession; +use vortex_session::registry::CachedId; use crate::ArrayRef; use crate::ExecutionCtx; @@ -33,7 +34,8 @@ impl ScalarFnVTable for Not { type Options = EmptyOptions; fn id(&self) -> ScalarFnId { - ScalarFnId::new("vortex.not") + static ID: CachedId = CachedId::new("vortex.not"); + *ID } fn serialize(&self, _options: &Self::Options) -> VortexResult>> { diff --git a/vortex-array/src/scalar_fn/fns/pack.rs b/vortex-array/src/scalar_fn/fns/pack.rs index 0a24f721064..2a87bf069bc 100644 --- a/vortex-array/src/scalar_fn/fns/pack.rs +++ b/vortex-array/src/scalar_fn/fns/pack.rs @@ -11,6 +11,7 @@ use prost::Message; use vortex_error::VortexResult; use vortex_proto::expr as pb; use vortex_session::VortexSession; +use vortex_session::registry::CachedId; use crate::ArrayRef; use crate::ExecutionCtx; @@ -55,7 +56,8 @@ impl ScalarFnVTable for Pack { type Options = PackOptions; fn id(&self) -> ScalarFnId { - ScalarFnId::from("vortex.pack") + static ID: CachedId = CachedId::new("vortex.pack"); + *ID } fn serialize(&self, instance: &Self::Options) -> VortexResult>> { diff --git a/vortex-array/src/scalar_fn/fns/root.rs b/vortex-array/src/scalar_fn/fns/root.rs index 99bb6fecf6e..87b8b62ccf4 100644 --- a/vortex-array/src/scalar_fn/fns/root.rs +++ b/vortex-array/src/scalar_fn/fns/root.rs @@ -6,6 +6,7 @@ use std::fmt::Formatter; use vortex_error::VortexResult; use vortex_error::vortex_bail; use vortex_session::VortexSession; +use vortex_session::registry::CachedId; use crate::ArrayRef; use crate::ExecutionCtx; @@ -30,7 +31,8 @@ impl ScalarFnVTable for Root { type Options = EmptyOptions; fn id(&self) -> ScalarFnId { - ScalarFnId::new("vortex.root") + static ID: CachedId = CachedId::new("vortex.root"); + *ID } fn serialize(&self, _instance: &Self::Options) -> VortexResult>> { diff --git a/vortex-array/src/scalar_fn/fns/select.rs b/vortex-array/src/scalar_fn/fns/select.rs index 2ca070c2bc4..54b63a00f89 100644 --- a/vortex-array/src/scalar_fn/fns/select.rs +++ b/vortex-array/src/scalar_fn/fns/select.rs @@ -14,6 +14,7 @@ use vortex_proto::expr::FieldNames as ProtoFieldNames; use vortex_proto::expr::SelectOpts; use vortex_proto::expr::select_opts::Opts; use vortex_session::VortexSession; +use vortex_session::registry::CachedId; use crate::ArrayRef; use crate::ExecutionCtx; @@ -48,7 +49,8 @@ impl ScalarFnVTable for Select { type Options = FieldSelection; fn id(&self) -> ScalarFnId { - ScalarFnId::from("vortex.select") + static ID: CachedId = CachedId::new("vortex.select"); + *ID } fn serialize(&self, instance: &FieldSelection) -> VortexResult>> { diff --git a/vortex-array/src/scalar_fn/fns/stat.rs b/vortex-array/src/scalar_fn/fns/stat.rs index ae07ba16c60..84fc5760495 100644 --- a/vortex-array/src/scalar_fn/fns/stat.rs +++ b/vortex-array/src/scalar_fn/fns/stat.rs @@ -8,6 +8,7 @@ use std::fmt::Formatter; use vortex_error::VortexResult; use vortex_error::vortex_bail; +use vortex_session::registry::CachedId; use crate::ArrayRef; use crate::ExecutionCtx; @@ -82,7 +83,8 @@ impl ScalarFnVTable for StatFn { type Options = StatOptions; fn id(&self) -> ScalarFnId { - ScalarFnId::new("vortex.stat") + static ID: CachedId = CachedId::new("vortex.stat"); + *ID } fn arity(&self, _options: &Self::Options) -> Arity { diff --git a/vortex-array/src/scalar_fn/fns/variant_get/mod.rs b/vortex-array/src/scalar_fn/fns/variant_get/mod.rs index 1f09c819d94..ac6aa562aa6 100644 --- a/vortex-array/src/scalar_fn/fns/variant_get/mod.rs +++ b/vortex-array/src/scalar_fn/fns/variant_get/mod.rs @@ -12,6 +12,7 @@ use vortex_error::vortex_err; use vortex_proto::expr as pb; use vortex_proto::expr::variant_path_element; use vortex_session::VortexSession; +use vortex_session::registry::CachedId; use vortex_utils::aliases::StringEscape; use crate::ArrayRef; @@ -45,7 +46,8 @@ impl ScalarFnVTable for VariantGet { type Options = VariantGetOptions; fn id(&self) -> ScalarFnId { - ScalarFnId::new("vortex.variant_get") + static ID: CachedId = CachedId::new("vortex.variant_get"); + *ID } fn serialize(&self, options: &Self::Options) -> VortexResult>> { diff --git a/vortex-array/src/scalar_fn/fns/zip/mod.rs b/vortex-array/src/scalar_fn/fns/zip/mod.rs index b1b17e40bb6..3a558ea44f8 100644 --- a/vortex-array/src/scalar_fn/fns/zip/mod.rs +++ b/vortex-array/src/scalar_fn/fns/zip/mod.rs @@ -12,6 +12,7 @@ use vortex_error::vortex_ensure; use vortex_mask::Mask; use vortex_mask::MaskValues; use vortex_session::VortexSession; +use vortex_session::registry::CachedId; use crate::ArrayRef; use crate::ExecutionCtx; @@ -46,7 +47,8 @@ impl ScalarFnVTable for Zip { type Options = EmptyOptions; fn id(&self) -> ScalarFnId { - ScalarFnId::new("vortex.zip") + static ID: CachedId = CachedId::new("vortex.zip"); + *ID } fn serialize(&self, _options: &Self::Options) -> VortexResult>> { diff --git a/vortex-array/src/scalar_fn/internal/row_count.rs b/vortex-array/src/scalar_fn/internal/row_count.rs index ea349f2ddf8..eee838f803d 100644 --- a/vortex-array/src/scalar_fn/internal/row_count.rs +++ b/vortex-array/src/scalar_fn/internal/row_count.rs @@ -21,6 +21,7 @@ use vortex_array::scalar_fn::ScalarFnVTable; use vortex_error::VortexResult; use vortex_error::vortex_bail; use vortex_error::vortex_ensure; +use vortex_session::registry::CachedId; /// Zero-argument placeholder for the row count of the current evaluation scope. /// @@ -48,7 +49,8 @@ impl ScalarFnVTable for RowCount { type Options = EmptyOptions; fn id(&self) -> ScalarFnId { - ScalarFnId::from("vortex.row_count") + static ID: CachedId = CachedId::new("vortex.row_count"); + *ID } fn arity(&self, _options: &Self::Options) -> Arity { diff --git a/vortex-layout/src/layouts/row_idx/expr.rs b/vortex-layout/src/layouts/row_idx/expr.rs index 2d85d6e43f5..95ddce3d762 100644 --- a/vortex-layout/src/layouts/row_idx/expr.rs +++ b/vortex-layout/src/layouts/row_idx/expr.rs @@ -17,6 +17,7 @@ use vortex_array::scalar_fn::ScalarFnVTable; use vortex_array::scalar_fn::ScalarFnVTableExt; use vortex_error::VortexResult; use vortex_error::vortex_bail; +use vortex_session::registry::CachedId; #[derive(Clone)] pub struct RowIdx; @@ -25,7 +26,8 @@ impl ScalarFnVTable for RowIdx { type Options = EmptyOptions; fn id(&self) -> ScalarFnId { - ScalarFnId::new("vortex.row_idx") + static ID: CachedId = CachedId::new("vortex.row_idx"); + *ID } fn arity(&self, _options: &Self::Options) -> Arity { diff --git a/vortex-layout/src/layouts/struct_/reader.rs b/vortex-layout/src/layouts/struct_/reader.rs index 5261f85f9a8..d27bd3746e7 100644 --- a/vortex-layout/src/layouts/struct_/reader.rs +++ b/vortex-layout/src/layouts/struct_/reader.rs @@ -157,21 +157,17 @@ impl StructReader { /// Utility for partitioning an expression over the fields of a struct. fn partition_expr(&self, expr: Expression) -> VortexResult { let key = ExactExpr(expr.clone()); - - if let Some(entry) = self.partitioned_expr_cache.get(&key) - && let Some(partitioning) = entry.value().get() - { - return Ok(partitioning.clone()); + let binding = self + .partitioned_expr_cache + .entry(key) + .or_insert_with(|| Arc::new(OnceLock::new())); + let entry = binding.value(); + if let Some(value) = entry.get() { + return Ok(value.clone()); } - let result = self.compute_partitioned_expr(expr)?; - - self.partitioned_expr_cache - .entry(key) - .or_insert_with(|| Arc::new(OnceLock::new())) - .get_or_init(|| result.clone()); - - Ok(result) + let result = entry.get_or_init(|| result); + Ok(result.clone()) } fn compute_partitioned_expr(&self, expr: Expression) -> VortexResult { diff --git a/vortex-tensor/src/scalar_fns/cosine_similarity.rs b/vortex-tensor/src/scalar_fns/cosine_similarity.rs index 7dc24a878a4..0b3176915fd 100644 --- a/vortex-tensor/src/scalar_fns/cosine_similarity.rs +++ b/vortex-tensor/src/scalar_fns/cosine_similarity.rs @@ -28,6 +28,7 @@ use vortex_array::serde::ArrayChildren; use vortex_buffer::Buffer; use vortex_error::VortexResult; use vortex_session::VortexSession; +use vortex_session::registry::CachedId; use crate::scalar_fns::inner_product::InnerProduct; use crate::scalar_fns::l2_denorm::DenormOrientation; @@ -79,7 +80,8 @@ impl ScalarFnVTable for CosineSimilarity { type Options = EmptyOptions; fn id(&self) -> ScalarFnId { - ScalarFnId::new("vortex.tensor.cosine_similarity") + static ID: CachedId = CachedId::new("vortex.tensor.cosine_similarity"); + *ID } fn arity(&self, _options: &Self::Options) -> Arity { diff --git a/vortex-tensor/src/scalar_fns/inner_product.rs b/vortex-tensor/src/scalar_fns/inner_product.rs index 197d5cc2fb3..b3ba7a7b557 100644 --- a/vortex-tensor/src/scalar_fns/inner_product.rs +++ b/vortex-tensor/src/scalar_fns/inner_product.rs @@ -42,6 +42,7 @@ use vortex_buffer::BufferMut; use vortex_error::VortexExpect; use vortex_error::VortexResult; use vortex_session::VortexSession; +use vortex_session::registry::CachedId; use crate::matcher::AnyTensor; use crate::scalar_fns::l2_denorm::DenormOrientation; @@ -90,7 +91,8 @@ impl ScalarFnVTable for InnerProduct { type Options = EmptyOptions; fn id(&self) -> ScalarFnId { - ScalarFnId::new("vortex.tensor.inner_product") + static ID: CachedId = CachedId::new("vortex.tensor.inner_product"); + *ID } fn arity(&self, _options: &Self::Options) -> Arity { diff --git a/vortex-tensor/src/scalar_fns/l2_denorm.rs b/vortex-tensor/src/scalar_fns/l2_denorm.rs index 00ad7d75e8a..c91a614ddff 100644 --- a/vortex-tensor/src/scalar_fns/l2_denorm.rs +++ b/vortex-tensor/src/scalar_fns/l2_denorm.rs @@ -54,6 +54,7 @@ use vortex_error::vortex_ensure; use vortex_error::vortex_ensure_eq; use vortex_error::vortex_err; use vortex_session::VortexSession; +use vortex_session::registry::CachedId; use crate::matcher::AnyTensor; use crate::scalar_fns::l2_norm::L2Norm; @@ -149,7 +150,8 @@ impl ScalarFnVTable for L2Denorm { type Options = EmptyOptions; fn id(&self) -> ScalarFnId { - ScalarFnId::new("vortex.tensor.l2_denorm") + static ID: CachedId = CachedId::new("vortex.tensor.l2_denorm"); + *ID } fn arity(&self, _options: &Self::Options) -> Arity { diff --git a/vortex-tensor/src/scalar_fns/l2_norm.rs b/vortex-tensor/src/scalar_fns/l2_norm.rs index d760c3429bd..fab22bc6f29 100644 --- a/vortex-tensor/src/scalar_fns/l2_norm.rs +++ b/vortex-tensor/src/scalar_fns/l2_norm.rs @@ -41,6 +41,7 @@ use vortex_error::VortexResult; use vortex_error::vortex_ensure_eq; use vortex_error::vortex_err; use vortex_session::VortexSession; +use vortex_session::registry::CachedId; use crate::matcher::AnyTensor; use crate::scalar_fns::l2_denorm::L2Denorm; @@ -83,7 +84,8 @@ impl ScalarFnVTable for L2Norm { type Options = EmptyOptions; fn id(&self) -> ScalarFnId { - ScalarFnId::new("vortex.tensor.l2_norm") + static ID: CachedId = CachedId::new("vortex.tensor.l2_norm"); + *ID } fn arity(&self, _options: &Self::Options) -> Arity { diff --git a/vortex-tensor/src/scalar_fns/sorf_transform/vtable.rs b/vortex-tensor/src/scalar_fns/sorf_transform/vtable.rs index 76648decae2..5e9a87754e0 100644 --- a/vortex-tensor/src/scalar_fns/sorf_transform/vtable.rs +++ b/vortex-tensor/src/scalar_fns/sorf_transform/vtable.rs @@ -43,6 +43,7 @@ use vortex_error::VortexResult; use vortex_error::vortex_ensure_eq; use vortex_error::vortex_err; use vortex_session::VortexSession; +use vortex_session::registry::CachedId; use super::SorfOptions; use super::SorfTransform; @@ -55,7 +56,8 @@ impl ScalarFnVTable for SorfTransform { type Options = SorfOptions; fn id(&self) -> ScalarFnId { - ScalarFnId::new("vortex.tensor.sorf_transform") + static ID: CachedId = CachedId::new("vortex.tensor.sorf_transform"); + *ID } fn arity(&self, _options: &Self::Options) -> Arity { diff --git a/vortex-turboquant/src/scalar_fns/decode.rs b/vortex-turboquant/src/scalar_fns/decode.rs index 6791a1aef61..ac27815f5a3 100644 --- a/vortex-turboquant/src/scalar_fns/decode.rs +++ b/vortex-turboquant/src/scalar_fns/decode.rs @@ -36,6 +36,7 @@ use vortex_error::vortex_ensure; use vortex_error::vortex_err; use vortex_mask::Mask; use vortex_session::VortexSession; +use vortex_session::registry::CachedId; use vortex_tensor::vector::Vector; use crate::centroids::compute_or_get_centroids; @@ -66,7 +67,8 @@ impl ScalarFnVTable for TQDecode { type Options = EmptyMetadata; fn id(&self) -> ScalarFnId { - ScalarFnId::new("vortex.turboquant.decode") + static ID: CachedId = CachedId::new("vortex.turboquant.decode"); + *ID } fn serialize(&self, _options: &Self::Options) -> VortexResult>> { diff --git a/vortex-turboquant/src/scalar_fns/encode.rs b/vortex-turboquant/src/scalar_fns/encode.rs index 29ce7cc580a..6dd16e4bb66 100644 --- a/vortex-turboquant/src/scalar_fns/encode.rs +++ b/vortex-turboquant/src/scalar_fns/encode.rs @@ -28,6 +28,7 @@ use vortex_error::VortexResult; use vortex_error::vortex_ensure; use vortex_error::vortex_err; use vortex_session::VortexSession; +use vortex_session::registry::CachedId; use vortex_tensor::vector::AnyVector; use super::metadata::deserialize_config; @@ -74,7 +75,8 @@ impl ScalarFnVTable for TQEncode { type Options = TurboQuantConfig; fn id(&self) -> ScalarFnId { - ScalarFnId::new("vortex.turboquant.encode") + static ID: CachedId = CachedId::new("vortex.turboquant.encode"); + *ID } fn serialize(&self, options: &Self::Options) -> VortexResult>> { From a958108d43d34004325a4b60eeadbfa0fdf64e3f Mon Sep 17 00:00:00 2001 From: Alexander Droste Date: Wed, 3 Jun 2026 18:38:15 +0100 Subject: [PATCH 016/115] feat[gpu]: arrow device array decimal export (#8155) Signed-off-by: Alexander Droste --- vortex-array/src/dtype/arrow.rs | 12 + vortex-cuda/kernels/src/decimal_cast.cu | 107 ++++++ vortex-cuda/src/arrow/canonical.rs | 413 ++++++++++++++++++++++-- vortex-cuda/src/arrow/mod.rs | 90 +++++- vortex-test/e2e-cuda/src/lib.rs | 66 +++- 5 files changed, 633 insertions(+), 55 deletions(-) create mode 100644 vortex-cuda/kernels/src/decimal_cast.cu diff --git a/vortex-array/src/dtype/arrow.rs b/vortex-array/src/dtype/arrow.rs index 8dd80082533..103c52daf93 100644 --- a/vortex-array/src/dtype/arrow.rs +++ b/vortex-array/src/dtype/arrow.rs @@ -450,6 +450,18 @@ mod test { ); } + #[rstest] + #[case(1, DataType::Decimal128(1, 0))] + #[case(38, DataType::Decimal128(38, 0))] + #[case(39, DataType::Decimal256(39, 0))] + #[case(76, DataType::Decimal256(76, 0))] + fn test_decimal_dtype_to_arrow(#[case] precision: u8, #[case] expected: DataType) { + use crate::dtype::DecimalDType; + + let dtype = DType::Decimal(DecimalDType::new(precision, 0), Nullability::NonNullable); + assert_eq!(dtype.to_arrow_dtype().unwrap(), expected); + } + #[test] fn test_variant_dtype_to_arrow_dtype_errors() { let err = DType::Variant(Nullability::NonNullable) diff --git a/vortex-cuda/kernels/src/decimal_cast.cu b/vortex-cuda/kernels/src/decimal_cast.cu new file mode 100644 index 00000000000..22a8a130729 --- /dev/null +++ b/vortex-cuda/kernels/src/decimal_cast.cu @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: Copyright the Vortex contributors + +#include "config.cuh" +#include "types.cuh" +#include +#include + +// Arrow decimal schemas fix the physical values buffer width: +// - Decimal32: 4 bytes per value. +// - Decimal64: 8 bytes per value. +// - Decimal128: 16 bytes per value. +// - Decimal256: 32 bytes per value. +// +// Vortex storage width can differ, so export casts to the schema-implied width. +// Rust-side export rejects narrowing casts because detecting overflow on-device +// would require synchronizing an overflow flag back to the host. + +// Low 64-bit conversion for Decimal32/64 outputs. +template +__device__ __forceinline__ int64_t decimal_to_i64(Input value) { + if constexpr (std::is_same_v) { + return value.lo; + } else if constexpr (std::is_same_v) { + return value.parts[0]; + } else { + return static_cast(value); + } +} + +// 128-bit conversion for Decimal128 outputs. +template +__device__ __forceinline__ int128_t decimal_to_i128(Input value) { + if constexpr (std::is_same_v) { + return value; + } else if constexpr (std::is_same_v) { + return int128_t {value.parts[0], value.parts[1]}; + } else { + const int64_t lo = static_cast(value); + const int64_t hi = value < 0 ? -1 : 0; + return int128_t {lo, hi}; + } +} + +// Convert one value to the Arrow schema's physical width. +template +__device__ __forceinline__ Output decimal_cast_value(Input value) { + if constexpr (std::is_same_v) { + return static_cast(decimal_to_i64(value)); + } else if constexpr (std::is_same_v) { + return decimal_to_i64(value); + } else if constexpr (std::is_same_v) { + return decimal_to_i128(value); + } else { + static_assert(std::is_same_v); + if constexpr (std::is_same_v) { + return value; + } else { + const int128_t value128 = decimal_to_i128(value); + const int64_t sign = value128.hi < 0 ? -1 : 0; + return int256_t {{value128.lo, value128.hi, sign, sign}}; + } + } +} + +// Cast a contiguous values buffer to the Arrow schema's physical width. +template +__device__ void +decimal_cast_device(const Input *__restrict input, Output *__restrict output, uint64_t array_len) { + const uint64_t worker = blockIdx.x * blockDim.x + threadIdx.x; + const uint64_t startElem = start_elem(worker, array_len); + const uint64_t stopElem = stop_elem(worker, array_len); + + if (startElem >= array_len) { + return; + } + + for (uint64_t idx = startElem; idx < stopElem; idx++) { + output[idx] = decimal_cast_value(input[idx]); + } +} + +// Generate Decimal32/64/128/256 cast kernels for one input storage type. +#define GENERATE_DECIMAL_CAST_KERNELS(input_suffix, InputType) \ + extern "C" __global__ void decimal_cast_##input_suffix##_i32(const InputType *__restrict input, \ + int32_t *__restrict output, \ + uint64_t array_len) { \ + decimal_cast_device(input, output, array_len); \ + } \ + extern "C" __global__ void decimal_cast_##input_suffix##_i64(const InputType *__restrict input, \ + int64_t *__restrict output, \ + uint64_t array_len) { \ + decimal_cast_device(input, output, array_len); \ + } \ + extern "C" __global__ void decimal_cast_##input_suffix##_i128(const InputType *__restrict input, \ + int128_t *__restrict output, \ + uint64_t array_len) { \ + decimal_cast_device(input, output, array_len); \ + } \ + extern "C" __global__ void decimal_cast_##input_suffix##_i256(const InputType *__restrict input, \ + int256_t *__restrict output, \ + uint64_t array_len) { \ + decimal_cast_device(input, output, array_len); \ + } + +FOR_EACH_SIGNED_INT(GENERATE_DECIMAL_CAST_KERNELS) +FOR_EACH_LARGE_DECIMAL(GENERATE_DECIMAL_CAST_KERNELS) diff --git a/vortex-cuda/src/arrow/canonical.rs b/vortex-cuda/src/arrow/canonical.rs index b1bee7c018f..af180400675 100644 --- a/vortex-cuda/src/arrow/canonical.rs +++ b/vortex-cuda/src/arrow/canonical.rs @@ -3,11 +3,15 @@ use std::mem; use std::ptr; +use std::sync::Arc; use async_trait::async_trait; +use cudarc::driver::DeviceRepr; +use cudarc::driver::PushKernelArg; use futures::future::BoxFuture; use vortex::array::ArrayRef; use vortex::array::Canonical; +use vortex::array::arrays::DecimalArray; use vortex::array::arrays::FixedSizeListArray; use vortex::array::arrays::ListArray; use vortex::array::arrays::PrimitiveArray; @@ -24,13 +28,16 @@ use vortex::array::arrays::struct_::StructDataParts; use vortex::array::arrays::varbinview::VarBinViewDataParts; use vortex::array::buffer::BufferHandle; use vortex::array::builtins::ArrayBuiltins; +use vortex::array::match_each_decimal_value_type; use vortex::array::validity::Validity; use vortex::buffer::Buffer; use vortex::buffer::ByteBuffer; use vortex::dtype::DType; use vortex::dtype::DecimalType; +use vortex::dtype::NativeDecimalType; use vortex::dtype::Nullability; use vortex::dtype::PType; +use vortex::dtype::i256; use vortex::error::VortexResult; use vortex::error::vortex_bail; use vortex::error::vortex_ensure; @@ -39,6 +46,8 @@ use vortex::extension::datetime::AnyTemporal; use vortex::mask::Mask; use super::list_view::export_device_list_view; +use crate::CudaBufferExt; +use crate::CudaDeviceBuffer; use crate::CudaExecutionCtx; use crate::arrow::ARROW_DEVICE_CUDA; use crate::arrow::ArrowArray; @@ -46,6 +55,7 @@ use crate::arrow::ArrowDeviceArray; use crate::arrow::ExportDeviceArray; use crate::arrow::PrivateData; use crate::arrow::SyncEvent; +use crate::arrow::cuda_decimal_value_type; use crate::executor::CudaArrayExt; /// An implementation of `ExportDeviceArray` that exports Vortex arrays to `ArrowDeviceArray` by @@ -106,27 +116,7 @@ fn export_canonical( // we don't need a sync event for Null since no data is copied. Ok((array, ptr::null_mut())) } - Canonical::Decimal(decimal) => { - let len = decimal.len(); - let DecimalDataParts { - values, - values_type, - validity, - .. - } = decimal.into_data_parts(); - - // TODO(aduffy): GPU kernel for upcasting. - vortex_ensure!( - values_type >= DecimalType::I32, - "cannot export DecimalArray with values type {values_type}. must be i32 or wider." - ); - - let (validity_buffer, null_count) = - export_arrow_validity_buffer(validity, len, 0, ctx).await?; - let buffer = ctx.ensure_on_device(values).await?; - - export_fixed_size(buffer, len, 0, validity_buffer, null_count, ctx) - } + Canonical::Decimal(decimal) => export_decimal(decimal, ctx).await, Canonical::Extension(extension) => { if !extension.ext_dtype().is::() { vortex_bail!("only support temporal extension types currently"); @@ -242,6 +232,115 @@ fn export_canonical( }) } +/// Exports decimals with value buffers cast to Arrow's Decimal32/64/128/256 layout. +/// +/// Decimal values are already decoded; this only adapts the physical buffer width. Storage-to-Arrow +/// narrowing is rejected instead of checked on-device to avoid a device-to-host synchronization +/// point. +async fn export_decimal( + decimal: DecimalArray, + ctx: &mut CudaExecutionCtx, +) -> VortexResult<(ArrowArray, SyncEvent)> { + let len = decimal.len(); + let DecimalDataParts { + decimal_dtype, + values, + values_type, + validity, + } = decimal.into_data_parts(); + + let (validity_buffer, null_count) = export_arrow_validity_buffer(validity, len, 0, ctx).await?; + let target_type = cuda_decimal_value_type(decimal_dtype); + let values = export_decimal_values(values, values_type, target_type, len, ctx).await?; + + export_fixed_size(values, len, 0, validity_buffer, null_count, ctx) +} + +/// Ensure the values buffer is on-device and has the Arrow-required decimal width. +/// +/// Storage wider than the precision-implied Arrow width is rejected. Callers that hit this +/// should narrow the storage via a decimal cast before exporting. +async fn export_decimal_values( + values: BufferHandle, + values_type: DecimalType, + target_type: DecimalType, + len: usize, + ctx: &mut CudaExecutionCtx, +) -> VortexResult { + if values_type.byte_width() > target_type.byte_width() { + vortex_bail!( + "cannot export decimal values from {values_type} storage to Arrow {target_type}: narrowing would require a device-to-host overflow check", + ); + } + let values = ctx.ensure_on_device(values).await?; + if values_type == target_type { + return Ok(values); + } + + match_each_decimal_value_type!(values_type, |S| { + export_decimal_values_from::(values, target_type, len, ctx).await + }) +} + +/// Dispatch from a concrete storage type `S` to the Arrow-required output width. +async fn export_decimal_values_from( + values: BufferHandle, + target_type: DecimalType, + len: usize, + ctx: &mut CudaExecutionCtx, +) -> VortexResult +where + S: NativeDecimalType + DeviceRepr, +{ + match target_type { + DecimalType::I32 => decimal_cast::(values, len, ctx).await, + DecimalType::I64 => decimal_cast::(values, len, ctx).await, + DecimalType::I128 => decimal_cast::(values, len, ctx).await, + DecimalType::I256 => decimal_cast::(values, len, ctx).await, + target_type => { + vortex_bail!("cannot export DecimalArray as Arrow decimal value type {target_type}") + } + } +} + +/// Launches the CUDA kernel that casts from Vortex storage type `S` to Arrow output type `D`. +/// +/// The caller must ensure this is not a narrowing cast. +async fn decimal_cast( + values: BufferHandle, + len: usize, + ctx: &mut CudaExecutionCtx, +) -> VortexResult +where + S: NativeDecimalType + DeviceRepr, + D: NativeDecimalType + DeviceRepr, +{ + if len == 0 { + return ctx + .ensure_on_device(BufferHandle::new_host( + Buffer::::empty().into_byte_buffer(), + )) + .await; + } + + let output_buffer = ctx.device_alloc::(len)?; + let output_device = CudaDeviceBuffer::new(output_buffer); + + let values_view = values.cuda_view::()?; + let output_view = output_device.as_view::(); + let len_u64 = len as u64; + let cuda_function = ctx.load_function_with_suffixes( + "decimal_cast", + &[&S::DECIMAL_TYPE.to_string(), &D::DECIMAL_TYPE.to_string()], + )?; + + ctx.launch_kernel(&cuda_function, len, |args| { + args.arg(&values_view).arg(&output_view).arg(&len_u64); + })?; + + Ok(BufferHandle::new_device(Arc::new(output_device))) +} + /// Export Vortex validity as an Arrow validity byte buffer. /// /// Returns `None` for the buffer when Arrow can omit validity because all rows are valid. @@ -537,16 +636,19 @@ mod tests { use vortex::array::arrays::VarBinViewArray; use vortex::array::arrays::primitive::PrimitiveArrayExt; use vortex::array::arrays::varbinview::BinaryView; + use vortex::array::buffer::BufferHandle; use vortex::array::validity::Validity; use vortex::buffer::Buffer; use vortex::buffer::ByteBuffer; use vortex::dtype::DType; use vortex::dtype::DecimalDType; use vortex::dtype::FieldNames; + use vortex::dtype::NativeDecimalType; use vortex::dtype::NativePType; use vortex::dtype::Nullability; use vortex::dtype::PType; use vortex::dtype::half::f16; + use vortex::dtype::i256; use vortex::error::VortexExpect; use vortex::error::VortexResult; use vortex::error::vortex_bail; @@ -761,6 +863,14 @@ mod tests { .collect()) } + fn assert_exported_decimal_values( + value_buffer: &BufferHandle, + expected: &[T], + ) { + let values = Buffer::::from_byte_buffer(value_buffer.to_host_sync()); + assert_eq!(values.as_slice(), expected); + } + // Build a nested struct fixture with an out-of-line string-view value. fn nested_struct_array() -> ArrayRef { let nested = StructArray::new( @@ -855,25 +965,197 @@ mod tests { Ok(()) } + async fn assert_exported_decimal( + array: ArrayRef, + expected_data_type: DataType, + expected_values: Vec, + ) -> VortexResult<()> { + let mut ctx = CudaSession::create_execution_ctx(&VortexSession::empty()) + .vortex_expect("failed to create execution context"); + + let mut exported = array.export_device_array_with_schema(&mut ctx).await?; + + let field = Field::try_from(&exported.schema)?; + assert_eq!(field, Field::new("", expected_data_type, false)); + assert_eq!( + exported.array.array.length, + i64::try_from(expected_values.len())? + ); + assert_eq!(exported.array.array.null_count, 0); + assert_eq!(exported.array.array.n_buffers, 2); + assert_eq!(exported.array.array.n_children, 0); + assert!(exported.array.array.release.is_some()); + assert_eq!(exported.array.device_type, ARROW_DEVICE_CUDA); + + let private_data = unsafe { &*exported.array.array.private_data.cast::() }; + let value_buffer = private_data.buffers[1] + .as_ref() + .vortex_expect("value buffer should be present"); + assert_eq!(value_buffer.len(), expected_values.len() * size_of::()); + assert_exported_decimal_values(value_buffer, &expected_values); + + unsafe { release_exported_array(&raw mut exported.array.array) }; + Ok(()) + } + + #[rstest] + #[case::i8( + DecimalArray::from_iter([1i8, -2, 3], DecimalDType::new(2, 1)).into_array(), + DataType::Decimal32(2, 1), + vec![1i32, -2, 3] + )] + #[case::i16( + DecimalArray::from_iter([100i16, -200, 300], DecimalDType::new(4, 2)).into_array(), + DataType::Decimal32(4, 2), + vec![100i32, -200, 300] + )] + #[case::i32( + DecimalArray::from_iter([10_000i32, -20_000, 30_000], DecimalDType::new(9, 2)).into_array(), + DataType::Decimal32(9, 2), + vec![10_000i32, -20_000, 30_000] + )] #[crate::test] - async fn test_export_decimal() -> VortexResult<()> { + async fn test_export_decimal32( + #[case] array: ArrayRef, + #[case] expected_data_type: DataType, + #[case] expected_values: Vec, + ) -> VortexResult<()> { + assert_exported_decimal(array, expected_data_type, expected_values).await + } + + #[rstest] + #[case::i32( + DecimalArray::from_iter([1_000_000i32, -2_000_000, 3_000_000], DecimalDType::new(10, 2)).into_array(), + DataType::Decimal64(10, 2), + vec![1_000_000i64, -2_000_000, 3_000_000] + )] + #[case::i32_boundary( + DecimalArray::from_iter([i32::MIN, -1i32, 0, 1, i32::MAX], DecimalDType::new(10, 0)).into_array(), + DataType::Decimal64(10, 0), + vec![i32::MIN as i64, -1, 0, 1, i32::MAX as i64] + )] + #[case::i64( + DecimalArray::from_iter([1_000_000i64, -2_000_000, 3_000_000], DecimalDType::new(18, 2)).into_array(), + DataType::Decimal64(18, 2), + vec![1_000_000i64, -2_000_000, 3_000_000] + )] + #[crate::test] + async fn test_export_decimal64( + #[case] array: ArrayRef, + #[case] expected_data_type: DataType, + #[case] expected_values: Vec, + ) -> VortexResult<()> { + assert_exported_decimal(array, expected_data_type, expected_values).await + } + + #[rstest] + #[case::i64_boundary( + DecimalArray::from_iter([i64::MIN, -1i64, 0, 1, i64::MAX], DecimalDType::new(19, 0)).into_array(), + DataType::Decimal128(19, 0), + vec![i64::MIN as i128, -1, 0, 1, i64::MAX as i128] + )] + #[case::i128( + DecimalArray::from_iter([1i128, -2, 3], DecimalDType::new(38, 2)).into_array(), + DataType::Decimal128(38, 2), + vec![1i128, -2, 3] + )] + #[crate::test] + async fn test_export_decimal128( + #[case] array: ArrayRef, + #[case] expected_data_type: DataType, + #[case] expected_values: Vec, + ) -> VortexResult<()> { + assert_exported_decimal(array, expected_data_type, expected_values).await + } + + #[crate::test] + async fn test_export_empty_decimal() -> VortexResult<()> { + assert_exported_decimal( + DecimalArray::new( + Buffer::::empty(), + DecimalDType::new(9, 2), + Validity::NonNullable, + ) + .into_array(), + DataType::Decimal32(9, 2), + Vec::::new(), + ) + .await + } + + #[crate::test] + async fn test_export_empty_decimal_widening() -> VortexResult<()> { + assert_exported_decimal( + DecimalArray::new( + Buffer::::empty(), + DecimalDType::new(9, 2), + Validity::NonNullable, + ) + .into_array(), + DataType::Decimal32(9, 2), + Vec::::new(), + ) + .await + } + + #[crate::test] + async fn test_export_decimal_narrowing_errors() -> VortexResult<()> { let mut ctx = CudaSession::create_execution_ctx(&VortexSession::empty()) .vortex_expect("failed to create execution context"); + let array = DecimalArray::from_iter([i256::from_parts(0, 1)], DecimalDType::new(38, 0)) + .into_array(); - let array = DecimalArray::from_iter(0i128..5, DecimalDType::new(38, 2)).into_array(); - let mut device_array = array.export_device_array(&mut ctx).await?; + let err = array + .export_device_array_with_schema(&mut ctx) + .await + .unwrap_err(); + assert!(err.to_string().contains("narrowing would require")); + Ok(()) + } - assert_eq!(device_array.array.length, 5); - assert_eq!(device_array.array.null_count, 0); - assert_eq!(device_array.array.n_buffers, 2); - assert_eq!(device_array.array.n_children, 0); - assert!(device_array.array.release.is_some()); - assert_eq!(device_array.device_type, ARROW_DEVICE_CUDA); + #[crate::test] + async fn test_export_decimal_narrowing_from_arrow_import() -> VortexResult<()> { + let mut ctx = CudaSession::create_execution_ctx(&VortexSession::empty()) + .vortex_expect("failed to create execution context"); + let array = DecimalArray::from_iter([0i128, 1, -2], DecimalDType::new(10, 2)).into_array(); - unsafe { release_exported_array(&raw mut device_array.array) }; + let err = array + .export_device_array_with_schema(&mut ctx) + .await + .unwrap_err(); + assert!(err.to_string().contains("narrowing would require")); Ok(()) } + #[rstest] + #[case::i64( + DecimalArray::from_iter([i64::MIN, -1i64, 1, i64::MAX], DecimalDType::new(39, 0)).into_array(), + DataType::Decimal256(39, 0), + vec![i256::from_i128(i64::MIN as i128), i256::from_i128(-1), i256::from_i128(1), i256::from_i128(i64::MAX as i128)] + )] + #[case::i128( + DecimalArray::from_iter([1i128, -2, 3], DecimalDType::new(39, 2)).into_array(), + DataType::Decimal256(39, 2), + vec![i256::from_i128(1), i256::from_i128(-2), i256::from_i128(3)] + )] + #[case::i256( + DecimalArray::from_iter( + [i256::from_i128(10), i256::from_i128(-20), i256::from_i128(30)], + DecimalDType::new(76, 2), + ) + .into_array(), + DataType::Decimal256(76, 2), + vec![i256::from_i128(10), i256::from_i128(-20), i256::from_i128(30)] + )] + #[crate::test] + async fn test_export_decimal256( + #[case] array: ArrayRef, + #[case] expected_data_type: DataType, + #[case] expected_values: Vec, + ) -> VortexResult<()> { + assert_exported_decimal(array, expected_data_type, expected_values).await + } + #[crate::test] async fn test_export_temporal() -> VortexResult<()> { let mut ctx = CudaSession::create_execution_ctx(&VortexSession::empty()) @@ -1519,6 +1801,14 @@ mod tests { &mut ctx, ) .await?; + + let private_data = unsafe { &*decimal.array.private_data.cast::() }; + let value_buffer = private_data.buffers[1] + .as_ref() + .vortex_expect("value buffer should be present"); + assert_eq!(value_buffer.len(), 3 * size_of::()); + assert_exported_decimal_values(value_buffer, &[100i64, 0, 300]); + unsafe { release_exported_array(&raw mut decimal.array) }; Ok(()) @@ -1794,6 +2084,67 @@ mod tests { Ok(()) } + #[crate::test] + async fn test_export_nested_struct_decimal_with_schema() -> VortexResult<()> { + let mut ctx = CudaSession::create_execution_ctx(&VortexSession::empty()) + .vortex_expect("failed to create execution context"); + + let nested = StructArray::new( + FieldNames::from_iter(["amount"]), + vec![ + DecimalArray::from_iter([100i32, -200, 300], DecimalDType::new(9, 2)).into_array(), + ], + 3, + Validity::NonNullable, + ) + .into_array(); + let array = StructArray::new( + FieldNames::from_iter(["nested"]), + vec![nested], + 3, + Validity::NonNullable, + ) + .into_array(); + let mut exported = array.export_device_array_with_schema(&mut ctx).await?; + + let schema = Schema::try_from(&exported.schema)?; + assert_eq!( + schema, + Schema::new(vec![Field::new( + "nested", + DataType::Struct(Fields::from(vec![Field::new( + "amount", + DataType::Decimal32(9, 2), + false, + )])), + false, + )]) + ); + + let children = unsafe { + std::slice::from_raw_parts( + exported.array.array.children, + usize::try_from(exported.array.array.n_children)?, + ) + }; + let nested_child = unsafe { &*children[0] }; + let nested_children = unsafe { + std::slice::from_raw_parts( + nested_child.children, + usize::try_from(nested_child.n_children)?, + ) + }; + let decimal_child = unsafe { &*nested_children[0] }; + let private_data = unsafe { &*decimal_child.private_data.cast::() }; + let value_buffer = private_data.buffers[1] + .as_ref() + .vortex_expect("value buffer should be present"); + assert_eq!(value_buffer.len(), 3 * size_of::()); + + unsafe { release_exported_array(&raw mut exported.array.array) }; + Ok(()) + } + #[crate::test] async fn test_export_primitive_with_schema_is_column_shaped() -> VortexResult<()> { let mut ctx = CudaSession::create_execution_ctx(&VortexSession::empty()) diff --git a/vortex-cuda/src/arrow/mod.rs b/vortex-cuda/src/arrow/mod.rs index e21383d96e7..99fa14add3d 100644 --- a/vortex-cuda/src/arrow/mod.rs +++ b/vortex-cuda/src/arrow/mod.rs @@ -16,6 +16,9 @@ use std::fmt::Debug; use std::ptr; use std::sync::Arc; +use arrow_schema::DataType; +use arrow_schema::Field; +use arrow_schema::Schema; use arrow_schema::ffi::FFI_ArrowSchema; use async_trait::async_trait; pub(crate) use canonical::CanonicalDeviceArrayExport; @@ -26,6 +29,8 @@ use vortex::array::ArrayRef; use vortex::array::arrow::ArrowSessionExt; use vortex::array::buffer::BufferHandle; use vortex::dtype::DType; +use vortex::dtype::DecimalDType; +use vortex::dtype::DecimalType; use vortex::dtype::StructFields; use vortex::error::VortexResult; use vortex::error::vortex_err; @@ -159,6 +164,10 @@ pub trait DeviceArrayExt { /// /// The returned array owns any device buffers allocated during export. Call the embedded /// Arrow release callback when the consumer is done with the array. + /// + /// Arrow arrays are not self-describing, so callers that use this method directly must provide + /// a matching schema out-of-band. Prefer [`Self::export_device_array_with_schema`] unless a + /// consumer already has the CUDA export schema. async fn export_device_array( self, ctx: &mut CudaExecutionCtx, @@ -167,13 +176,15 @@ pub trait DeviceArrayExt { /// Export this array as an Arrow C Device array together with its matching Arrow C schema. /// /// Arrow arrays are not self-describing: consumers need both the [`ArrowDeviceArray`] and an - /// Arrow schema to interpret the buffer layout. This helper derives the schema from the - /// Vortex dtype using the session's Arrow conversion rules and returns it alongside the device - /// array. + /// Arrow schema to interpret the buffer layout. This helper derives the schema that matches the + /// CUDA device export layout and returns it alongside the device array. /// /// Top-level struct arrays are exported as table-like Arrow schemas and struct-shaped device /// arrays. Top-level non-struct arrays are exported as column-shaped field schemas and /// column-shaped device arrays; this method does not wrap them in a single-field struct. + /// + /// Decimal exports use the Arrow decimal width implied by precision; storage wider than that + /// width is rejected rather than narrowed on device. async fn export_device_array_with_schema( self, ctx: &mut CudaExecutionCtx, @@ -200,22 +211,75 @@ impl DeviceArrayExt for ArrayRef { } } -/// Build the Arrow C schema that describes the device array exported for `array`. -/// -/// Top-level Vortex structs are represented as Arrow schemas, which is the shape expected for -/// table-like consumers. Non-struct arrays are represented as a single Arrow field schema, matching -/// the column-shaped [`ArrowDeviceArray`] returned by [`DeviceArrayExt::export_device_array`]. +/// Build the Arrow C schema that describes the exported device array. fn arrow_schema_for_array( array: &ArrayRef, ctx: &mut CudaExecutionCtx, ) -> VortexResult { - let arrow = ctx.execution_ctx().session().arrow(); let dtype = arrow_device_export_dtype(array.dtype()); match &dtype { - DType::Struct(..) => Ok(FFI_ArrowSchema::try_from(arrow.to_arrow_schema(&dtype)?)?), - _ => Ok(FFI_ArrowSchema::try_from( - arrow.to_arrow_field("", &dtype)?, - )?), + DType::Struct(struct_dtype, _) => Ok(FFI_ArrowSchema::try_from(Schema::new( + cuda_arrow_struct_fields(struct_dtype, ctx)?, + ))?), + _ => Ok(FFI_ArrowSchema::try_from(cuda_arrow_field( + "", &dtype, ctx, + )?)?), + } +} + +fn cuda_arrow_struct_fields( + struct_dtype: &StructFields, + ctx: &mut CudaExecutionCtx, +) -> VortexResult> { + let mut fields = Vec::with_capacity(struct_dtype.nfields()); + for (field_name, field_dtype) in struct_dtype.names().iter().zip(struct_dtype.fields()) { + fields.push(cuda_arrow_field(field_name.as_ref(), &field_dtype, ctx)?); + } + Ok(fields) +} + +fn cuda_arrow_field( + name: impl AsRef, + dtype: &DType, + ctx: &mut CudaExecutionCtx, +) -> VortexResult { + let field = ctx + .execution_ctx() + .session() + .arrow() + .to_arrow_field(name.as_ref(), dtype)?; + + let data_type = match dtype { + DType::Decimal(decimal_dtype, _) => cuda_arrow_decimal_data_type(*decimal_dtype), + DType::Struct(struct_dtype, _) => { + DataType::Struct(cuda_arrow_struct_fields(struct_dtype, ctx)?.into()) + } + _ => return Ok(field), + }; + + Ok( + Field::new(field.name().clone(), data_type, field.is_nullable()) + .with_metadata(field.metadata().clone()), + ) +} + +fn cuda_arrow_decimal_data_type(decimal_dtype: DecimalDType) -> DataType { + match cuda_decimal_value_type(decimal_dtype) { + DecimalType::I32 => DataType::Decimal32(decimal_dtype.precision(), decimal_dtype.scale()), + DecimalType::I64 => DataType::Decimal64(decimal_dtype.precision(), decimal_dtype.scale()), + DecimalType::I128 => DataType::Decimal128(decimal_dtype.precision(), decimal_dtype.scale()), + DecimalType::I256 => DataType::Decimal256(decimal_dtype.precision(), decimal_dtype.scale()), + decimal_type => unreachable!("unsupported decimal value type {decimal_type}"), + } +} + +pub(crate) fn cuda_decimal_value_type(decimal_dtype: DecimalDType) -> DecimalType { + match decimal_dtype.precision() { + 1..=9 => DecimalType::I32, + 10..=18 => DecimalType::I64, + 19..=38 => DecimalType::I128, + 39..=76 => DecimalType::I256, + p => unreachable!("precision {p} is invalid for a DecimalDType"), } } diff --git a/vortex-test/e2e-cuda/src/lib.rs b/vortex-test/e2e-cuda/src/lib.rs index 11a540ff70e..04b84efb03d 100644 --- a/vortex-test/e2e-cuda/src/lib.rs +++ b/vortex-test/e2e-cuda/src/lib.rs @@ -14,6 +14,8 @@ use std::sync::LazyLock; use arrow_array::Array; use arrow_array::ArrayRef as ArrowArrayRef; use arrow_array::Date32Array; +use arrow_array::Decimal32Array; +use arrow_array::Decimal64Array; use arrow_array::Decimal128Array; use arrow_array::StringArray; use arrow_array::cast::AsArray; @@ -146,9 +148,19 @@ fn export_array_inner(schema_ptr: &mut FFI_ArrowSchema, array_ptr: &mut ArrowDev return 1; } }; - let decimal = DecimalArray::from_option_iter( - [Some(0i128), Some(1), None, Some(3), Some(4)], - DecimalDType::new(38, 2), + // cuDF supports Arrow decimal device imports through Decimal128. Decimal256 is intentionally + // not included here because cuDF has no DECIMAL256 type_id or Arrow interop mapping. + let decimal32 = DecimalArray::from_option_iter( + [Some(0i8), Some(1), None, Some(3), Some(4)], + DecimalDType::new(9, 2), + ); + let decimal64 = DecimalArray::from_option_iter( + [Some(0i32), Some(1), None, Some(3), Some(4)], + DecimalDType::new(10, 2), + ); + let decimal128 = DecimalArray::from_option_iter( + [Some(0i64), Some(1), None, Some(3), Some(4)], + DecimalDType::new(19, 2), ); let strings = VarBinViewArray::from_iter_nullable_str([ Some("one"), @@ -166,7 +178,9 @@ fn export_array_inner(schema_ptr: &mut FFI_ArrowSchema, array_ptr: &mut ArrowDev let array = StructArray::new( FieldNames::from_iter([ "prims", - "decimals", + "decimal32", + "decimal64", + "decimal128", "strings", "dates", "lists", @@ -174,7 +188,9 @@ fn export_array_inner(schema_ptr: &mut FFI_ArrowSchema, array_ptr: &mut ArrowDev ]), vec![ primitive, - decimal.into_array(), + decimal32.into_array(), + decimal64.into_array(), + decimal128.into_array(), strings.into_array(), dates.into_array(), list_array(), @@ -244,7 +260,14 @@ fn validate_array_inner(ffi_schema: &FFI_ArrowSchema, ffi_array: &mut FFI_ArrowA &mut SESSION.create_execution_ctx(), ) .expect("expected primitive Arrow array"); - let decimal = Decimal128Array::from_iter([Some(0i128), Some(1), None, Some(3), Some(4)]) + let decimal32 = Decimal32Array::from_iter([Some(0i32), Some(1), None, Some(3), Some(4)]) + // cuDF stores decimals using the maximum precision for the physical width and preserves scale. + .with_precision_and_scale(9, 2) + .expect("with_precision_and_scale"); + let decimal64 = Decimal64Array::from_iter([Some(0i64), Some(1), None, Some(3), Some(4)]) + .with_precision_and_scale(18, 2) + .expect("with_precision_and_scale"); + let decimal128 = Decimal128Array::from_iter([Some(0i128), Some(1), None, Some(3), Some(4)]) .with_precision_and_scale(38, 2) .expect("with_precision_and_scale"); let string = StringArray::from_iter([ @@ -270,7 +293,9 @@ fn validate_array_inner(ffi_schema: &FFI_ArrowSchema, ffi_array: &mut FFI_ArrowA let expected_fields = Fields::from_iter([ Field::new("prims", primitive.data_type().clone(), true), - Field::new("decimals", decimal.data_type().clone(), true), + Field::new("decimal32", decimal32.data_type().clone(), true), + Field::new("decimal64", decimal64.data_type().clone(), true), + Field::new("decimal128", decimal128.data_type().clone(), true), Field::new("strings", string.data_type().clone(), true), Field::new("dates", date.data_type().clone(), true), cudf_list_field("lists"), @@ -278,12 +303,16 @@ fn validate_array_inner(ffi_schema: &FFI_ArrowSchema, ffi_array: &mut FFI_ArrowA ]); if &expected_fields != struct_array.fields() { eprintln!("wrong fields for host array"); + eprintln!("expected fields: {}", format_fields(&expected_fields)); + eprintln!("actual fields: {}", format_fields(struct_array.fields())); return 1; } - let expected_arrays: [ArrowArrayRef; 4] = [ + let expected_arrays: [ArrowArrayRef; 6] = [ primitive, - Arc::new(decimal), + Arc::new(decimal32), + Arc::new(decimal64), + Arc::new(decimal128), Arc::new(string), Arc::new(date), ]; @@ -299,11 +328,11 @@ fn validate_array_inner(ffi_schema: &FFI_ArrowSchema, ffi_array: &mut FFI_ArrowA } } - if !list_values_eq(list.as_ref(), struct_array.column(4).as_ref()) { + if !list_values_eq(list.as_ref(), struct_array.column(6).as_ref()) { eprintln!("wrong values for lists column"); return 1; } - if !list_values_eq(fixed_size_list.as_ref(), struct_array.column(5).as_ref()) { + if !list_values_eq(fixed_size_list.as_ref(), struct_array.column(7).as_ref()) { eprintln!("wrong values for fixed_lists column"); return 1; } @@ -315,6 +344,21 @@ fn cudf_list_field(name: &str) -> Field { Field::new_list(name, Field::new("element", DataType::Int32, false), true) } +fn format_fields(fields: &Fields) -> String { + fields + .iter() + .map(|field| { + format!( + "{}: {}{}", + field.name(), + field.data_type(), + if field.is_nullable() { "?" } else { "" } + ) + }) + .collect::>() + .join(", ") +} + fn list_values_eq(expected: &dyn Array, actual: &dyn Array) -> bool { let expected = expected.as_list::(); let actual = actual.as_list::(); From a26c0d6e6e2805217da945609bc8058607aa7bcb Mon Sep 17 00:00:00 2001 From: Alexander Droste Date: Wed, 3 Jun 2026 20:19:57 +0100 Subject: [PATCH 017/115] clean: drop unused API lock-file (#8241) Wipe the API lock file, as we're not using them anymore. Signed-off-by: Alexander Droste --- vortex-layout/public-api.lock | 2203 --------------------------------- 1 file changed, 2203 deletions(-) delete mode 100644 vortex-layout/public-api.lock diff --git a/vortex-layout/public-api.lock b/vortex-layout/public-api.lock deleted file mode 100644 index c794e93aa41..00000000000 --- a/vortex-layout/public-api.lock +++ /dev/null @@ -1,2203 +0,0 @@ -pub mod vortex_layout - -pub mod vortex_layout::aliases - -pub mod vortex_layout::aliases::paste - -pub use vortex_layout::aliases::paste::paste - -pub mod vortex_layout::display - -pub struct vortex_layout::display::DisplayLayoutTree - -impl vortex_layout::display::DisplayLayoutTree - -pub fn vortex_layout::display::DisplayLayoutTree::new(vortex_layout::LayoutRef, bool) -> Self - -impl core::fmt::Display for vortex_layout::display::DisplayLayoutTree - -pub fn vortex_layout::display::DisplayLayoutTree::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result - -pub mod vortex_layout::layouts - -pub mod vortex_layout::layouts::buffered - -pub struct vortex_layout::layouts::buffered::BufferedStrategy - -impl vortex_layout::layouts::buffered::BufferedStrategy - -pub fn vortex_layout::layouts::buffered::BufferedStrategy::new(S, u64) -> Self - -impl core::clone::Clone for vortex_layout::layouts::buffered::BufferedStrategy - -pub fn vortex_layout::layouts::buffered::BufferedStrategy::clone(&self) -> vortex_layout::layouts::buffered::BufferedStrategy - -impl vortex_layout::LayoutStrategy for vortex_layout::layouts::buffered::BufferedStrategy - -pub fn vortex_layout::layouts::buffered::BufferedStrategy::buffered_bytes(&self) -> u64 - -pub fn vortex_layout::layouts::buffered::BufferedStrategy::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait - -pub mod vortex_layout::layouts::chunked - -pub mod vortex_layout::layouts::chunked::writer - -pub struct vortex_layout::layouts::chunked::writer::ChunkedLayoutStrategy - -pub vortex_layout::layouts::chunked::writer::ChunkedLayoutStrategy::chunk_strategy: alloc::sync::Arc - -impl vortex_layout::layouts::chunked::writer::ChunkedLayoutStrategy - -pub fn vortex_layout::layouts::chunked::writer::ChunkedLayoutStrategy::new(S) -> Self - -impl core::clone::Clone for vortex_layout::layouts::chunked::writer::ChunkedLayoutStrategy - -pub fn vortex_layout::layouts::chunked::writer::ChunkedLayoutStrategy::clone(&self) -> vortex_layout::layouts::chunked::writer::ChunkedLayoutStrategy - -impl vortex_layout::LayoutStrategy for vortex_layout::layouts::chunked::writer::ChunkedLayoutStrategy - -pub fn vortex_layout::layouts::chunked::writer::ChunkedLayoutStrategy::buffered_bytes(&self) -> u64 - -pub fn vortex_layout::layouts::chunked::writer::ChunkedLayoutStrategy::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait - -pub struct vortex_layout::layouts::chunked::Chunked - -impl core::fmt::Debug for vortex_layout::layouts::chunked::Chunked - -pub fn vortex_layout::layouts::chunked::Chunked::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result - -impl vortex_layout::VTable for vortex_layout::layouts::chunked::Chunked - -pub type vortex_layout::layouts::chunked::Chunked::Encoding = vortex_layout::layouts::chunked::ChunkedLayoutEncoding - -pub type vortex_layout::layouts::chunked::Chunked::Layout = vortex_layout::layouts::chunked::ChunkedLayout - -pub type vortex_layout::layouts::chunked::Chunked::Metadata = vortex_array::metadata::EmptyMetadata - -pub fn vortex_layout::layouts::chunked::Chunked::build(&Self::Encoding, &vortex_array::dtype::DType, u64, &::Output, alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::chunked::Chunked::child(&Self::Layout, usize) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::chunked::Chunked::child_type(&Self::Layout, usize) -> vortex_layout::LayoutChildType - -pub fn vortex_layout::layouts::chunked::Chunked::dtype(&Self::Layout) -> &vortex_array::dtype::DType - -pub fn vortex_layout::layouts::chunked::Chunked::encoding(&Self::Layout) -> vortex_layout::LayoutEncodingRef - -pub fn vortex_layout::layouts::chunked::Chunked::id(&Self::Encoding) -> vortex_layout::LayoutId - -pub fn vortex_layout::layouts::chunked::Chunked::metadata(&Self::Layout) -> Self::Metadata - -pub fn vortex_layout::layouts::chunked::Chunked::nchildren(&Self::Layout) -> usize - -pub fn vortex_layout::layouts::chunked::Chunked::new_reader(&Self::Layout, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::chunked::Chunked::row_count(&Self::Layout) -> u64 - -pub fn vortex_layout::layouts::chunked::Chunked::segment_ids(&Self::Layout) -> alloc::vec::Vec - -pub fn vortex_layout::layouts::chunked::Chunked::with_children(&mut Self::Layout, alloc::vec::Vec) -> vortex_error::VortexResult<()> - -pub struct vortex_layout::layouts::chunked::ChunkedLayout - -impl vortex_layout::layouts::chunked::ChunkedLayout - -pub fn vortex_layout::layouts::chunked::ChunkedLayout::children(&self) -> &alloc::sync::Arc - -pub fn vortex_layout::layouts::chunked::ChunkedLayout::new(u64, vortex_array::dtype::DType, alloc::sync::Arc) -> Self - -impl core::clone::Clone for vortex_layout::layouts::chunked::ChunkedLayout - -pub fn vortex_layout::layouts::chunked::ChunkedLayout::clone(&self) -> vortex_layout::layouts::chunked::ChunkedLayout - -impl core::convert::AsRef for vortex_layout::layouts::chunked::ChunkedLayout - -pub fn vortex_layout::layouts::chunked::ChunkedLayout::as_ref(&self) -> &dyn vortex_layout::Layout - -impl core::convert::From for vortex_layout::LayoutRef - -pub fn vortex_layout::LayoutRef::from(vortex_layout::layouts::chunked::ChunkedLayout) -> vortex_layout::LayoutRef - -impl core::fmt::Debug for vortex_layout::layouts::chunked::ChunkedLayout - -pub fn vortex_layout::layouts::chunked::ChunkedLayout::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result - -impl core::ops::deref::Deref for vortex_layout::layouts::chunked::ChunkedLayout - -pub type vortex_layout::layouts::chunked::ChunkedLayout::Target = dyn vortex_layout::Layout - -pub fn vortex_layout::layouts::chunked::ChunkedLayout::deref(&self) -> &Self::Target - -impl vortex_layout::IntoLayout for vortex_layout::layouts::chunked::ChunkedLayout - -pub fn vortex_layout::layouts::chunked::ChunkedLayout::into_layout(self) -> vortex_layout::LayoutRef - -pub struct vortex_layout::layouts::chunked::ChunkedLayoutEncoding - -impl core::convert::AsRef for vortex_layout::layouts::chunked::ChunkedLayoutEncoding - -pub fn vortex_layout::layouts::chunked::ChunkedLayoutEncoding::as_ref(&self) -> &dyn vortex_layout::LayoutEncoding - -impl core::fmt::Debug for vortex_layout::layouts::chunked::ChunkedLayoutEncoding - -pub fn vortex_layout::layouts::chunked::ChunkedLayoutEncoding::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result - -impl core::ops::deref::Deref for vortex_layout::layouts::chunked::ChunkedLayoutEncoding - -pub type vortex_layout::layouts::chunked::ChunkedLayoutEncoding::Target = dyn vortex_layout::LayoutEncoding - -pub fn vortex_layout::layouts::chunked::ChunkedLayoutEncoding::deref(&self) -> &Self::Target - -pub mod vortex_layout::layouts::collect - -pub struct vortex_layout::layouts::collect::CollectStrategy - -impl vortex_layout::layouts::collect::CollectStrategy - -pub fn vortex_layout::layouts::collect::CollectStrategy::new(S) -> vortex_layout::layouts::collect::CollectStrategy - -impl vortex_layout::LayoutStrategy for vortex_layout::layouts::collect::CollectStrategy - -pub fn vortex_layout::layouts::collect::CollectStrategy::buffered_bytes(&self) -> u64 - -pub fn vortex_layout::layouts::collect::CollectStrategy::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait - -pub mod vortex_layout::layouts::compressed - -pub struct vortex_layout::layouts::compressed::CompressingStrategy - -impl vortex_layout::layouts::compressed::CompressingStrategy - -pub fn vortex_layout::layouts::compressed::CompressingStrategy::new(S, C) -> Self - -pub fn vortex_layout::layouts::compressed::CompressingStrategy::with_concurrency(self, usize) -> Self - -pub fn vortex_layout::layouts::compressed::CompressingStrategy::with_stats(self, &[vortex_array::expr::stats::Stat]) -> Self - -impl core::clone::Clone for vortex_layout::layouts::compressed::CompressingStrategy - -pub fn vortex_layout::layouts::compressed::CompressingStrategy::clone(&self) -> vortex_layout::layouts::compressed::CompressingStrategy - -impl vortex_layout::LayoutStrategy for vortex_layout::layouts::compressed::CompressingStrategy - -pub fn vortex_layout::layouts::compressed::CompressingStrategy::buffered_bytes(&self) -> u64 - -pub fn vortex_layout::layouts::compressed::CompressingStrategy::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait - -pub trait vortex_layout::layouts::compressed::CompressorPlugin: core::marker::Send + core::marker::Sync + 'static - -pub fn vortex_layout::layouts::compressed::CompressorPlugin::compress_chunk(&self, &vortex_array::array::erased::ArrayRef, &mut vortex_array::executor::ExecutionCtx) -> vortex_error::VortexResult - -impl vortex_layout::layouts::compressed::CompressorPlugin for alloc::sync::Arc - -pub fn alloc::sync::Arc::compress_chunk(&self, &vortex_array::array::erased::ArrayRef, &mut vortex_array::executor::ExecutionCtx) -> vortex_error::VortexResult - -impl vortex_layout::layouts::compressed::CompressorPlugin for vortex_btrblocks::canonical_compressor::BtrBlocksCompressor - -pub fn vortex_btrblocks::canonical_compressor::BtrBlocksCompressor::compress_chunk(&self, &vortex_array::array::erased::ArrayRef, &mut vortex_array::executor::ExecutionCtx) -> vortex_error::VortexResult - -impl vortex_layout::layouts::compressed::CompressorPlugin for F where F: core::ops::function::Fn(&vortex_array::array::erased::ArrayRef, &mut vortex_array::executor::ExecutionCtx) -> vortex_error::VortexResult + core::marker::Send + core::marker::Sync + 'static - -pub fn F::compress_chunk(&self, &vortex_array::array::erased::ArrayRef, &mut vortex_array::executor::ExecutionCtx) -> vortex_error::VortexResult - -pub mod vortex_layout::layouts::dict - -pub mod vortex_layout::layouts::dict::writer - -pub struct vortex_layout::layouts::dict::writer::DictLayoutConstraints - -pub vortex_layout::layouts::dict::writer::DictLayoutConstraints::max_bytes: usize - -pub vortex_layout::layouts::dict::writer::DictLayoutConstraints::max_len: u16 - -impl core::clone::Clone for vortex_layout::layouts::dict::writer::DictLayoutConstraints - -pub fn vortex_layout::layouts::dict::writer::DictLayoutConstraints::clone(&self) -> vortex_layout::layouts::dict::writer::DictLayoutConstraints - -impl core::convert::From for vortex_array::builders::dict::DictConstraints - -pub fn vortex_array::builders::dict::DictConstraints::from(vortex_layout::layouts::dict::writer::DictLayoutConstraints) -> Self - -impl core::default::Default for vortex_layout::layouts::dict::writer::DictLayoutConstraints - -pub fn vortex_layout::layouts::dict::writer::DictLayoutConstraints::default() -> Self - -pub struct vortex_layout::layouts::dict::writer::DictLayoutMetadata - -impl vortex_layout::layouts::dict::writer::DictLayoutMetadata - -pub fn vortex_layout::layouts::dict::writer::DictLayoutMetadata::codes_ptype(&self) -> vortex_array::dtype::ptype::PType - -pub fn vortex_layout::layouts::dict::writer::DictLayoutMetadata::set_codes_ptype(&mut self, vortex_array::dtype::ptype::PType) - -impl vortex_layout::layouts::dict::writer::DictLayoutMetadata - -pub fn vortex_layout::layouts::dict::writer::DictLayoutMetadata::new(vortex_array::dtype::ptype::PType) -> Self - -impl core::default::Default for vortex_layout::layouts::dict::writer::DictLayoutMetadata - -pub fn vortex_layout::layouts::dict::writer::DictLayoutMetadata::default() -> Self - -impl core::fmt::Debug for vortex_layout::layouts::dict::writer::DictLayoutMetadata - -pub fn vortex_layout::layouts::dict::writer::DictLayoutMetadata::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result - -impl prost::message::Message for vortex_layout::layouts::dict::writer::DictLayoutMetadata - -pub fn vortex_layout::layouts::dict::writer::DictLayoutMetadata::clear(&mut self) - -pub fn vortex_layout::layouts::dict::writer::DictLayoutMetadata::encoded_len(&self) -> usize - -pub struct vortex_layout::layouts::dict::writer::DictLayoutOptions - -pub vortex_layout::layouts::dict::writer::DictLayoutOptions::constraints: vortex_layout::layouts::dict::writer::DictLayoutConstraints - -impl core::clone::Clone for vortex_layout::layouts::dict::writer::DictLayoutOptions - -pub fn vortex_layout::layouts::dict::writer::DictLayoutOptions::clone(&self) -> vortex_layout::layouts::dict::writer::DictLayoutOptions - -impl core::default::Default for vortex_layout::layouts::dict::writer::DictLayoutOptions - -pub fn vortex_layout::layouts::dict::writer::DictLayoutOptions::default() -> vortex_layout::layouts::dict::writer::DictLayoutOptions - -pub struct vortex_layout::layouts::dict::writer::DictStrategy - -impl vortex_layout::layouts::dict::writer::DictStrategy - -pub fn vortex_layout::layouts::dict::writer::DictStrategy::new(Codes, Values, Fallback, vortex_layout::layouts::dict::writer::DictLayoutOptions) -> Self - -impl core::clone::Clone for vortex_layout::layouts::dict::writer::DictStrategy - -pub fn vortex_layout::layouts::dict::writer::DictStrategy::clone(&self) -> vortex_layout::layouts::dict::writer::DictStrategy - -impl vortex_layout::LayoutStrategy for vortex_layout::layouts::dict::writer::DictStrategy - -pub fn vortex_layout::layouts::dict::writer::DictStrategy::buffered_bytes(&self) -> u64 - -pub fn vortex_layout::layouts::dict::writer::DictStrategy::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait - -pub fn vortex_layout::layouts::dict::writer::dict_layout_supported(&vortex_array::dtype::DType) -> bool - -pub struct vortex_layout::layouts::dict::Dict - -impl core::fmt::Debug for vortex_layout::layouts::dict::Dict - -pub fn vortex_layout::layouts::dict::Dict::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result - -impl vortex_layout::VTable for vortex_layout::layouts::dict::Dict - -pub type vortex_layout::layouts::dict::Dict::Encoding = vortex_layout::layouts::dict::DictLayoutEncoding - -pub type vortex_layout::layouts::dict::Dict::Layout = vortex_layout::layouts::dict::DictLayout - -pub type vortex_layout::layouts::dict::Dict::Metadata = vortex_array::metadata::ProstMetadata - -pub fn vortex_layout::layouts::dict::Dict::build(&Self::Encoding, &vortex_array::dtype::DType, u64, &::Output, alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::dict::Dict::child(&Self::Layout, usize) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::dict::Dict::child_type(&Self::Layout, usize) -> vortex_layout::LayoutChildType - -pub fn vortex_layout::layouts::dict::Dict::dtype(&Self::Layout) -> &vortex_array::dtype::DType - -pub fn vortex_layout::layouts::dict::Dict::encoding(&Self::Layout) -> vortex_layout::LayoutEncodingRef - -pub fn vortex_layout::layouts::dict::Dict::id(&Self::Encoding) -> vortex_layout::LayoutId - -pub fn vortex_layout::layouts::dict::Dict::metadata(&Self::Layout) -> Self::Metadata - -pub fn vortex_layout::layouts::dict::Dict::nchildren(&Self::Layout) -> usize - -pub fn vortex_layout::layouts::dict::Dict::new_reader(&Self::Layout, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::dict::Dict::row_count(&Self::Layout) -> u64 - -pub fn vortex_layout::layouts::dict::Dict::segment_ids(&Self::Layout) -> alloc::vec::Vec - -pub fn vortex_layout::layouts::dict::Dict::with_children(&mut Self::Layout, alloc::vec::Vec) -> vortex_error::VortexResult<()> - -pub struct vortex_layout::layouts::dict::DictLayout - -impl vortex_layout::layouts::dict::DictLayout - -pub fn vortex_layout::layouts::dict::DictLayout::has_all_values_referenced(&self) -> bool - -pub unsafe fn vortex_layout::layouts::dict::DictLayout::set_all_values_referenced(self, bool) -> Self - -impl core::clone::Clone for vortex_layout::layouts::dict::DictLayout - -pub fn vortex_layout::layouts::dict::DictLayout::clone(&self) -> vortex_layout::layouts::dict::DictLayout - -impl core::convert::AsRef for vortex_layout::layouts::dict::DictLayout - -pub fn vortex_layout::layouts::dict::DictLayout::as_ref(&self) -> &dyn vortex_layout::Layout - -impl core::convert::From for vortex_layout::LayoutRef - -pub fn vortex_layout::LayoutRef::from(vortex_layout::layouts::dict::DictLayout) -> vortex_layout::LayoutRef - -impl core::fmt::Debug for vortex_layout::layouts::dict::DictLayout - -pub fn vortex_layout::layouts::dict::DictLayout::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result - -impl core::ops::deref::Deref for vortex_layout::layouts::dict::DictLayout - -pub type vortex_layout::layouts::dict::DictLayout::Target = dyn vortex_layout::Layout - -pub fn vortex_layout::layouts::dict::DictLayout::deref(&self) -> &Self::Target - -impl vortex_layout::IntoLayout for vortex_layout::layouts::dict::DictLayout - -pub fn vortex_layout::layouts::dict::DictLayout::into_layout(self) -> vortex_layout::LayoutRef - -pub struct vortex_layout::layouts::dict::DictLayoutEncoding - -impl core::convert::AsRef for vortex_layout::layouts::dict::DictLayoutEncoding - -pub fn vortex_layout::layouts::dict::DictLayoutEncoding::as_ref(&self) -> &dyn vortex_layout::LayoutEncoding - -impl core::fmt::Debug for vortex_layout::layouts::dict::DictLayoutEncoding - -pub fn vortex_layout::layouts::dict::DictLayoutEncoding::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result - -impl core::ops::deref::Deref for vortex_layout::layouts::dict::DictLayoutEncoding - -pub type vortex_layout::layouts::dict::DictLayoutEncoding::Target = dyn vortex_layout::LayoutEncoding - -pub fn vortex_layout::layouts::dict::DictLayoutEncoding::deref(&self) -> &Self::Target - -pub struct vortex_layout::layouts::dict::DictLayoutMetadata - -impl vortex_layout::layouts::dict::DictLayoutMetadata - -pub fn vortex_layout::layouts::dict::DictLayoutMetadata::all_values_referenced(&self) -> bool - -pub fn vortex_layout::layouts::dict::DictLayoutMetadata::codes_ptype(&self) -> vortex_array::dtype::ptype::PType - -pub fn vortex_layout::layouts::dict::DictLayoutMetadata::is_nullable_codes(&self) -> bool - -pub fn vortex_layout::layouts::dict::DictLayoutMetadata::set_codes_ptype(&mut self, vortex_array::dtype::ptype::PType) - -impl vortex_layout::layouts::dict::DictLayoutMetadata - -pub fn vortex_layout::layouts::dict::DictLayoutMetadata::new(vortex_array::dtype::ptype::PType) -> Self - -impl core::default::Default for vortex_layout::layouts::dict::DictLayoutMetadata - -pub fn vortex_layout::layouts::dict::DictLayoutMetadata::default() -> Self - -impl core::fmt::Debug for vortex_layout::layouts::dict::DictLayoutMetadata - -pub fn vortex_layout::layouts::dict::DictLayoutMetadata::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result - -impl prost::message::Message for vortex_layout::layouts::dict::DictLayoutMetadata - -pub fn vortex_layout::layouts::dict::DictLayoutMetadata::clear(&mut self) - -pub fn vortex_layout::layouts::dict::DictLayoutMetadata::encoded_len(&self) -> usize - -pub mod vortex_layout::layouts::file_stats - -pub struct vortex_layout::layouts::file_stats::FileStatsAccumulator - -impl vortex_layout::layouts::file_stats::FileStatsAccumulator - -pub fn vortex_layout::layouts::file_stats::FileStatsAccumulator::stats_sets(&self) -> alloc::vec::Vec - -impl core::clone::Clone for vortex_layout::layouts::file_stats::FileStatsAccumulator - -pub fn vortex_layout::layouts::file_stats::FileStatsAccumulator::clone(&self) -> vortex_layout::layouts::file_stats::FileStatsAccumulator - -pub fn vortex_layout::layouts::file_stats::accumulate_stats(vortex_layout::sequence::SendableSequentialStream, alloc::sync::Arc<[vortex_array::expr::stats::Stat]>, usize, &vortex_session::VortexSession) -> (vortex_layout::layouts::file_stats::FileStatsAccumulator, vortex_layout::sequence::SendableSequentialStream) - -pub mod vortex_layout::layouts::flat - -pub mod vortex_layout::layouts::flat::writer - -pub struct vortex_layout::layouts::flat::writer::FlatLayoutStrategy - -pub vortex_layout::layouts::flat::writer::FlatLayoutStrategy::allowed_encodings: core::option::Option> - -pub vortex_layout::layouts::flat::writer::FlatLayoutStrategy::include_padding: bool - -pub vortex_layout::layouts::flat::writer::FlatLayoutStrategy::max_variable_length_statistics_size: usize - -impl vortex_layout::layouts::flat::writer::FlatLayoutStrategy - -pub fn vortex_layout::layouts::flat::writer::FlatLayoutStrategy::with_allow_encodings(self, vortex_utils::aliases::hash_set::HashSet) -> Self - -pub fn vortex_layout::layouts::flat::writer::FlatLayoutStrategy::with_include_padding(self, bool) -> Self - -pub fn vortex_layout::layouts::flat::writer::FlatLayoutStrategy::with_max_variable_length_statistics_size(self, usize) -> Self - -impl core::clone::Clone for vortex_layout::layouts::flat::writer::FlatLayoutStrategy - -pub fn vortex_layout::layouts::flat::writer::FlatLayoutStrategy::clone(&self) -> vortex_layout::layouts::flat::writer::FlatLayoutStrategy - -impl core::default::Default for vortex_layout::layouts::flat::writer::FlatLayoutStrategy - -pub fn vortex_layout::layouts::flat::writer::FlatLayoutStrategy::default() -> Self - -impl vortex_layout::LayoutStrategy for vortex_layout::layouts::flat::writer::FlatLayoutStrategy - -pub fn vortex_layout::layouts::flat::writer::FlatLayoutStrategy::buffered_bytes(&self) -> u64 - -pub fn vortex_layout::layouts::flat::writer::FlatLayoutStrategy::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait - -pub struct vortex_layout::layouts::flat::Flat - -impl core::fmt::Debug for vortex_layout::layouts::flat::Flat - -pub fn vortex_layout::layouts::flat::Flat::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result - -impl vortex_layout::VTable for vortex_layout::layouts::flat::Flat - -pub type vortex_layout::layouts::flat::Flat::Encoding = vortex_layout::layouts::flat::FlatLayoutEncoding - -pub type vortex_layout::layouts::flat::Flat::Layout = vortex_layout::layouts::flat::FlatLayout - -pub type vortex_layout::layouts::flat::Flat::Metadata = vortex_array::metadata::ProstMetadata - -pub fn vortex_layout::layouts::flat::Flat::build(&Self::Encoding, &vortex_array::dtype::DType, u64, &::Output, alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::flat::Flat::child(&Self::Layout, usize) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::flat::Flat::child_type(&Self::Layout, usize) -> vortex_layout::LayoutChildType - -pub fn vortex_layout::layouts::flat::Flat::dtype(&Self::Layout) -> &vortex_array::dtype::DType - -pub fn vortex_layout::layouts::flat::Flat::encoding(&Self::Layout) -> vortex_layout::LayoutEncodingRef - -pub fn vortex_layout::layouts::flat::Flat::id(&Self::Encoding) -> vortex_layout::LayoutId - -pub fn vortex_layout::layouts::flat::Flat::metadata(&Self::Layout) -> Self::Metadata - -pub fn vortex_layout::layouts::flat::Flat::nchildren(&Self::Layout) -> usize - -pub fn vortex_layout::layouts::flat::Flat::new_reader(&Self::Layout, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::flat::Flat::row_count(&Self::Layout) -> u64 - -pub fn vortex_layout::layouts::flat::Flat::segment_ids(&Self::Layout) -> alloc::vec::Vec - -pub fn vortex_layout::layouts::flat::Flat::with_children(&mut Self::Layout, alloc::vec::Vec) -> vortex_error::VortexResult<()> - -pub struct vortex_layout::layouts::flat::FlatLayout - -impl vortex_layout::layouts::flat::FlatLayout - -pub fn vortex_layout::layouts::flat::FlatLayout::array_ctx(&self) -> &vortex_session::registry::ReadContext - -pub fn vortex_layout::layouts::flat::FlatLayout::array_tree(&self) -> core::option::Option<&vortex_buffer::ByteBuffer> - -pub fn vortex_layout::layouts::flat::FlatLayout::new(u64, vortex_array::dtype::DType, vortex_layout::segments::SegmentId, vortex_session::registry::ReadContext) -> Self - -pub fn vortex_layout::layouts::flat::FlatLayout::new_with_metadata(u64, vortex_array::dtype::DType, vortex_layout::segments::SegmentId, vortex_session::registry::ReadContext, core::option::Option) -> Self - -pub fn vortex_layout::layouts::flat::FlatLayout::segment_id(&self) -> vortex_layout::segments::SegmentId - -impl core::clone::Clone for vortex_layout::layouts::flat::FlatLayout - -pub fn vortex_layout::layouts::flat::FlatLayout::clone(&self) -> vortex_layout::layouts::flat::FlatLayout - -impl core::convert::AsRef for vortex_layout::layouts::flat::FlatLayout - -pub fn vortex_layout::layouts::flat::FlatLayout::as_ref(&self) -> &dyn vortex_layout::Layout - -impl core::convert::From for vortex_layout::LayoutRef - -pub fn vortex_layout::LayoutRef::from(vortex_layout::layouts::flat::FlatLayout) -> vortex_layout::LayoutRef - -impl core::fmt::Debug for vortex_layout::layouts::flat::FlatLayout - -pub fn vortex_layout::layouts::flat::FlatLayout::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result - -impl core::ops::deref::Deref for vortex_layout::layouts::flat::FlatLayout - -pub type vortex_layout::layouts::flat::FlatLayout::Target = dyn vortex_layout::Layout - -pub fn vortex_layout::layouts::flat::FlatLayout::deref(&self) -> &Self::Target - -impl vortex_layout::IntoLayout for vortex_layout::layouts::flat::FlatLayout - -pub fn vortex_layout::layouts::flat::FlatLayout::into_layout(self) -> vortex_layout::LayoutRef - -pub struct vortex_layout::layouts::flat::FlatLayoutEncoding - -impl core::convert::AsRef for vortex_layout::layouts::flat::FlatLayoutEncoding - -pub fn vortex_layout::layouts::flat::FlatLayoutEncoding::as_ref(&self) -> &dyn vortex_layout::LayoutEncoding - -impl core::fmt::Debug for vortex_layout::layouts::flat::FlatLayoutEncoding - -pub fn vortex_layout::layouts::flat::FlatLayoutEncoding::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result - -impl core::ops::deref::Deref for vortex_layout::layouts::flat::FlatLayoutEncoding - -pub type vortex_layout::layouts::flat::FlatLayoutEncoding::Target = dyn vortex_layout::LayoutEncoding - -pub fn vortex_layout::layouts::flat::FlatLayoutEncoding::deref(&self) -> &Self::Target - -pub struct vortex_layout::layouts::flat::FlatLayoutMetadata - -pub vortex_layout::layouts::flat::FlatLayoutMetadata::array_encoding_tree: core::option::Option> - -impl vortex_layout::layouts::flat::FlatLayoutMetadata - -pub fn vortex_layout::layouts::flat::FlatLayoutMetadata::array_encoding_tree(&self) -> &[u8] - -impl core::default::Default for vortex_layout::layouts::flat::FlatLayoutMetadata - -pub fn vortex_layout::layouts::flat::FlatLayoutMetadata::default() -> Self - -impl core::fmt::Debug for vortex_layout::layouts::flat::FlatLayoutMetadata - -pub fn vortex_layout::layouts::flat::FlatLayoutMetadata::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result - -impl prost::message::Message for vortex_layout::layouts::flat::FlatLayoutMetadata - -pub fn vortex_layout::layouts::flat::FlatLayoutMetadata::clear(&mut self) - -pub fn vortex_layout::layouts::flat::FlatLayoutMetadata::encoded_len(&self) -> usize - -pub mod vortex_layout::layouts::repartition - -pub struct vortex_layout::layouts::repartition::RepartitionStrategy - -impl vortex_layout::layouts::repartition::RepartitionStrategy - -pub fn vortex_layout::layouts::repartition::RepartitionStrategy::new(S, vortex_layout::layouts::repartition::RepartitionWriterOptions) -> Self - -impl core::clone::Clone for vortex_layout::layouts::repartition::RepartitionStrategy - -pub fn vortex_layout::layouts::repartition::RepartitionStrategy::clone(&self) -> vortex_layout::layouts::repartition::RepartitionStrategy - -impl vortex_layout::LayoutStrategy for vortex_layout::layouts::repartition::RepartitionStrategy - -pub fn vortex_layout::layouts::repartition::RepartitionStrategy::buffered_bytes(&self) -> u64 - -pub fn vortex_layout::layouts::repartition::RepartitionStrategy::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait - -pub struct vortex_layout::layouts::repartition::RepartitionWriterOptions - -pub vortex_layout::layouts::repartition::RepartitionWriterOptions::block_len_multiple: usize - -pub vortex_layout::layouts::repartition::RepartitionWriterOptions::block_size_minimum: u64 - -pub vortex_layout::layouts::repartition::RepartitionWriterOptions::block_size_target: core::option::Option - -pub vortex_layout::layouts::repartition::RepartitionWriterOptions::canonicalize: bool - -impl core::clone::Clone for vortex_layout::layouts::repartition::RepartitionWriterOptions - -pub fn vortex_layout::layouts::repartition::RepartitionWriterOptions::clone(&self) -> vortex_layout::layouts::repartition::RepartitionWriterOptions - -pub mod vortex_layout::layouts::row_idx - -pub struct vortex_layout::layouts::row_idx::RowIdx - -impl core::clone::Clone for vortex_layout::layouts::row_idx::RowIdx - -pub fn vortex_layout::layouts::row_idx::RowIdx::clone(&self) -> vortex_layout::layouts::row_idx::RowIdx - -impl vortex_array::scalar_fn::vtable::ScalarFnVTable for vortex_layout::layouts::row_idx::RowIdx - -pub type vortex_layout::layouts::row_idx::RowIdx::Options = vortex_array::scalar_fn::vtable::EmptyOptions - -pub fn vortex_layout::layouts::row_idx::RowIdx::arity(&self, &Self::Options) -> vortex_array::scalar_fn::vtable::Arity - -pub fn vortex_layout::layouts::row_idx::RowIdx::child_name(&self, &Self::Options, usize) -> vortex_array::scalar_fn::vtable::ChildName - -pub fn vortex_layout::layouts::row_idx::RowIdx::execute(&self, &Self::Options, &dyn vortex_array::scalar_fn::vtable::ExecutionArgs, &mut vortex_array::executor::ExecutionCtx) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::row_idx::RowIdx::fmt_sql(&self, &Self::Options, &vortex_array::expr::expression::Expression, &mut core::fmt::Formatter<'_>) -> core::fmt::Result - -pub fn vortex_layout::layouts::row_idx::RowIdx::id(&self) -> vortex_array::scalar_fn::ScalarFnId - -pub fn vortex_layout::layouts::row_idx::RowIdx::return_dtype(&self, &Self::Options, &[vortex_array::dtype::DType]) -> vortex_error::VortexResult - -pub struct vortex_layout::layouts::row_idx::RowIdxLayoutReader - -impl vortex_layout::layouts::row_idx::RowIdxLayoutReader - -pub fn vortex_layout::layouts::row_idx::RowIdxLayoutReader::new(u64, alloc::sync::Arc, vortex_session::VortexSession) -> Self - -impl vortex_layout::LayoutReader for vortex_layout::layouts::row_idx::RowIdxLayoutReader - -pub fn vortex_layout::layouts::row_idx::RowIdxLayoutReader::as_any(&self) -> &dyn core::any::Any - -pub fn vortex_layout::layouts::row_idx::RowIdxLayoutReader::dtype(&self) -> &vortex_array::dtype::DType - -pub fn vortex_layout::layouts::row_idx::RowIdxLayoutReader::filter_evaluation(&self, &core::ops::range::Range, &vortex_array::expr::expression::Expression, vortex_array::mask_future::MaskFuture) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::row_idx::RowIdxLayoutReader::name(&self) -> &alloc::sync::Arc - -pub fn vortex_layout::layouts::row_idx::RowIdxLayoutReader::projection_evaluation(&self, &core::ops::range::Range, &vortex_array::expr::expression::Expression, vortex_array::mask_future::MaskFuture) -> vortex_error::VortexResult>> - -pub fn vortex_layout::layouts::row_idx::RowIdxLayoutReader::pruning_evaluation(&self, &core::ops::range::Range, &vortex_array::expr::expression::Expression, vortex_mask::Mask) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::row_idx::RowIdxLayoutReader::register_splits(&self, &[vortex_array::dtype::field_mask::FieldMask], &vortex_layout::SplitRange, &mut alloc::collections::btree::set::BTreeSet) -> vortex_error::VortexResult<()> - -pub fn vortex_layout::layouts::row_idx::RowIdxLayoutReader::row_count(&self) -> u64 - -pub fn vortex_layout::layouts::row_idx::row_idx() -> vortex_array::expr::expression::Expression - -pub mod vortex_layout::layouts::struct_ - -pub struct vortex_layout::layouts::struct_::Struct - -impl core::fmt::Debug for vortex_layout::layouts::struct_::Struct - -pub fn vortex_layout::layouts::struct_::Struct::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result - -impl vortex_layout::VTable for vortex_layout::layouts::struct_::Struct - -pub type vortex_layout::layouts::struct_::Struct::Encoding = vortex_layout::layouts::struct_::StructLayoutEncoding - -pub type vortex_layout::layouts::struct_::Struct::Layout = vortex_layout::layouts::struct_::StructLayout - -pub type vortex_layout::layouts::struct_::Struct::Metadata = vortex_array::metadata::EmptyMetadata - -pub fn vortex_layout::layouts::struct_::Struct::build(&Self::Encoding, &vortex_array::dtype::DType, u64, &::Output, alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::struct_::Struct::child(&Self::Layout, usize) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::struct_::Struct::child_type(&Self::Layout, usize) -> vortex_layout::LayoutChildType - -pub fn vortex_layout::layouts::struct_::Struct::dtype(&Self::Layout) -> &vortex_array::dtype::DType - -pub fn vortex_layout::layouts::struct_::Struct::encoding(&Self::Layout) -> vortex_layout::LayoutEncodingRef - -pub fn vortex_layout::layouts::struct_::Struct::id(&Self::Encoding) -> vortex_layout::LayoutId - -pub fn vortex_layout::layouts::struct_::Struct::metadata(&Self::Layout) -> Self::Metadata - -pub fn vortex_layout::layouts::struct_::Struct::nchildren(&Self::Layout) -> usize - -pub fn vortex_layout::layouts::struct_::Struct::new_reader(&Self::Layout, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::struct_::Struct::row_count(&Self::Layout) -> u64 - -pub fn vortex_layout::layouts::struct_::Struct::segment_ids(&Self::Layout) -> alloc::vec::Vec - -pub fn vortex_layout::layouts::struct_::Struct::with_children(&mut Self::Layout, alloc::vec::Vec) -> vortex_error::VortexResult<()> - -pub struct vortex_layout::layouts::struct_::StructLayout - -impl vortex_layout::layouts::struct_::StructLayout - -pub fn vortex_layout::layouts::struct_::StructLayout::children(&self) -> &alloc::sync::Arc - -pub fn vortex_layout::layouts::struct_::StructLayout::matching_fields(&self, &[vortex_array::dtype::field_mask::FieldMask], F) -> vortex_error::VortexResult<()> where F: core::ops::function::FnMut(vortex_array::dtype::field_mask::FieldMask, usize) -> vortex_error::VortexResult<()> - -pub fn vortex_layout::layouts::struct_::StructLayout::new(u64, vortex_array::dtype::DType, alloc::vec::Vec) -> Self - -pub fn vortex_layout::layouts::struct_::StructLayout::row_count(&self) -> u64 - -pub fn vortex_layout::layouts::struct_::StructLayout::struct_fields(&self) -> &vortex_array::dtype::struct_::StructFields - -impl core::clone::Clone for vortex_layout::layouts::struct_::StructLayout - -pub fn vortex_layout::layouts::struct_::StructLayout::clone(&self) -> vortex_layout::layouts::struct_::StructLayout - -impl core::convert::AsRef for vortex_layout::layouts::struct_::StructLayout - -pub fn vortex_layout::layouts::struct_::StructLayout::as_ref(&self) -> &dyn vortex_layout::Layout - -impl core::convert::From for vortex_layout::LayoutRef - -pub fn vortex_layout::LayoutRef::from(vortex_layout::layouts::struct_::StructLayout) -> vortex_layout::LayoutRef - -impl core::fmt::Debug for vortex_layout::layouts::struct_::StructLayout - -pub fn vortex_layout::layouts::struct_::StructLayout::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result - -impl core::ops::deref::Deref for vortex_layout::layouts::struct_::StructLayout - -pub type vortex_layout::layouts::struct_::StructLayout::Target = dyn vortex_layout::Layout - -pub fn vortex_layout::layouts::struct_::StructLayout::deref(&self) -> &Self::Target - -impl vortex_layout::IntoLayout for vortex_layout::layouts::struct_::StructLayout - -pub fn vortex_layout::layouts::struct_::StructLayout::into_layout(self) -> vortex_layout::LayoutRef - -pub struct vortex_layout::layouts::struct_::StructLayoutEncoding - -impl core::convert::AsRef for vortex_layout::layouts::struct_::StructLayoutEncoding - -pub fn vortex_layout::layouts::struct_::StructLayoutEncoding::as_ref(&self) -> &dyn vortex_layout::LayoutEncoding - -impl core::fmt::Debug for vortex_layout::layouts::struct_::StructLayoutEncoding - -pub fn vortex_layout::layouts::struct_::StructLayoutEncoding::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result - -impl core::ops::deref::Deref for vortex_layout::layouts::struct_::StructLayoutEncoding - -pub type vortex_layout::layouts::struct_::StructLayoutEncoding::Target = dyn vortex_layout::LayoutEncoding - -pub fn vortex_layout::layouts::struct_::StructLayoutEncoding::deref(&self) -> &Self::Target - -pub mod vortex_layout::layouts::table - -pub struct vortex_layout::layouts::table::TableStrategy - -impl vortex_layout::layouts::table::TableStrategy - -pub fn vortex_layout::layouts::table::TableStrategy::new(alloc::sync::Arc, alloc::sync::Arc) -> Self - -pub fn vortex_layout::layouts::table::TableStrategy::with_default_strategy(self, alloc::sync::Arc) -> Self - -pub fn vortex_layout::layouts::table::TableStrategy::with_field_writer(self, impl core::convert::Into, alloc::sync::Arc) -> Self - -pub fn vortex_layout::layouts::table::TableStrategy::with_field_writers(self, impl core::iter::traits::collect::IntoIterator)>) -> Self - -pub fn vortex_layout::layouts::table::TableStrategy::with_validity_strategy(self, alloc::sync::Arc) -> Self - -impl vortex_layout::LayoutStrategy for vortex_layout::layouts::table::TableStrategy - -pub fn vortex_layout::layouts::table::TableStrategy::buffered_bytes(&self) -> u64 - -pub fn vortex_layout::layouts::table::TableStrategy::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait - -pub mod vortex_layout::layouts::zoned - -pub mod vortex_layout::layouts::zoned::writer - -pub struct vortex_layout::layouts::zoned::writer::ZonedLayoutOptions - -pub vortex_layout::layouts::zoned::writer::ZonedLayoutOptions::block_size: usize - -pub vortex_layout::layouts::zoned::writer::ZonedLayoutOptions::concurrency: usize - -pub vortex_layout::layouts::zoned::writer::ZonedLayoutOptions::max_variable_length_statistics_size: usize - -pub vortex_layout::layouts::zoned::writer::ZonedLayoutOptions::stats: alloc::sync::Arc<[vortex_array::expr::stats::Stat]> - -impl core::default::Default for vortex_layout::layouts::zoned::writer::ZonedLayoutOptions - -pub fn vortex_layout::layouts::zoned::writer::ZonedLayoutOptions::default() -> Self - -pub struct vortex_layout::layouts::zoned::writer::ZonedStrategy - -impl vortex_layout::layouts::zoned::writer::ZonedStrategy - -pub fn vortex_layout::layouts::zoned::writer::ZonedStrategy::new(Child, Stats, vortex_layout::layouts::zoned::writer::ZonedLayoutOptions) -> Self - -impl vortex_layout::LayoutStrategy for vortex_layout::layouts::zoned::writer::ZonedStrategy - -pub fn vortex_layout::layouts::zoned::writer::ZonedStrategy::buffered_bytes(&self) -> u64 - -pub fn vortex_layout::layouts::zoned::writer::ZonedStrategy::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait - -pub mod vortex_layout::layouts::zoned::zone_map - -pub struct vortex_layout::layouts::zoned::zone_map::ZoneMap - -impl vortex_layout::layouts::zoned::zone_map::ZoneMap - -pub fn vortex_layout::layouts::zoned::zone_map::ZoneMap::dtype_for_stats_table(&vortex_array::dtype::DType, &[vortex_array::expr::stats::Stat]) -> vortex_array::dtype::DType - -pub unsafe fn vortex_layout::layouts::zoned::zone_map::ZoneMap::new_unchecked(vortex_array::arrays::struct_::vtable::StructArray, u64, u64) -> Self - -pub fn vortex_layout::layouts::zoned::zone_map::ZoneMap::prune(&self, &vortex_array::expr::expression::Expression, &vortex_session::VortexSession) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::zoned::zone_map::ZoneMap::try_new(vortex_array::dtype::DType, vortex_array::arrays::struct_::vtable::StructArray, alloc::sync::Arc<[vortex_array::expr::stats::Stat]>, u64, u64) -> vortex_error::VortexResult - -impl core::clone::Clone for vortex_layout::layouts::zoned::zone_map::ZoneMap - -pub fn vortex_layout::layouts::zoned::zone_map::ZoneMap::clone(&self) -> vortex_layout::layouts::zoned::zone_map::ZoneMap - -pub struct vortex_layout::layouts::zoned::Zoned - -impl core::fmt::Debug for vortex_layout::layouts::zoned::Zoned - -pub fn vortex_layout::layouts::zoned::Zoned::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result - -impl vortex_layout::VTable for vortex_layout::layouts::zoned::Zoned - -pub type vortex_layout::layouts::zoned::Zoned::Encoding = vortex_layout::layouts::zoned::ZonedLayoutEncoding - -pub type vortex_layout::layouts::zoned::Zoned::Layout = vortex_layout::layouts::zoned::ZonedLayout - -pub type vortex_layout::layouts::zoned::Zoned::Metadata = vortex_layout::layouts::zoned::ZonedMetadata - -pub fn vortex_layout::layouts::zoned::Zoned::build(&Self::Encoding, &vortex_array::dtype::DType, u64, &vortex_layout::layouts::zoned::ZonedMetadata, alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::zoned::Zoned::child(&Self::Layout, usize) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::zoned::Zoned::child_type(&Self::Layout, usize) -> vortex_layout::LayoutChildType - -pub fn vortex_layout::layouts::zoned::Zoned::dtype(&Self::Layout) -> &vortex_array::dtype::DType - -pub fn vortex_layout::layouts::zoned::Zoned::encoding(&Self::Layout) -> vortex_layout::LayoutEncodingRef - -pub fn vortex_layout::layouts::zoned::Zoned::id(&Self::Encoding) -> vortex_layout::LayoutId - -pub fn vortex_layout::layouts::zoned::Zoned::metadata(&Self::Layout) -> Self::Metadata - -pub fn vortex_layout::layouts::zoned::Zoned::nchildren(&Self::Layout) -> usize - -pub fn vortex_layout::layouts::zoned::Zoned::new_reader(&Self::Layout, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::zoned::Zoned::row_count(&Self::Layout) -> u64 - -pub fn vortex_layout::layouts::zoned::Zoned::segment_ids(&Self::Layout) -> alloc::vec::Vec - -pub fn vortex_layout::layouts::zoned::Zoned::with_children(&mut Self::Layout, alloc::vec::Vec) -> vortex_error::VortexResult<()> - -pub struct vortex_layout::layouts::zoned::ZonedLayout - -impl vortex_layout::layouts::zoned::ZonedLayout - -pub fn vortex_layout::layouts::zoned::ZonedLayout::new(vortex_layout::LayoutRef, vortex_layout::LayoutRef, usize, alloc::sync::Arc<[vortex_array::expr::stats::Stat]>) -> Self - -pub fn vortex_layout::layouts::zoned::ZonedLayout::nzones(&self) -> usize - -pub fn vortex_layout::layouts::zoned::ZonedLayout::present_stats(&self) -> &alloc::sync::Arc<[vortex_array::expr::stats::Stat]> - -pub fn vortex_layout::layouts::zoned::ZonedLayout::zone_len(&self) -> usize - -impl core::clone::Clone for vortex_layout::layouts::zoned::ZonedLayout - -pub fn vortex_layout::layouts::zoned::ZonedLayout::clone(&self) -> vortex_layout::layouts::zoned::ZonedLayout - -impl core::convert::AsRef for vortex_layout::layouts::zoned::ZonedLayout - -pub fn vortex_layout::layouts::zoned::ZonedLayout::as_ref(&self) -> &dyn vortex_layout::Layout - -impl core::convert::From for vortex_layout::LayoutRef - -pub fn vortex_layout::LayoutRef::from(vortex_layout::layouts::zoned::ZonedLayout) -> vortex_layout::LayoutRef - -impl core::fmt::Debug for vortex_layout::layouts::zoned::ZonedLayout - -pub fn vortex_layout::layouts::zoned::ZonedLayout::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result - -impl core::ops::deref::Deref for vortex_layout::layouts::zoned::ZonedLayout - -pub type vortex_layout::layouts::zoned::ZonedLayout::Target = dyn vortex_layout::Layout - -pub fn vortex_layout::layouts::zoned::ZonedLayout::deref(&self) -> &Self::Target - -impl vortex_layout::IntoLayout for vortex_layout::layouts::zoned::ZonedLayout - -pub fn vortex_layout::layouts::zoned::ZonedLayout::into_layout(self) -> vortex_layout::LayoutRef - -pub struct vortex_layout::layouts::zoned::ZonedLayoutEncoding - -impl core::convert::AsRef for vortex_layout::layouts::zoned::ZonedLayoutEncoding - -pub fn vortex_layout::layouts::zoned::ZonedLayoutEncoding::as_ref(&self) -> &dyn vortex_layout::LayoutEncoding - -impl core::fmt::Debug for vortex_layout::layouts::zoned::ZonedLayoutEncoding - -pub fn vortex_layout::layouts::zoned::ZonedLayoutEncoding::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result - -impl core::ops::deref::Deref for vortex_layout::layouts::zoned::ZonedLayoutEncoding - -pub type vortex_layout::layouts::zoned::ZonedLayoutEncoding::Target = dyn vortex_layout::LayoutEncoding - -pub fn vortex_layout::layouts::zoned::ZonedLayoutEncoding::deref(&self) -> &Self::Target - -pub struct vortex_layout::layouts::zoned::ZonedMetadata - -impl core::clone::Clone for vortex_layout::layouts::zoned::ZonedMetadata - -pub fn vortex_layout::layouts::zoned::ZonedMetadata::clone(&self) -> vortex_layout::layouts::zoned::ZonedMetadata - -impl core::cmp::Eq for vortex_layout::layouts::zoned::ZonedMetadata - -impl core::cmp::PartialEq for vortex_layout::layouts::zoned::ZonedMetadata - -pub fn vortex_layout::layouts::zoned::ZonedMetadata::eq(&self, &vortex_layout::layouts::zoned::ZonedMetadata) -> bool - -impl core::fmt::Debug for vortex_layout::layouts::zoned::ZonedMetadata - -pub fn vortex_layout::layouts::zoned::ZonedMetadata::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result - -impl core::marker::StructuralPartialEq for vortex_layout::layouts::zoned::ZonedMetadata - -impl vortex_array::metadata::DeserializeMetadata for vortex_layout::layouts::zoned::ZonedMetadata - -pub type vortex_layout::layouts::zoned::ZonedMetadata::Output = vortex_layout::layouts::zoned::ZonedMetadata - -pub fn vortex_layout::layouts::zoned::ZonedMetadata::deserialize(&[u8]) -> vortex_error::VortexResult - -impl vortex_array::metadata::SerializeMetadata for vortex_layout::layouts::zoned::ZonedMetadata - -pub fn vortex_layout::layouts::zoned::ZonedMetadata::serialize(self) -> alloc::vec::Vec - -pub const vortex_layout::layouts::zoned::MAX_IS_TRUNCATED: &str - -pub const vortex_layout::layouts::zoned::MIN_IS_TRUNCATED: &str - -pub type vortex_layout::layouts::SharedArrayFuture = futures_util::future::future::shared::Shared>> - -pub mod vortex_layout::scan - -pub mod vortex_layout::scan::arrow - -pub struct vortex_layout::scan::arrow::RecordBatchIteratorAdapter - -impl vortex_layout::scan::arrow::RecordBatchIteratorAdapter - -pub fn vortex_layout::scan::arrow::RecordBatchIteratorAdapter::new(I, arrow_schema::schema::SchemaRef) -> Self - -impl core::clone::Clone for vortex_layout::scan::arrow::RecordBatchIteratorAdapter - -pub fn vortex_layout::scan::arrow::RecordBatchIteratorAdapter::clone(&self) -> vortex_layout::scan::arrow::RecordBatchIteratorAdapter - -impl arrow_array::record_batch::RecordBatchReader for vortex_layout::scan::arrow::RecordBatchIteratorAdapter where I: core::iter::traits::iterator::Iterator> - -pub fn vortex_layout::scan::arrow::RecordBatchIteratorAdapter::schema(&self) -> arrow_schema::schema::SchemaRef - -impl core::iter::traits::iterator::Iterator for vortex_layout::scan::arrow::RecordBatchIteratorAdapter where I: core::iter::traits::iterator::Iterator> - -pub type vortex_layout::scan::arrow::RecordBatchIteratorAdapter::Item = core::result::Result - -pub fn vortex_layout::scan::arrow::RecordBatchIteratorAdapter::next(&mut self) -> core::option::Option - -pub mod vortex_layout::scan::layout - -pub struct vortex_layout::scan::layout::LayoutReaderDataSource - -impl vortex_layout::scan::layout::LayoutReaderDataSource - -pub fn vortex_layout::scan::layout::LayoutReaderDataSource::new(vortex_layout::LayoutReaderRef, vortex_session::VortexSession) -> Self - -pub fn vortex_layout::scan::layout::LayoutReaderDataSource::with_metrics_registry(self, alloc::sync::Arc) -> Self - -pub fn vortex_layout::scan::layout::LayoutReaderDataSource::with_some_metrics_registry(self, core::option::Option>) -> Self - -pub fn vortex_layout::scan::layout::LayoutReaderDataSource::with_split_max_row_count(self, u64) -> Self - -impl vortex_scan::DataSource for vortex_layout::scan::layout::LayoutReaderDataSource - -pub fn vortex_layout::scan::layout::LayoutReaderDataSource::byte_size(&self) -> core::option::Option> - -pub fn vortex_layout::scan::layout::LayoutReaderDataSource::deserialize_partition(&self, &[u8], &vortex_session::VortexSession) -> vortex_error::VortexResult - -pub fn vortex_layout::scan::layout::LayoutReaderDataSource::dtype(&self) -> &vortex_array::dtype::DType - -pub fn vortex_layout::scan::layout::LayoutReaderDataSource::field_statistics<'life0, 'life1, 'async_trait>(&'life0 self, &'life1 vortex_array::dtype::field::FieldPath) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait - -pub fn vortex_layout::scan::layout::LayoutReaderDataSource::row_count(&self) -> core::option::Option> - -pub fn vortex_layout::scan::layout::LayoutReaderDataSource::scan<'life0, 'async_trait>(&'life0 self, vortex_scan::ScanRequest) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait - -pub mod vortex_layout::scan::multi - -pub enum vortex_layout::scan::multi::MultiLayoutChild - -pub vortex_layout::scan::multi::MultiLayoutChild::Deferred(alloc::sync::Arc) - -pub vortex_layout::scan::multi::MultiLayoutChild::Opened(vortex_layout::LayoutReaderRef) - -pub struct vortex_layout::scan::multi::MultiLayoutDataSource - -impl vortex_layout::scan::multi::MultiLayoutDataSource - -pub fn vortex_layout::scan::multi::MultiLayoutDataSource::children(&self) -> &alloc::vec::Vec - -pub fn vortex_layout::scan::multi::MultiLayoutDataSource::new_deferred(vortex_array::dtype::DType, alloc::vec::Vec>, &vortex_session::VortexSession) -> Self - -pub fn vortex_layout::scan::multi::MultiLayoutDataSource::new_with_first(vortex_layout::LayoutReaderRef, alloc::vec::Vec>, &vortex_session::VortexSession) -> Self - -pub fn vortex_layout::scan::multi::MultiLayoutDataSource::with_concurrency(self, usize) -> Self - -impl vortex_scan::DataSource for vortex_layout::scan::multi::MultiLayoutDataSource - -pub fn vortex_layout::scan::multi::MultiLayoutDataSource::deserialize_partition(&self, &[u8], &vortex_session::VortexSession) -> vortex_error::VortexResult - -pub fn vortex_layout::scan::multi::MultiLayoutDataSource::dtype(&self) -> &vortex_array::dtype::DType - -pub fn vortex_layout::scan::multi::MultiLayoutDataSource::field_statistics<'life0, 'life1, 'async_trait>(&'life0 self, &'life1 vortex_array::dtype::field::FieldPath) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait - -pub fn vortex_layout::scan::multi::MultiLayoutDataSource::row_count(&self) -> core::option::Option> - -pub fn vortex_layout::scan::multi::MultiLayoutDataSource::scan<'life0, 'async_trait>(&'life0 self, vortex_scan::ScanRequest) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait - -pub trait vortex_layout::scan::multi::LayoutReaderFactory: 'static + core::marker::Send + core::marker::Sync - -pub fn vortex_layout::scan::multi::LayoutReaderFactory::open<'life0, 'async_trait>(&'life0 self) -> core::pin::Pin>> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait - -pub mod vortex_layout::scan::repeated_scan - -pub struct vortex_layout::scan::repeated_scan::RepeatedScan - -impl vortex_layout::scan::repeated_scan::RepeatedScan - -pub fn vortex_layout::scan::repeated_scan::RepeatedScan::dtype(&self) -> &vortex_array::dtype::DType - -pub fn vortex_layout::scan::repeated_scan::RepeatedScan::execute_array_iter(&self, core::option::Option>, &B) -> vortex_error::VortexResult - -pub fn vortex_layout::scan::repeated_scan::RepeatedScan::execute_array_stream(&self, core::option::Option>) -> vortex_error::VortexResult - -impl vortex_layout::scan::repeated_scan::RepeatedScan - -pub fn vortex_layout::scan::repeated_scan::RepeatedScan::execute(&self, core::option::Option>) -> vortex_error::VortexResult>>>> - -pub fn vortex_layout::scan::repeated_scan::RepeatedScan::execute_stream(&self, core::option::Option>) -> vortex_error::VortexResult> + core::marker::Send + 'static + use> - -pub fn vortex_layout::scan::repeated_scan::RepeatedScan::new(vortex_session::VortexSession, vortex_layout::LayoutReaderRef, vortex_array::expr::expression::Expression, core::option::Option, bool, core::option::Option>, vortex_scan::selection::Selection, vortex_layout::scan::splits::Splits, usize, alloc::sync::Arc<(dyn core::ops::function::Fn(vortex_array::array::erased::ArrayRef) -> vortex_error::VortexResult + core::marker::Send + core::marker::Sync)>, core::option::Option, vortex_array::dtype::DType) -> Self - -pub mod vortex_layout::scan::scan_builder - -pub struct vortex_layout::scan::scan_builder::ScanBuilder - -impl vortex_layout::scan::scan_builder::ScanBuilder - -pub fn vortex_layout::scan::scan_builder::ScanBuilder::into_array_iter(self, &B) -> vortex_error::VortexResult - -pub fn vortex_layout::scan::scan_builder::ScanBuilder::into_array_stream(self) -> vortex_error::VortexResult - -pub fn vortex_layout::scan::scan_builder::ScanBuilder::new(vortex_session::VortexSession, alloc::sync::Arc) -> Self - -impl vortex_layout::scan::scan_builder::ScanBuilder - -pub fn vortex_layout::scan::scan_builder::ScanBuilder::into_record_batch_reader(self, arrow_schema::schema::SchemaRef, &B) -> vortex_error::VortexResult - -pub fn vortex_layout::scan::scan_builder::ScanBuilder::into_record_batch_stream(self, arrow_schema::schema::SchemaRef) -> vortex_error::VortexResult> + core::marker::Send + 'static> - -impl vortex_layout::scan::scan_builder::ScanBuilder - -pub fn vortex_layout::scan::scan_builder::ScanBuilder::build(self) -> vortex_error::VortexResult>>>> - -pub fn vortex_layout::scan::scan_builder::ScanBuilder::concurrency(&self) -> usize - -pub fn vortex_layout::scan::scan_builder::ScanBuilder::dtype(&self) -> vortex_error::VortexResult - -pub fn vortex_layout::scan::scan_builder::ScanBuilder::into_iter(self, &B) -> vortex_error::VortexResult> + 'static> - -pub fn vortex_layout::scan::scan_builder::ScanBuilder::into_stream(self) -> vortex_error::VortexResult> + core::marker::Send + 'static + use> - -pub fn vortex_layout::scan::scan_builder::ScanBuilder::map(self, impl core::ops::function::Fn(A) -> vortex_error::VortexResult + 'static + core::marker::Send + core::marker::Sync) -> vortex_layout::scan::scan_builder::ScanBuilder - -pub fn vortex_layout::scan::scan_builder::ScanBuilder::ordered(&self) -> bool - -pub fn vortex_layout::scan::scan_builder::ScanBuilder::prepare(self) -> vortex_error::VortexResult> - -pub fn vortex_layout::scan::scan_builder::ScanBuilder::session(&self) -> &vortex_session::VortexSession - -pub fn vortex_layout::scan::scan_builder::ScanBuilder::with_concurrency(self, usize) -> Self - -pub fn vortex_layout::scan::scan_builder::ScanBuilder::with_filter(self, vortex_array::expr::expression::Expression) -> Self - -pub fn vortex_layout::scan::scan_builder::ScanBuilder::with_limit(self, u64) -> Self - -pub fn vortex_layout::scan::scan_builder::ScanBuilder::with_metrics_registry(self, alloc::sync::Arc) -> Self - -pub fn vortex_layout::scan::scan_builder::ScanBuilder::with_ordered(self, bool) -> Self - -pub fn vortex_layout::scan::scan_builder::ScanBuilder::with_projection(self, vortex_array::expr::expression::Expression) -> Self - -pub fn vortex_layout::scan::scan_builder::ScanBuilder::with_row_indices(self, vortex_buffer::buffer::Buffer) -> Self - -pub fn vortex_layout::scan::scan_builder::ScanBuilder::with_row_offset(self, u64) -> Self - -pub fn vortex_layout::scan::scan_builder::ScanBuilder::with_row_range(self, core::ops::range::Range) -> Self - -pub fn vortex_layout::scan::scan_builder::ScanBuilder::with_selection(self, vortex_scan::selection::Selection) -> Self - -pub fn vortex_layout::scan::scan_builder::ScanBuilder::with_some_filter(self, core::option::Option) -> Self - -pub fn vortex_layout::scan::scan_builder::ScanBuilder::with_some_limit(self, core::option::Option) -> Self - -pub fn vortex_layout::scan::scan_builder::ScanBuilder::with_some_metrics_registry(self, core::option::Option>) -> Self - -pub fn vortex_layout::scan::scan_builder::ScanBuilder::with_split_by(self, vortex_layout::scan::split_by::SplitBy) -> Self - -pub fn vortex_layout::scan::scan_builder::filter_and_projection_masks(&vortex_array::expr::expression::Expression, core::option::Option<&vortex_array::expr::expression::Expression>, &vortex_array::dtype::DType) -> vortex_error::VortexResult<(alloc::vec::Vec, alloc::vec::Vec)> - -pub mod vortex_layout::scan::split_by - -pub enum vortex_layout::scan::split_by::SplitBy - -pub vortex_layout::scan::split_by::SplitBy::Layout - -pub vortex_layout::scan::split_by::SplitBy::RowCount(usize) - -impl vortex_layout::scan::split_by::SplitBy - -pub fn vortex_layout::scan::split_by::SplitBy::splits(&self, &dyn vortex_layout::LayoutReader, &core::ops::range::Range, &[vortex_array::dtype::field_mask::FieldMask]) -> vortex_error::VortexResult> - -impl core::clone::Clone for vortex_layout::scan::split_by::SplitBy - -pub fn vortex_layout::scan::split_by::SplitBy::clone(&self) -> vortex_layout::scan::split_by::SplitBy - -impl core::default::Default for vortex_layout::scan::split_by::SplitBy - -pub fn vortex_layout::scan::split_by::SplitBy::default() -> vortex_layout::scan::split_by::SplitBy - -impl core::fmt::Debug for vortex_layout::scan::split_by::SplitBy - -pub fn vortex_layout::scan::split_by::SplitBy::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result - -impl core::marker::Copy for vortex_layout::scan::split_by::SplitBy - -pub mod vortex_layout::segments - -pub struct vortex_layout::segments::InstrumentedSegmentCache - -impl vortex_layout::segments::InstrumentedSegmentCache - -pub fn vortex_layout::segments::InstrumentedSegmentCache::new(C, &dyn vortex_metrics::MetricsRegistry, alloc::vec::Vec) -> Self - -impl vortex_layout::segments::SegmentCache for vortex_layout::segments::InstrumentedSegmentCache - -pub fn vortex_layout::segments::InstrumentedSegmentCache::get<'life0, 'async_trait>(&'life0 self, vortex_layout::segments::SegmentId) -> core::pin::Pin>> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait - -pub fn vortex_layout::segments::InstrumentedSegmentCache::put<'life0, 'async_trait>(&'life0 self, vortex_layout::segments::SegmentId, vortex_buffer::ByteBuffer) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait - -pub struct vortex_layout::segments::MokaSegmentCache(_) - -impl vortex_layout::segments::MokaSegmentCache - -pub fn vortex_layout::segments::MokaSegmentCache::new(u64) -> Self - -impl vortex_layout::segments::SegmentCache for vortex_layout::segments::MokaSegmentCache - -pub fn vortex_layout::segments::MokaSegmentCache::get<'life0, 'async_trait>(&'life0 self, vortex_layout::segments::SegmentId) -> core::pin::Pin>> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait - -pub fn vortex_layout::segments::MokaSegmentCache::put<'life0, 'async_trait>(&'life0 self, vortex_layout::segments::SegmentId, vortex_buffer::ByteBuffer) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait - -pub struct vortex_layout::segments::NoOpSegmentCache - -impl vortex_layout::segments::SegmentCache for vortex_layout::segments::NoOpSegmentCache - -pub fn vortex_layout::segments::NoOpSegmentCache::get<'life0, 'async_trait>(&'life0 self, vortex_layout::segments::SegmentId) -> core::pin::Pin>> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait - -pub fn vortex_layout::segments::NoOpSegmentCache::put<'life0, 'async_trait>(&'life0 self, vortex_layout::segments::SegmentId, vortex_buffer::ByteBuffer) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait - -pub struct vortex_layout::segments::SegmentCacheSourceAdapter - -impl vortex_layout::segments::SegmentCacheSourceAdapter - -pub fn vortex_layout::segments::SegmentCacheSourceAdapter::new(alloc::sync::Arc, alloc::sync::Arc) -> Self - -impl vortex_layout::segments::SegmentSource for vortex_layout::segments::SegmentCacheSourceAdapter - -pub fn vortex_layout::segments::SegmentCacheSourceAdapter::request(&self, vortex_layout::segments::SegmentId) -> vortex_layout::segments::SegmentFuture - -pub struct vortex_layout::segments::SegmentId(_) - -impl core::clone::Clone for vortex_layout::segments::SegmentId - -pub fn vortex_layout::segments::SegmentId::clone(&self) -> vortex_layout::segments::SegmentId - -impl core::cmp::Eq for vortex_layout::segments::SegmentId - -impl core::cmp::Ord for vortex_layout::segments::SegmentId - -pub fn vortex_layout::segments::SegmentId::cmp(&self, &vortex_layout::segments::SegmentId) -> core::cmp::Ordering - -impl core::cmp::PartialEq for vortex_layout::segments::SegmentId - -pub fn vortex_layout::segments::SegmentId::eq(&self, &vortex_layout::segments::SegmentId) -> bool - -impl core::cmp::PartialOrd for vortex_layout::segments::SegmentId - -pub fn vortex_layout::segments::SegmentId::partial_cmp(&self, &vortex_layout::segments::SegmentId) -> core::option::Option - -impl core::convert::From for vortex_layout::segments::SegmentId - -pub fn vortex_layout::segments::SegmentId::from(u32) -> Self - -impl core::convert::TryFrom for vortex_layout::segments::SegmentId - -pub type vortex_layout::segments::SegmentId::Error = vortex_error::VortexError - -pub fn vortex_layout::segments::SegmentId::try_from(usize) -> core::result::Result - -impl core::default::Default for vortex_layout::segments::SegmentId - -pub fn vortex_layout::segments::SegmentId::default() -> vortex_layout::segments::SegmentId - -impl core::fmt::Debug for vortex_layout::segments::SegmentId - -pub fn vortex_layout::segments::SegmentId::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result - -impl core::fmt::Display for vortex_layout::segments::SegmentId - -pub fn vortex_layout::segments::SegmentId::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result - -impl core::hash::Hash for vortex_layout::segments::SegmentId - -pub fn vortex_layout::segments::SegmentId::hash<__H: core::hash::Hasher>(&self, &mut __H) - -impl core::marker::Copy for vortex_layout::segments::SegmentId - -impl core::marker::StructuralPartialEq for vortex_layout::segments::SegmentId - -impl core::ops::deref::Deref for vortex_layout::segments::SegmentId - -pub type vortex_layout::segments::SegmentId::Target = u32 - -pub fn vortex_layout::segments::SegmentId::deref(&self) -> &Self::Target - -pub struct vortex_layout::segments::SharedSegmentSource - -impl vortex_layout::segments::SharedSegmentSource - -pub fn vortex_layout::segments::SharedSegmentSource::new(S) -> Self - -impl vortex_layout::segments::SegmentSource for vortex_layout::segments::SharedSegmentSource - -pub fn vortex_layout::segments::SharedSegmentSource::request(&self, vortex_layout::segments::SegmentId) -> vortex_layout::segments::SegmentFuture - -pub trait vortex_layout::segments::SegmentCache: core::marker::Send + core::marker::Sync - -pub fn vortex_layout::segments::SegmentCache::get<'life0, 'async_trait>(&'life0 self, vortex_layout::segments::SegmentId) -> core::pin::Pin>> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait - -pub fn vortex_layout::segments::SegmentCache::put<'life0, 'async_trait>(&'life0 self, vortex_layout::segments::SegmentId, vortex_buffer::ByteBuffer) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait - -impl vortex_layout::segments::SegmentCache for vortex_layout::segments::MokaSegmentCache - -pub fn vortex_layout::segments::MokaSegmentCache::get<'life0, 'async_trait>(&'life0 self, vortex_layout::segments::SegmentId) -> core::pin::Pin>> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait - -pub fn vortex_layout::segments::MokaSegmentCache::put<'life0, 'async_trait>(&'life0 self, vortex_layout::segments::SegmentId, vortex_buffer::ByteBuffer) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait - -impl vortex_layout::segments::SegmentCache for vortex_layout::segments::NoOpSegmentCache - -pub fn vortex_layout::segments::NoOpSegmentCache::get<'life0, 'async_trait>(&'life0 self, vortex_layout::segments::SegmentId) -> core::pin::Pin>> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait - -pub fn vortex_layout::segments::NoOpSegmentCache::put<'life0, 'async_trait>(&'life0 self, vortex_layout::segments::SegmentId, vortex_buffer::ByteBuffer) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait - -impl vortex_layout::segments::SegmentCache for vortex_layout::segments::InstrumentedSegmentCache - -pub fn vortex_layout::segments::InstrumentedSegmentCache::get<'life0, 'async_trait>(&'life0 self, vortex_layout::segments::SegmentId) -> core::pin::Pin>> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait - -pub fn vortex_layout::segments::InstrumentedSegmentCache::put<'life0, 'async_trait>(&'life0 self, vortex_layout::segments::SegmentId, vortex_buffer::ByteBuffer) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait - -pub trait vortex_layout::segments::SegmentSink: core::marker::Send + core::marker::Sync - -pub fn vortex_layout::segments::SegmentSink::write<'life0, 'async_trait>(&'life0 self, vortex_layout::sequence::SequenceId, alloc::vec::Vec) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait - -pub trait vortex_layout::segments::SegmentSource: 'static + core::marker::Send + core::marker::Sync - -pub fn vortex_layout::segments::SegmentSource::request(&self, vortex_layout::segments::SegmentId) -> vortex_layout::segments::SegmentFuture - -impl vortex_layout::segments::SegmentSource for vortex_layout::segments::SegmentCacheSourceAdapter - -pub fn vortex_layout::segments::SegmentCacheSourceAdapter::request(&self, vortex_layout::segments::SegmentId) -> vortex_layout::segments::SegmentFuture - -impl vortex_layout::segments::SegmentSource for vortex_layout::segments::SharedSegmentSource - -pub fn vortex_layout::segments::SharedSegmentSource::request(&self, vortex_layout::segments::SegmentId) -> vortex_layout::segments::SegmentFuture - -pub type vortex_layout::segments::SegmentFuture = futures_core::future::BoxFuture<'static, vortex_error::VortexResult> - -pub type vortex_layout::segments::SegmentSinkRef = alloc::sync::Arc - -pub mod vortex_layout::sequence - -pub struct vortex_layout::sequence::SequenceId - -impl vortex_layout::sequence::SequenceId - -pub async fn vortex_layout::sequence::SequenceId::collapse(&mut self) - -pub fn vortex_layout::sequence::SequenceId::descend(self) -> vortex_layout::sequence::SequencePointer - -pub fn vortex_layout::sequence::SequenceId::root() -> vortex_layout::sequence::SequencePointer - -impl core::cmp::Eq for vortex_layout::sequence::SequenceId - -impl core::cmp::Ord for vortex_layout::sequence::SequenceId - -pub fn vortex_layout::sequence::SequenceId::cmp(&self, &Self) -> core::cmp::Ordering - -impl core::cmp::PartialEq for vortex_layout::sequence::SequenceId - -pub fn vortex_layout::sequence::SequenceId::eq(&self, &Self) -> bool - -impl core::cmp::PartialOrd for vortex_layout::sequence::SequenceId - -pub fn vortex_layout::sequence::SequenceId::partial_cmp(&self, &Self) -> core::option::Option - -impl core::fmt::Debug for vortex_layout::sequence::SequenceId - -pub fn vortex_layout::sequence::SequenceId::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result - -impl core::hash::Hash for vortex_layout::sequence::SequenceId - -pub fn vortex_layout::sequence::SequenceId::hash(&self, &mut H) - -impl core::ops::drop::Drop for vortex_layout::sequence::SequenceId - -pub fn vortex_layout::sequence::SequenceId::drop(&mut self) - -pub struct vortex_layout::sequence::SequencePointer(_) - -impl vortex_layout::sequence::SequencePointer - -pub fn vortex_layout::sequence::SequencePointer::advance(&mut self) -> vortex_layout::sequence::SequenceId - -pub fn vortex_layout::sequence::SequencePointer::downgrade(self) -> vortex_layout::sequence::SequenceId - -pub fn vortex_layout::sequence::SequencePointer::split(self) -> (vortex_layout::sequence::SequencePointer, vortex_layout::sequence::SequencePointer) - -pub fn vortex_layout::sequence::SequencePointer::split_off(&mut self) -> vortex_layout::sequence::SequencePointer - -impl core::fmt::Debug for vortex_layout::sequence::SequencePointer - -pub fn vortex_layout::sequence::SequencePointer::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result - -pub struct vortex_layout::sequence::SequentialStreamAdapter - -impl vortex_layout::sequence::SequentialStreamAdapter - -pub fn vortex_layout::sequence::SequentialStreamAdapter::new(vortex_array::dtype::DType, S) -> Self - -impl<'__pin, S> core::marker::Unpin for vortex_layout::sequence::SequentialStreamAdapter where pin_project_lite::__private::PinnedFieldsOf<__Origin<'__pin, S>>: core::marker::Unpin - -impl futures_core::stream::Stream for vortex_layout::sequence::SequentialStreamAdapter where S: futures_core::stream::Stream> - -pub type vortex_layout::sequence::SequentialStreamAdapter::Item = core::result::Result<(vortex_layout::sequence::SequenceId, vortex_array::array::erased::ArrayRef), vortex_error::VortexError> - -pub fn vortex_layout::sequence::SequentialStreamAdapter::poll_next(core::pin::Pin<&mut Self>, &mut core::task::wake::Context<'_>) -> core::task::poll::Poll> - -pub fn vortex_layout::sequence::SequentialStreamAdapter::size_hint(&self) -> (usize, core::option::Option) - -impl vortex_layout::sequence::SequentialStream for vortex_layout::sequence::SequentialStreamAdapter where S: futures_core::stream::Stream> - -pub fn vortex_layout::sequence::SequentialStreamAdapter::dtype(&self) -> &vortex_array::dtype::DType - -pub trait vortex_layout::sequence::SequentialArrayStreamExt: vortex_array::stream::ArrayStream - -pub fn vortex_layout::sequence::SequentialArrayStreamExt::sequenced(self, vortex_layout::sequence::SequencePointer) -> vortex_layout::sequence::SendableSequentialStream where Self: core::marker::Sized + core::marker::Send + 'static - -impl vortex_layout::sequence::SequentialArrayStreamExt for S - -pub fn S::sequenced(self, vortex_layout::sequence::SequencePointer) -> vortex_layout::sequence::SendableSequentialStream where Self: core::marker::Sized + core::marker::Send + 'static - -pub trait vortex_layout::sequence::SequentialStream: futures_core::stream::Stream> - -pub fn vortex_layout::sequence::SequentialStream::dtype(&self) -> &vortex_array::dtype::DType - -impl vortex_layout::sequence::SequentialStream for vortex_layout::sequence::SendableSequentialStream - -pub fn vortex_layout::sequence::SendableSequentialStream::dtype(&self) -> &vortex_array::dtype::DType - -impl vortex_layout::sequence::SequentialStream for vortex_layout::sequence::SequentialStreamAdapter where S: futures_core::stream::Stream> - -pub fn vortex_layout::sequence::SequentialStreamAdapter::dtype(&self) -> &vortex_array::dtype::DType - -pub trait vortex_layout::sequence::SequentialStreamExt: vortex_layout::sequence::SequentialStream - -pub fn vortex_layout::sequence::SequentialStreamExt::sendable(self) -> vortex_layout::sequence::SendableSequentialStream where Self: core::marker::Sized + core::marker::Send + 'static - -impl vortex_layout::sequence::SequentialStreamExt for S - -pub fn S::sendable(self) -> vortex_layout::sequence::SendableSequentialStream where Self: core::marker::Sized + core::marker::Send + 'static - -pub type vortex_layout::sequence::SendableSequentialStream = core::pin::Pin> - -pub mod vortex_layout::session - -pub struct vortex_layout::session::LayoutSession - -impl vortex_layout::session::LayoutSession - -pub fn vortex_layout::session::LayoutSession::register(&self, vortex_layout::LayoutEncodingRef) - -pub fn vortex_layout::session::LayoutSession::register_many(&self, impl core::iter::traits::collect::IntoIterator) - -pub fn vortex_layout::session::LayoutSession::registry(&self) -> &vortex_layout::session::LayoutRegistry - -impl core::default::Default for vortex_layout::session::LayoutSession - -pub fn vortex_layout::session::LayoutSession::default() -> Self - -impl core::fmt::Debug for vortex_layout::session::LayoutSession - -pub fn vortex_layout::session::LayoutSession::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result - -impl vortex_session::SessionVar for vortex_layout::session::LayoutSession - -pub fn vortex_layout::session::LayoutSession::as_any(&self) -> &dyn core::any::Any - -pub fn vortex_layout::session::LayoutSession::as_any_mut(&mut self) -> &mut dyn core::any::Any - -pub trait vortex_layout::session::LayoutSessionExt: vortex_session::SessionExt - -pub fn vortex_layout::session::LayoutSessionExt::layouts(&self) -> vortex_session::Ref<'_, vortex_layout::session::LayoutSession> - -impl vortex_layout::session::LayoutSessionExt for S - -pub fn S::layouts(&self) -> vortex_session::Ref<'_, vortex_layout::session::LayoutSession> - -pub type vortex_layout::session::LayoutRegistry = vortex_session::registry::Registry - -pub mod vortex_layout::vtable - -pub trait vortex_layout::vtable::VTable: 'static + core::marker::Sized + core::marker::Send + core::marker::Sync + core::fmt::Debug - -pub type vortex_layout::vtable::VTable::Encoding: 'static + core::marker::Send + core::marker::Sync + core::ops::deref::Deref - -pub type vortex_layout::vtable::VTable::Layout: 'static + core::marker::Send + core::marker::Sync + core::clone::Clone + core::fmt::Debug + core::ops::deref::Deref + vortex_layout::IntoLayout - -pub type vortex_layout::vtable::VTable::Metadata: vortex_array::metadata::SerializeMetadata + vortex_array::metadata::DeserializeMetadata + core::fmt::Debug - -pub fn vortex_layout::vtable::VTable::build(&Self::Encoding, &vortex_array::dtype::DType, u64, &::Output, alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult - -pub fn vortex_layout::vtable::VTable::child(&Self::Layout, usize) -> vortex_error::VortexResult - -pub fn vortex_layout::vtable::VTable::child_type(&Self::Layout, usize) -> vortex_layout::LayoutChildType - -pub fn vortex_layout::vtable::VTable::dtype(&Self::Layout) -> &vortex_array::dtype::DType - -pub fn vortex_layout::vtable::VTable::encoding(&Self::Layout) -> vortex_layout::LayoutEncodingRef - -pub fn vortex_layout::vtable::VTable::id(&Self::Encoding) -> vortex_layout::LayoutId - -pub fn vortex_layout::vtable::VTable::metadata(&Self::Layout) -> Self::Metadata - -pub fn vortex_layout::vtable::VTable::nchildren(&Self::Layout) -> usize - -pub fn vortex_layout::vtable::VTable::new_reader(&Self::Layout, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult - -pub fn vortex_layout::vtable::VTable::row_count(&Self::Layout) -> u64 - -pub fn vortex_layout::vtable::VTable::segment_ids(&Self::Layout) -> alloc::vec::Vec - -pub fn vortex_layout::vtable::VTable::with_children(&mut Self::Layout, alloc::vec::Vec) -> vortex_error::VortexResult<()> - -impl vortex_layout::VTable for vortex_layout::layouts::chunked::Chunked - -pub type vortex_layout::layouts::chunked::Chunked::Encoding = vortex_layout::layouts::chunked::ChunkedLayoutEncoding - -pub type vortex_layout::layouts::chunked::Chunked::Layout = vortex_layout::layouts::chunked::ChunkedLayout - -pub type vortex_layout::layouts::chunked::Chunked::Metadata = vortex_array::metadata::EmptyMetadata - -pub fn vortex_layout::layouts::chunked::Chunked::build(&Self::Encoding, &vortex_array::dtype::DType, u64, &::Output, alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::chunked::Chunked::child(&Self::Layout, usize) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::chunked::Chunked::child_type(&Self::Layout, usize) -> vortex_layout::LayoutChildType - -pub fn vortex_layout::layouts::chunked::Chunked::dtype(&Self::Layout) -> &vortex_array::dtype::DType - -pub fn vortex_layout::layouts::chunked::Chunked::encoding(&Self::Layout) -> vortex_layout::LayoutEncodingRef - -pub fn vortex_layout::layouts::chunked::Chunked::id(&Self::Encoding) -> vortex_layout::LayoutId - -pub fn vortex_layout::layouts::chunked::Chunked::metadata(&Self::Layout) -> Self::Metadata - -pub fn vortex_layout::layouts::chunked::Chunked::nchildren(&Self::Layout) -> usize - -pub fn vortex_layout::layouts::chunked::Chunked::new_reader(&Self::Layout, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::chunked::Chunked::row_count(&Self::Layout) -> u64 - -pub fn vortex_layout::layouts::chunked::Chunked::segment_ids(&Self::Layout) -> alloc::vec::Vec - -pub fn vortex_layout::layouts::chunked::Chunked::with_children(&mut Self::Layout, alloc::vec::Vec) -> vortex_error::VortexResult<()> - -impl vortex_layout::VTable for vortex_layout::layouts::dict::Dict - -pub type vortex_layout::layouts::dict::Dict::Encoding = vortex_layout::layouts::dict::DictLayoutEncoding - -pub type vortex_layout::layouts::dict::Dict::Layout = vortex_layout::layouts::dict::DictLayout - -pub type vortex_layout::layouts::dict::Dict::Metadata = vortex_array::metadata::ProstMetadata - -pub fn vortex_layout::layouts::dict::Dict::build(&Self::Encoding, &vortex_array::dtype::DType, u64, &::Output, alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::dict::Dict::child(&Self::Layout, usize) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::dict::Dict::child_type(&Self::Layout, usize) -> vortex_layout::LayoutChildType - -pub fn vortex_layout::layouts::dict::Dict::dtype(&Self::Layout) -> &vortex_array::dtype::DType - -pub fn vortex_layout::layouts::dict::Dict::encoding(&Self::Layout) -> vortex_layout::LayoutEncodingRef - -pub fn vortex_layout::layouts::dict::Dict::id(&Self::Encoding) -> vortex_layout::LayoutId - -pub fn vortex_layout::layouts::dict::Dict::metadata(&Self::Layout) -> Self::Metadata - -pub fn vortex_layout::layouts::dict::Dict::nchildren(&Self::Layout) -> usize - -pub fn vortex_layout::layouts::dict::Dict::new_reader(&Self::Layout, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::dict::Dict::row_count(&Self::Layout) -> u64 - -pub fn vortex_layout::layouts::dict::Dict::segment_ids(&Self::Layout) -> alloc::vec::Vec - -pub fn vortex_layout::layouts::dict::Dict::with_children(&mut Self::Layout, alloc::vec::Vec) -> vortex_error::VortexResult<()> - -impl vortex_layout::VTable for vortex_layout::layouts::flat::Flat - -pub type vortex_layout::layouts::flat::Flat::Encoding = vortex_layout::layouts::flat::FlatLayoutEncoding - -pub type vortex_layout::layouts::flat::Flat::Layout = vortex_layout::layouts::flat::FlatLayout - -pub type vortex_layout::layouts::flat::Flat::Metadata = vortex_array::metadata::ProstMetadata - -pub fn vortex_layout::layouts::flat::Flat::build(&Self::Encoding, &vortex_array::dtype::DType, u64, &::Output, alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::flat::Flat::child(&Self::Layout, usize) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::flat::Flat::child_type(&Self::Layout, usize) -> vortex_layout::LayoutChildType - -pub fn vortex_layout::layouts::flat::Flat::dtype(&Self::Layout) -> &vortex_array::dtype::DType - -pub fn vortex_layout::layouts::flat::Flat::encoding(&Self::Layout) -> vortex_layout::LayoutEncodingRef - -pub fn vortex_layout::layouts::flat::Flat::id(&Self::Encoding) -> vortex_layout::LayoutId - -pub fn vortex_layout::layouts::flat::Flat::metadata(&Self::Layout) -> Self::Metadata - -pub fn vortex_layout::layouts::flat::Flat::nchildren(&Self::Layout) -> usize - -pub fn vortex_layout::layouts::flat::Flat::new_reader(&Self::Layout, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::flat::Flat::row_count(&Self::Layout) -> u64 - -pub fn vortex_layout::layouts::flat::Flat::segment_ids(&Self::Layout) -> alloc::vec::Vec - -pub fn vortex_layout::layouts::flat::Flat::with_children(&mut Self::Layout, alloc::vec::Vec) -> vortex_error::VortexResult<()> - -impl vortex_layout::VTable for vortex_layout::layouts::struct_::Struct - -pub type vortex_layout::layouts::struct_::Struct::Encoding = vortex_layout::layouts::struct_::StructLayoutEncoding - -pub type vortex_layout::layouts::struct_::Struct::Layout = vortex_layout::layouts::struct_::StructLayout - -pub type vortex_layout::layouts::struct_::Struct::Metadata = vortex_array::metadata::EmptyMetadata - -pub fn vortex_layout::layouts::struct_::Struct::build(&Self::Encoding, &vortex_array::dtype::DType, u64, &::Output, alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::struct_::Struct::child(&Self::Layout, usize) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::struct_::Struct::child_type(&Self::Layout, usize) -> vortex_layout::LayoutChildType - -pub fn vortex_layout::layouts::struct_::Struct::dtype(&Self::Layout) -> &vortex_array::dtype::DType - -pub fn vortex_layout::layouts::struct_::Struct::encoding(&Self::Layout) -> vortex_layout::LayoutEncodingRef - -pub fn vortex_layout::layouts::struct_::Struct::id(&Self::Encoding) -> vortex_layout::LayoutId - -pub fn vortex_layout::layouts::struct_::Struct::metadata(&Self::Layout) -> Self::Metadata - -pub fn vortex_layout::layouts::struct_::Struct::nchildren(&Self::Layout) -> usize - -pub fn vortex_layout::layouts::struct_::Struct::new_reader(&Self::Layout, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::struct_::Struct::row_count(&Self::Layout) -> u64 - -pub fn vortex_layout::layouts::struct_::Struct::segment_ids(&Self::Layout) -> alloc::vec::Vec - -pub fn vortex_layout::layouts::struct_::Struct::with_children(&mut Self::Layout, alloc::vec::Vec) -> vortex_error::VortexResult<()> - -impl vortex_layout::VTable for vortex_layout::layouts::zoned::Zoned - -pub type vortex_layout::layouts::zoned::Zoned::Encoding = vortex_layout::layouts::zoned::ZonedLayoutEncoding - -pub type vortex_layout::layouts::zoned::Zoned::Layout = vortex_layout::layouts::zoned::ZonedLayout - -pub type vortex_layout::layouts::zoned::Zoned::Metadata = vortex_layout::layouts::zoned::ZonedMetadata - -pub fn vortex_layout::layouts::zoned::Zoned::build(&Self::Encoding, &vortex_array::dtype::DType, u64, &vortex_layout::layouts::zoned::ZonedMetadata, alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::zoned::Zoned::child(&Self::Layout, usize) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::zoned::Zoned::child_type(&Self::Layout, usize) -> vortex_layout::LayoutChildType - -pub fn vortex_layout::layouts::zoned::Zoned::dtype(&Self::Layout) -> &vortex_array::dtype::DType - -pub fn vortex_layout::layouts::zoned::Zoned::encoding(&Self::Layout) -> vortex_layout::LayoutEncodingRef - -pub fn vortex_layout::layouts::zoned::Zoned::id(&Self::Encoding) -> vortex_layout::LayoutId - -pub fn vortex_layout::layouts::zoned::Zoned::metadata(&Self::Layout) -> Self::Metadata - -pub fn vortex_layout::layouts::zoned::Zoned::nchildren(&Self::Layout) -> usize - -pub fn vortex_layout::layouts::zoned::Zoned::new_reader(&Self::Layout, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::zoned::Zoned::row_count(&Self::Layout) -> u64 - -pub fn vortex_layout::layouts::zoned::Zoned::segment_ids(&Self::Layout) -> alloc::vec::Vec - -pub fn vortex_layout::layouts::zoned::Zoned::with_children(&mut Self::Layout, alloc::vec::Vec) -> vortex_error::VortexResult<()> - -pub macro vortex_layout::vtable! - -pub enum vortex_layout::LayoutChildType - -pub vortex_layout::LayoutChildType::Auxiliary(alloc::sync::Arc) - -pub vortex_layout::LayoutChildType::Chunk((usize, u64)) - -pub vortex_layout::LayoutChildType::Field(vortex_array::dtype::field_names::FieldName) - -pub vortex_layout::LayoutChildType::Transparent(alloc::sync::Arc) - -impl vortex_layout::LayoutChildType - -pub fn vortex_layout::LayoutChildType::name(&self) -> alloc::sync::Arc - -pub fn vortex_layout::LayoutChildType::row_offset(&self) -> core::option::Option - -impl core::clone::Clone for vortex_layout::LayoutChildType - -pub fn vortex_layout::LayoutChildType::clone(&self) -> vortex_layout::LayoutChildType - -impl core::cmp::Eq for vortex_layout::LayoutChildType - -impl core::cmp::PartialEq for vortex_layout::LayoutChildType - -pub fn vortex_layout::LayoutChildType::eq(&self, &vortex_layout::LayoutChildType) -> bool - -impl core::fmt::Debug for vortex_layout::LayoutChildType - -pub fn vortex_layout::LayoutChildType::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result - -impl core::marker::StructuralPartialEq for vortex_layout::LayoutChildType - -#[repr(transparent)] pub struct vortex_layout::LayoutAdapter(_) - -impl core::fmt::Debug for vortex_layout::LayoutAdapter - -pub fn vortex_layout::LayoutAdapter::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result - -impl vortex_layout::Layout for vortex_layout::LayoutAdapter - -pub fn vortex_layout::LayoutAdapter::as_any(&self) -> &dyn core::any::Any - -pub fn vortex_layout::LayoutAdapter::as_any_arc(alloc::sync::Arc) -> alloc::sync::Arc<(dyn core::any::Any + core::marker::Send + core::marker::Sync)> - -pub fn vortex_layout::LayoutAdapter::child(&self, usize) -> vortex_error::VortexResult - -pub fn vortex_layout::LayoutAdapter::child_type(&self, usize) -> vortex_layout::LayoutChildType - -pub fn vortex_layout::LayoutAdapter::dtype(&self) -> &vortex_array::dtype::DType - -pub fn vortex_layout::LayoutAdapter::encoding(&self) -> vortex_layout::LayoutEncodingRef - -pub fn vortex_layout::LayoutAdapter::metadata(&self) -> alloc::vec::Vec - -pub fn vortex_layout::LayoutAdapter::nchildren(&self) -> usize - -pub fn vortex_layout::LayoutAdapter::new_reader(&self, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult - -pub fn vortex_layout::LayoutAdapter::row_count(&self) -> u64 - -pub fn vortex_layout::LayoutAdapter::segment_ids(&self) -> alloc::vec::Vec - -pub fn vortex_layout::LayoutAdapter::to_layout(&self) -> vortex_layout::LayoutRef - -#[repr(transparent)] pub struct vortex_layout::LayoutEncodingAdapter(_) - -impl core::fmt::Debug for vortex_layout::LayoutEncodingAdapter - -pub fn vortex_layout::LayoutEncodingAdapter::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result - -impl vortex_layout::LayoutEncoding for vortex_layout::LayoutEncodingAdapter - -pub fn vortex_layout::LayoutEncodingAdapter::as_any(&self) -> &dyn core::any::Any - -pub fn vortex_layout::LayoutEncodingAdapter::build(&self, &vortex_array::dtype::DType, u64, &[u8], alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult - -pub fn vortex_layout::LayoutEncodingAdapter::id(&self) -> vortex_layout::LayoutEncodingId - -pub struct vortex_layout::LayoutReaderContext - -impl vortex_layout::LayoutReaderContext - -pub fn vortex_layout::LayoutReaderContext::get(&self, vortex_session::registry::Id) -> core::option::Option> - -pub fn vortex_layout::LayoutReaderContext::new() -> Self - -pub fn vortex_layout::LayoutReaderContext::with(&self, vortex_session::registry::Id, alloc::sync::Arc) -> Self - -impl core::clone::Clone for vortex_layout::LayoutReaderContext - -pub fn vortex_layout::LayoutReaderContext::clone(&self) -> vortex_layout::LayoutReaderContext - -impl core::default::Default for vortex_layout::LayoutReaderContext - -pub fn vortex_layout::LayoutReaderContext::default() -> vortex_layout::LayoutReaderContext - -impl core::fmt::Debug for vortex_layout::LayoutReaderContext - -pub fn vortex_layout::LayoutReaderContext::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result - -pub struct vortex_layout::LazyReaderChildren - -impl vortex_layout::LazyReaderChildren - -pub fn vortex_layout::LazyReaderChildren::get(&self, usize) -> vortex_error::VortexResult<&vortex_layout::LayoutReaderRef> - -pub fn vortex_layout::LazyReaderChildren::new(alloc::sync::Arc, alloc::vec::Vec, alloc::vec::Vec>, alloc::sync::Arc, vortex_session::VortexSession, vortex_layout::LayoutReaderContext) -> Self - -pub struct vortex_layout::SplitRange - -impl vortex_layout::SplitRange - -pub fn vortex_layout::SplitRange::check_bounds(&self, u64) -> vortex_error::VortexResult<()> - -pub fn vortex_layout::SplitRange::is_empty(&self) -> bool - -pub fn vortex_layout::SplitRange::len(&self) -> u64 - -pub fn vortex_layout::SplitRange::root(core::ops::range::Range) -> vortex_error::VortexResult - -pub fn vortex_layout::SplitRange::root_row_range(&self) -> core::ops::range::Range - -pub fn vortex_layout::SplitRange::row_offset(&self) -> u64 - -pub fn vortex_layout::SplitRange::row_range(&self) -> &core::ops::range::Range - -pub fn vortex_layout::SplitRange::try_new(u64, core::ops::range::Range) -> vortex_error::VortexResult - -impl core::clone::Clone for vortex_layout::SplitRange - -pub fn vortex_layout::SplitRange::clone(&self) -> vortex_layout::SplitRange - -impl core::cmp::Eq for vortex_layout::SplitRange - -impl core::cmp::PartialEq for vortex_layout::SplitRange - -pub fn vortex_layout::SplitRange::eq(&self, &vortex_layout::SplitRange) -> bool - -impl core::fmt::Debug for vortex_layout::SplitRange - -pub fn vortex_layout::SplitRange::fmt(&self, &mut core::fmt::Formatter<'_>) -> core::fmt::Result - -impl core::marker::StructuralPartialEq for vortex_layout::SplitRange - -pub trait vortex_layout::ArrayFutureExt - -pub fn vortex_layout::ArrayFutureExt::masked(self, vortex_array::mask_future::MaskFuture) -> Self - -impl vortex_layout::ArrayFutureExt for vortex_layout::ArrayFuture - -pub fn vortex_layout::ArrayFuture::masked(self, vortex_array::mask_future::MaskFuture) -> Self - -pub trait vortex_layout::IntoLayout - -pub fn vortex_layout::IntoLayout::into_layout(self) -> vortex_layout::LayoutRef - -impl vortex_layout::IntoLayout for vortex_layout::layouts::chunked::ChunkedLayout - -pub fn vortex_layout::layouts::chunked::ChunkedLayout::into_layout(self) -> vortex_layout::LayoutRef - -impl vortex_layout::IntoLayout for vortex_layout::layouts::dict::DictLayout - -pub fn vortex_layout::layouts::dict::DictLayout::into_layout(self) -> vortex_layout::LayoutRef - -impl vortex_layout::IntoLayout for vortex_layout::layouts::flat::FlatLayout - -pub fn vortex_layout::layouts::flat::FlatLayout::into_layout(self) -> vortex_layout::LayoutRef - -impl vortex_layout::IntoLayout for vortex_layout::layouts::struct_::StructLayout - -pub fn vortex_layout::layouts::struct_::StructLayout::into_layout(self) -> vortex_layout::LayoutRef - -impl vortex_layout::IntoLayout for vortex_layout::layouts::zoned::ZonedLayout - -pub fn vortex_layout::layouts::zoned::ZonedLayout::into_layout(self) -> vortex_layout::LayoutRef - -pub trait vortex_layout::Layout: 'static + core::marker::Send + core::marker::Sync + core::fmt::Debug + vortex_layout::layout::private::Sealed - -pub fn vortex_layout::Layout::as_any(&self) -> &dyn core::any::Any - -pub fn vortex_layout::Layout::as_any_arc(alloc::sync::Arc) -> alloc::sync::Arc<(dyn core::any::Any + core::marker::Send + core::marker::Sync)> - -pub fn vortex_layout::Layout::child(&self, usize) -> vortex_error::VortexResult - -pub fn vortex_layout::Layout::child_type(&self, usize) -> vortex_layout::LayoutChildType - -pub fn vortex_layout::Layout::dtype(&self) -> &vortex_array::dtype::DType - -pub fn vortex_layout::Layout::encoding(&self) -> vortex_layout::LayoutEncodingRef - -pub fn vortex_layout::Layout::metadata(&self) -> alloc::vec::Vec - -pub fn vortex_layout::Layout::nchildren(&self) -> usize - -pub fn vortex_layout::Layout::new_reader(&self, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult - -pub fn vortex_layout::Layout::row_count(&self) -> u64 - -pub fn vortex_layout::Layout::segment_ids(&self) -> alloc::vec::Vec - -pub fn vortex_layout::Layout::to_layout(&self) -> vortex_layout::LayoutRef - -impl vortex_layout::Layout for vortex_layout::LayoutAdapter - -pub fn vortex_layout::LayoutAdapter::as_any(&self) -> &dyn core::any::Any - -pub fn vortex_layout::LayoutAdapter::as_any_arc(alloc::sync::Arc) -> alloc::sync::Arc<(dyn core::any::Any + core::marker::Send + core::marker::Sync)> - -pub fn vortex_layout::LayoutAdapter::child(&self, usize) -> vortex_error::VortexResult - -pub fn vortex_layout::LayoutAdapter::child_type(&self, usize) -> vortex_layout::LayoutChildType - -pub fn vortex_layout::LayoutAdapter::dtype(&self) -> &vortex_array::dtype::DType - -pub fn vortex_layout::LayoutAdapter::encoding(&self) -> vortex_layout::LayoutEncodingRef - -pub fn vortex_layout::LayoutAdapter::metadata(&self) -> alloc::vec::Vec - -pub fn vortex_layout::LayoutAdapter::nchildren(&self) -> usize - -pub fn vortex_layout::LayoutAdapter::new_reader(&self, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult - -pub fn vortex_layout::LayoutAdapter::row_count(&self) -> u64 - -pub fn vortex_layout::LayoutAdapter::segment_ids(&self) -> alloc::vec::Vec - -pub fn vortex_layout::LayoutAdapter::to_layout(&self) -> vortex_layout::LayoutRef - -pub trait vortex_layout::LayoutChildren: 'static + core::marker::Send + core::marker::Sync - -pub fn vortex_layout::LayoutChildren::child(&self, usize, &vortex_array::dtype::DType) -> vortex_error::VortexResult - -pub fn vortex_layout::LayoutChildren::child_row_count(&self, usize) -> u64 - -pub fn vortex_layout::LayoutChildren::nchildren(&self) -> usize - -pub fn vortex_layout::LayoutChildren::to_arc(&self) -> alloc::sync::Arc - -impl vortex_layout::LayoutChildren for alloc::sync::Arc - -pub fn alloc::sync::Arc::child(&self, usize, &vortex_array::dtype::DType) -> vortex_error::VortexResult - -pub fn alloc::sync::Arc::child_row_count(&self, usize) -> u64 - -pub fn alloc::sync::Arc::nchildren(&self) -> usize - -pub fn alloc::sync::Arc::to_arc(&self) -> alloc::sync::Arc - -pub trait vortex_layout::LayoutEncoding: 'static + core::marker::Send + core::marker::Sync + core::fmt::Debug + vortex_layout::encoding::private::Sealed - -pub fn vortex_layout::LayoutEncoding::as_any(&self) -> &dyn core::any::Any - -pub fn vortex_layout::LayoutEncoding::build(&self, &vortex_array::dtype::DType, u64, &[u8], alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult - -pub fn vortex_layout::LayoutEncoding::id(&self) -> vortex_layout::LayoutEncodingId - -impl vortex_layout::LayoutEncoding for vortex_layout::LayoutEncodingAdapter - -pub fn vortex_layout::LayoutEncodingAdapter::as_any(&self) -> &dyn core::any::Any - -pub fn vortex_layout::LayoutEncodingAdapter::build(&self, &vortex_array::dtype::DType, u64, &[u8], alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult - -pub fn vortex_layout::LayoutEncodingAdapter::id(&self) -> vortex_layout::LayoutEncodingId - -pub trait vortex_layout::LayoutReader: 'static + core::marker::Send + core::marker::Sync - -pub fn vortex_layout::LayoutReader::as_any(&self) -> &dyn core::any::Any - -pub fn vortex_layout::LayoutReader::dtype(&self) -> &vortex_array::dtype::DType - -pub fn vortex_layout::LayoutReader::filter_evaluation(&self, &core::ops::range::Range, &vortex_array::expr::expression::Expression, vortex_array::mask_future::MaskFuture) -> vortex_error::VortexResult - -pub fn vortex_layout::LayoutReader::name(&self) -> &alloc::sync::Arc - -pub fn vortex_layout::LayoutReader::projection_evaluation(&self, &core::ops::range::Range, &vortex_array::expr::expression::Expression, vortex_array::mask_future::MaskFuture) -> vortex_error::VortexResult - -pub fn vortex_layout::LayoutReader::pruning_evaluation(&self, &core::ops::range::Range, &vortex_array::expr::expression::Expression, vortex_mask::Mask) -> vortex_error::VortexResult - -pub fn vortex_layout::LayoutReader::register_splits(&self, &[vortex_array::dtype::field_mask::FieldMask], &vortex_layout::SplitRange, &mut alloc::collections::btree::set::BTreeSet) -> vortex_error::VortexResult<()> - -pub fn vortex_layout::LayoutReader::row_count(&self) -> u64 - -impl vortex_layout::LayoutReader for vortex_layout::layouts::row_idx::RowIdxLayoutReader - -pub fn vortex_layout::layouts::row_idx::RowIdxLayoutReader::as_any(&self) -> &dyn core::any::Any - -pub fn vortex_layout::layouts::row_idx::RowIdxLayoutReader::dtype(&self) -> &vortex_array::dtype::DType - -pub fn vortex_layout::layouts::row_idx::RowIdxLayoutReader::filter_evaluation(&self, &core::ops::range::Range, &vortex_array::expr::expression::Expression, vortex_array::mask_future::MaskFuture) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::row_idx::RowIdxLayoutReader::name(&self) -> &alloc::sync::Arc - -pub fn vortex_layout::layouts::row_idx::RowIdxLayoutReader::projection_evaluation(&self, &core::ops::range::Range, &vortex_array::expr::expression::Expression, vortex_array::mask_future::MaskFuture) -> vortex_error::VortexResult>> - -pub fn vortex_layout::layouts::row_idx::RowIdxLayoutReader::pruning_evaluation(&self, &core::ops::range::Range, &vortex_array::expr::expression::Expression, vortex_mask::Mask) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::row_idx::RowIdxLayoutReader::register_splits(&self, &[vortex_array::dtype::field_mask::FieldMask], &vortex_layout::SplitRange, &mut alloc::collections::btree::set::BTreeSet) -> vortex_error::VortexResult<()> - -pub fn vortex_layout::layouts::row_idx::RowIdxLayoutReader::row_count(&self) -> u64 - -pub trait vortex_layout::LayoutStrategy: 'static + core::marker::Send + core::marker::Sync - -pub fn vortex_layout::LayoutStrategy::buffered_bytes(&self) -> u64 - -pub fn vortex_layout::LayoutStrategy::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait - -impl vortex_layout::LayoutStrategy for alloc::sync::Arc - -pub fn alloc::sync::Arc::buffered_bytes(&self) -> u64 - -pub fn alloc::sync::Arc::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait - -impl vortex_layout::LayoutStrategy for vortex_layout::layouts::buffered::BufferedStrategy - -pub fn vortex_layout::layouts::buffered::BufferedStrategy::buffered_bytes(&self) -> u64 - -pub fn vortex_layout::layouts::buffered::BufferedStrategy::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait - -impl vortex_layout::LayoutStrategy for vortex_layout::layouts::chunked::writer::ChunkedLayoutStrategy - -pub fn vortex_layout::layouts::chunked::writer::ChunkedLayoutStrategy::buffered_bytes(&self) -> u64 - -pub fn vortex_layout::layouts::chunked::writer::ChunkedLayoutStrategy::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait - -impl vortex_layout::LayoutStrategy for vortex_layout::layouts::collect::CollectStrategy - -pub fn vortex_layout::layouts::collect::CollectStrategy::buffered_bytes(&self) -> u64 - -pub fn vortex_layout::layouts::collect::CollectStrategy::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait - -impl vortex_layout::LayoutStrategy for vortex_layout::layouts::compressed::CompressingStrategy - -pub fn vortex_layout::layouts::compressed::CompressingStrategy::buffered_bytes(&self) -> u64 - -pub fn vortex_layout::layouts::compressed::CompressingStrategy::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait - -impl vortex_layout::LayoutStrategy for vortex_layout::layouts::dict::writer::DictStrategy - -pub fn vortex_layout::layouts::dict::writer::DictStrategy::buffered_bytes(&self) -> u64 - -pub fn vortex_layout::layouts::dict::writer::DictStrategy::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait - -impl vortex_layout::LayoutStrategy for vortex_layout::layouts::flat::writer::FlatLayoutStrategy - -pub fn vortex_layout::layouts::flat::writer::FlatLayoutStrategy::buffered_bytes(&self) -> u64 - -pub fn vortex_layout::layouts::flat::writer::FlatLayoutStrategy::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait - -impl vortex_layout::LayoutStrategy for vortex_layout::layouts::repartition::RepartitionStrategy - -pub fn vortex_layout::layouts::repartition::RepartitionStrategy::buffered_bytes(&self) -> u64 - -pub fn vortex_layout::layouts::repartition::RepartitionStrategy::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait - -impl vortex_layout::LayoutStrategy for vortex_layout::layouts::table::TableStrategy - -pub fn vortex_layout::layouts::table::TableStrategy::buffered_bytes(&self) -> u64 - -pub fn vortex_layout::layouts::table::TableStrategy::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait - -impl vortex_layout::LayoutStrategy for vortex_layout::layouts::zoned::writer::ZonedStrategy - -pub fn vortex_layout::layouts::zoned::writer::ZonedStrategy::buffered_bytes(&self) -> u64 - -pub fn vortex_layout::layouts::zoned::writer::ZonedStrategy::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, vortex_array::ArrayContext, vortex_layout::segments::SegmentSinkRef, vortex_layout::sequence::SendableSequentialStream, vortex_layout::sequence::SequencePointer, &'life1 vortex_session::VortexSession) -> core::pin::Pin> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait - -pub trait vortex_layout::VTable: 'static + core::marker::Sized + core::marker::Send + core::marker::Sync + core::fmt::Debug - -pub type vortex_layout::VTable::Encoding: 'static + core::marker::Send + core::marker::Sync + core::ops::deref::Deref - -pub type vortex_layout::VTable::Layout: 'static + core::marker::Send + core::marker::Sync + core::clone::Clone + core::fmt::Debug + core::ops::deref::Deref + vortex_layout::IntoLayout - -pub type vortex_layout::VTable::Metadata: vortex_array::metadata::SerializeMetadata + vortex_array::metadata::DeserializeMetadata + core::fmt::Debug - -pub fn vortex_layout::VTable::build(&Self::Encoding, &vortex_array::dtype::DType, u64, &::Output, alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult - -pub fn vortex_layout::VTable::child(&Self::Layout, usize) -> vortex_error::VortexResult - -pub fn vortex_layout::VTable::child_type(&Self::Layout, usize) -> vortex_layout::LayoutChildType - -pub fn vortex_layout::VTable::dtype(&Self::Layout) -> &vortex_array::dtype::DType - -pub fn vortex_layout::VTable::encoding(&Self::Layout) -> vortex_layout::LayoutEncodingRef - -pub fn vortex_layout::VTable::id(&Self::Encoding) -> vortex_layout::LayoutId - -pub fn vortex_layout::VTable::metadata(&Self::Layout) -> Self::Metadata - -pub fn vortex_layout::VTable::nchildren(&Self::Layout) -> usize - -pub fn vortex_layout::VTable::new_reader(&Self::Layout, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult - -pub fn vortex_layout::VTable::row_count(&Self::Layout) -> u64 - -pub fn vortex_layout::VTable::segment_ids(&Self::Layout) -> alloc::vec::Vec - -pub fn vortex_layout::VTable::with_children(&mut Self::Layout, alloc::vec::Vec) -> vortex_error::VortexResult<()> - -impl vortex_layout::VTable for vortex_layout::layouts::chunked::Chunked - -pub type vortex_layout::layouts::chunked::Chunked::Encoding = vortex_layout::layouts::chunked::ChunkedLayoutEncoding - -pub type vortex_layout::layouts::chunked::Chunked::Layout = vortex_layout::layouts::chunked::ChunkedLayout - -pub type vortex_layout::layouts::chunked::Chunked::Metadata = vortex_array::metadata::EmptyMetadata - -pub fn vortex_layout::layouts::chunked::Chunked::build(&Self::Encoding, &vortex_array::dtype::DType, u64, &::Output, alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::chunked::Chunked::child(&Self::Layout, usize) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::chunked::Chunked::child_type(&Self::Layout, usize) -> vortex_layout::LayoutChildType - -pub fn vortex_layout::layouts::chunked::Chunked::dtype(&Self::Layout) -> &vortex_array::dtype::DType - -pub fn vortex_layout::layouts::chunked::Chunked::encoding(&Self::Layout) -> vortex_layout::LayoutEncodingRef - -pub fn vortex_layout::layouts::chunked::Chunked::id(&Self::Encoding) -> vortex_layout::LayoutId - -pub fn vortex_layout::layouts::chunked::Chunked::metadata(&Self::Layout) -> Self::Metadata - -pub fn vortex_layout::layouts::chunked::Chunked::nchildren(&Self::Layout) -> usize - -pub fn vortex_layout::layouts::chunked::Chunked::new_reader(&Self::Layout, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::chunked::Chunked::row_count(&Self::Layout) -> u64 - -pub fn vortex_layout::layouts::chunked::Chunked::segment_ids(&Self::Layout) -> alloc::vec::Vec - -pub fn vortex_layout::layouts::chunked::Chunked::with_children(&mut Self::Layout, alloc::vec::Vec) -> vortex_error::VortexResult<()> - -impl vortex_layout::VTable for vortex_layout::layouts::dict::Dict - -pub type vortex_layout::layouts::dict::Dict::Encoding = vortex_layout::layouts::dict::DictLayoutEncoding - -pub type vortex_layout::layouts::dict::Dict::Layout = vortex_layout::layouts::dict::DictLayout - -pub type vortex_layout::layouts::dict::Dict::Metadata = vortex_array::metadata::ProstMetadata - -pub fn vortex_layout::layouts::dict::Dict::build(&Self::Encoding, &vortex_array::dtype::DType, u64, &::Output, alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::dict::Dict::child(&Self::Layout, usize) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::dict::Dict::child_type(&Self::Layout, usize) -> vortex_layout::LayoutChildType - -pub fn vortex_layout::layouts::dict::Dict::dtype(&Self::Layout) -> &vortex_array::dtype::DType - -pub fn vortex_layout::layouts::dict::Dict::encoding(&Self::Layout) -> vortex_layout::LayoutEncodingRef - -pub fn vortex_layout::layouts::dict::Dict::id(&Self::Encoding) -> vortex_layout::LayoutId - -pub fn vortex_layout::layouts::dict::Dict::metadata(&Self::Layout) -> Self::Metadata - -pub fn vortex_layout::layouts::dict::Dict::nchildren(&Self::Layout) -> usize - -pub fn vortex_layout::layouts::dict::Dict::new_reader(&Self::Layout, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::dict::Dict::row_count(&Self::Layout) -> u64 - -pub fn vortex_layout::layouts::dict::Dict::segment_ids(&Self::Layout) -> alloc::vec::Vec - -pub fn vortex_layout::layouts::dict::Dict::with_children(&mut Self::Layout, alloc::vec::Vec) -> vortex_error::VortexResult<()> - -impl vortex_layout::VTable for vortex_layout::layouts::flat::Flat - -pub type vortex_layout::layouts::flat::Flat::Encoding = vortex_layout::layouts::flat::FlatLayoutEncoding - -pub type vortex_layout::layouts::flat::Flat::Layout = vortex_layout::layouts::flat::FlatLayout - -pub type vortex_layout::layouts::flat::Flat::Metadata = vortex_array::metadata::ProstMetadata - -pub fn vortex_layout::layouts::flat::Flat::build(&Self::Encoding, &vortex_array::dtype::DType, u64, &::Output, alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::flat::Flat::child(&Self::Layout, usize) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::flat::Flat::child_type(&Self::Layout, usize) -> vortex_layout::LayoutChildType - -pub fn vortex_layout::layouts::flat::Flat::dtype(&Self::Layout) -> &vortex_array::dtype::DType - -pub fn vortex_layout::layouts::flat::Flat::encoding(&Self::Layout) -> vortex_layout::LayoutEncodingRef - -pub fn vortex_layout::layouts::flat::Flat::id(&Self::Encoding) -> vortex_layout::LayoutId - -pub fn vortex_layout::layouts::flat::Flat::metadata(&Self::Layout) -> Self::Metadata - -pub fn vortex_layout::layouts::flat::Flat::nchildren(&Self::Layout) -> usize - -pub fn vortex_layout::layouts::flat::Flat::new_reader(&Self::Layout, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::flat::Flat::row_count(&Self::Layout) -> u64 - -pub fn vortex_layout::layouts::flat::Flat::segment_ids(&Self::Layout) -> alloc::vec::Vec - -pub fn vortex_layout::layouts::flat::Flat::with_children(&mut Self::Layout, alloc::vec::Vec) -> vortex_error::VortexResult<()> - -impl vortex_layout::VTable for vortex_layout::layouts::struct_::Struct - -pub type vortex_layout::layouts::struct_::Struct::Encoding = vortex_layout::layouts::struct_::StructLayoutEncoding - -pub type vortex_layout::layouts::struct_::Struct::Layout = vortex_layout::layouts::struct_::StructLayout - -pub type vortex_layout::layouts::struct_::Struct::Metadata = vortex_array::metadata::EmptyMetadata - -pub fn vortex_layout::layouts::struct_::Struct::build(&Self::Encoding, &vortex_array::dtype::DType, u64, &::Output, alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::struct_::Struct::child(&Self::Layout, usize) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::struct_::Struct::child_type(&Self::Layout, usize) -> vortex_layout::LayoutChildType - -pub fn vortex_layout::layouts::struct_::Struct::dtype(&Self::Layout) -> &vortex_array::dtype::DType - -pub fn vortex_layout::layouts::struct_::Struct::encoding(&Self::Layout) -> vortex_layout::LayoutEncodingRef - -pub fn vortex_layout::layouts::struct_::Struct::id(&Self::Encoding) -> vortex_layout::LayoutId - -pub fn vortex_layout::layouts::struct_::Struct::metadata(&Self::Layout) -> Self::Metadata - -pub fn vortex_layout::layouts::struct_::Struct::nchildren(&Self::Layout) -> usize - -pub fn vortex_layout::layouts::struct_::Struct::new_reader(&Self::Layout, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::struct_::Struct::row_count(&Self::Layout) -> u64 - -pub fn vortex_layout::layouts::struct_::Struct::segment_ids(&Self::Layout) -> alloc::vec::Vec - -pub fn vortex_layout::layouts::struct_::Struct::with_children(&mut Self::Layout, alloc::vec::Vec) -> vortex_error::VortexResult<()> - -impl vortex_layout::VTable for vortex_layout::layouts::zoned::Zoned - -pub type vortex_layout::layouts::zoned::Zoned::Encoding = vortex_layout::layouts::zoned::ZonedLayoutEncoding - -pub type vortex_layout::layouts::zoned::Zoned::Layout = vortex_layout::layouts::zoned::ZonedLayout - -pub type vortex_layout::layouts::zoned::Zoned::Metadata = vortex_layout::layouts::zoned::ZonedMetadata - -pub fn vortex_layout::layouts::zoned::Zoned::build(&Self::Encoding, &vortex_array::dtype::DType, u64, &vortex_layout::layouts::zoned::ZonedMetadata, alloc::vec::Vec, &dyn vortex_layout::LayoutChildren, &vortex_session::registry::ReadContext) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::zoned::Zoned::child(&Self::Layout, usize) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::zoned::Zoned::child_type(&Self::Layout, usize) -> vortex_layout::LayoutChildType - -pub fn vortex_layout::layouts::zoned::Zoned::dtype(&Self::Layout) -> &vortex_array::dtype::DType - -pub fn vortex_layout::layouts::zoned::Zoned::encoding(&Self::Layout) -> vortex_layout::LayoutEncodingRef - -pub fn vortex_layout::layouts::zoned::Zoned::id(&Self::Encoding) -> vortex_layout::LayoutId - -pub fn vortex_layout::layouts::zoned::Zoned::metadata(&Self::Layout) -> Self::Metadata - -pub fn vortex_layout::layouts::zoned::Zoned::nchildren(&Self::Layout) -> usize - -pub fn vortex_layout::layouts::zoned::Zoned::new_reader(&Self::Layout, alloc::sync::Arc, alloc::sync::Arc, &vortex_session::VortexSession, &vortex_layout::LayoutReaderContext) -> vortex_error::VortexResult - -pub fn vortex_layout::layouts::zoned::Zoned::row_count(&Self::Layout) -> u64 - -pub fn vortex_layout::layouts::zoned::Zoned::segment_ids(&Self::Layout) -> alloc::vec::Vec - -pub fn vortex_layout::layouts::zoned::Zoned::with_children(&mut Self::Layout, alloc::vec::Vec) -> vortex_error::VortexResult<()> - -pub fn vortex_layout::layout_from_flatbuffer(vortex_flatbuffers::FlatBuffer, &vortex_array::dtype::DType, &vortex_session::registry::ReadContext, &vortex_session::registry::ReadContext, &vortex_layout::session::LayoutRegistry) -> vortex_error::VortexResult - -pub fn vortex_layout::layout_from_flatbuffer_with_options(vortex_flatbuffers::FlatBuffer, &vortex_array::dtype::DType, &vortex_session::registry::ReadContext, &vortex_session::registry::ReadContext, &vortex_layout::session::LayoutRegistry, bool) -> vortex_error::VortexResult - -pub type vortex_layout::ArrayFuture = futures_core::future::BoxFuture<'static, vortex_error::VortexResult> - -pub type vortex_layout::LayoutContext = vortex_session::registry::Context - -pub type vortex_layout::LayoutEncodingId = vortex_session::registry::Id - -pub type vortex_layout::LayoutEncodingRef = arcref::ArcRef - -pub type vortex_layout::LayoutId = vortex_session::registry::Id - -pub type vortex_layout::LayoutReaderRef = alloc::sync::Arc - -pub type vortex_layout::LayoutRef = alloc::sync::Arc From b1e8f767b94a4f73e58731fa315df238d7024dfa Mon Sep 17 00:00:00 2001 From: Connor Tsui Date: Wed, 3 Jun 2026 16:05:56 -0400 Subject: [PATCH 018/115] Make `from_arrow` not panic (#8242) ## Summary Removes the assertion in the `nulls` function so that `from_arrow` errors instead of panicking if the `nullable` does not match the actually nullability of the arrow array. Also documents the `FromArrowArray` trait and method. ## Testing 2 basic unit tests. Signed-off-by: Connor Tsui --- vortex-array/src/arrow/convert.rs | 126 ++++++++++++++++------- vortex-array/src/arrow/mod.rs | 21 ++++ vortex-array/src/arrow/session.rs | 14 +-- vortex-array/src/extension/uuid/arrow.rs | 2 +- 4 files changed, 119 insertions(+), 44 deletions(-) diff --git a/vortex-array/src/arrow/convert.rs b/vortex-array/src/arrow/convert.rs index 7c64d937704..ee1171a1848 100644 --- a/vortex-array/src/arrow/convert.rs +++ b/vortex-array/src/arrow/convert.rs @@ -57,14 +57,15 @@ use arrow_buffer::buffer::NullBuffer; use arrow_buffer::buffer::OffsetBuffer; use arrow_schema::DataType; use arrow_schema::TimeUnit as ArrowTimeUnit; -use itertools::Itertools; use vortex_buffer::Alignment; use vortex_buffer::BitBuffer; use vortex_buffer::Buffer; use vortex_buffer::ByteBuffer; -use vortex_error::VortexExpect as _; use vortex_error::VortexResult; use vortex_error::vortex_bail; +use vortex_error::vortex_ensure; +use vortex_error::vortex_ensure_eq; +use vortex_error::vortex_err; use vortex_error::vortex_panic; use crate::ArrayRef; @@ -140,7 +141,7 @@ macro_rules! impl_from_arrow_primitive { impl FromArrowArray<&ArrowPrimitiveArray<$T>> for ArrayRef { fn from_arrow(value: &ArrowPrimitiveArray<$T>, nullable: bool) -> VortexResult { let buffer = Buffer::from_arrow_scalar_buffer(value.values().clone()); - let validity = nulls(value.nulls(), nullable); + let validity = nulls(value.nulls(), nullable)?; Ok(PrimitiveArray::new(buffer, validity).into_array()) } } @@ -166,7 +167,7 @@ impl FromArrowArray<&ArrowPrimitiveArray> for ArrayRef { ) -> VortexResult { let decimal_type = DecimalDType::new(array.precision(), array.scale()); let buffer = Buffer::from_arrow_scalar_buffer(array.values().clone()); - let validity = nulls(array.nulls(), nullable); + let validity = nulls(array.nulls(), nullable)?; Ok(DecimalArray::new(buffer, decimal_type, validity).into_array()) } } @@ -178,7 +179,7 @@ impl FromArrowArray<&ArrowPrimitiveArray> for ArrayRef { ) -> VortexResult { let decimal_type = DecimalDType::new(array.precision(), array.scale()); let buffer = Buffer::from_arrow_scalar_buffer(array.values().clone()); - let validity = nulls(array.nulls(), nullable); + let validity = nulls(array.nulls(), nullable)?; Ok(DecimalArray::new(buffer, decimal_type, validity).into_array()) } } @@ -190,7 +191,7 @@ impl FromArrowArray<&ArrowPrimitiveArray> for ArrayRef { ) -> VortexResult { let decimal_type = DecimalDType::new(array.precision(), array.scale()); let buffer = Buffer::from_arrow_scalar_buffer(array.values().clone()); - let validity = nulls(array.nulls(), nullable); + let validity = nulls(array.nulls(), nullable)?; Ok(DecimalArray::new(buffer, decimal_type, validity).into_array()) } } @@ -207,7 +208,7 @@ impl FromArrowArray<&ArrowPrimitiveArray> for ArrayRef { // of either type. let buffer = unsafe { std::mem::transmute::, Buffer>(buffer) }; - let validity = nulls(array.nulls(), nullable); + let validity = nulls(array.nulls(), nullable)?; Ok(DecimalArray::new(buffer, decimal_type, validity).into_array()) } } @@ -219,7 +220,7 @@ macro_rules! impl_from_arrow_temporal { value: &ArrowPrimitiveArray<$T>, nullable: bool, ) -> vortex_error::VortexResult { - Ok(temporal_array(value, nullable)) + temporal_array(value, nullable) } } }; @@ -241,17 +242,20 @@ impl_from_arrow_temporal!(Time64NanosecondType); impl_from_arrow_temporal!(Date32Type); impl_from_arrow_temporal!(Date64Type); -fn temporal_array(value: &ArrowPrimitiveArray, nullable: bool) -> ArrayRef +fn temporal_array( + value: &ArrowPrimitiveArray, + nullable: bool, +) -> VortexResult where T::Native: NativePType, { let arr = PrimitiveArray::new( Buffer::from_arrow_scalar_buffer(value.values().clone()), - nulls(value.nulls(), nullable), + nulls(value.nulls(), nullable)?, ) .into_array(); - match value.data_type() { + Ok(match value.data_type() { DataType::Timestamp(time_unit, tz) => { TemporalArray::new_timestamp(arr, time_unit.into(), tz.clone()).into() } @@ -262,7 +266,7 @@ where DataType::Duration(_) => unimplemented!(), DataType::Interval(_) => unimplemented!(), _ => vortex_panic!("Invalid temporal type: {}", value.data_type()), - } + }) } impl FromArrowArray<&GenericByteArray> for ArrayRef @@ -281,7 +285,7 @@ where value.offsets().clone().into_array(), ByteBuffer::from_arrow_buffer(value.values().clone(), Alignment::of::()), dtype, - nulls(value.nulls(), nullable), + nulls(value.nulls(), nullable)?, ) } .into_array()) @@ -313,7 +317,7 @@ impl FromArrowArray<&GenericByteViewArray> for ArrayRef { .collect::>(), ), dtype, - nulls(value.nulls(), nullable), + nulls(value.nulls(), nullable)?, ) .into_array() }) @@ -324,17 +328,17 @@ impl FromArrowArray<&ArrowBooleanArray> for ArrayRef { fn from_arrow(value: &ArrowBooleanArray, nullable: bool) -> VortexResult { Ok(BoolArray::new( value.values().clone().into(), - nulls(value.nulls(), nullable), + nulls(value.nulls(), nullable)?, ) .into_array()) } } /// Strip out the nulls from this array and return a new array without nulls. -pub(crate) fn remove_nulls(data: arrow_data::ArrayData) -> arrow_data::ArrayData { +pub(crate) fn remove_nulls(data: arrow_data::ArrayData) -> VortexResult { if data.null_count() == 0 { // No nulls to remove, return the array as is - return data; + return Ok(data); } let children = match data.data_type() { @@ -344,12 +348,12 @@ pub(crate) fn remove_nulls(data: arrow_data::ArrayData) -> arrow_data::ArrayData .zip(data.child_data().iter()) .map(|(field, child_data)| { if field.is_nullable() { - child_data.clone() + Ok(child_data.clone()) } else { remove_nulls(child_data.clone()) } }) - .collect_vec(), + .collect::>>()?, ), DataType::List(f) | DataType::LargeList(f) @@ -359,12 +363,12 @@ pub(crate) fn remove_nulls(data: arrow_data::ArrayData) -> arrow_data::ArrayData if !f.is_nullable() => { // All list types only have one child - assert_eq!( + vortex_ensure_eq!( data.child_data().len(), 1, "List types should have one child" ); - Some(vec![remove_nulls(data.child_data()[0].clone())]) + Some(vec![remove_nulls(data.child_data()[0].clone())?]) } _ => None, }; @@ -375,7 +379,7 @@ pub(crate) fn remove_nulls(data: arrow_data::ArrayData) -> arrow_data::ArrayData } builder .build() - .vortex_expect("reconstructing array without nulls") + .map_err(|e| vortex_err!("Failed to reconstruct Arrow array without nulls: {e}")) } impl FromArrowArray<&ArrowStructArray> for ArrayRef { @@ -390,7 +394,7 @@ impl FromArrowArray<&ArrowStructArray> for ArrayRef { // Arrow pushes down nulls, even into non-nullable fields. So we strip them // out here because Vortex is a little more strict. if c.null_count() > 0 && !field.is_nullable() { - let stripped = make_array(remove_nulls(c.into_data())); + let stripped = make_array(remove_nulls(c.into_data())?); Self::from_arrow(stripped.as_ref(), false) } else { Self::from_arrow(c.as_ref(), field.is_nullable()) @@ -398,7 +402,7 @@ impl FromArrowArray<&ArrowStructArray> for ArrayRef { }) .collect::>>()?, value.len(), - nulls(value.nulls(), nullable), + nulls(value.nulls(), nullable)?, )? .into_array()) } @@ -417,7 +421,7 @@ impl FromArrowArray<&GenericListArray> for // `offsets` are always non-nullable. let offsets = value.offsets().clone().into_array(); - let nulls = nulls(value.nulls(), nullable); + let nulls = nulls(value.nulls(), nullable)?; Ok(ListArray::try_new(elements, offsets, nulls)?.into_array()) } @@ -437,7 +441,7 @@ impl FromArrowArray<&GenericListViewArray> // `offsets` and `sizes` are always non-nullable. let offsets = array.offsets().clone().into_array(); let sizes = array.sizes().clone().into_array(); - let nulls = nulls(array.nulls(), nullable); + let nulls = nulls(array.nulls(), nullable)?; Ok(ListViewArray::try_new(elements, offsets, sizes, nulls)?.into_array()) } @@ -452,7 +456,7 @@ impl FromArrowArray<&ArrowFixedSizeListArray> for ArrayRef { Ok(FixedSizeListArray::try_new( Self::from_arrow(array.values().as_ref(), field.is_nullable())?, *list_size as u32, - nulls(array.nulls(), nullable), + nulls(array.nulls(), nullable)?, array.len(), )? .into_array()) @@ -461,7 +465,10 @@ impl FromArrowArray<&ArrowFixedSizeListArray> for ArrayRef { impl FromArrowArray<&ArrowNullArray> for ArrayRef { fn from_arrow(value: &ArrowNullArray, nullable: bool) -> VortexResult { - assert!(nullable); + vortex_ensure!( + nullable, + "Cannot convert an Arrow NullArray into a non-nullable Vortex array" + ); Ok(NullArray::new(value.len()).into_array()) } } @@ -476,9 +483,9 @@ impl FromArrowArray<&DictionaryArray> for DictArra } } -pub(crate) fn nulls(nulls: Option<&NullBuffer>, nullable: bool) -> Validity { +pub(crate) fn nulls(nulls: Option<&NullBuffer>, nullable: bool) -> VortexResult { if nullable { - nulls + Ok(nulls .map(|nulls| { if nulls.null_count() == nulls.len() { Validity::AllInvalid @@ -486,10 +493,15 @@ pub(crate) fn nulls(nulls: Option<&NullBuffer>, nullable: bool) -> Validity { Validity::from(BitBuffer::from(nulls.inner().clone())) } }) - .unwrap_or_else(|| Validity::AllValid) + .unwrap_or(Validity::AllValid)) } else { - assert!(nulls.map(|x| x.null_count() == 0).unwrap_or(true)); - Validity::NonNullable + let null_count = nulls.map(NullBuffer::null_count).unwrap_or(0); + vortex_ensure_eq!( + null_count, + 0, + "Cannot convert an Arrow array containing {null_count} nulls into a non-nullable Vortex array" + ); + Ok(Validity::NonNullable) } } @@ -1488,8 +1500,48 @@ mod tests { } #[test] - #[should_panic] - pub fn cannot_handle_nullable_struct_containing_non_nullable_dictionary() { + fn non_nullable_request_rejects_nulls() { + // Requesting `nullable = false` on an Arrow array that physically contains nulls is a + // contradiction and must surface as an error, not a panic. + let arrow_array = Int32Array::from(vec![Some(1), None, Some(3)]); + assert!(ArrayRef::from_arrow(&arrow_array, false).is_err()); + } + + #[test] + fn non_nullable_request_rejects_null_array() { + // An Arrow NullArray is entirely null, so it cannot be converted to a non-nullable + // Vortex array. + let arrow_array = NullArray::new(5); + assert!(ArrayRef::from_arrow(&arrow_array, false).is_err()); + } + + #[test] + fn non_nullable_struct_with_nulls_errors() { + // A struct array carrying top-level nulls cannot be converted to a non-nullable Vortex + // struct; the struct-level validity reconciliation must error rather than panic. + let struct_array = new_null_array( + &DataType::Struct(Fields::from(vec![Field::new("a", DataType::Int32, true)])), + 3, + ); + assert!(ArrayRef::from_arrow(struct_array.as_ref(), false).is_err()); + } + + #[test] + fn non_nullable_list_with_nulls_errors() { + // Likewise for a list array with a null entry: requesting a non-nullable list must error + // rather than panic. + let mut builder = ListBuilder::new(Int32Builder::new()); + builder.append_value([Some(1), Some(2)]); + builder.append_null(); + let list = builder.finish(); + assert!(ArrayRef::from_arrow(&list, false).is_err()); + } + + #[test] + pub fn nullable_struct_containing_non_nullable_dictionary_with_nulls_errors() { + // `remove_nulls` cannot strip pushed-down nulls out of a non-nullable dictionary field, + // so the values end up converted with `nullable = false` while still containing nulls. + // This must surface as an error rather than panicking. let null_struct_array_with_non_nullable_field = new_null_array( &DataType::Struct(Fields::from(vec![Field::new( "non_nullable_deeper_inner", @@ -1499,6 +1551,8 @@ mod tests { 1, ); - ArrayRef::from_arrow(null_struct_array_with_non_nullable_field.as_ref(), true).unwrap(); + assert!( + ArrayRef::from_arrow(null_struct_array_with_non_nullable_field.as_ref(), true).is_err() + ); } } diff --git a/vortex-array/src/arrow/mod.rs b/vortex-array/src/arrow/mod.rs index 81e5f635bcb..5259005dffc 100644 --- a/vortex-array/src/arrow/mod.rs +++ b/vortex-array/src/arrow/mod.rs @@ -26,7 +26,28 @@ use crate::ArrayRef; use crate::LEGACY_SESSION; use crate::VortexSessionExecute; +/// Construct a Vortex array from an Arrow array (or other Arrow container) of type `A`. +/// +/// Implementations reuse the underlying Arrow buffers without copying wherever the Arrow and +/// Vortex memory layouts allow it. pub trait FromArrowArray { + /// Convert `array` into a Vortex array whose [`DType`](crate::dtype::DType) has the requested + /// `nullable` [`Nullability`](crate::dtype::Nullability). + /// + /// An Arrow array can carry a validity (null) buffer regardless of whether its schema declares + /// the field nullable, so the desired nullability is supplied separately by the caller + /// (typically from the corresponding Arrow `Field`'s `is_nullable`). This flag is reconciled + /// with the array's physical nulls as follows: + /// + /// - `nullable == true`: the resulting validity is derived from the array's null buffer, or + /// all-valid when the array has none. + /// - `nullable == false`: the array must contain no nulls, and the result is non-nullable. + /// + /// # Errors + /// + /// Returns an error if `nullable` is `false` but `array` physically contains one or more nulls + /// (including an Arrow `NullArray`, which is entirely null), or if the Arrow data type is not + /// supported. fn from_arrow(array: A, nullable: bool) -> VortexResult where Self: Sized; diff --git a/vortex-array/src/arrow/session.rs b/vortex-array/src/arrow/session.rs index 598a903cfa2..705f7971f90 100644 --- a/vortex-array/src/arrow/session.rs +++ b/vortex-array/src/arrow/session.rs @@ -515,14 +515,14 @@ impl ArrowSession { // Arrow pushes nulls into non-nullable fields; strip before recursing // so Vortex's stricter validity invariants are upheld. let inner = if col.null_count() > 0 && !child_field.is_nullable() { - make_array(remove_nulls(col.to_data())) + make_array(remove_nulls(col.to_data())?) } else { ArrowArrayRef::clone(col) }; self.from_arrow_array(inner, child_field.as_ref()) }) .collect::>>()?; - let validity = nulls(arrow_struct.nulls(), field.is_nullable()); + let validity = nulls(arrow_struct.nulls(), field.is_nullable())?; Ok( StructArray::try_new(names, columns, arrow_struct.len(), validity)? .into_array(), @@ -533,7 +533,7 @@ impl ArrowSession { let elements = self .from_arrow_array(ArrowArrayRef::clone(list.values()), elem_field.as_ref())?; let offsets = list.offsets().clone().into_array(); - let validity = nulls(list.nulls(), field.is_nullable()); + let validity = nulls(list.nulls(), field.is_nullable())?; Ok(crate::arrays::ListArray::try_new(elements, offsets, validity)?.into_array()) } DataType::LargeList(elem_field) => { @@ -541,14 +541,14 @@ impl ArrowSession { let elements = self .from_arrow_array(ArrowArrayRef::clone(list.values()), elem_field.as_ref())?; let offsets = list.offsets().clone().into_array(); - let validity = nulls(list.nulls(), field.is_nullable()); + let validity = nulls(list.nulls(), field.is_nullable())?; Ok(crate::arrays::ListArray::try_new(elements, offsets, validity)?.into_array()) } DataType::FixedSizeList(elem_field, list_size) => { let fsl = array.as_fixed_size_list(); let elements = self.from_arrow_array(ArrowArrayRef::clone(fsl.values()), elem_field.as_ref())?; - let validity = nulls(fsl.nulls(), field.is_nullable()); + let validity = nulls(fsl.nulls(), field.is_nullable())?; Ok(crate::arrays::FixedSizeListArray::try_new( elements, *list_size as u32, @@ -563,7 +563,7 @@ impl ArrowSession { .from_arrow_array(ArrowArrayRef::clone(list.values()), elem_field.as_ref())?; let offsets = list.offsets().clone().into_array(); let sizes = list.sizes().clone().into_array(); - let validity = nulls(list.nulls(), field.is_nullable()); + let validity = nulls(list.nulls(), field.is_nullable())?; Ok( crate::arrays::ListViewArray::try_new(elements, offsets, sizes, validity)? .into_array(), @@ -575,7 +575,7 @@ impl ArrowSession { .from_arrow_array(ArrowArrayRef::clone(list.values()), elem_field.as_ref())?; let offsets = list.offsets().clone().into_array(); let sizes = list.sizes().clone().into_array(); - let validity = nulls(list.nulls(), field.is_nullable()); + let validity = nulls(list.nulls(), field.is_nullable())?; Ok( crate::arrays::ListViewArray::try_new(elements, offsets, sizes, validity)? .into_array(), diff --git a/vortex-array/src/extension/uuid/arrow.rs b/vortex-array/src/extension/uuid/arrow.rs index 3d66ce99d58..cd4b97ea60a 100644 --- a/vortex-array/src/extension/uuid/arrow.rs +++ b/vortex-array/src/extension/uuid/arrow.rs @@ -142,7 +142,7 @@ impl ArrowImportVTable for Uuid { PType::U8, Validity::NonNullable, ); - let validity = nulls(fsb.nulls(), dtype.is_nullable()); + let validity = nulls(fsb.nulls(), dtype.is_nullable())?; let storage = FixedSizeListArray::new( u8_array.into_array(), From e724fe77cc2195bb8fbb68d5dbe5a21d38dce493 Mon Sep 17 00:00:00 2001 From: Nicholas Gates Date: Thu, 4 Jun 2026 03:41:02 -0400 Subject: [PATCH 019/115] Fix zoned min/max over fixed-size-list stats (#8243) Fixes #8189 Signed-off-by: "Nicholas Gates" --- .../src/aggregate_fn/fns/min_max/mod.rs | 90 ++++++++++++++++--- vortex-layout/src/layouts/zoned/zone_map.rs | 59 ++++++++---- 2 files changed, 121 insertions(+), 28 deletions(-) diff --git a/vortex-array/src/aggregate_fn/fns/min_max/mod.rs b/vortex-array/src/aggregate_fn/fns/min_max/mod.rs index f91ae351392..540b5608e28 100644 --- a/vortex-array/src/aggregate_fn/fns/min_max/mod.rs +++ b/vortex-array/src/aggregate_fn/fns/min_max/mod.rs @@ -61,8 +61,8 @@ pub fn min_max(array: &ArrayRef, ctx: &mut ExecutionCtx) -> VortexResult DType { ) } +fn minmax_supported_dtype(input_dtype: &DType) -> bool { + match input_dtype { + DType::Bool(_) + | DType::Primitive(..) + | DType::Decimal(..) + | DType::Utf8(..) + | DType::Binary(..) + | DType::Extension(..) => true, + DType::List(element_dtype, _) => minmax_supported_dtype(element_dtype), + DType::FixedSizeList(element_dtype, ..) => minmax_supported_dtype(element_dtype), + _ => false, + } +} + +/// Returns whether [`min_max`] can currently compute extrema for this logical dtype. +/// +/// This is intentionally narrower than [`minmax_supported_dtype`]. List and fixed-size-list +/// extrema have a defined output dtype for aggregate expression lowering, but the accumulator does +/// not yet implement lexicographic list comparison. +fn minmax_compute_supported_dtype(input_dtype: &DType) -> bool { + matches!( + input_dtype, + DType::Bool(_) + | DType::Primitive(..) + | DType::Decimal(..) + | DType::Utf8(..) + | DType::Binary(..) + | DType::Extension(..) + ) +} + impl AggregateFnVTable for MinMax { type Options = EmptyOptions; type Partial = MinMaxPartial; @@ -175,15 +206,7 @@ impl AggregateFnVTable for MinMax { } fn return_dtype(&self, _options: &Self::Options, input_dtype: &DType) -> Option { - match input_dtype { - DType::Bool(_) - | DType::Primitive(..) - | DType::Decimal(..) - | DType::Utf8(..) - | DType::Binary(..) - | DType::Extension(..) => Some(make_minmax_dtype(input_dtype)), - _ => None, - } + minmax_supported_dtype(input_dtype).then(|| make_minmax_dtype(input_dtype)) } fn partial_dtype(&self, options: &Self::Options, input_dtype: &DType) -> Option { @@ -278,6 +301,8 @@ impl AggregateFnVTable for MinMax { #[cfg(test)] mod tests { + use std::sync::Arc; + use vortex_buffer::BitBuffer; use vortex_buffer::buffer; use vortex_error::VortexExpect; @@ -298,6 +323,8 @@ mod tests { use crate::arrays::ChunkedArray; use crate::arrays::ConstantArray; use crate::arrays::DecimalArray; + use crate::arrays::FixedSizeListArray; + use crate::arrays::ListArray; use crate::arrays::NullArray; use crate::arrays::PrimitiveArray; use crate::arrays::VarBinArray; @@ -570,6 +597,47 @@ mod tests { Ok(()) } + #[test] + fn list_and_fixed_size_list_return_dtype() { + let element_dtype = DType::Primitive(PType::I32, Nullability::Nullable); + let list_dtype = DType::List(Arc::new(element_dtype.clone()), Nullability::Nullable); + let fixed_size_list_dtype = + DType::FixedSizeList(Arc::new(element_dtype), 1, Nullability::Nullable); + + assert_eq!( + MinMax.return_dtype(&EmptyOptions, &list_dtype), + Some(make_minmax_dtype(&list_dtype)) + ); + assert_eq!( + MinMax.return_dtype(&EmptyOptions, &fixed_size_list_dtype), + Some(make_minmax_dtype(&fixed_size_list_dtype)) + ); + } + + #[test] + fn list_and_fixed_size_list_min_max_returns_none() -> VortexResult<()> { + let mut ctx = LEGACY_SESSION.create_execution_ctx(); + + let list_array = ListArray::try_new( + buffer![1i32, 2, 3].into_array(), + buffer![0u32, 2, 3].into_array(), + Validity::NonNullable, + )? + .into_array(); + assert_eq!(min_max(&list_array, &mut ctx)?, None); + + let fixed_size_list_array = FixedSizeListArray::try_new( + buffer![1i32, 2, 3, 4].into_array(), + 2, + Validity::NonNullable, + 2, + )? + .into_array(); + assert_eq!(min_max(&fixed_size_list_array, &mut ctx)?, None); + + Ok(()) + } + use crate::dtype::half::f16; #[test] diff --git a/vortex-layout/src/layouts/zoned/zone_map.rs b/vortex-layout/src/layouts/zoned/zone_map.rs index a3719e8e17e..96154e69571 100644 --- a/vortex-layout/src/layouts/zoned/zone_map.rs +++ b/vortex-layout/src/layouts/zoned/zone_map.rs @@ -179,13 +179,13 @@ impl ZoneMap { return Ok(lit(0u64)); } - // When the aggregate function does not support the column dtype the stat is - // not computable on this layout, so treat it the same as a missing stat and - // lower to a nullable "unknown" rather than failing the whole scan. Min/Max - // share their input dtype, so falling back to `input_dtype.as_nullable()` - // keeps the rewrite well-typed for the most common case. - let Some(return_dtype) = options.aggregate_fn().return_dtype(&input_dtype) else { - return Ok(null_expr(input_dtype.as_nullable())); + let return_dtype = match options.aggregate_fn().return_dtype(&input_dtype) { + Some(return_dtype) => return_dtype, + None => vortex_bail!( + "Aggregate function {} does not support input dtype {}", + options.aggregate_fn(), + input_dtype + ), }; if !input_is_root { @@ -277,6 +277,7 @@ mod tests { use vortex_array::arrays::StructArray; use vortex_array::assert_arrays_eq; use vortex_array::dtype::DType; + use vortex_array::dtype::DecimalDType; use vortex_array::dtype::FieldNames; use vortex_array::dtype::Nullability; use vortex_array::dtype::PType; @@ -606,14 +607,14 @@ mod tests { } #[test] - fn unsupported_aggregate_input_dtype_lowers_to_unknown() { - // Regression test for issue #8189: a pruning predicate that contains a - // `StatFn(Max, $)` (or `Min`) over a column dtype that the aggregate - // function does not support (e.g. `FixedSizeList`) used to - // bail out of `lower_stat_fn` with "Aggregate function vortex.max() does - // not support input dtype ...", panicking the scan instead of treating - // the stat as unknown. - let elem_dtype = Arc::new(DType::Primitive(PType::I32, Nullability::Nullable)); + fn fixed_size_list_min_max_stat_fn_lowers_to_unknown_mask() { + // Regression test for issue #8189: Min/Max is defined for FixedSizeList + // when T is orderable. If the zone map does not carry the requested stat, + // lowering should produce an unknown typed null rather than rejecting the dtype. + let elem_dtype = Arc::new(DType::Decimal( + DecimalDType::new(10, 2), + Nullability::Nullable, + )); let column_dtype = DType::FixedSizeList(elem_dtype, 1, Nullability::Nullable); let zone_map = ZoneMap::try_new( @@ -630,12 +631,36 @@ mod tests { .expect("max should have an aggregate function"); let predicate = is_null(vortex_array::stats::stat(root(), max_fn)); - // Must not panic; the unsupported StatFn lowers to a nullable null - // literal, so `is_null(...)` is true for every zone. + // Missing StatFn lowers to a nullable null literal, so `is_null(...)` is true for every zone. let mask = zone_map.prune(&predicate, &SESSION).unwrap(); assert_arrays_eq!(mask.into_array(), BoolArray::from_iter([true, true, true])); } + #[test] + fn unsupported_aggregate_input_dtype_errors() { + let zone_map = ZoneMap::try_new( + DType::Null, + StructArray::try_new(FieldNames::empty(), vec![], 3, Validity::NonNullable).unwrap(), + Arc::new([]), + 4, + 10, + ) + .unwrap(); + + let max_fn = Stat::Max + .aggregate_fn() + .expect("max should have an aggregate function"); + let predicate = is_null(vortex_array::stats::stat(root(), max_fn)); + let error = zone_map.prune(&predicate, &SESSION).unwrap_err(); + + assert!( + error + .to_string() + .contains("Aggregate function vortex.max() does not support input dtype null"), + "{error}" + ); + } + #[test] fn row_count_prunes_all_null_uniform_zones() { let zone_map = ZoneMap::try_new( From 030dcb875a38b55a6f193c74a44c54eb33ab63ac Mon Sep 17 00:00:00 2001 From: Robert Kruszewski Date: Thu, 4 Jun 2026 09:23:05 +0100 Subject: [PATCH 020/115] Initialize LazyBitBufferBuilder from Mask if available, when appending masks pass them by reference (#8221) At the same time the set methods can initialize the validity buffer from the passed mask instead of allocating and copying --------- Signed-off-by: Robert Kruszewski --- .../bitpacking/array/bitpack_decompress.rs | 2 +- .../fastlanes/src/for/array/for_decompress.rs | 2 +- vortex-array/src/builders/bool.rs | 5 +- vortex-array/src/builders/decimal.rs | 5 +- vortex-array/src/builders/fixed_size_list.rs | 5 +- .../src/builders/lazy_null_builder.rs | 45 +++++++++- vortex-array/src/builders/list.rs | 5 +- vortex-array/src/builders/listview.rs | 5 +- vortex-array/src/builders/primitive.rs | 17 ++-- vortex-array/src/builders/struct_.rs | 5 +- vortex-array/src/builders/tests.rs | 61 ++++++++++++++ vortex-array/src/builders/varbinview.rs | 82 ++++++++----------- 12 files changed, 158 insertions(+), 81 deletions(-) diff --git a/encodings/fastlanes/src/bitpacking/array/bitpack_decompress.rs b/encodings/fastlanes/src/bitpacking/array/bitpack_decompress.rs index 5b81580d7a6..a4f8224966a 100644 --- a/encodings/fastlanes/src/bitpacking/array/bitpack_decompress.rs +++ b/encodings/fastlanes/src/bitpacking/array/bitpack_decompress.rs @@ -113,7 +113,7 @@ where // SAFETY: We initialize all `len` values below via `decode` and the patch loop. unsafe { - uninit_range.append_mask(array.validity()?.execute_mask(len, ctx)?); + uninit_range.append_mask(&array.validity()?.execute_mask(len, ctx)?); } // SAFETY: `decode` writes a value to every slot in this range. diff --git a/encodings/fastlanes/src/for/array/for_decompress.rs b/encodings/fastlanes/src/for/array/for_decompress.rs index 073864411fb..ffe9449f962 100644 --- a/encodings/fastlanes/src/for/array/for_decompress.rs +++ b/encodings/fastlanes/src/for/array/for_decompress.rs @@ -109,7 +109,7 @@ pub(crate) fn fused_decompress< let mut uninit_range = builder.uninit_range(bp.len()); unsafe { // Append a dense null Mask. - uninit_range.append_mask(bp.validity()?.execute_mask(bp.as_ref().len(), ctx)?); + uninit_range.append_mask(&bp.validity()?.execute_mask(bp.as_ref().len(), ctx)?); } // SAFETY: `decode_into` will initialize all values in this range. diff --git a/vortex-array/src/builders/bool.rs b/vortex-array/src/builders/bool.rs index c09d69a5acd..fdae7984844 100644 --- a/vortex-array/src/builders/bool.rs +++ b/vortex-array/src/builders/bool.rs @@ -121,7 +121,7 @@ impl ArrayBuilder for BoolBuilder { self.inner.append_buffer(&bool_array.to_bit_buffer()); self.nulls.append_validity_mask( - bool_array + &bool_array .as_ref() .validity() .vortex_expect("validity_mask") @@ -139,8 +139,7 @@ impl ArrayBuilder for BoolBuilder { } unsafe fn set_validity_unchecked(&mut self, validity: Mask) { - self.nulls = LazyBitBufferBuilder::new(validity.len()); - self.nulls.append_validity_mask(validity); + self.nulls = LazyBitBufferBuilder::from_validity_mask(validity); } fn finish(&mut self) -> ArrayRef { diff --git a/vortex-array/src/builders/decimal.rs b/vortex-array/src/builders/decimal.rs index c80f97d9ea2..f8d438d477b 100644 --- a/vortex-array/src/builders/decimal.rs +++ b/vortex-array/src/builders/decimal.rs @@ -207,7 +207,7 @@ impl ArrayBuilder for DecimalBuilder { }); self.nulls.append_validity_mask( - decimal_array + &decimal_array .as_ref() .validity() .vortex_expect("validity_mask") @@ -225,8 +225,7 @@ impl ArrayBuilder for DecimalBuilder { } unsafe fn set_validity_unchecked(&mut self, validity: Mask) { - self.nulls = LazyBitBufferBuilder::new(validity.len()); - self.nulls.append_validity_mask(validity); + self.nulls = LazyBitBufferBuilder::from_validity_mask(validity); } fn finish(&mut self) -> ArrayRef { diff --git a/vortex-array/src/builders/fixed_size_list.rs b/vortex-array/src/builders/fixed_size_list.rs index 5937c37b041..aece8c21465 100644 --- a/vortex-array/src/builders/fixed_size_list.rs +++ b/vortex-array/src/builders/fixed_size_list.rs @@ -246,7 +246,7 @@ impl ArrayBuilder for FixedSizeListBuilder { self.elements_builder.extend_from_array(fsl.elements()); self.nulls.append_validity_mask( - array + &array .validity() .vortex_expect("validity_mask in extend_from_array_unchecked") .execute_mask(array.len(), &mut LEGACY_SESSION.create_execution_ctx()) @@ -261,8 +261,7 @@ impl ArrayBuilder for FixedSizeListBuilder { } unsafe fn set_validity_unchecked(&mut self, validity: Mask) { - self.nulls = LazyBitBufferBuilder::new(validity.len()); - self.nulls.append_validity_mask(validity); + self.nulls = LazyBitBufferBuilder::from_validity_mask(validity); } fn finish(&mut self) -> ArrayRef { diff --git a/vortex-array/src/builders/lazy_null_builder.rs b/vortex-array/src/builders/lazy_null_builder.rs index 9ca4b28b484..24abe9ba17d 100644 --- a/vortex-array/src/builders/lazy_null_builder.rs +++ b/vortex-array/src/builders/lazy_null_builder.rs @@ -31,6 +31,42 @@ impl LazyBitBufferBuilder { } } + /// Creates a builder pre-populated from a validity mask, taking ownership of the mask's buffer + /// instead of copying it where possible. + /// + /// This is the counterpart to [`append_validity_mask`](Self::append_validity_mask) for callers + /// that want to *replace* the builder's contents with the mask rather than extend them: because + /// we own the mask, we can move its buffer in instead of copying it. + pub fn from_validity_mask(validity_mask: Mask) -> Self { + match validity_mask { + // An unmaterialized builder already represents `len` non-null values, so an all-valid + // mask stays lazy. + Mask::AllTrue(len) => Self { + inner: None, + len, + capacity: len, + }, + Mask::AllFalse(len) => Self::from_buffer(BitBufferMut::new_unset(len)), + // Take ownership of the underlying buffer; `into_bit_buffer` and `try_into_mut` only + // copy when the buffer is shared, otherwise this is a move. + values @ Mask::Values(_) => Self::from_buffer( + values + .into_bit_buffer() + .try_into_mut() + .unwrap_or_else(|buffer| BitBufferMut::copy_from(&buffer)), + ), + } + } + + /// Creates a builder backed by an already-materialized buffer. + fn from_buffer(inner: BitBufferMut) -> Self { + Self { + inner: Some(inner), + len: 0, + capacity: 0, + } + } + /// Appends `n` non-null values to the builder. #[inline] pub fn append_n_non_nulls(&mut self, n: usize) { @@ -82,10 +118,13 @@ impl LazyBitBufferBuilder { } /// Appends values from a validity mask. - pub fn append_validity_mask(&mut self, validity_mask: Mask) { + /// + /// Takes the mask by reference: the `Mask::Values` case copies the underlying buffer into the + /// running null buffer, so there is nothing to gain from owning the mask. + pub fn append_validity_mask(&mut self, validity_mask: &Mask) { match validity_mask { - Mask::AllTrue(len) => self.append_n_non_nulls(len), - Mask::AllFalse(len) => self.append_n_nulls(len), + Mask::AllTrue(len) => self.append_n_non_nulls(*len), + Mask::AllFalse(len) => self.append_n_nulls(*len), Mask::Values(is_valid) => self.append_buffer(is_valid.bit_buffer()), } } diff --git a/vortex-array/src/builders/list.rs b/vortex-array/src/builders/list.rs index ae1bb20dfa9..a0c1fdd581b 100644 --- a/vortex-array/src/builders/list.rs +++ b/vortex-array/src/builders/list.rs @@ -226,7 +226,7 @@ impl ArrayBuilder for ListBuilder { // Append validity information. self.nulls.append_validity_mask( - array + &array .validity() .vortex_expect("validity_mask in extend_from_array_unchecked") .execute_mask(array.len(), &mut LEGACY_SESSION.create_execution_ctx()) @@ -300,8 +300,7 @@ impl ArrayBuilder for ListBuilder { } unsafe fn set_validity_unchecked(&mut self, validity: Mask) { - self.nulls = LazyBitBufferBuilder::new(validity.len()); - self.nulls.append_validity_mask(validity); + self.nulls = LazyBitBufferBuilder::from_validity_mask(validity); } fn finish(&mut self) -> ArrayRef { diff --git a/vortex-array/src/builders/listview.rs b/vortex-array/src/builders/listview.rs index 3c7bb8e8885..d4f8f908dcb 100644 --- a/vortex-array/src/builders/listview.rs +++ b/vortex-array/src/builders/listview.rs @@ -308,7 +308,7 @@ impl ArrayBuilder for ListViewBuilder { debug_assert!(listview.is_zero_copy_to_list()); self.nulls.append_validity_mask( - array + &array .validity() .vortex_expect("validity_mask in extend_from_array_unchecked") .execute_mask(array.len(), &mut LEGACY_SESSION.create_execution_ctx()) @@ -364,8 +364,7 @@ impl ArrayBuilder for ListViewBuilder { } unsafe fn set_validity_unchecked(&mut self, validity: Mask) { - self.nulls = LazyBitBufferBuilder::new(validity.len()); - self.nulls.append_validity_mask(validity); + self.nulls = LazyBitBufferBuilder::from_validity_mask(validity); } fn finish(&mut self) -> ArrayRef { diff --git a/vortex-array/src/builders/primitive.rs b/vortex-array/src/builders/primitive.rs index 59381fc602f..657f4b1a603 100644 --- a/vortex-array/src/builders/primitive.rs +++ b/vortex-array/src/builders/primitive.rs @@ -127,7 +127,7 @@ impl PrimitiveBuilder { } /// Extends the primitive array with an iterator. - pub fn extend_with_iterator(&mut self, iter: impl IntoIterator, mask: Mask) { + pub fn extend_with_iterator(&mut self, iter: impl IntoIterator, mask: &Mask) { self.values.extend(iter); self.nulls.append_validity_mask(mask); } @@ -190,7 +190,7 @@ impl ArrayBuilder for PrimitiveBuilder { self.values.extend_from_slice(array.as_slice::()); self.nulls.append_validity_mask( - array + &array .as_ref() .validity() .vortex_expect("validity_mask") @@ -208,8 +208,7 @@ impl ArrayBuilder for PrimitiveBuilder { } unsafe fn set_validity_unchecked(&mut self, validity: Mask) { - self.nulls = LazyBitBufferBuilder::new(validity.len()); - self.nulls.append_validity_mask(validity); + self.nulls = LazyBitBufferBuilder::from_validity_mask(validity); } fn finish(&mut self) -> ArrayRef { @@ -271,7 +270,7 @@ impl UninitRange<'_, T> { /// - The caller must ensure that they safely initialize `mask.len()` primitive values via /// [`UninitRange::copy_from_slice`]. /// - The caller must also ensure that they only call this method once. - pub unsafe fn append_mask(&mut self, mask: Mask) { + pub unsafe fn append_mask(&mut self, mask: &Mask) { assert_eq!( mask.len(), self.len, @@ -426,7 +425,7 @@ mod tests { // SAFETY: We're about to initialize the values. unsafe { - range.append_mask(mask); + range.append_mask(&mask); } // Initialize the values. @@ -476,7 +475,7 @@ mod tests { // SAFETY: This is expected to panic due to length mismatch. unsafe { - range.append_mask(wrong_mask); + range.append_mask(&wrong_mask); } } @@ -522,7 +521,7 @@ mod tests { let initial_mask = Mask::from_iter([false, false, false]); // SAFETY: We're about to initialize the values. unsafe { - range.append_mask(initial_mask); + range.append_mask(&initial_mask); } // Now we can use set_bit to modify individual bits with relative indexing. @@ -623,7 +622,7 @@ mod tests { let mask = Mask::from_iter([true, true, false]); // SAFETY: We're about to initialize the matching number of values. unsafe { - range.append_mask(mask); + range.append_mask(&mask); } // Initialize all values. diff --git a/vortex-array/src/builders/struct_.rs b/vortex-array/src/builders/struct_.rs index b7c737b3f1c..d396cf186a0 100644 --- a/vortex-array/src/builders/struct_.rs +++ b/vortex-array/src/builders/struct_.rs @@ -180,7 +180,7 @@ impl ArrayBuilder for StructBuilder { } self.nulls.append_validity_mask( - array + &array .validity() .vortex_expect("validity_mask") .execute_mask(array.len(), &mut LEGACY_SESSION.create_execution_ctx()) @@ -196,8 +196,7 @@ impl ArrayBuilder for StructBuilder { } unsafe fn set_validity_unchecked(&mut self, validity: Mask) { - self.nulls = LazyBitBufferBuilder::new(validity.len()); - self.nulls.append_validity_mask(validity); + self.nulls = LazyBitBufferBuilder::from_validity_mask(validity); } fn finish(&mut self) -> ArrayRef { diff --git a/vortex-array/src/builders/tests.rs b/vortex-array/src/builders/tests.rs index 53b010597cc..69adfd5b893 100644 --- a/vortex-array/src/builders/tests.rs +++ b/vortex-array/src/builders/tests.rs @@ -5,6 +5,8 @@ use std::sync::Arc; use rstest::rstest; use vortex_error::VortexExpect; +use vortex_error::VortexResult; +use vortex_mask::Mask; use crate::LEGACY_SESSION; use crate::VortexSessionExecute; @@ -820,3 +822,62 @@ fn test_append_scalar_repeated_same_instance() { ); } } + +/// Test that `set_validity` correctly overrides a builder's validity across all mask variants. +/// +/// `set_validity` moves the mask's buffer into the builder rather than copying it, so the +/// `sliced_offset` case is important: slicing a `Mask::Values` at a non-byte-aligned boundary +/// yields a buffer with a non-zero bit offset, which the move path must preserve. +#[rstest] +#[case::all_true(Mask::new_true(8), vec![true; 8])] +#[case::all_false(Mask::new_false(8), vec![false; 8])] +#[case::values( + Mask::from_iter([true, false, true, true, false, false, true, false]), + vec![true, false, true, true, false, false, true, false] +)] +#[case::sliced_offset( + Mask::from_iter([ + false, false, false, // dropped by the slice + true, false, true, true, false, false, true, false, // kept: indices 3..11 + true, true, true, true, true, // dropped by the slice + ]) + .slice(3..11), + vec![true, false, true, true, false, false, true, false] +)] +fn test_set_validity_overrides_validity( + #[case] mask: Mask, + #[case] expected: Vec, +) -> VortexResult<()> { + let dtype = DType::Primitive(PType::I32, Nullability::Nullable); + let mut builder = builder_with_capacity(&dtype, mask.len()); + builder.append_zeros(mask.len()); + + builder.set_validity(mask); + + let validity = builder.finish().validity()?; + for (i, &valid) in expected.iter().enumerate() { + assert_eq!( + validity.is_valid(i)?, + valid, + "validity mismatch at index {i}" + ); + } + Ok(()) +} + +/// Test that `set_validity` is a no-op on a non-nullable builder. +#[test] +fn test_set_validity_noop_when_non_nullable() -> VortexResult<()> { + let dtype = DType::Primitive(PType::I32, Nullability::NonNullable); + let mut builder = builder_with_capacity(&dtype, 4); + builder.append_zeros(4); + + // Providing an all-false mask must not make the non-nullable array invalid. + builder.set_validity(Mask::new_false(4)); + + let validity = builder.finish().validity()?; + for i in 0..4 { + assert!(validity.is_valid(i)?, "index {i} should remain valid"); + } + Ok(()) +} diff --git a/vortex-array/src/builders/varbinview.rs b/vortex-array/src/builders/varbinview.rs index 9150e8e70e1..e06b54dd78c 100644 --- a/vortex-array/src/builders/varbinview.rs +++ b/vortex-array/src/builders/varbinview.rs @@ -206,7 +206,7 @@ impl VarBinViewBuilder { "Some buffers already exist", ); self.views_builder.extend_trusted(views.iter().copied()); - self.push_only_validity_mask(validity_mask); + self.push_only_validity_mask(&validity_mask); debug_assert_eq!(self.nulls.len(), self.views_builder.len()) } @@ -236,7 +236,7 @@ impl VarBinViewBuilder { } // Pushes a validity mask into the builder not affecting the views or buffers - fn push_only_validity_mask(&mut self, validity_mask: Mask) { + fn push_only_validity_mask(&mut self, validity_mask: &Mask) { self.nulls.append_validity_mask(validity_mask); } } @@ -299,17 +299,13 @@ impl ArrayBuilder for VarBinViewBuilder { let array = array.to_varbinview(); self.flush_in_progress(); - self.push_only_validity_mask( - array - .as_ref() - .validity() - .vortex_expect("validity_mask") - .execute_mask( - array.as_ref().len(), - &mut LEGACY_SESSION.create_execution_ctx(), - ) - .vortex_expect("Failed to compute validity mask"), - ); + let mask = array + .validity() + .vortex_expect("validity_mask") + .execute_mask(array.len(), &mut LEGACY_SESSION.create_execution_ctx()) + .vortex_expect("Failed to compute validity mask"); + + self.push_only_validity_mask(&mask); let view_adjustment = self.completed @@ -325,41 +321,30 @@ impl ArrayBuilder for VarBinViewBuilder { .iter() .map(|view| adjustment.adjust_view(view)), ), - ViewAdjustment::Rewriting(adjustment) => { - match array - .as_ref() - .validity() - .vortex_expect("validity_mask") - .execute_mask( - array.as_ref().len(), - &mut LEGACY_SESSION.create_execution_ctx(), - ) - .vortex_expect("Failed to compute validity mask") - { - Mask::AllTrue(_) => { - for (idx, &view) in array.views().iter().enumerate() { - let new_view = self.push_view(view, &adjustment, &array, idx); - self.views_builder.push(new_view); - } - } - Mask::AllFalse(_) => { - self.views_builder - .push_n(BinaryView::empty_view(), array.len()); + ViewAdjustment::Rewriting(adjustment) => match mask { + Mask::AllTrue(_) => { + for (idx, &view) in array.views().iter().enumerate() { + let new_view = self.push_view(view, &adjustment, &array, idx); + self.views_builder.push(new_view); } - Mask::Values(v) => { - for (idx, (&view, is_valid)) in - array.views().iter().zip(v.bit_buffer().iter()).enumerate() - { - let new_view = if !is_valid { - BinaryView::empty_view() - } else { - self.push_view(view, &adjustment, &array, idx) - }; - self.views_builder.push(new_view); - } + } + Mask::AllFalse(_) => { + self.views_builder + .push_n(BinaryView::empty_view(), array.len()); + } + Mask::Values(v) => { + for (idx, (&view, is_valid)) in + array.views().iter().zip(v.bit_buffer().iter()).enumerate() + { + let new_view = if !is_valid { + BinaryView::empty_view() + } else { + self.push_view(view, &adjustment, &array, idx) + }; + self.views_builder.push(new_view); } } - } + }, } } @@ -369,8 +354,7 @@ impl ArrayBuilder for VarBinViewBuilder { } unsafe fn set_validity_unchecked(&mut self, validity: Mask) { - self.nulls = LazyBitBufferBuilder::new(validity.len()); - self.nulls.append_validity_mask(validity); + self.nulls = LazyBitBufferBuilder::from_validity_mask(validity); } fn finish(&mut self) -> ArrayRef { @@ -626,8 +610,8 @@ impl BuffersWithOffsets { buffers: Arc::from( array .data_buffers() - .to_vec() - .into_iter() + .iter() + .cloned() .map(|b| b.unwrap_host()) .collect_vec(), ), From b12d4047b9cb9372e980ce8e9c920750dcfc265e Mon Sep 17 00:00:00 2001 From: Onur Satici Date: Thu, 4 Jun 2026 09:57:39 +0100 Subject: [PATCH 021/115] split registration to respect nested projection and filters (#8213) ## Summary split registration only cared about the immediate accessed fields of the projection and filter expression. We do handle these nested expressions correctly at layout readers, but we did still register splits as if we are referencing all nested fields. so if we had a `select a.x, a.y`, before, we were registering splits for `Prefix(a)`, meaning all fields under a would have their splits registered, ending up with many more tasks than needed. Now we do pass `Prefix(a.y) | Prefix(a.x)` correctly to splits --------- Signed-off-by: Onur Satici --- vortex-array/src/dtype/field.rs | 67 ++++- vortex-array/src/expr/analysis/mod.rs | 2 + .../expr/analysis/referenced_field_paths.rs | 264 ++++++++++++++++++ vortex-layout/src/scan/scan_builder.rs | 112 +++++--- 4 files changed, 405 insertions(+), 40 deletions(-) create mode 100644 vortex-array/src/expr/analysis/referenced_field_paths.rs diff --git a/vortex-array/src/dtype/field.rs b/vortex-array/src/dtype/field.rs index 45ddb70c9a7..84f631b5074 100644 --- a/vortex-array/src/dtype/field.rs +++ b/vortex-array/src/dtype/field.rs @@ -239,18 +239,56 @@ impl Display for FieldPath { } #[derive(Default, Clone, Debug)] -/// Contains a set of field paths, and can answer an efficient field path contains queries. +/// A set of field paths supporting efficient `contains` queries. +/// +/// Paths are stored as inserted. Prefix-minimization—collapsing a path into an ancestor that +/// already covers it—is deferred until the set is iterated via [`IntoIterator`], so insertion stays +/// cheap. pub struct FieldPathSet { - /// While this is currently a set wrapper it can be replaced with a trie. + /// While this is currently a set wrapper it can be replaced with a trie, at which point the + /// deferred minimization in [`IntoIterator`] becomes cheap. // TODO(joe): this can be replaced with a `FieldPath` trie set: HashSet, } impl FieldPathSet { - /// Checks if a set contains a field path + /// Checks if the set contains exactly this field path. pub fn contains(&self, path: &FieldPath) -> bool { self.set.contains(path) } + + /// Iterates over the field paths in the set, as inserted (not prefix-minimized). + pub fn iter(&self) -> impl Iterator { + self.set.iter() + } + + /// Inserts a field path. Prefix-minimization is deferred until the set is iterated. + pub fn insert(&mut self, path: FieldPath) { + self.set.insert(path); + } +} + +/// Reduces field paths to their minimal covering set: any path that has another path in the set as +/// a prefix is redundant and dropped. +fn minimal_covering_set(paths: impl IntoIterator) -> Vec { + let mut covering: Vec = Vec::new(); + for path in paths { + if covering + .iter() + .any(|existing| path.parts().starts_with(existing.parts())) + { + continue; + } + covering.retain(|existing| !existing.parts().starts_with(path.parts())); + covering.push(path); + } + covering +} + +impl Extend for FieldPathSet { + fn extend>(&mut self, iter: T) { + self.set.extend(iter); + } } impl FromIterator for FieldPathSet { @@ -260,6 +298,16 @@ impl FromIterator for FieldPathSet { } } +impl IntoIterator for FieldPathSet { + type Item = FieldPath; + type IntoIter = std::vec::IntoIter; + + /// Iterates the prefix-minimal covering set: redundant descendants are dropped. + fn into_iter(self) -> Self::IntoIter { + minimal_covering_set(self.set).into_iter() + } +} + #[cfg(test)] mod tests { use super::*; @@ -418,4 +466,17 @@ mod tests { assert!(!path1.overlap(&path3)); assert!(!path3.overlap(&path1)); } + + #[test] + fn iteration_yields_minimal_covering_set() { + let mut paths = FieldPathSet::default(); + paths.extend([field_path!(a.b), field_path!(x), field_path!(a)]); + paths.insert(field_path!(a.c)); + + // Iteration collapses `a.b`/`a.c` into the covering `a`. + assert_eq!( + paths.into_iter().collect::>(), + HashSet::from_iter([field_path!(a), field_path!(x)]) + ); + } } diff --git a/vortex-array/src/expr/analysis/mod.rs b/vortex-array/src/expr/analysis/mod.rs index a0b07eb96e6..f5208a31be8 100644 --- a/vortex-array/src/expr/analysis/mod.rs +++ b/vortex-array/src/expr/analysis/mod.rs @@ -6,6 +6,7 @@ mod fallible; pub mod immediate_access; mod labeling; mod null_sensitive; +mod referenced_field_paths; pub use annotation::*; pub use fallible::label_is_fallible; @@ -13,3 +14,4 @@ pub use immediate_access::*; pub use labeling::*; pub use null_sensitive::BooleanLabels; pub use null_sensitive::label_null_sensitive; +pub use referenced_field_paths::referenced_field_paths; diff --git a/vortex-array/src/expr/analysis/referenced_field_paths.rs b/vortex-array/src/expr/analysis/referenced_field_paths.rs new file mode 100644 index 00000000000..5239e4a691c --- /dev/null +++ b/vortex-array/src/expr/analysis/referenced_field_paths.rs @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: Copyright the Vortex contributors + +use vortex_error::VortexResult; +use vortex_error::vortex_err; + +use crate::dtype::DType; +use crate::dtype::Field; +use crate::dtype::FieldPath; +use crate::dtype::FieldPathSet; +use crate::expr::Expression; +use crate::expr::traversal::FoldDownContext; +use crate::expr::traversal::FoldUp; +use crate::expr::traversal::NodeExt; +use crate::expr::traversal::NodeFolderContext; +use crate::scalar_fn::fns::get_item::GetItem; +use crate::scalar_fn::fns::root::Root; +use crate::scalar_fn::fns::select::Select; + +/// Returns the rooted field paths referenced by an expression. +/// +/// Iterating the returned set (via [`IntoIterator`]) yields the prefix-minimal covering set: when +/// one referenced path is a prefix of another, only the prefix is kept. A standalone root +/// expression is represented by [`FieldPath::root`], which conservatively selects all fields. +/// Scalar functions other than `GetItem` and `Select` conservatively reference each complete child +/// output. +pub fn referenced_field_paths(expr: &Expression, scope: &DType) -> VortexResult { + // Validate the whole expression so plain GetItem paths and Select paths behave consistently. + expr.return_dtype(scope)?; + + let mut collector = ReferencedFieldPaths { + scope, + field_paths: FieldPathSet::default(), + }; + expr.clone() + .fold_context(&vec![FieldPath::root()], &mut collector)?; + let field_paths = collector.field_paths; + + // The top-level field of every referenced path must be one of the immediately accessed scope + // fields: this analysis only refines *which nested fields* are read, never which top-level + // fields. `FieldPath::root()` stands in for "all fields", so it expands to the whole scope. + #[cfg(debug_assertions)] + if let Some(scope_fields) = scope.as_struct_fields_opt() { + use vortex_utils::aliases::hash_set::HashSet; + + use crate::dtype::FieldName; + use crate::expr::analysis::immediate_access::immediate_scope_access; + + let referenced_heads: HashSet = if field_paths.iter().any(FieldPath::is_root) { + scope_fields.names().iter().cloned().collect() + } else { + field_paths + .iter() + .filter_map(|path| match path.parts().first() { + Some(Field::Name(name)) => Some(name.clone()), + _ => None, + }) + .collect() + }; + debug_assert_eq!( + referenced_heads, + immediate_scope_access(expr, scope_fields), + "referenced field path heads must match the immediately accessed scope fields" + ); + } + + Ok(field_paths) +} + +/// Threads the set of currently-requested field paths down the expression tree, narrowing it at +/// each `GetItem`/`Select`, and records the rooted paths reached at each `Root` leaf. +/// +/// Paths are carried reversed so a `GetItem` can `push` its field instead of prepending it; they +/// are reversed back to rooted order when recorded at a `Root`, and `Select` reads a path's head +/// from its last element. +/// +/// Narrowing is only sound through `GetItem` (a genuine field access) and `Select` (a genuine +/// column projection). Any other function is opaque—we cannot assume it preserves a field's +/// provenance—so its children conservatively re-request the whole scope, which is what keeps an +/// expression like `f($).x` reading every field of `$` rather than just `x`. +struct ReferencedFieldPaths<'a> { + scope: &'a DType, + field_paths: FieldPathSet, +} + +impl NodeFolderContext for ReferencedFieldPaths<'_> { + type NodeTy = Expression; + type Result = (); + type Context = Vec; + + fn visit_down( + &mut self, + requested: &Self::Context, + node: &Expression, + ) -> VortexResult> { + if node.is::() { + self.field_paths.extend( + requested + .iter() + .map(|path| FieldPath::from_iter(path.parts().iter().rev().cloned())), + ); + return Ok(FoldDownContext::Skip(())); + } + + if let Some(field_name) = node.as_opt::() { + let appended = requested + .iter() + .map(|path| path.clone().push(Field::Name(field_name.clone()))) + .collect(); + return Ok(FoldDownContext::Continue(appended)); + } + + // Keep requested paths whose head is included, expanding a whole-scope request into one + // path per included field. + if let Some(selection) = node.as_opt::`. -/// -/// All buttons are `