use vortex_array::scalar::Scalar;
use vortex_array::vtable::OperationsVTable;
use vortex_error::VortexResult;
use crate::BitPackedArray;
use crate::BitPackedVTable;
use crate::bitpack_decompress;
impl OperationsVTable<BitPackedVTable> for BitPackedVTable {
fn scalar_at(array: &BitPackedArray, index: usize) -> VortexResult<Scalar> {
Ok(
if let Some(patches) = array.patches()
&& let Some(patch) = patches.get_patched(index)?
{
patch
} else {
bitpack_decompress::unpack_single(array, index)
},
)
}
}
#[cfg(test)]
mod test {
use std::ops::Range;
use std::sync::LazyLock;
use vortex_array::DynArray;
use vortex_array::IntoArray;
use vortex_array::VortexSessionExecute;
use vortex_array::arrays::PrimitiveArray;
use vortex_array::arrays::SliceArray;
use vortex_array::assert_arrays_eq;
use vortex_array::assert_nth_scalar;
use vortex_array::buffer::BufferHandle;
use vortex_array::dtype::DType;
use vortex_array::dtype::Nullability;
use vortex_array::dtype::PType;
use vortex_array::patches::Patches;
use vortex_array::scalar::Scalar;
use vortex_array::session::ArraySession;
use vortex_array::validity::Validity;
use vortex_array::vtable::VTable;
use vortex_buffer::Alignment;
use vortex_buffer::Buffer;
use vortex_buffer::ByteBuffer;
use vortex_buffer::buffer;
use crate::BitPackedArray;
use crate::BitPackedVTable;
static SESSION: LazyLock<vortex_session::VortexSession> =
LazyLock::new(|| vortex_session::VortexSession::empty().with::<ArraySession>());
fn slice_via_kernel(array: &BitPackedArray, range: Range<usize>) -> BitPackedArray {
let slice_array = SliceArray::new(array.clone().into_array(), range);
let mut ctx = SESSION.create_execution_ctx();
let sliced = <BitPackedVTable as VTable>::execute_parent(
array,
&slice_array.into_array(),
0,
&mut ctx,
)
.expect("execute_parent failed")
.expect("expected slice kernel to execute");
sliced.as_::<BitPackedVTable>().clone()
}
#[test]
pub fn slice_block() {
let arr = BitPackedArray::encode(
&PrimitiveArray::from_iter((0u32..2048).map(|v| v % 64)).into_array(),
6,
)
.unwrap();
let sliced = slice_via_kernel(&arr, 1024..2048);
assert_nth_scalar!(sliced, 0, 1024u32 % 64);
assert_nth_scalar!(sliced, 1023, 2047u32 % 64);
assert_eq!(sliced.offset(), 0);
assert_eq!(sliced.len(), 1024);
}
#[test]
pub fn slice_within_block() {
let arr = BitPackedArray::encode(
&PrimitiveArray::from_iter((0u32..2048).map(|v| v % 64)).into_array(),
6,
)
.unwrap();
let sliced = slice_via_kernel(&arr, 512..1434);
assert_nth_scalar!(sliced, 0, 512u32 % 64);
assert_nth_scalar!(sliced, 921, 1433u32 % 64);
assert_eq!(sliced.offset(), 512);
assert_eq!(sliced.len(), 922);
}
#[test]
fn slice_within_block_u8s() {
let packed = BitPackedArray::encode(
&PrimitiveArray::from_iter((0..10_000).map(|i| (i % 63) as u8)).into_array(),
7,
)
.unwrap();
let compressed = packed.slice(768..9999).unwrap();
assert_nth_scalar!(compressed, 0, (768 % 63) as u8);
assert_nth_scalar!(compressed, compressed.len() - 1, (9998 % 63) as u8);
}
#[test]
fn slice_block_boundary_u8s() {
let packed = BitPackedArray::encode(
&PrimitiveArray::from_iter((0..10_000).map(|i| (i % 63) as u8)).into_array(),
7,
)
.unwrap();
let compressed = packed.slice(7168..9216).unwrap();
assert_nth_scalar!(compressed, 0, (7168 % 63) as u8);
assert_nth_scalar!(compressed, compressed.len() - 1, (9215 % 63) as u8);
}
#[test]
fn double_slice_within_block() {
let arr = BitPackedArray::encode(
&PrimitiveArray::from_iter((0u32..2048).map(|v| v % 64)).into_array(),
6,
)
.unwrap();
let sliced = slice_via_kernel(&arr, 512..1434);
assert_nth_scalar!(sliced, 0, 512u32 % 64);
assert_nth_scalar!(sliced, 921, 1433u32 % 64);
assert_eq!(sliced.offset(), 512);
assert_eq!(sliced.len(), 922);
let doubly_sliced = slice_via_kernel(&sliced, 127..911);
assert_nth_scalar!(doubly_sliced, 0, (512u32 + 127) % 64);
assert_nth_scalar!(doubly_sliced, 783, (512u32 + 910) % 64);
assert_eq!(doubly_sliced.offset(), 639);
assert_eq!(doubly_sliced.len(), 784);
}
#[test]
fn slice_empty_patches() {
let array = BitPackedArray::encode(&buffer![0u32..=64].into_array(), 6).unwrap();
assert!(array.patches().is_some());
let patch_indices = array.patches().unwrap().indices().clone();
assert_eq!(patch_indices.len(), 1);
let sliced_bp = slice_via_kernel(&array, 0..64);
assert!(sliced_bp.patches().is_none());
}
#[test]
fn take_after_slice() {
let array = BitPackedArray::encode(
&PrimitiveArray::from_iter((63u32..).take(3072)).into_array(),
6,
)
.unwrap();
let sliced = array.slice(922..2061).unwrap();
let taken = sliced
.take(buffer![101i64, 1125, 1138].into_array())
.unwrap();
assert_eq!(taken.len(), 3);
}
#[test]
fn scalar_at_invalid_patches() {
let packed_array = unsafe {
BitPackedArray::new_unchecked(
BufferHandle::new_host(ByteBuffer::copy_from_aligned(
[0u8; 128],
Alignment::of::<u32>(),
)),
DType::Primitive(PType::U32, true.into()),
Validity::AllInvalid,
Some(
Patches::new(
8,
0,
buffer![1u32].into_array(),
PrimitiveArray::new(buffer![999u32], Validity::AllValid).into_array(),
None,
)
.unwrap(),
),
1,
8,
0,
)
.into_array()
};
assert_eq!(
packed_array.scalar_at(1).unwrap(),
Scalar::null(DType::Primitive(PType::U32, Nullability::Nullable))
);
}
#[test]
fn scalar_at() {
let values = (0u32..257).collect::<Buffer<_>>();
let uncompressed = values.clone().into_array();
let packed = BitPackedArray::encode(&uncompressed, 8).unwrap();
assert!(packed.patches().is_some());
let patches = packed.patches().unwrap().indices().clone();
assert_eq!(
usize::try_from(&patches.scalar_at(0).unwrap()).unwrap(),
256
);
let expected = PrimitiveArray::from_iter(values.iter().copied());
assert_arrays_eq!(packed, expected);
}
}