vortex_fastlanes/bitpacking/vtable/
mod.rs1use std::hash::Hash;
5use std::hash::Hasher;
6
7use prost::Message;
8use vortex_array::Array;
9use vortex_array::ArrayEq;
10use vortex_array::ArrayHash;
11use vortex_array::ArrayId;
12use vortex_array::ArrayParts;
13use vortex_array::ArrayRef;
14use vortex_array::ArraySlots;
15use vortex_array::ArrayView;
16use vortex_array::EqMode;
17use vortex_array::ExecutionCtx;
18use vortex_array::ExecutionResult;
19use vortex_array::IntoArray;
20use vortex_array::buffer::BufferHandle;
21use vortex_array::builders::ArrayBuilder;
22use vortex_array::dtype::DType;
23use vortex_array::dtype::PType;
24use vortex_array::match_each_integer_ptype;
25use vortex_array::patches::Patches;
26use vortex_array::patches::PatchesData;
27use vortex_array::patches::PatchesMetadata;
28use vortex_array::require_patches;
29use vortex_array::require_validity;
30use vortex_array::serde::ArrayChildren;
31use vortex_array::validity::Validity;
32use vortex_array::vtable::VTable;
33use vortex_array::vtable::child_to_validity;
34use vortex_array::vtable::validity_to_child;
35use vortex_error::VortexExpect;
36use vortex_error::VortexResult;
37use vortex_error::vortex_bail;
38use vortex_error::vortex_ensure;
39use vortex_error::vortex_err;
40use vortex_error::vortex_panic;
41use vortex_session::VortexSession;
42use vortex_session::registry::CachedId;
43
44use crate::BitPackedArrayExt;
45use crate::BitPackedData;
46use crate::BitPackedDataParts;
47use crate::bitpack_decompress::unpack_array;
48use crate::bitpack_decompress::unpack_into_primitive_builder;
49use crate::bitpacking::array::BitPackedSlots;
50use crate::bitpacking::array::BitPackedSlotsView;
51use crate::bitpacking::array::PATCH_SLOTS;
52use crate::bitpacking::vtable::rules::RULES;
53mod kernels;
54mod operations;
55mod rules;
56mod validity;
57
58pub type BitPackedArray = Array<BitPacked>;
60
61pub(crate) fn initialize(session: &VortexSession) {
62 kernels::initialize(session);
63}
64
65#[derive(Clone, prost::Message)]
66pub struct BitPackedMetadata {
67 #[prost(uint32, tag = "1")]
68 pub(crate) bit_width: u32,
69 #[prost(uint32, tag = "2")]
70 pub(crate) offset: u32, #[prost(message, optional, tag = "3")]
72 pub(crate) patches: Option<PatchesMetadata>,
73}
74
75impl ArrayHash for BitPackedData {
76 fn array_hash<H: Hasher>(&self, state: &mut H, accuracy: EqMode) {
77 self.offset.hash(state);
78 self.bit_width.hash(state);
79 self.packed.array_hash(state, accuracy);
80 self.patches_data.hash(state);
81 }
82}
83
84impl ArrayEq for BitPackedData {
85 fn array_eq(&self, other: &Self, accuracy: EqMode) -> bool {
86 self.offset == other.offset
87 && self.bit_width == other.bit_width
88 && self.packed.array_eq(&other.packed, accuracy)
89 && self.patches_data == other.patches_data
90 }
91}
92
93impl VTable for BitPacked {
94 type TypedArrayData = BitPackedData;
95
96 type OperationsVTable = Self;
97 type ValidityVTable = Self;
98
99 fn id(&self) -> ArrayId {
100 static ID: CachedId = CachedId::new("fastlanes.bitpacked");
101 *ID
102 }
103
104 fn validate(
105 &self,
106 data: &Self::TypedArrayData,
107 dtype: &DType,
108 len: usize,
109 slots: &[Option<ArrayRef>],
110 ) -> VortexResult<()> {
111 let bp_slots = BitPackedSlotsView::from_slots(slots);
112
113 let validity = child_to_validity(bp_slots.validity_child, dtype.nullability());
114 let patches =
115 PatchesData::patches_from_slots(data.patches_data.as_ref(), len, slots, PATCH_SLOTS);
116 BitPackedData::validate(
117 &data.packed,
118 dtype.as_ptype(),
119 &validity,
120 patches.as_ref(),
121 data.bit_width,
122 len,
123 data.offset,
124 )
125 }
126
127 fn nbuffers(_array: ArrayView<'_, Self>) -> usize {
128 1
129 }
130
131 fn buffer(array: ArrayView<'_, Self>, idx: usize) -> BufferHandle {
132 match idx {
133 0 => array.packed().clone(),
134 _ => vortex_panic!("BitPackedArray buffer index {idx} out of bounds"),
135 }
136 }
137
138 fn buffer_name(_array: ArrayView<'_, Self>, idx: usize) -> Option<String> {
139 match idx {
140 0 => Some("packed".to_string()),
141 _ => None,
142 }
143 }
144
145 fn with_buffers(
146 &self,
147 array: ArrayView<'_, Self>,
148 buffers: &[BufferHandle],
149 ) -> VortexResult<ArrayParts<Self>> {
150 vortex_ensure!(
151 buffers.len() == 1,
152 "Expected 1 buffer, got {}",
153 buffers.len()
154 );
155 let mut data = array.data().clone();
156 data.packed = buffers[0].clone();
157 Ok(
158 ArrayParts::new(self.clone(), array.dtype().clone(), array.len(), data)
159 .with_slots(array.slots().iter().cloned().collect()),
160 )
161 }
162
163 fn serialize(
164 array: ArrayView<'_, Self>,
165 _session: &VortexSession,
166 ) -> VortexResult<Option<Vec<u8>>> {
167 Ok(Some(
168 BitPackedMetadata {
169 bit_width: array.bit_width() as u32,
170 offset: array.offset() as u32,
171 patches: array
172 .patches()
173 .map(|p| p.to_metadata(array.len(), array.dtype()))
174 .transpose()?,
175 }
176 .encode_to_vec(),
177 ))
178 }
179
180 fn deserialize(
181 &self,
182 dtype: &DType,
183 len: usize,
184 metadata: &[u8],
185 buffers: &[BufferHandle],
186 children: &dyn ArrayChildren,
187 _session: &VortexSession,
188 ) -> VortexResult<ArrayParts<Self>> {
189 let metadata = BitPackedMetadata::decode(metadata)?;
190 if buffers.len() != 1 {
191 vortex_bail!("Expected 1 buffer, got {}", buffers.len());
192 }
193 let packed = buffers[0].clone();
194
195 let load_validity = |child_idx: usize| {
196 if children.len() == child_idx {
197 Ok(Validity::from(dtype.nullability()))
198 } else if children.len() == child_idx + 1 {
199 let validity = children.get(child_idx, &Validity::DTYPE, len)?;
200 Ok(Validity::Array(validity))
201 } else {
202 vortex_bail!(
203 "Expected {} or {} children, got {}",
204 child_idx,
205 child_idx + 1,
206 children.len()
207 );
208 }
209 };
210
211 let validity_idx = match &metadata.patches {
212 None => 0,
213 Some(patches_meta) if patches_meta.chunk_offsets_dtype()?.is_some() => 3,
214 Some(_) => 2,
215 };
216
217 let validity = load_validity(validity_idx)?;
218
219 let patches = metadata
220 .patches
221 .map(|p| {
222 let indices = children.get(0, &p.indices_dtype()?, p.len()?)?;
223 let values = children.get(1, dtype, p.len()?)?;
224 let chunk_offsets = p
225 .chunk_offsets_dtype()?
226 .map(|dtype| children.get(2, &dtype, p.chunk_offsets_len() as usize))
227 .transpose()?;
228
229 Patches::new(len, p.offset()?, indices, values, chunk_offsets)
230 })
231 .transpose()?;
232
233 let slots = {
234 let mut s = ArraySlots::with_capacity(4);
235 PatchesData::push_slots(&mut s, patches.as_ref());
236 s.push(validity_to_child(&validity, len));
237 s
238 };
239 let data = BitPackedData::try_new(
240 packed,
241 patches,
242 u8::try_from(metadata.bit_width).map_err(|_| {
243 vortex_err!(
244 "BitPackedMetadata bit_width {} does not fit in u8",
245 metadata.bit_width
246 )
247 })?,
248 u16::try_from(metadata.offset).map_err(|_| {
249 vortex_err!(
250 "BitPackedMetadata offset {} does not fit in u16",
251 metadata.offset
252 )
253 })?,
254 )?;
255 Ok(ArrayParts::new(self.clone(), dtype.clone(), len, data).with_slots(slots))
256 }
257
258 fn append_to_builder(
259 array: ArrayView<'_, Self>,
260 builder: &mut dyn ArrayBuilder,
261 ctx: &mut ExecutionCtx,
262 ) -> VortexResult<()> {
263 match_each_integer_ptype!(array.dtype().as_ptype(), |T| {
264 unpack_into_primitive_builder::<T>(
265 array,
266 builder
267 .as_any_mut()
268 .downcast_mut()
269 .vortex_expect("bit packed array must canonicalize into a primitive array"),
270 ctx,
271 )
272 })
273 }
274
275 fn slot_name(_array: ArrayView<'_, Self>, idx: usize) -> String {
276 BitPackedSlots::NAMES[idx].to_string()
277 }
278
279 fn execute(array: Array<Self>, ctx: &mut ExecutionCtx) -> VortexResult<ExecutionResult> {
280 require_patches!(
281 array,
282 BitPackedSlots::PATCH_INDICES,
283 BitPackedSlots::PATCH_VALUES,
284 BitPackedSlots::PATCH_CHUNK_OFFSETS
285 );
286 require_validity!(array, BitPackedSlots::VALIDITY_CHILD);
287
288 Ok(ExecutionResult::done(
289 unpack_array(array.as_view(), ctx)?.into_array(),
290 ))
291 }
292
293 fn reduce_parent(
294 array: ArrayView<'_, Self>,
295 parent: &ArrayRef,
296 child_idx: usize,
297 ) -> VortexResult<Option<ArrayRef>> {
298 RULES.evaluate(array, parent, child_idx)
299 }
300}
301
302#[derive(Clone, Debug)]
303pub struct BitPacked;
304
305impl BitPacked {
306 pub fn try_new(
307 packed: BufferHandle,
308 ptype: PType,
309 validity: Validity,
310 patches: Option<Patches>,
311 bit_width: u8,
312 len: usize,
313 offset: u16,
314 ) -> VortexResult<BitPackedArray> {
315 let dtype = DType::Primitive(ptype, validity.nullability());
316 let slots = {
317 let mut s = ArraySlots::with_capacity(4);
318 PatchesData::push_slots(&mut s, patches.as_ref());
319 s.push(validity_to_child(&validity, len));
320 s
321 };
322 let data = BitPackedData::try_new(packed, patches, bit_width, offset)?;
323 Array::try_from_parts(ArrayParts::new(BitPacked, dtype, len, data).with_slots(slots))
324 }
325
326 pub fn into_parts(array: BitPackedArray) -> BitPackedDataParts {
327 let len = array.len();
328 let patches = array.patches();
329 let validity = array.validity().vortex_expect("BitPacked validity");
330 let data = array.into_data();
331 BitPackedDataParts {
332 offset: data.offset,
333 bit_width: data.bit_width,
334 len,
335 packed: data.packed,
336 patches,
337 validity,
338 }
339 }
340
341 pub fn encode(
343 array: &ArrayRef,
344 bit_width: u8,
345 ctx: &mut ExecutionCtx,
346 ) -> VortexResult<BitPackedArray> {
347 BitPackedData::encode(array, bit_width, ctx)
348 }
349}