vortex_fastlanes/bitpacking/vtable/
operations.rs1use vortex_array::scalar::Scalar;
5use vortex_array::vtable::OperationsVTable;
6use vortex_error::VortexResult;
7
8use crate::BitPackedArray;
9use crate::BitPackedVTable;
10use crate::bitpack_decompress;
11
12impl OperationsVTable<BitPackedVTable> for BitPackedVTable {
13 fn scalar_at(array: &BitPackedArray, index: usize) -> VortexResult<Scalar> {
14 Ok(
15 if let Some(patches) = array.patches()
16 && let Some(patch) = patches.get_patched(index)?
17 {
18 patch
19 } else {
20 bitpack_decompress::unpack_single(array, index)
21 },
22 )
23 }
24}
25
26#[cfg(test)]
27mod test {
28 use std::ops::Range;
29 use std::sync::LazyLock;
30
31 use vortex_array::Array;
32 use vortex_array::IntoArray;
33 use vortex_array::VortexSessionExecute;
34 use vortex_array::arrays::PrimitiveArray;
35 use vortex_array::arrays::SliceArray;
36 use vortex_array::assert_arrays_eq;
37 use vortex_array::assert_nth_scalar;
38 use vortex_array::buffer::BufferHandle;
39 use vortex_array::dtype::DType;
40 use vortex_array::dtype::Nullability;
41 use vortex_array::dtype::PType;
42 use vortex_array::patches::Patches;
43 use vortex_array::scalar::Scalar;
44 use vortex_array::session::ArraySession;
45 use vortex_array::validity::Validity;
46 use vortex_array::vtable::VTable;
47 use vortex_buffer::Alignment;
48 use vortex_buffer::Buffer;
49 use vortex_buffer::ByteBuffer;
50 use vortex_buffer::buffer;
51
52 use crate::BitPackedArray;
53 use crate::BitPackedVTable;
54
55 static SESSION: LazyLock<vortex_session::VortexSession> =
56 LazyLock::new(|| vortex_session::VortexSession::empty().with::<ArraySession>());
57
58 fn slice_via_kernel(array: &BitPackedArray, range: Range<usize>) -> BitPackedArray {
59 let slice_array = SliceArray::new(array.clone().into_array(), range);
60 let mut ctx = SESSION.create_execution_ctx();
61 let sliced = <BitPackedVTable as VTable>::execute_parent(
62 array,
63 &slice_array.into_array(),
64 0,
65 &mut ctx,
66 )
67 .expect("execute_parent failed")
68 .expect("expected slice kernel to execute");
69 sliced.as_::<BitPackedVTable>().clone()
70 }
71
72 #[test]
73 pub fn slice_block() {
74 let arr = BitPackedArray::encode(
75 PrimitiveArray::from_iter((0u32..2048).map(|v| v % 64)).as_ref(),
76 6,
77 )
78 .unwrap();
79 let sliced = slice_via_kernel(&arr, 1024..2048);
80 assert_nth_scalar!(sliced, 0, 1024u32 % 64);
81 assert_nth_scalar!(sliced, 1023, 2047u32 % 64);
82 assert_eq!(sliced.offset(), 0);
83 assert_eq!(sliced.len(), 1024);
84 }
85
86 #[test]
87 pub fn slice_within_block() {
88 let arr = BitPackedArray::encode(
89 PrimitiveArray::from_iter((0u32..2048).map(|v| v % 64)).as_ref(),
90 6,
91 )
92 .unwrap();
93 let sliced = slice_via_kernel(&arr, 512..1434);
94 assert_nth_scalar!(sliced, 0, 512u32 % 64);
95 assert_nth_scalar!(sliced, 921, 1433u32 % 64);
96 assert_eq!(sliced.offset(), 512);
97 assert_eq!(sliced.len(), 922);
98 }
99
100 #[test]
101 fn slice_within_block_u8s() {
102 let packed = BitPackedArray::encode(
103 PrimitiveArray::from_iter((0..10_000).map(|i| (i % 63) as u8)).as_ref(),
104 7,
105 )
106 .unwrap();
107
108 let compressed = packed.slice(768..9999).unwrap();
109 assert_nth_scalar!(compressed, 0, (768 % 63) as u8);
110 assert_nth_scalar!(compressed, compressed.len() - 1, (9998 % 63) as u8);
111 }
112
113 #[test]
114 fn slice_block_boundary_u8s() {
115 let packed = BitPackedArray::encode(
116 PrimitiveArray::from_iter((0..10_000).map(|i| (i % 63) as u8)).as_ref(),
117 7,
118 )
119 .unwrap();
120
121 let compressed = packed.slice(7168..9216).unwrap();
122 assert_nth_scalar!(compressed, 0, (7168 % 63) as u8);
123 assert_nth_scalar!(compressed, compressed.len() - 1, (9215 % 63) as u8);
124 }
125
126 #[test]
127 fn double_slice_within_block() {
128 let arr = BitPackedArray::encode(
129 PrimitiveArray::from_iter((0u32..2048).map(|v| v % 64)).as_ref(),
130 6,
131 )
132 .unwrap();
133 let sliced = slice_via_kernel(&arr, 512..1434);
134 assert_nth_scalar!(sliced, 0, 512u32 % 64);
135 assert_nth_scalar!(sliced, 921, 1433u32 % 64);
136 assert_eq!(sliced.offset(), 512);
137 assert_eq!(sliced.len(), 922);
138 let doubly_sliced = slice_via_kernel(&sliced, 127..911);
139 assert_nth_scalar!(doubly_sliced, 0, (512u32 + 127) % 64);
140 assert_nth_scalar!(doubly_sliced, 783, (512u32 + 910) % 64);
141 assert_eq!(doubly_sliced.offset(), 639);
142 assert_eq!(doubly_sliced.len(), 784);
143 }
144
145 #[test]
146 fn slice_empty_patches() {
147 let array = BitPackedArray::encode(&buffer![0u32..=64].into_array(), 6).unwrap();
149
150 assert!(array.patches().is_some());
151
152 let patch_indices = array.patches().unwrap().indices().clone();
153 assert_eq!(patch_indices.len(), 1);
154
155 let sliced_bp = slice_via_kernel(&array, 0..64);
157 assert!(sliced_bp.patches().is_none());
158 }
159
160 #[test]
161 fn take_after_slice() {
162 let array =
165 BitPackedArray::encode(PrimitiveArray::from_iter((63u32..).take(3072)).as_ref(), 6)
166 .unwrap();
167
168 let sliced = array.slice(922..2061).unwrap();
171
172 let taken = sliced
178 .take(buffer![101i64, 1125, 1138].into_array())
179 .unwrap();
180 assert_eq!(taken.len(), 3);
181 }
182
183 #[test]
184 fn scalar_at_invalid_patches() {
185 let packed_array = unsafe {
186 BitPackedArray::new_unchecked(
187 BufferHandle::new_host(ByteBuffer::copy_from_aligned(
188 [0u8; 128],
189 Alignment::of::<u32>(),
190 )),
191 DType::Primitive(PType::U32, true.into()),
192 Validity::AllInvalid,
193 Some(
194 Patches::new(
195 8,
196 0,
197 buffer![1u32].into_array(),
198 PrimitiveArray::new(buffer![999u32], Validity::AllValid).to_array(),
199 None,
200 )
201 .unwrap(),
202 ),
203 1,
204 8,
205 0,
206 )
207 .into_array()
208 };
209 assert_eq!(
210 packed_array.scalar_at(1).unwrap(),
211 Scalar::null(DType::Primitive(PType::U32, Nullability::Nullable))
212 );
213 }
214
215 #[test]
216 fn scalar_at() {
217 let values = (0u32..257).collect::<Buffer<_>>();
218 let uncompressed = values.clone().into_array();
219 let packed = BitPackedArray::encode(&uncompressed, 8).unwrap();
220 assert!(packed.patches().is_some());
221
222 let patches = packed.patches().unwrap().indices().clone();
223 assert_eq!(
224 usize::try_from(&patches.scalar_at(0).unwrap()).unwrap(),
225 256
226 );
227
228 let expected = PrimitiveArray::from_iter(values.iter().copied());
229 assert_arrays_eq!(packed, expected);
230 }
231}