vortex_array/arrays/bool/
array.rs1use std::fmt::Display;
5use std::fmt::Formatter;
6
7use smallvec::smallvec;
8use vortex_buffer::BitBuffer;
9use vortex_buffer::BitBufferMeta;
10use vortex_buffer::BitBufferMut;
11use vortex_buffer::BitBufferView;
12use vortex_error::VortexExpect;
13use vortex_error::VortexResult;
14use vortex_error::vortex_ensure;
15use vortex_mask::Mask;
16
17use crate::ArrayRef;
18use crate::ArraySlots;
19use crate::ExecutionCtx;
20use crate::IntoArray;
21use crate::array::Array;
22use crate::array::ArrayParts;
23use crate::array::TypedArrayRef;
24use crate::array::child_to_validity;
25use crate::array::validity_to_child;
26use crate::arrays::Bool;
27use crate::arrays::BoolArray;
28use crate::buffer::BufferHandle;
29use crate::dtype::DType;
30use crate::validity::Validity;
31
32pub(super) const VALIDITY_SLOT: usize = 0;
34pub(super) const NUM_SLOTS: usize = 1;
35pub(super) const SLOT_NAMES: [&str; NUM_SLOTS] = ["validity"];
36
37#[derive(Clone, Debug)]
71pub struct BoolData {
72 pub(super) bits: BufferHandle,
73 pub(super) meta: BitBufferMeta,
74}
75
76impl Display for BoolData {
77 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
78 write!(f, "offset: {}", self.meta.offset())
79 }
80}
81
82pub struct BoolDataParts {
83 pub bits: BufferHandle,
84 pub meta: BitBufferMeta,
85}
86
87pub trait BoolArrayExt: TypedArrayRef<Bool> {
88 fn nullability(&self) -> crate::dtype::Nullability {
89 match self.as_ref().dtype() {
90 DType::Bool(nullability) => *nullability,
91 _ => unreachable!("BoolArrayExt requires a bool dtype"),
92 }
93 }
94
95 fn validity(&self) -> Validity {
96 child_to_validity(
97 self.as_ref().slots()[VALIDITY_SLOT].as_ref(),
98 self.nullability(),
99 )
100 }
101
102 fn to_bit_buffer(&self) -> BitBuffer {
103 let buffer = self.bits.as_host().clone();
104 BitBuffer::new_with_offset(buffer, self.meta.len(), self.meta.offset())
105 }
106
107 fn bit_buffer_view(&self) -> BitBufferView<'_> {
109 BitBufferView::from_meta(self.bits.as_host().as_slice(), self.meta)
110 }
111
112 fn maybe_execute_mask(&self, ctx: &mut ExecutionCtx) -> VortexResult<Option<Mask>> {
113 let all_valid = match &self.validity() {
114 Validity::NonNullable | Validity::AllValid => true,
115 Validity::AllInvalid => false,
116 Validity::Array(a) => a.statistics().compute_min::<bool>(ctx).unwrap_or(false),
117 };
118 Ok(all_valid.then(|| Mask::from_buffer(self.to_bit_buffer())))
119 }
120
121 fn execute_mask(&self, ctx: &mut ExecutionCtx) -> Mask {
122 self.maybe_execute_mask(ctx)
123 .vortex_expect("failed to check validity")
124 .vortex_expect("cannot convert nullable boolean array to mask")
125 }
126
127 fn to_mask_fill_null_false(&self, ctx: &mut ExecutionCtx) -> Mask {
128 let validity_mask = self
129 .validity()
130 .execute_mask(self.as_ref().len(), ctx)
131 .vortex_expect("Failed to compute validity mask");
132 let buffer = match validity_mask {
133 Mask::AllTrue(_) => self.to_bit_buffer(),
134 Mask::AllFalse(_) => return Mask::new_false(self.as_ref().len()),
135 Mask::Values(validity) => validity.bit_buffer() & self.to_bit_buffer(),
136 };
137 Mask::from_buffer(buffer)
138 }
139}
140impl<T: TypedArrayRef<Bool>> BoolArrayExt for T {}
141
142impl BoolData {
144 #[inline]
146 pub fn into_parts(self, len: usize) -> BoolDataParts {
147 BoolDataParts {
148 bits: self.bits,
149 meta: BitBufferMeta::new(self.meta.offset(), len),
150 }
151 }
152
153 pub(crate) fn make_slots(validity: &Validity, len: usize) -> ArraySlots {
154 smallvec![validity_to_child(validity, len)]
155 }
156}
157
158impl Array<Bool> {
160 pub fn new(bits: BitBuffer, validity: Validity) -> Self {
166 Self::try_new(bits, validity).vortex_expect("Failed to create BoolArray")
167 }
168
169 pub fn new_handle(handle: BufferHandle, offset: usize, len: usize, validity: Validity) -> Self {
175 Self::try_new_from_handle(handle, offset, len, validity)
176 .vortex_expect("Failed to create BoolArray from BufferHandle")
177 }
178
179 pub fn try_new(bits: BitBuffer, validity: Validity) -> VortexResult<Self> {
185 let dtype = DType::Bool(validity.nullability());
186 let len = bits.len();
187 let slots = BoolData::make_slots(&validity, len);
188 let data = BoolData::try_new(bits, validity)?;
189 Ok(unsafe {
190 Array::from_parts_unchecked(ArrayParts::new(Bool, dtype, len, data).with_slots(slots))
191 })
192 }
193
194 pub fn try_new_from_handle(
197 bits: BufferHandle,
198 offset: usize,
199 len: usize,
200 validity: Validity,
201 ) -> VortexResult<Self> {
202 let dtype = DType::Bool(validity.nullability());
203 let slots = BoolData::make_slots(&validity, len);
204 let data = BoolData::try_new_from_handle(bits, offset, len, validity)?;
205 Ok(unsafe {
206 Array::from_parts_unchecked(ArrayParts::new(Bool, dtype, len, data).with_slots(slots))
207 })
208 }
209
210 pub unsafe fn new_unchecked(bits: BitBuffer, validity: Validity) -> Self {
216 let dtype = DType::Bool(validity.nullability());
217 let len = bits.len();
218 let slots = BoolData::make_slots(&validity, len);
219 let data = unsafe { BoolData::new_unchecked(bits, validity) };
221 unsafe {
222 Array::from_parts_unchecked(ArrayParts::new(Bool, dtype, len, data).with_slots(slots))
223 }
224 }
225
226 pub fn validate(bits: &BitBuffer, validity: &Validity) -> VortexResult<()> {
228 BoolData::validate(bits, validity)
229 }
230
231 pub fn from_indices<I: IntoIterator<Item = usize>>(
235 length: usize,
236 indices: I,
237 validity: Validity,
238 ) -> Self {
239 let mut buffer = BitBufferMut::new_unset(length);
240 indices.into_iter().for_each(|idx| buffer.set(idx));
241 Self::new(buffer.freeze(), validity)
242 }
243
244 pub fn into_bit_buffer(self) -> BitBuffer {
246 let len = self.len();
247 let data = self.into_data();
248 let buffer = data.bits.unwrap_host();
249 BitBuffer::new_with_offset(buffer, len, data.meta.offset())
250 }
251}
252
253impl BoolData {
255 pub(super) fn try_new(bits: BitBuffer, validity: Validity) -> VortexResult<Self> {
256 let bits = bits.shrink_offset();
257 Self::validate(&bits, &validity)?;
258
259 let (offset, len, buffer) = bits.into_inner();
260
261 Ok(Self {
262 bits: BufferHandle::new_host(buffer),
263 meta: BitBufferMeta::new(offset, len),
264 })
265 }
266
267 pub(super) fn try_new_from_handle(
268 bits: BufferHandle,
269 offset: usize,
270 len: usize,
271 validity: Validity,
272 ) -> VortexResult<Self> {
273 vortex_ensure!(offset < 8, "BitBuffer offset must be <8, got {}", offset);
274 if let Some(validity_len) = validity.maybe_len() {
275 vortex_ensure!(
276 validity_len == len,
277 "BoolArray of size {} cannot be built with validity of size {validity_len}",
278 len,
279 );
280 }
281
282 vortex_ensure!(
283 bits.len() * 8 >= (len + offset),
284 "provided BufferHandle with offset {offset} len {len} had size {} bits",
285 bits.len() * 8,
286 );
287
288 Ok(Self {
289 bits,
290 meta: BitBufferMeta::new(offset, len),
291 })
292 }
293
294 pub(super) unsafe fn new_unchecked(bits: BitBuffer, validity: Validity) -> Self {
295 if cfg!(debug_assertions) {
296 Self::try_new(bits, validity).vortex_expect("Failed to create BoolData")
297 } else {
298 let (offset, len, buffer) = bits.into_inner();
299
300 Self {
301 bits: BufferHandle::new_host(buffer),
302 meta: BitBufferMeta::new(offset, len),
303 }
304 }
305 }
306
307 pub(super) fn validate(bits: &BitBuffer, validity: &Validity) -> VortexResult<()> {
308 vortex_ensure!(
309 bits.offset() < 8,
310 "BitBuffer offset must be <8, got {}",
311 bits.offset()
312 );
313
314 if let Some(validity_len) = validity.maybe_len() {
315 vortex_ensure!(
316 validity_len == bits.len(),
317 "BoolArray of size {} cannot be built with validity of size {validity_len}",
318 bits.len()
319 );
320 }
321
322 Ok(())
323 }
324}
325
326impl From<BitBuffer> for BoolArray {
327 fn from(value: BitBuffer) -> Self {
328 BoolArray::new(value, Validity::NonNullable)
329 }
330}
331
332impl FromIterator<bool> for BoolArray {
333 fn from_iter<T: IntoIterator<Item = bool>>(iter: T) -> Self {
334 BoolArray::from(BitBuffer::from_iter(iter))
335 }
336}
337
338impl FromIterator<Option<bool>> for BoolArray {
339 fn from_iter<I: IntoIterator<Item = Option<bool>>>(iter: I) -> Self {
340 let iter = iter.into_iter();
341 let capacity = iter.size_hint().0;
342 let mut bits = BitBufferMut::with_capacity(capacity);
343 let mut validity = BitBufferMut::with_capacity(capacity);
344 for value in iter {
345 bits.append(value.unwrap_or_default());
346 validity.append(value.is_some());
347 }
348
349 BoolArray::new(bits.freeze(), Validity::from(validity.freeze()))
350 }
351}
352
353impl IntoArray for BitBuffer {
354 fn into_array(self) -> ArrayRef {
355 BoolArray::new(self, Validity::NonNullable).into_array()
356 }
357}
358
359impl IntoArray for BitBufferMut {
360 fn into_array(self) -> ArrayRef {
361 self.freeze().into_array()
362 }
363}
364
365#[cfg(test)]
366mod tests {
367 use std::iter::once;
368 use std::iter::repeat_n;
369
370 use vortex_buffer::BitBuffer;
371 use vortex_buffer::BitBufferMut;
372 use vortex_buffer::buffer;
373
374 use crate::IntoArray;
375 use crate::VortexSessionExecute;
376 use crate::array_session;
377 use crate::arrays::BoolArray;
378 use crate::arrays::PrimitiveArray;
379 use crate::arrays::bool::BoolArrayExt;
380 use crate::assert_arrays_eq;
381 use crate::patches::Patches;
382 use crate::validity::Validity;
383
384 #[test]
385 fn bool_array() {
386 let mut ctx = array_session().create_execution_ctx();
387
388 let arr = BoolArray::from_iter([true, false, true]);
389 let scalar = bool::try_from(&arr.execute_scalar(0, &mut ctx).unwrap()).unwrap();
390 assert!(scalar);
391 }
392
393 #[test]
394 fn test_all_some_iter() {
395 let mut ctx = array_session().create_execution_ctx();
396
397 let arr = BoolArray::from_iter([Some(true), Some(false)]);
398
399 assert!(matches!(arr.validity(), Ok(Validity::AllValid)));
400
401 let scalar = bool::try_from(&arr.execute_scalar(0, &mut ctx).unwrap()).unwrap();
402 assert!(scalar);
403 let scalar = bool::try_from(&arr.execute_scalar(1, &mut ctx).unwrap()).unwrap();
404 assert!(!scalar);
405 }
406
407 #[test]
408 fn test_bool_from_iter() {
409 let mut ctx = array_session().create_execution_ctx();
410 let arr = BoolArray::from_iter([Some(true), Some(true), None, Some(false), None]);
411
412 let scalar = bool::try_from(&arr.execute_scalar(0, &mut ctx).unwrap()).unwrap();
413 assert!(scalar);
414
415 let scalar = bool::try_from(&arr.execute_scalar(1, &mut ctx).unwrap()).unwrap();
416 assert!(scalar);
417
418 let scalar = arr.execute_scalar(2, &mut ctx).unwrap();
419 assert!(scalar.is_null());
420
421 let scalar = bool::try_from(&arr.execute_scalar(3, &mut ctx).unwrap()).unwrap();
422 assert!(!scalar);
423
424 let scalar = arr.execute_scalar(4, &mut ctx).unwrap();
425 assert!(scalar.is_null());
426 }
427
428 #[test]
429 fn patch_sliced_bools() {
430 let mut ctx = array_session().create_execution_ctx();
431 let arr = BoolArray::from(BitBuffer::new_set(12));
432 let sliced = arr.slice(4..12).unwrap();
433 assert_arrays_eq!(sliced, BoolArray::from_iter([true; 8]), &mut ctx);
434
435 let arr = {
436 let mut builder = BitBufferMut::new_unset(12);
437 (1..12).for_each(|i| builder.set(i));
438 BoolArray::from(builder.freeze())
439 };
440 let sliced = arr.slice(4..12).unwrap();
441 let expected_slice: Vec<bool> = (4..12).map(|i| (1..12).contains(&i)).collect();
442 assert_arrays_eq!(
443 sliced,
444 BoolArray::from_iter(expected_slice.clone()),
445 &mut ctx
446 );
447
448 let patches = Patches::new(
450 arr.len(),
451 0,
452 buffer![4u32].into_array(),
453 BoolArray::from(BitBuffer::new_unset(1)).into_array(),
454 None,
455 )
456 .unwrap();
457 let arr = arr.patch(&patches, &mut ctx).unwrap();
458 let expected_patched: Vec<bool> = (0..12).map(|i| (1..12).contains(&i) && i != 4).collect();
460 assert_arrays_eq!(arr, BoolArray::from_iter(expected_patched), &mut ctx);
461
462 assert_arrays_eq!(sliced, BoolArray::from_iter(expected_slice), &mut ctx);
464 }
465
466 #[test]
467 fn slice_array_in_middle() {
468 let mut ctx = array_session().create_execution_ctx();
469 let arr = BoolArray::from(BitBuffer::new_set(16));
470 let sliced = arr.slice(4..12).unwrap();
471 assert_arrays_eq!(sliced, BoolArray::from_iter([true; 8]), &mut ctx);
472 }
473
474 #[test]
475 fn patch_bools_owned() {
476 let mut ctx = array_session().create_execution_ctx();
477 let arr = BoolArray::from(BitBuffer::new_set(16));
478 let buf_ptr = arr.to_bit_buffer().inner().as_ptr();
479
480 let patches = Patches::new(
481 arr.len(),
482 0,
483 PrimitiveArray::new(buffer![0u32], Validity::NonNullable).into_array(),
484 BoolArray::from(BitBuffer::new_unset(1)).into_array(),
485 None,
486 )
487 .unwrap();
488 let arr = arr.patch(&patches, &mut ctx).unwrap();
489 assert_eq!(arr.to_bit_buffer().inner().as_ptr(), buf_ptr);
491
492 let expected: BoolArray = once(false).chain(repeat_n(true, 15)).collect();
494 assert_arrays_eq!(arr, expected, &mut ctx);
495 }
496
497 #[test]
498 fn patch_sliced_bools_offset() {
499 let mut ctx = array_session().create_execution_ctx();
500 let arr = BoolArray::from(BitBuffer::new_set(15));
501 let sliced = arr.slice(4..15).unwrap();
502 assert_arrays_eq!(sliced, BoolArray::from_iter([true; 11]), &mut ctx);
503 }
504}