Skip to main content

reifydb_value/value/container/
bool_array.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use arrow_array::{Array, BooleanArray};
5use arrow_buffer::{BooleanBuffer, NullBuffer};
6use arrow_select::filter::FilterPredicate;
7
8use crate::{
9	Result,
10	util::{bitmap, kernel},
11	value::{Value, value_type::ValueType},
12};
13
14pub fn get_value(array: &BooleanArray, index: usize) -> Value {
15	if index < array.len() {
16		Value::Boolean(array.value(index))
17	} else {
18		Value::none_of(ValueType::Boolean)
19	}
20}
21
22pub fn as_string(array: &BooleanArray, index: usize) -> String {
23	if index < array.len() {
24		array.value(index).to_string()
25	} else {
26		"none".to_string()
27	}
28}
29
30pub fn slice(array: &BooleanArray, start: usize, end: usize) -> BooleanArray {
31	BooleanArray::new(
32		bitmap::slice(array.values(), start, end),
33		bitmap::slice_nulls(array.logical_nulls().as_ref(), start, end),
34	)
35}
36
37pub fn take(array: &BooleanArray, num: usize) -> BooleanArray {
38	slice(array, 0, num)
39}
40
41pub fn filter(array: &BooleanArray, mask: &BooleanBuffer) -> BooleanArray {
42	filter_with(array, &kernel::predicate(mask, array.len()))
43}
44
45pub fn filter_with(array: &BooleanArray, predicate: &FilterPredicate) -> BooleanArray {
46	let selected = kernel::filtered(array, predicate);
47	let nulls = kernel::kept_nulls(array, &selected);
48	BooleanArray::new(selected.into_parts().0, nulls)
49}
50
51pub fn reorder(array: &BooleanArray, indices: &[usize]) -> Result<BooleanArray> {
52	kernel::rows_in_range(indices, array.len())?;
53	Ok(BooleanArray::new(
54		bitmap::reorder(array.values(), indices),
55		bitmap::reorder_nulls(array.logical_nulls().as_ref(), indices),
56	))
57}
58
59pub fn attach_nulls(array: BooleanArray, nulls: Option<NullBuffer>) -> BooleanArray {
60	bitmap::assert_nulls_len(nulls.as_ref(), array.len());
61	let (values, _) = array.into_parts();
62	BooleanArray::new(values, nulls)
63}
64
65pub fn capacity(array: &BooleanArray) -> usize {
66	let buffer = array.values().inner();
67	if buffer.strong_count() == 1 {
68		buffer.capacity() * 8
69	} else {
70		array.len()
71	}
72}
73
74pub fn heap_size(array: &BooleanArray) -> usize {
75	capacity(array).div_ceil(8)
76}
77
78#[cfg(test)]
79mod tests {
80	use arrow_array::BooleanArray;
81
82	use super::{as_string, filter, get_value, reorder, slice, take};
83	use crate::value::{Value, value_type::ValueType};
84
85	fn values(array: &BooleanArray) -> Vec<bool> {
86		array.values().iter().collect()
87	}
88
89	#[test]
90	fn get_value_and_as_string_past_the_end_read_as_none() {
91		// A row past the end must read as a typed none, never panic.
92		let array = BooleanArray::from(vec![true, false]);
93		assert_eq!(get_value(&array, 1), Value::Boolean(false));
94		assert_eq!(get_value(&array, 2), Value::none_of(ValueType::Boolean));
95		assert_eq!(as_string(&array, 0), "true");
96		assert_eq!(as_string(&array, 2), "none");
97	}
98
99	#[test]
100	fn slice_and_take_clamp_like_the_old_container() {
101		// Arrow's slice(offset, len) panics past the end; the column slice must clamp instead.
102		let array = BooleanArray::from(vec![true, false, true, false]);
103		assert_eq!(values(&slice(&array, 1, 3)), vec![false, true]);
104		assert_eq!(values(&slice(&array, 2, 40)), vec![true, false]);
105		assert_eq!(slice(&array, 3, 1).len(), 0);
106		assert_eq!(take(&array, 9).len(), 4);
107	}
108
109	#[test]
110	fn filter_ignores_a_long_mask_and_reorder_refuses_an_out_of_range_row() {
111		// A long mask must not read past len, and an out of range index must fail, never read as false.
112		let array = BooleanArray::from(vec![true, false, true]);
113		let mask = BooleanArray::from(vec![true, false, true, true, true]);
114		assert_eq!(values(&filter(&array, mask.values())), vec![true, true]);
115		let error = reorder(&array, &[2, 7, 1]).unwrap_err();
116		assert_eq!(error.diagnostic().message, "row index 7 out of range for a column of 3 rows");
117	}
118}