From cfa9850f286b76f7d4745ecbdb97c9dd5c292f2a Mon Sep 17 00:00:00 2001 From: Matt Katz Date: Fri, 18 Sep 2026 15:16:37 -0400 Subject: [PATCH] feat: add bitpacked chunk offset child Signed-off-by: "Matt Katz" --- .../fastlanes/benches/bitpack_compare.rs | 1 + .../benches/bitpack_compare_sweep.rs | 1 + .../src/bitpacking/array/bitpack_compress.rs | 10 +- .../fastlanes/src/bitpacking/array/mod.rs | 156 ++++++++++++++---- .../src/bitpacking/chunk_widths_tests.rs | 53 ++++++ .../fastlanes/src/bitpacking/compute/cast.rs | 1 + .../fastlanes/src/bitpacking/compute/slice.rs | 16 +- encodings/fastlanes/src/bitpacking/plugin.rs | 6 +- .../fastlanes/src/bitpacking/serde_tests.rs | 2 +- .../fastlanes/src/bitpacking/vtable/mod.rs | 32 +++- .../src/bitpacking/vtable/operations.rs | 1 + .../src/schemes/integer/bitpacking.rs | 2 + vortex-btrblocks/src/trace_tests.rs | 11 +- ...olden__onpair__string_fsst_structured.snap | 6 +- ...lden__regular__binary_low_cardinality.snap | 6 +- .../golden__regular__decimal_prices.snap | 6 +- .../golden__regular__float_alp_prices.snap | 6 +- ...golden__regular__float_full_precision.snap | 10 +- ...olden__regular__float_low_cardinality.snap | 8 +- .../golden__regular__int_low_cardinality.snap | 6 +- .../golden__regular__int_monotone_jitter.snap | 6 +- .../golden__regular__int_mostly_null.snap | 6 +- .../golden__regular__int_negatives.snap | 6 +- .../snapshots/golden__regular__int_runs.snap | 14 +- .../golden__regular__int_sparse_outliers.snap | 8 +- .../golden__regular__list_of_int_runs.snap | 20 ++- ...lden__regular__string_fsst_structured.snap | 6 +- ...lden__regular__string_low_cardinality.snap | 6 +- .../golden__regular__struct_mixed.snap | 14 +- ...n__regular__temporal_timestamp_micros.snap | 8 +- vortex-cuda/src/kernel/encodings/bitpacked.rs | 3 + 31 files changed, 347 insertions(+), 90 deletions(-) diff --git a/encodings/fastlanes/benches/bitpack_compare.rs b/encodings/fastlanes/benches/bitpack_compare.rs index 6dde3e13a56..40076dd3fed 100644 --- a/encodings/fastlanes/benches/bitpack_compare.rs +++ b/encodings/fastlanes/benches/bitpack_compare.rs @@ -60,6 +60,7 @@ fn page_aligned(array: BitPackedArray) -> BitPackedArray { parts.validity, parts.patches, parts.widths, + parts.chunk_offsets, parts.len, parts.offset, ) diff --git a/encodings/fastlanes/benches/bitpack_compare_sweep.rs b/encodings/fastlanes/benches/bitpack_compare_sweep.rs index 6bac192a754..d9f16fb24dd 100644 --- a/encodings/fastlanes/benches/bitpack_compare_sweep.rs +++ b/encodings/fastlanes/benches/bitpack_compare_sweep.rs @@ -86,6 +86,7 @@ fn page_aligned(array: BitPackedArray) -> BitPackedArray { parts.validity, parts.patches, parts.widths, + parts.chunk_offsets, parts.len, parts.offset, ) diff --git a/encodings/fastlanes/src/bitpacking/array/bitpack_compress.rs b/encodings/fastlanes/src/bitpacking/array/bitpack_compress.rs index d30eacd301c..96b1c2f79cc 100644 --- a/encodings/fastlanes/src/bitpacking/array/bitpack_compress.rs +++ b/encodings/fastlanes/src/bitpacking/array/bitpack_compress.rs @@ -81,12 +81,15 @@ pub fn bitpack_encode( .transpose()? .flatten(); + let widths = ChunkWidths::uniform(bit_width, array.len().div_ceil(FL_CHUNK_SIZE)); + let offsets = widths.offsets_array(); let bitpacked = BitPacked::try_new( BufferHandle::new_host(packed), array.ptype(), array.validity()?, patches, - ChunkWidths::uniform(bit_width, array.len().div_ceil(FL_CHUNK_SIZE)).into_array(), + widths.into_array(), + offsets, array.len(), 0, )?; @@ -110,12 +113,15 @@ pub unsafe fn bitpack_encode_unchecked( let packed = unsafe { bitpack_unchecked(&array, bit_width) }; let arr_ref = array.clone().into_array(); + let widths = ChunkWidths::uniform(bit_width, array.len().div_ceil(FL_CHUNK_SIZE)); + let offsets = widths.offsets_array(); let bitpacked = BitPacked::try_new( BufferHandle::new_host(packed), array.ptype(), array.validity()?, None, - ChunkWidths::uniform(bit_width, array.len().div_ceil(FL_CHUNK_SIZE)).into_array(), + widths.into_array(), + offsets, array.len(), 0, ) diff --git a/encodings/fastlanes/src/bitpacking/array/mod.rs b/encodings/fastlanes/src/bitpacking/array/mod.rs index e88fe2b1770..1002d7e1064 100644 --- a/encodings/fastlanes/src/bitpacking/array/mod.rs +++ b/encodings/fastlanes/src/bitpacking/array/mod.rs @@ -51,7 +51,7 @@ pub const fn chunk_packed_bytes(bit_width: u8) -> usize { } /// Chunk widths and byte offsets used while encoding or executing bit-packed data. -/// Operations use this view to address each packed chunk. +/// Execution borrows the materialized children; only encoding computes prefix sums. #[derive(Clone, Debug)] pub struct ChunkWidths { widths: Widths, @@ -238,6 +238,10 @@ pub struct BitPackedSlots { /// One non-nullable `u8` width per 1024-element chunk. Uniform widths use a constant array. #[slot(4)] pub width_table: ArrayRef, + /// Non-nullable `u64` byte boundaries, with one trailing entry after the last chunk. + /// The first offset is the origin of the packed buffer and may be nonzero after slicing. + #[slot(5)] + pub chunk_offsets: ArrayRef, } pub(crate) const PATCH_SLOTS: PatchSlotIndices = PatchSlotIndices { @@ -249,6 +253,9 @@ pub(crate) const PATCH_SLOTS: PatchSlotIndices = PatchSlotIndices { /// The dtype of the width table child: one byte per chunk. pub(crate) const WIDTH_TABLE_DTYPE: DType = DType::Primitive(PType::U8, Nullability::NonNullable); +pub(crate) const CHUNK_OFFSETS_DTYPE: DType = + DType::Primitive(PType::U64, Nullability::NonNullable); + impl IntoArray for ChunkWidths { fn into_array(self) -> ArrayRef { if self.is_uniform() { @@ -259,23 +266,34 @@ impl IntoArray for ChunkWidths { } } -/// Read the width child without executing it during reduction. -pub(crate) fn materialized_widths(table: &ArrayRef) -> VortexResult> { - if let Some(constant) = table.as_opt::() { - return Ok(Some(ChunkWidths::uniform( - u8::try_from(constant.scalar())?, - table.len(), - ))); - } - Ok(table +/// Read materialized children without executing them during reduction. +pub(crate) fn materialized_widths( + table: &ArrayRef, + offsets: &ArrayRef, +) -> VortexResult> { + let widths = if let Some(constant) = table.as_opt::() { + Widths::Uniform { + width: u8::try_from(constant.scalar())?, + len: table.len(), + } + } else if let Some(primitive) = table + .as_opt::() + .filter(|a| a.buffer_handle().is_on_host()) + { + Widths::PerChunk(primitive.to_buffer::()) + } else { + return Ok(None); + }; + Ok(offsets .as_opt::() .filter(|a| a.buffer_handle().is_on_host()) - .map(|a| ChunkWidths::new(a.to_buffer::()))) + .map(|a| ChunkWidths::from_buffers(widths, a.to_buffer::()))) } pub struct BitPackedDataParts { pub offset: u16, pub widths: ArrayRef, + pub chunk_offsets: ArrayRef, pub len: usize, pub packed: BufferHandle, pub patches: Option, @@ -324,9 +342,11 @@ impl BitPackedData { /// * `validity` must have `length` len /// * Any patches must have any `array_len` equal to `length` /// * The width-table child must hold one non-nullable `u8` per chunk. + /// * The offsets child must hold `num_chunks + 1` non-nullable `u64` byte boundaries. /// /// Once the widths are materialized, they must be no wider than `ptype`, and the packed - /// buffer must be exactly the sum of the chunks' packed sizes. Compressed children are checked at execution time, before unpacking. + /// buffer must be exactly the sum of the chunks' packed sizes. Offset differences must + /// match the widths. Compressed children are checked at execution time, before unpacking. /// /// Any violation of these preconditions will result in an error. pub fn try_new( @@ -352,6 +372,7 @@ impl BitPackedData { validity: &Validity, patches: Option<&Patches>, table: &ArrayRef, + offsets: &ArrayRef, length: usize, ) -> VortexResult<()> { vortex_ensure!(ptype.is_int(), MismatchedTypes: "integer", ptype); @@ -378,8 +399,19 @@ impl BitPackedData { "Expected {num_chunks} chunk widths, got {}", table.len() ); + vortex_ensure!( + offsets.dtype() == &CHUNK_OFFSETS_DTYPE, + "BitPacked chunk offsets must be {CHUNK_OFFSETS_DTYPE}, got {}", + offsets.dtype() + ); + vortex_ensure!( + offsets.len() == num_chunks + 1, + "Expected {} chunk offsets, got {}", + num_chunks + 1, + offsets.len() + ); // Compressed children are checked once materialized, before any unchecked unpacking. - if let Some(widths) = materialized_widths(table)? { + if let Some(widths) = materialized_widths(table, offsets)? { Self::validate_widths(&self.packed, ptype, &widths)?; } Ok(()) @@ -504,24 +536,40 @@ pub trait BitPackedArrayExt: BitPackedArraySlotsExt { BitPackedData::packed(self) } - /// Prepare and validate the width child once for a bulk operation. + /// Prepare and validate both children once for a bulk operation, without computing prefix sums. fn chunk_widths(&self, ctx: &mut ExecutionCtx) -> VortexResult { - let widths = match materialized_widths(self.width_table())? { + let widths = match materialized_widths(self.width_table(), self.chunk_offsets())? { Some(widths) => widths, - None => ChunkWidths::new( - self.width_table() + None => { + let table = self.width_table(); + let widths = if let Some(constant) = table.as_opt::() { + Widths::Uniform { + width: u8::try_from(constant.scalar())?, + len: table.len(), + } + } else { + Widths::PerChunk( + table + .clone() + .execute::(ctx)? + .to_buffer::(), + ) + }; + let offsets = self + .chunk_offsets() .clone() .execute::(ctx)? - .to_buffer::(), - ), + .to_buffer::(); + ChunkWidths::from_buffers(widths, offsets) + } }; BitPackedData::validate_widths(self.packed(), self.as_ref().dtype().as_ptype(), &widths)?; Ok(widths) } - /// Read and validate widths only when the child is already materialized. + /// Read and validate widths and offsets only when their children are already materialized. fn materialized_chunk_widths(&self) -> VortexResult> { - let widths = materialized_widths(self.width_table())?; + let widths = materialized_widths(self.width_table(), self.chunk_offsets())?; if let Some(widths) = &widths { BitPackedData::validate_widths( self.packed(), @@ -532,18 +580,70 @@ pub trait BitPackedArrayExt: BitPackedArraySlotsExt { Ok(widths) } - /// Locate and validate one chunk using the width table. + /// Read one byte boundary without executing the entire offsets child. + fn chunk_byte_offset(&self, boundary: usize, ctx: &mut ExecutionCtx) -> VortexResult { + vortex_ensure!( + boundary < self.chunk_offsets().len(), + "Chunk boundary out of bounds" + ); + if let Some(offsets) = self + .chunk_offsets() + .as_opt::() + .filter(|a| a.buffer_handle().is_on_host()) + { + Ok(offsets.as_slice::()[boundary]) + } else { + u64::try_from(&self.chunk_offsets().execute_scalar(boundary, ctx)?) + } + } + + /// Locate and validate one chunk using scalar child access, without materializing the tables. fn chunk_range( &self, chunk: usize, ctx: &mut ExecutionCtx, ) -> VortexResult<(Range, u8)> { - let widths = self.chunk_widths(ctx)?; - vortex_ensure!(chunk < widths.len(), "Chunk index out of bounds"); - Ok(( - widths.byte_offset(chunk)..widths.byte_offset(chunk + 1), - widths.width(chunk), - )) + vortex_ensure!( + chunk < self.width_table().len(), + "Chunk index out of bounds" + ); + let width = if let Some(table) = self + .width_table() + .as_opt::() + .filter(|a| a.buffer_handle().is_on_host()) + { + table.as_slice::()[chunk] + } else if let Some(table) = self.width_table().as_opt::() { + u8::try_from(table.scalar())? + } else { + u8::try_from(&self.width_table().execute_scalar(chunk, ctx)?)? + }; + vortex_ensure!( + width as usize <= self.as_ref().dtype().as_ptype().bit_width(), + "Unsupported bit width {width}" + ); + let base = self.chunk_byte_offset(0, ctx)?; + let start = if chunk == 0 { + base + } else { + self.chunk_byte_offset(chunk, ctx)? + }; + let end = self.chunk_byte_offset(chunk + 1, ctx)?; + vortex_ensure!( + end.checked_sub(start) == Some(chunk_packed_bytes(width) as u64), + "Chunk {chunk} offsets do not match its bit width" + ); + let start = start + .checked_sub(base) + .ok_or_else(|| vortex_err!("Chunk offset precedes buffer origin"))?; + let end = end + .checked_sub(base) + .ok_or_else(|| vortex_err!("Chunk offset precedes buffer origin"))?; + vortex_ensure!( + start % (FL_CHUNK_SIZE / 8) as u64 == 0 && end <= self.packed().len() as u64, + "Chunk offsets are unaligned or exceed the packed buffer" + ); + Ok((usize::try_from(start)?..usize::try_from(end)?, width)) } #[inline] diff --git a/encodings/fastlanes/src/bitpacking/chunk_widths_tests.rs b/encodings/fastlanes/src/bitpacking/chunk_widths_tests.rs index 0d425ce8005..4c88329434a 100644 --- a/encodings/fastlanes/src/bitpacking/chunk_widths_tests.rs +++ b/encodings/fastlanes/src/bitpacking/chunk_widths_tests.rs @@ -5,11 +5,16 @@ use std::sync::LazyLock; +use rstest::rstest; use vortex_array::IntoArray; use vortex_array::VortexSessionExecute; use vortex_array::arrays::Constant; use vortex_array::arrays::PrimitiveArray; +use vortex_array::arrays::slice::SliceKernel; use vortex_array::assert_arrays_eq; +use vortex_array::scalar::Scalar; +use vortex_buffer::Buffer; +use vortex_buffer::buffer; use vortex_error::VortexResult; use vortex_session::VortexSession; @@ -51,6 +56,54 @@ fn encode(values: &[u32]) -> VortexResult { bitpack_to_best_bit_width(&PrimitiveArray::from_iter(values.iter().copied()), &mut ctx) } +#[rstest] +#[case::decreasing(buffer![0u64, 128, 256, 128])] +#[case::wrong_width(buffer![0u64, 128, 256, 385])] +#[case::out_of_bounds(buffer![0u64, 128, 256, u64::MAX])] +fn invalid_offsets_rejected_before_unpacking(#[case] offsets: Buffer) -> VortexResult<()> { + let mut ctx = SESSION.create_execution_ctx(); + let values = PrimitiveArray::from_iter((0..3072u32).map(|i| i % 2)); + let packed = bitpack_to_best_bit_width(&values, &mut ctx)?; + let widths = packed.width_table().clone(); + let offsets = offsets.into_array(); + assert!(BitPacked::with_chunk_layout(packed.clone(), widths.clone(), offsets.clone()).is_err()); + let offsets = offsets.execute::(&mut ctx)?; + let offsets = bitpack_to_best_bit_width(&offsets, &mut ctx)?.into_array(); + let packed = BitPacked::with_chunk_layout(packed, widths, offsets)?.into_array(); + // An isolated scalar checks only its own chunk; bulk unpacking validates the whole layout. + assert_eq!(packed.execute_scalar(1, &mut ctx)?, Scalar::from(1u32)); + assert!(packed.execute_scalar(2048, &mut ctx).is_err()); + assert!(packed.execute::(&mut ctx).is_err()); + Ok(()) +} + +#[test] +fn slice_rejects_unaligned_offsets() -> VortexResult<()> { + let mut ctx = SESSION.create_execution_ctx(); + let values = PrimitiveArray::from_iter((0..3072u32).map(|i| i % 2)); + let packed = bitpack_to_best_bit_width(&values, &mut ctx)?; + let widths = packed.width_table().clone(); + let offsets = PrimitiveArray::from_iter([0u64, 127, 255, 383]); + let offsets = bitpack_to_best_bit_width(&offsets, &mut ctx)?.into_array(); + let packed = BitPacked::with_chunk_layout(packed, widths, offsets)?; + assert!(::slice(packed.as_view(), 1024..2048, &mut ctx).is_err()); + Ok(()) +} + +#[test] +fn offset_child_shape_is_validated() -> VortexResult<()> { + let packed = encode(&varied(100))?; + let widths = packed.width_table().clone(); + assert!( + BitPacked::with_chunk_layout(packed.clone(), widths.clone(), buffer![0u64].into_array()) + .is_err() + ); + let wrong_dtype = + PrimitiveArray::from_iter(vec![0u32; packed.chunk_offsets().len()]).into_array(); + assert!(BitPacked::with_chunk_layout(packed, widths, wrong_dtype).is_err()); + Ok(()) +} + /// Every array carries a non-nullable `u8` width per chunk, including uniform arrays. #[test] fn width_table_is_validated() -> VortexResult<()> { diff --git a/encodings/fastlanes/src/bitpacking/compute/cast.rs b/encodings/fastlanes/src/bitpacking/compute/cast.rs index 8765f415f2f..748b5bb8635 100644 --- a/encodings/fastlanes/src/bitpacking/compute/cast.rs +++ b/encodings/fastlanes/src/bitpacking/compute/cast.rs @@ -46,6 +46,7 @@ fn build_with_validity( .map(|patches| patches.map_values(|values| values.cast(dtype.clone()))) .transpose()?, array.width_table().clone(), + array.chunk_offsets().clone(), array.len(), array.offset(), )? diff --git a/encodings/fastlanes/src/bitpacking/compute/slice.rs b/encodings/fastlanes/src/bitpacking/compute/slice.rs index 3f7d6314565..b916af4e005 100644 --- a/encodings/fastlanes/src/bitpacking/compute/slice.rs +++ b/encodings/fastlanes/src/bitpacking/compute/slice.rs @@ -11,6 +11,7 @@ use vortex_array::arrays::slice::SliceKernel; use vortex_array::arrays::slice::SliceReduce; use vortex_array::patches::Patches; use vortex_error::VortexResult; +use vortex_error::vortex_ensure; use crate::BitPacked; use crate::BitPackedArraySlotsExt; @@ -45,8 +46,18 @@ impl SliceKernel for BitPacked { .flatten(); let (chunks, _) = slice_chunks(array.offset(), &range); - let widths = array.chunk_widths(ctx)?; - let encoded = widths.byte_offset(chunks.start)..widths.byte_offset(chunks.end); + let base = array.chunk_byte_offset(0, ctx)?; + let start = array.chunk_byte_offset(chunks.start, ctx)?; + let end = array.chunk_byte_offset(chunks.end, ctx)?; + vortex_ensure!( + base <= start + && start <= end + && end - base <= array.packed().len() as u64 + && (start - base).is_multiple_of(128) + && (end - base).is_multiple_of(128), + "Slice chunk offsets exceed the packed buffer" + ); + let encoded = usize::try_from(start - base)?..usize::try_from(end - base)?; Ok(Some(slice_bitpacked(array, encoded, range, patches)?)) } } @@ -67,6 +78,7 @@ fn slice_bitpacked( array.validity()?.slice(range.clone())?, patches, array.width_table().slice(chunk_start..chunk_stop)?, + array.chunk_offsets().slice(chunk_start..chunk_stop + 1)?, range.len(), offset as u16, )? diff --git a/encodings/fastlanes/src/bitpacking/plugin.rs b/encodings/fastlanes/src/bitpacking/plugin.rs index b434ca77442..b52dd5c89d8 100644 --- a/encodings/fastlanes/src/bitpacking/plugin.rs +++ b/encodings/fastlanes/src/bitpacking/plugin.rs @@ -77,7 +77,9 @@ fn deserialize_v1(parts: ArrayDeserialization<'_>) -> VortexResult { PatchesData::push_slots(&mut s, patches.as_ref()); s.push(validity_to_child(&validity, len)); let widths = ChunkWidths::uniform(bit_width, num_chunks); + let offsets = widths.offsets_array(); s.push(Some(widths.into_array())); + s.push(Some(offsets)); s }; let data = BitPackedData::try_new(packed, patches, offset)?; @@ -249,11 +251,13 @@ impl ArrayPlugin for BitPackedPatchedPlugin { let ptype = bitpacked.dtype().as_ptype(); let validity = bitpacked.validity()?; let bw = bitpacked.width_table().clone(); + let offsets = bitpacked.chunk_offsets().clone(); let len = bitpacked.len(); let offset = bitpacked.offset(); let bitpacked_without_patches = - BitPacked::try_new(packed, ptype, validity, None, bw, len, offset)?.into_array(); + BitPacked::try_new(packed, ptype, validity, None, bw, offsets, len, offset)? + .into_array(); let patched = Patched::from_array_and_patches( bitpacked_without_patches, diff --git a/encodings/fastlanes/src/bitpacking/serde_tests.rs b/encodings/fastlanes/src/bitpacking/serde_tests.rs index 90940d6d073..bd750697fc9 100644 --- a/encodings/fastlanes/src/bitpacking/serde_tests.rs +++ b/encodings/fastlanes/src/bitpacking/serde_tests.rs @@ -57,7 +57,7 @@ fn uniform_widths_serialize_as_original_format() -> VortexResult<()> { let values: Vec = (0..3000).map(|i| i % 128).collect(); let packed = bitpack_to_best_bit_width(&PrimitiveArray::from_iter(values.iter().copied()), &mut ctx)?; - assert_eq!(packed.as_array().children().len(), 1); + assert_eq!(packed.as_array().children().len(), 2); assert!( SESSION .array_serialize(packed.as_array())? diff --git a/encodings/fastlanes/src/bitpacking/vtable/mod.rs b/encodings/fastlanes/src/bitpacking/vtable/mod.rs index b879e6a4d40..8eb0e16608d 100644 --- a/encodings/fastlanes/src/bitpacking/vtable/mod.rs +++ b/encodings/fastlanes/src/bitpacking/vtable/mod.rs @@ -101,7 +101,8 @@ impl VTable for BitPacked { slots.len() ); vortex_ensure!( - slots[BitPackedSlots::WIDTH_TABLE].is_some(), + slots[BitPackedSlots::WIDTH_TABLE].is_some() + && slots[BitPackedSlots::CHUNK_OFFSETS].is_some(), "Missing width table or chunk offsets" ); let bp_slots = BitPackedSlotsView::from_slots(slots); @@ -114,6 +115,7 @@ impl VTable for BitPacked { &validity, patches.as_ref(), bp_slots.width_table, + bp_slots.chunk_offsets, len, ) } @@ -221,13 +223,19 @@ impl VTable for BitPacked { pub struct BitPacked; impl BitPacked { - /// Build a bit-packed array with one width per chunk. + /// Build a bit-packed array with one width per chunk and a trailing byte-offset boundary. + /// Offsets may have a nonzero origin, which is subtracted when indexing the packed buffer. + #[expect( + clippy::too_many_arguments, + reason = "Each physical component of the encoding is explicit" + )] pub fn try_new( packed: BufferHandle, ptype: PType, validity: Validity, patches: Option, widths: ArrayRef, + chunk_offsets: ArrayRef, len: usize, offset: u16, ) -> VortexResult { @@ -237,19 +245,33 @@ impl BitPacked { PatchesData::push_slots(&mut s, patches.as_ref()); s.push(validity_to_child(&validity, len)); s.push(Some(widths)); + s.push(Some(chunk_offsets)); s }; let data = BitPackedData::try_new(packed, patches, offset)?; Array::try_from_parts(ArrayParts::new(BitPacked, dtype, len, data).with_slots(slots)) } - /// Replace the width child, dropping statistics that may no longer describe the values. + /// Replace the width table, preserving the offsets. Values must agree with the offsets. + /// Value-dependent validation of compressed children is deferred until execution. pub fn with_width_table( array: BitPackedArray, table: ArrayRef, + ) -> VortexResult { + let offsets = array.chunk_offsets().clone(); + Self::with_chunk_layout(array, table, offsets) + } + + /// Replace both chunk-layout children. Widths must be non-nullable `u8`, and offsets + /// non-nullable `u64`, with adjacent differences equal to `128 * width`. + pub fn with_chunk_layout( + array: BitPackedArray, + widths: ArrayRef, + offsets: ArrayRef, ) -> VortexResult { let mut slots: ArraySlots = array.slots().iter().cloned().collect(); - slots[BitPackedSlots::WIDTH_TABLE] = Some(table); + slots[BitPackedSlots::WIDTH_TABLE] = Some(widths); + slots[BitPackedSlots::CHUNK_OFFSETS] = Some(offsets); let dtype = array.dtype().clone(); let len = array.len(); Array::try_from_parts( @@ -262,10 +284,12 @@ impl BitPacked { let patches = array.patches(); let validity = array.validity().vortex_expect("BitPacked validity"); let widths = array.width_table().clone(); + let chunk_offsets = array.chunk_offsets().clone(); let data = array.into_data(); BitPackedDataParts { offset: data.offset, widths, + chunk_offsets, len, packed: data.packed, patches, diff --git a/encodings/fastlanes/src/bitpacking/vtable/operations.rs b/encodings/fastlanes/src/bitpacking/vtable/operations.rs index 00a4f4fab4c..a86523dabf9 100644 --- a/encodings/fastlanes/src/bitpacking/vtable/operations.rs +++ b/encodings/fastlanes/src/bitpacking/vtable/operations.rs @@ -257,6 +257,7 @@ mod test { .unwrap(), ), ChunkWidths::uniform(1, 1).into_array(), + buffer![0u64, 128].into_array(), 8, 0, ) diff --git a/vortex-btrblocks/src/schemes/integer/bitpacking.rs b/vortex-btrblocks/src/schemes/integer/bitpacking.rs index 78e63fb6dee..e2855b0c190 100644 --- a/vortex-btrblocks/src/schemes/integer/bitpacking.rs +++ b/vortex-btrblocks/src/schemes/integer/bitpacking.rs @@ -98,6 +98,7 @@ impl Scheme for BitPackingScheme { parts.validity, None, parts.widths, + parts.chunk_offsets, parts.len, parts.offset, )? @@ -123,6 +124,7 @@ impl Scheme for BitPackingScheme { parts.validity, parts.patches, parts.widths, + parts.chunk_offsets, parts.len, parts.offset, )? diff --git a/vortex-btrblocks/src/trace_tests.rs b/vortex-btrblocks/src/trace_tests.rs index fa6dca51bb4..e1b4556b75e 100644 --- a/vortex-btrblocks/src/trace_tests.rs +++ b/vortex-btrblocks/src/trace_tests.rs @@ -360,9 +360,14 @@ fn trace_scan_like_on_compressed_comment() -> VortexResult<()> { iter 0 current=vortex.like(bool, len=4096) builder_active=false execute_until target=AnyCanonical root=fastlanes.delta(u16, len=4097) iter 0 current=fastlanes.delta(u16, len=4097) builder_active=false - execute_until target=AnyCanonical root=fastlanes.bitpacked(u16, len=5120) - iter 0 current=fastlanes.bitpacked(u16, len=5120) builder_active=false - Done array=vortex.primitive(u16, len=5120) + execute_until target=AnyCanonical root=vortex.dict(u16, len=5120) + iter 0 current=vortex.dict(u16, len=5120) builder_active=false + execute_until target=AnyCanonical root=fastlanes.bitpacked(u8, len=5120) + iter 0 current=fastlanes.bitpacked(u8, len=5120) builder_active=false + Done array=vortex.primitive(u8, len=5120) + iter 1 current=vortex.primitive(u8, len=5120) builder_active=false + return output=vortex.primitive(u8, len=5120) + child_execute_parent session[0]:execute_parent_fn slot=1 parent=vortex.dict(u16, len=5120) child=vortex.primitive(u16, len=24) -> vortex.primitive(u16, len=5120) iter 1 current=vortex.primitive(u16, len=5120) builder_active=false return output=vortex.primitive(u16, len=5120) Done array=vortex.primitive(u16, len=4097) diff --git a/vortex-btrblocks/tests/snapshots/golden__onpair__string_fsst_structured.snap b/vortex-btrblocks/tests/snapshots/golden__onpair__string_fsst_structured.snap index 2dcf421a04e..d205e4a4e87 100644 --- a/vortex-btrblocks/tests/snapshots/golden__onpair__string_fsst_structured.snap +++ b/vortex-btrblocks/tests/snapshots/golden__onpair__string_fsst_structured.snap @@ -3,14 +3,16 @@ source: vortex-btrblocks/tests/golden.rs expression: rendered --- input: utf8, len=16384, nbytes=653785 -root: vortex.onpair(utf8, len=16384) nbytes=139748 +root: vortex.onpair(utf8, len=16384) nbytes=140260 metadata: dict_bytes_len: 11866 dict_offsets: vortex.primitive(u16, len=1626) nbytes=3252 metadata: ptype: u16 - codes: fastlanes.bitpacked(u16, len=63845) nbytes=88706 + codes: fastlanes.bitpacked(u16, len=63845) nbytes=89218 metadata: offset: 0 width_table: vortex.constant(u8, len=63) nbytes=2 metadata: scalar: 11u8 + chunk_offsets: vortex.primitive(u64, len=64) nbytes=512 + metadata: ptype: u64 codes_offsets: vortex.primitive(u16, len=16385) nbytes=32770 metadata: ptype: u16 uncompressed_lengths: vortex.sparse(u8, len=16384) nbytes=3154 diff --git a/vortex-btrblocks/tests/snapshots/golden__regular__binary_low_cardinality.snap b/vortex-btrblocks/tests/snapshots/golden__regular__binary_low_cardinality.snap index 6d1b6de8c18..321218fb843 100644 --- a/vortex-btrblocks/tests/snapshots/golden__regular__binary_low_cardinality.snap +++ b/vortex-btrblocks/tests/snapshots/golden__regular__binary_low_cardinality.snap @@ -3,12 +3,14 @@ source: vortex-btrblocks/tests/golden.rs expression: rendered --- input: binary, len=16384, nbytes=315856 -root: vortex.dict(binary, len=16384) nbytes=6198 +root: vortex.dict(binary, len=16384) nbytes=6334 metadata: all_values_referenced: true - codes: fastlanes.bitpacked(u8, len=16384) nbytes=6146 + codes: fastlanes.bitpacked(u8, len=16384) nbytes=6282 metadata: offset: 0 width_table: vortex.constant(u8, len=16) nbytes=2 metadata: scalar: 3u8 + chunk_offsets: vortex.primitive(u64, len=17) nbytes=136 + metadata: ptype: u64 values: vortex.varbin(binary, len=5) nbytes=52 metadata: offsets: vortex.primitive(u8, len=6) nbytes=6 diff --git a/vortex-btrblocks/tests/snapshots/golden__regular__decimal_prices.snap b/vortex-btrblocks/tests/snapshots/golden__regular__decimal_prices.snap index 98b05b7e4e1..c4ed2313dae 100644 --- a/vortex-btrblocks/tests/snapshots/golden__regular__decimal_prices.snap +++ b/vortex-btrblocks/tests/snapshots/golden__regular__decimal_prices.snap @@ -3,9 +3,11 @@ source: vortex-btrblocks/tests/golden.rs expression: rendered --- input: decimal(12,2), len=16384, nbytes=131072 -root: vortex.decimal_byte_parts.v2(decimal(12,2), len=16384) nbytes=49154 +root: vortex.decimal_byte_parts.v2(decimal(12,2), len=16384) nbytes=49290 metadata: - msp: fastlanes.bitpacked(i32, len=16384) nbytes=49154 + msp: fastlanes.bitpacked(i32, len=16384) nbytes=49290 metadata: offset: 0 width_table: vortex.constant(u8, len=16) nbytes=2 metadata: scalar: 24u8 + chunk_offsets: vortex.primitive(u64, len=17) nbytes=136 + metadata: ptype: u64 diff --git a/vortex-btrblocks/tests/snapshots/golden__regular__float_alp_prices.snap b/vortex-btrblocks/tests/snapshots/golden__regular__float_alp_prices.snap index 881812695ba..b32dfb56cdd 100644 --- a/vortex-btrblocks/tests/snapshots/golden__regular__float_alp_prices.snap +++ b/vortex-btrblocks/tests/snapshots/golden__regular__float_alp_prices.snap @@ -3,9 +3,11 @@ source: vortex-btrblocks/tests/golden.rs expression: rendered --- input: f64, len=16384, nbytes=131072 -root: vortex.alp(f64, len=16384) nbytes=49154 +root: vortex.alp(f64, len=16384) nbytes=49290 metadata: exponents: e: 14, f: 12 - encoded: fastlanes.bitpacked(i64, len=16384) nbytes=49154 + encoded: fastlanes.bitpacked(i64, len=16384) nbytes=49290 metadata: offset: 0 width_table: vortex.constant(u8, len=16) nbytes=2 metadata: scalar: 24u8 + chunk_offsets: vortex.primitive(u64, len=17) nbytes=136 + metadata: ptype: u64 diff --git a/vortex-btrblocks/tests/snapshots/golden__regular__float_full_precision.snap b/vortex-btrblocks/tests/snapshots/golden__regular__float_full_precision.snap index 7dee3501126..6eedf096cf0 100644 --- a/vortex-btrblocks/tests/snapshots/golden__regular__float_full_precision.snap +++ b/vortex-btrblocks/tests/snapshots/golden__regular__float_full_precision.snap @@ -3,16 +3,20 @@ source: vortex-btrblocks/tests/golden.rs expression: rendered --- input: f64, len=16384, nbytes=131072 -root: vortex.alprd(f64, len=16384) nbytes=112884 +root: vortex.alprd(f64, len=16384) nbytes=113156 metadata: right_bit_width: 52, patch_offset: 0 - left_parts: fastlanes.bitpacked(u16, len=16384) nbytes=6146 + left_parts: fastlanes.bitpacked(u16, len=16384) nbytes=6282 metadata: offset: 0 width_table: vortex.constant(u8, len=16) nbytes=2 metadata: scalar: 3u8 - right_parts: fastlanes.bitpacked(u64, len=16384) nbytes=106498 + chunk_offsets: vortex.primitive(u64, len=17) nbytes=136 + metadata: ptype: u64 + right_parts: fastlanes.bitpacked(u64, len=16384) nbytes=106634 metadata: offset: 0 width_table: vortex.constant(u8, len=16) nbytes=2 metadata: scalar: 52u8 + chunk_offsets: vortex.primitive(u64, len=17) nbytes=136 + metadata: ptype: u64 patch_indices: vortex.primitive(u16, len=60) nbytes=120 metadata: ptype: u16 patch_values: vortex.primitive(u16, len=60) nbytes=120 diff --git a/vortex-btrblocks/tests/snapshots/golden__regular__float_low_cardinality.snap b/vortex-btrblocks/tests/snapshots/golden__regular__float_low_cardinality.snap index dfe78f90377..40dd7b8cc7d 100644 --- a/vortex-btrblocks/tests/snapshots/golden__regular__float_low_cardinality.snap +++ b/vortex-btrblocks/tests/snapshots/golden__regular__float_low_cardinality.snap @@ -3,13 +3,15 @@ source: vortex-btrblocks/tests/golden.rs expression: rendered --- input: f64, len=16384, nbytes=131072 -root: vortex.alp(f64, len=16384) nbytes=6210 +root: vortex.alp(f64, len=16384) nbytes=6346 metadata: exponents: e: 16, f: 11 - encoded: vortex.dict(i64, len=16384) nbytes=6210 + encoded: vortex.dict(i64, len=16384) nbytes=6346 metadata: all_values_referenced: true - codes: fastlanes.bitpacked(u8, len=16384) nbytes=6146 + codes: fastlanes.bitpacked(u8, len=16384) nbytes=6282 metadata: offset: 0 width_table: vortex.constant(u8, len=16) nbytes=2 metadata: scalar: 3u8 + chunk_offsets: vortex.primitive(u64, len=17) nbytes=136 + metadata: ptype: u64 values: vortex.primitive(i64, len=8) nbytes=64 metadata: ptype: i64 diff --git a/vortex-btrblocks/tests/snapshots/golden__regular__int_low_cardinality.snap b/vortex-btrblocks/tests/snapshots/golden__regular__int_low_cardinality.snap index 58df4406720..b3e632087e7 100644 --- a/vortex-btrblocks/tests/snapshots/golden__regular__int_low_cardinality.snap +++ b/vortex-btrblocks/tests/snapshots/golden__regular__int_low_cardinality.snap @@ -3,11 +3,13 @@ source: vortex-btrblocks/tests/golden.rs expression: rendered --- input: i64, len=16384, nbytes=131072 -root: vortex.dict(i64, len=16384) nbytes=6194 +root: vortex.dict(i64, len=16384) nbytes=6330 metadata: all_values_referenced: true - codes: fastlanes.bitpacked(u8, len=16384) nbytes=6146 + codes: fastlanes.bitpacked(u8, len=16384) nbytes=6282 metadata: offset: 0 width_table: vortex.constant(u8, len=16) nbytes=2 metadata: scalar: 3u8 + chunk_offsets: vortex.primitive(u64, len=17) nbytes=136 + metadata: ptype: u64 values: vortex.primitive(i64, len=6) nbytes=48 metadata: ptype: i64 diff --git a/vortex-btrblocks/tests/snapshots/golden__regular__int_monotone_jitter.snap b/vortex-btrblocks/tests/snapshots/golden__regular__int_monotone_jitter.snap index 531b01ee424..d3193ba249b 100644 --- a/vortex-btrblocks/tests/snapshots/golden__regular__int_monotone_jitter.snap +++ b/vortex-btrblocks/tests/snapshots/golden__regular__int_monotone_jitter.snap @@ -3,9 +3,11 @@ source: vortex-btrblocks/tests/golden.rs expression: rendered --- input: u64, len=16384, nbytes=131072 -root: fastlanes.for(u64, len=16384) nbytes=49154 +root: fastlanes.for(u64, len=16384) nbytes=49290 metadata: reference: 1700000001036u64 - encoded: fastlanes.bitpacked(u64, len=16384) nbytes=49154 + encoded: fastlanes.bitpacked(u64, len=16384) nbytes=49290 metadata: offset: 0 width_table: vortex.constant(u8, len=16) nbytes=2 metadata: scalar: 24u8 + chunk_offsets: vortex.primitive(u64, len=17) nbytes=136 + metadata: ptype: u64 diff --git a/vortex-btrblocks/tests/snapshots/golden__regular__int_mostly_null.snap b/vortex-btrblocks/tests/snapshots/golden__regular__int_mostly_null.snap index c0ba7ca5240..6bee89b57e7 100644 --- a/vortex-btrblocks/tests/snapshots/golden__regular__int_mostly_null.snap +++ b/vortex-btrblocks/tests/snapshots/golden__regular__int_mostly_null.snap @@ -3,13 +3,15 @@ source: vortex-btrblocks/tests/golden.rs expression: rendered --- input: i32?, len=16384, nbytes=67584 -root: vortex.sparse(i32?, len=16384) nbytes=3033 +root: vortex.sparse(i32?, len=16384) nbytes=3049 metadata: fill_value: null patch_indices: vortex.primitive(u16, len=823) nbytes=1646 metadata: ptype: u16 - patch_values: fastlanes.bitpacked(i32?, len=823) nbytes=1385 + patch_values: fastlanes.bitpacked(i32?, len=823) nbytes=1401 metadata: offset: 0 validity_child: vortex.bool(bool, len=823) nbytes=103 metadata: offset: 0 width_table: vortex.constant(u8, len=1) nbytes=2 metadata: scalar: 10u8 + chunk_offsets: vortex.primitive(u64, len=2) nbytes=16 + metadata: ptype: u64 diff --git a/vortex-btrblocks/tests/snapshots/golden__regular__int_negatives.snap b/vortex-btrblocks/tests/snapshots/golden__regular__int_negatives.snap index 761958a68a6..85ade148d17 100644 --- a/vortex-btrblocks/tests/snapshots/golden__regular__int_negatives.snap +++ b/vortex-btrblocks/tests/snapshots/golden__regular__int_negatives.snap @@ -3,9 +3,11 @@ source: vortex-btrblocks/tests/golden.rs expression: rendered --- input: i64, len=16384, nbytes=131072 -root: fastlanes.for(i64, len=16384) nbytes=16386 +root: fastlanes.for(i64, len=16384) nbytes=16522 metadata: reference: -128i64 - encoded: fastlanes.bitpacked(i64, len=16384) nbytes=16386 + encoded: fastlanes.bitpacked(i64, len=16384) nbytes=16522 metadata: offset: 0 width_table: vortex.constant(u8, len=16) nbytes=2 metadata: scalar: 8u8 + chunk_offsets: vortex.primitive(u64, len=17) nbytes=136 + metadata: ptype: u64 diff --git a/vortex-btrblocks/tests/snapshots/golden__regular__int_runs.snap b/vortex-btrblocks/tests/snapshots/golden__regular__int_runs.snap index 2f3e237bc26..04bdb393b2f 100644 --- a/vortex-btrblocks/tests/snapshots/golden__regular__int_runs.snap +++ b/vortex-btrblocks/tests/snapshots/golden__regular__int_runs.snap @@ -3,17 +3,21 @@ source: vortex-btrblocks/tests/golden.rs expression: rendered --- input: i32, len=16384, nbytes=65536 -root: vortex.runend(i32, len=16384) nbytes=3972 +root: vortex.runend(i32, len=16384) nbytes=4004 metadata: offset: 0 - ends: fastlanes.for(u16, len=1020) nbytes=1794 + ends: fastlanes.for(u16, len=1020) nbytes=1810 metadata: reference: 13u16 - encoded: fastlanes.bitpacked(u16, len=1020) nbytes=1794 + encoded: fastlanes.bitpacked(u16, len=1020) nbytes=1810 metadata: offset: 0 width_table: vortex.constant(u8, len=1) nbytes=2 metadata: scalar: 14u8 - values: fastlanes.for(i32, len=1020) nbytes=2178 + chunk_offsets: vortex.primitive(u64, len=2) nbytes=16 + metadata: ptype: u64 + values: fastlanes.for(i32, len=1020) nbytes=2194 metadata: reference: -49931i32 - encoded: fastlanes.bitpacked(i32, len=1020) nbytes=2178 + encoded: fastlanes.bitpacked(i32, len=1020) nbytes=2194 metadata: offset: 0 width_table: vortex.constant(u8, len=1) nbytes=2 metadata: scalar: 17u8 + chunk_offsets: vortex.primitive(u64, len=2) nbytes=16 + metadata: ptype: u64 diff --git a/vortex-btrblocks/tests/snapshots/golden__regular__int_sparse_outliers.snap b/vortex-btrblocks/tests/snapshots/golden__regular__int_sparse_outliers.snap index f2bb58d7016..6447f8d6e8e 100644 --- a/vortex-btrblocks/tests/snapshots/golden__regular__int_sparse_outliers.snap +++ b/vortex-btrblocks/tests/snapshots/golden__regular__int_sparse_outliers.snap @@ -3,13 +3,15 @@ source: vortex-btrblocks/tests/golden.rs expression: rendered --- input: i64, len=16384, nbytes=131072 -root: vortex.sparse(i64, len=16384) nbytes=5542 +root: vortex.sparse(i64, len=16384) nbytes=5558 metadata: fill_value: 1000000i64 patch_indices: vortex.primitive(u16, len=848) nbytes=1696 metadata: ptype: u16 - patch_values: fastlanes.for(i64, len=848) nbytes=3842 + patch_values: fastlanes.for(i64, len=848) nbytes=3858 metadata: reference: 1000830099i64 - encoded: fastlanes.bitpacked(i64, len=848) nbytes=3842 + encoded: fastlanes.bitpacked(i64, len=848) nbytes=3858 metadata: offset: 0 width_table: vortex.constant(u8, len=1) nbytes=2 metadata: scalar: 30u8 + chunk_offsets: vortex.primitive(u64, len=2) nbytes=16 + metadata: ptype: u64 diff --git a/vortex-btrblocks/tests/snapshots/golden__regular__list_of_int_runs.snap b/vortex-btrblocks/tests/snapshots/golden__regular__list_of_int_runs.snap index bd235627afc..5e8767c159b 100644 --- a/vortex-btrblocks/tests/snapshots/golden__regular__list_of_int_runs.snap +++ b/vortex-btrblocks/tests/snapshots/golden__regular__list_of_int_runs.snap @@ -3,23 +3,27 @@ source: vortex-btrblocks/tests/golden.rs expression: rendered --- input: list(i32), len=4066, nbytes=81804 -root: vortex.list(list(i32), len=4066) nbytes=11152 +root: vortex.list(list(i32), len=4066) nbytes=11224 metadata: - elements: vortex.runend(i32, len=16384) nbytes=3972 + elements: vortex.runend(i32, len=16384) nbytes=4004 metadata: offset: 0 - ends: fastlanes.for(u16, len=1020) nbytes=1794 + ends: fastlanes.for(u16, len=1020) nbytes=1810 metadata: reference: 13u16 - encoded: fastlanes.bitpacked(u16, len=1020) nbytes=1794 + encoded: fastlanes.bitpacked(u16, len=1020) nbytes=1810 metadata: offset: 0 width_table: vortex.constant(u8, len=1) nbytes=2 metadata: scalar: 14u8 - values: fastlanes.for(i32, len=1020) nbytes=2178 + chunk_offsets: vortex.primitive(u64, len=2) nbytes=16 + metadata: ptype: u64 + values: fastlanes.for(i32, len=1020) nbytes=2194 metadata: reference: -49931i32 - encoded: fastlanes.bitpacked(i32, len=1020) nbytes=2178 + encoded: fastlanes.bitpacked(i32, len=1020) nbytes=2194 metadata: offset: 0 width_table: vortex.constant(u8, len=1) nbytes=2 metadata: scalar: 17u8 - offsets: fastlanes.bitpacked(u16, len=4067) nbytes=7180 + chunk_offsets: vortex.primitive(u64, len=2) nbytes=16 + metadata: ptype: u64 + offsets: fastlanes.bitpacked(u16, len=4067) nbytes=7220 metadata: offset: 0 patch_indices: vortex.primitive(u16, len=1) nbytes=2 metadata: ptype: u16 @@ -29,3 +33,5 @@ root: vortex.list(list(i32), len=4066) nbytes=11152 metadata: ptype: u8 width_table: vortex.constant(u8, len=4) nbytes=2 metadata: scalar: 14u8 + chunk_offsets: vortex.primitive(u64, len=5) nbytes=40 + metadata: ptype: u64 diff --git a/vortex-btrblocks/tests/snapshots/golden__regular__string_fsst_structured.snap b/vortex-btrblocks/tests/snapshots/golden__regular__string_fsst_structured.snap index 1fb4bb8092b..98a9d39ce39 100644 --- a/vortex-btrblocks/tests/snapshots/golden__regular__string_fsst_structured.snap +++ b/vortex-btrblocks/tests/snapshots/golden__regular__string_fsst_structured.snap @@ -3,7 +3,7 @@ source: vortex-btrblocks/tests/golden.rs expression: rendered --- input: utf8, len=16384, nbytes=653785 -root: vortex.fsst(utf8, len=16384) nbytes=151384 +root: vortex.fsst(utf8, len=16384) nbytes=151528 metadata: len: 16384, nsymbols: 223 uncompressed_lengths: vortex.sparse(u8, len=16384) nbytes=3154 metadata: fill_value: 24u8 @@ -11,7 +11,9 @@ root: vortex.fsst(utf8, len=16384) nbytes=151384 metadata: ptype: u16 patch_values: vortex.constant(u8, len=1575) nbytes=2 metadata: scalar: 23u8 - codes_offsets: fastlanes.bitpacked(u32, len=16385) nbytes=36994 + codes_offsets: fastlanes.bitpacked(u32, len=16385) nbytes=37138 metadata: offset: 0 width_table: vortex.constant(u8, len=17) nbytes=2 metadata: scalar: 17u8 + chunk_offsets: vortex.primitive(u64, len=18) nbytes=144 + metadata: ptype: u64 diff --git a/vortex-btrblocks/tests/snapshots/golden__regular__string_low_cardinality.snap b/vortex-btrblocks/tests/snapshots/golden__regular__string_low_cardinality.snap index fa867d2d4b4..c97c5756251 100644 --- a/vortex-btrblocks/tests/snapshots/golden__regular__string_low_cardinality.snap +++ b/vortex-btrblocks/tests/snapshots/golden__regular__string_low_cardinality.snap @@ -3,12 +3,14 @@ source: vortex-btrblocks/tests/golden.rs expression: rendered --- input: utf8, len=16384, nbytes=262144 -root: vortex.dict(utf8, len=16384) nbytes=8375 +root: vortex.dict(utf8, len=16384) nbytes=8511 metadata: all_values_referenced: true - codes: fastlanes.bitpacked(u8, len=16384) nbytes=8194 + codes: fastlanes.bitpacked(u8, len=16384) nbytes=8330 metadata: offset: 0 width_table: vortex.constant(u8, len=16) nbytes=2 metadata: scalar: 4u8 + chunk_offsets: vortex.primitive(u64, len=17) nbytes=136 + metadata: ptype: u64 values: vortex.fsst(utf8, len=12) nbytes=181 metadata: len: 12, nsymbols: 9 uncompressed_lengths: vortex.primitive(u8, len=12) nbytes=12 diff --git a/vortex-btrblocks/tests/snapshots/golden__regular__struct_mixed.snap b/vortex-btrblocks/tests/snapshots/golden__regular__struct_mixed.snap index 68302be477d..51c3168d18d 100644 --- a/vortex-btrblocks/tests/snapshots/golden__regular__struct_mixed.snap +++ b/vortex-btrblocks/tests/snapshots/golden__regular__struct_mixed.snap @@ -3,25 +3,29 @@ source: vortex-btrblocks/tests/golden.rs expression: rendered --- input: {id=i64, category=utf8, value=f64}, len=16384, nbytes=524288 -root: vortex.struct({id=i64, category=utf8, value=f64}, len=16384) nbytes=57529 +root: vortex.struct({id=i64, category=utf8, value=f64}, len=16384) nbytes=57801 metadata: id: vortex.sequence(i64, len=16384) nbytes=0 metadata: base: 10000i64, multiplier: 7i64 - category: vortex.dict(utf8, len=16384) nbytes=8375 + category: vortex.dict(utf8, len=16384) nbytes=8511 metadata: all_values_referenced: true - codes: fastlanes.bitpacked(u8, len=16384) nbytes=8194 + codes: fastlanes.bitpacked(u8, len=16384) nbytes=8330 metadata: offset: 0 width_table: vortex.constant(u8, len=16) nbytes=2 metadata: scalar: 4u8 + chunk_offsets: vortex.primitive(u64, len=17) nbytes=136 + metadata: ptype: u64 values: vortex.fsst(utf8, len=12) nbytes=181 metadata: len: 12, nsymbols: 9 uncompressed_lengths: vortex.primitive(u8, len=12) nbytes=12 metadata: ptype: u8 codes_offsets: vortex.primitive(u8, len=13) nbytes=13 metadata: ptype: u8 - value: vortex.alp(f64, len=16384) nbytes=49154 + value: vortex.alp(f64, len=16384) nbytes=49290 metadata: exponents: e: 14, f: 12 - encoded: fastlanes.bitpacked(i64, len=16384) nbytes=49154 + encoded: fastlanes.bitpacked(i64, len=16384) nbytes=49290 metadata: offset: 0 width_table: vortex.constant(u8, len=16) nbytes=2 metadata: scalar: 24u8 + chunk_offsets: vortex.primitive(u64, len=17) nbytes=136 + metadata: ptype: u64 diff --git a/vortex-btrblocks/tests/snapshots/golden__regular__temporal_timestamp_micros.snap b/vortex-btrblocks/tests/snapshots/golden__regular__temporal_timestamp_micros.snap index a9dc41403d7..7695d1820de 100644 --- a/vortex-btrblocks/tests/snapshots/golden__regular__temporal_timestamp_micros.snap +++ b/vortex-btrblocks/tests/snapshots/golden__regular__temporal_timestamp_micros.snap @@ -3,11 +3,13 @@ source: vortex-btrblocks/tests/golden.rs expression: rendered --- input: vortex.timestamp[µs, tz=UTC](i64), len=16384, nbytes=131072 -root: vortex.ext(vortex.timestamp[µs, tz=UTC](i64), len=16384) nbytes=67586 +root: vortex.ext(vortex.timestamp[µs, tz=UTC](i64), len=16384) nbytes=67722 metadata: - storage: fastlanes.for(i64, len=16384) nbytes=67586 + storage: fastlanes.for(i64, len=16384) nbytes=67722 metadata: reference: 1700000000891673i64 - encoded: fastlanes.bitpacked(i64, len=16384) nbytes=67586 + encoded: fastlanes.bitpacked(i64, len=16384) nbytes=67722 metadata: offset: 0 width_table: vortex.constant(u8, len=16) nbytes=2 metadata: scalar: 33u8 + chunk_offsets: vortex.primitive(u64, len=17) nbytes=136 + metadata: ptype: u64 diff --git a/vortex-cuda/src/kernel/encodings/bitpacked.rs b/vortex-cuda/src/kernel/encodings/bitpacked.rs index f72702fd16e..e70060d6fc7 100644 --- a/vortex-cuda/src/kernel/encodings/bitpacked.rs +++ b/vortex-cuda/src/kernel/encodings/bitpacked.rs @@ -102,12 +102,14 @@ impl BitPackedExecutor { let widths = bp.chunk_widths(ctx)?; let (packed, widths, bitpacked_offset, patch_range) = bitpacked_slice_view(bp, &widths, offset, len)?; + let offsets = widths.offsets_array(); let sliced = BitPacked::try_new( packed, bp.ptype(bp.dtype()), child.validity()?.slice(patch_range.clone())?, bp.patches(), widths.into_array(), + offsets, len, bitpacked_offset, )?; @@ -175,6 +177,7 @@ where let BitPackedDataParts { offset, widths: _, + chunk_offsets: _, len, packed, patches,