vortex_array/builders/
bool.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4use std::any::Any;
5use std::mem;
6
7use vortex_buffer::BitBufferMut;
8use vortex_dtype::DType;
9use vortex_dtype::Nullability;
10use vortex_error::VortexResult;
11use vortex_error::vortex_ensure;
12use vortex_mask::Mask;
13use vortex_scalar::BoolScalar;
14use vortex_scalar::Scalar;
15
16use crate::Array;
17use crate::ArrayRef;
18use crate::IntoArray;
19use crate::arrays::BoolArray;
20use crate::builders::ArrayBuilder;
21use crate::builders::DEFAULT_BUILDER_CAPACITY;
22use crate::builders::LazyBitBufferBuilder;
23use crate::canonical::Canonical;
24use crate::canonical::ToCanonical;
25
26pub struct BoolBuilder {
27    dtype: DType,
28    inner: BitBufferMut,
29    nulls: LazyBitBufferBuilder,
30}
31
32impl BoolBuilder {
33    pub fn new(nullability: Nullability) -> Self {
34        Self::with_capacity(nullability, DEFAULT_BUILDER_CAPACITY)
35    }
36
37    pub fn with_capacity(nullability: Nullability, capacity: usize) -> Self {
38        Self {
39            inner: BitBufferMut::with_capacity(capacity),
40            nulls: LazyBitBufferBuilder::new(capacity),
41            dtype: DType::Bool(nullability),
42        }
43    }
44
45    /// Appends a boolean value to the builder.
46    pub fn append_value(&mut self, value: bool) {
47        self.append_values(value, 1)
48    }
49
50    /// Appends the same boolean value multiple times to the builder.
51    ///
52    /// This method appends the given boolean value `n` times.
53    pub fn append_values(&mut self, value: bool, n: usize) {
54        self.inner.append_n(value, n);
55        self.nulls.append_n_non_nulls(n)
56    }
57
58    /// Finishes the builder directly into a [`BoolArray`].
59    pub fn finish_into_bool(&mut self) -> BoolArray {
60        assert_eq!(
61            self.nulls.len(),
62            self.inner.len(),
63            "Null count and value count should match when calling BoolBuilder::finish."
64        );
65
66        BoolArray::from_bit_buffer(
67            mem::take(&mut self.inner).freeze(),
68            self.nulls.finish_with_nullability(self.dtype.nullability()),
69        )
70    }
71}
72
73impl ArrayBuilder for BoolBuilder {
74    fn as_any(&self) -> &dyn Any {
75        self
76    }
77
78    fn as_any_mut(&mut self) -> &mut dyn Any {
79        self
80    }
81
82    fn dtype(&self) -> &DType {
83        &self.dtype
84    }
85
86    fn len(&self) -> usize {
87        self.inner.len()
88    }
89
90    fn append_zeros(&mut self, n: usize) {
91        self.append_values(false, n)
92    }
93
94    unsafe fn append_nulls_unchecked(&mut self, n: usize) {
95        self.inner.append_n(false, n);
96        self.nulls.append_n_nulls(n)
97    }
98
99    fn append_scalar(&mut self, scalar: &Scalar) -> VortexResult<()> {
100        vortex_ensure!(
101            scalar.dtype() == self.dtype(),
102            "BoolBuilder expected scalar with dtype {:?}, got {:?}",
103            self.dtype(),
104            scalar.dtype()
105        );
106
107        let bool_scalar = BoolScalar::try_from(scalar)?;
108        match bool_scalar.value() {
109            Some(value) => self.append_value(value),
110            None => self.append_null(),
111        }
112
113        Ok(())
114    }
115
116    unsafe fn extend_from_array_unchecked(&mut self, array: &dyn Array) {
117        let bool_array = array.to_bool();
118
119        self.inner.append_buffer(bool_array.bit_buffer());
120        self.nulls.append_validity_mask(bool_array.validity_mask());
121    }
122
123    fn reserve_exact(&mut self, additional: usize) {
124        self.inner.reserve(additional);
125        self.nulls.reserve_exact(additional);
126    }
127
128    unsafe fn set_validity_unchecked(&mut self, validity: Mask) {
129        self.nulls = LazyBitBufferBuilder::new(validity.len());
130        self.nulls.append_validity_mask(validity);
131    }
132
133    fn finish(&mut self) -> ArrayRef {
134        self.finish_into_bool().into_array()
135    }
136
137    fn finish_into_canonical(&mut self) -> Canonical {
138        Canonical::Bool(self.finish_into_bool())
139    }
140}
141
142#[cfg(test)]
143mod tests {
144    use rand::Rng;
145    use rand::SeedableRng;
146    use rand::prelude::StdRng;
147    use vortex_dtype::DType;
148    use vortex_dtype::Nullability;
149    use vortex_scalar::Scalar;
150
151    use crate::ArrayRef;
152    use crate::IntoArray;
153    use crate::array::Array;
154    use crate::arrays::BoolArray;
155    use crate::arrays::ChunkedArray;
156    use crate::assert_arrays_eq;
157    use crate::builders::ArrayBuilder;
158    use crate::builders::BoolBuilder;
159    use crate::builders::builder_with_capacity;
160    use crate::canonical::ToCanonical;
161    use crate::vtable::ValidityHelper;
162
163    fn make_opt_bool_chunks(len: usize, chunk_count: usize) -> ArrayRef {
164        let mut rng = StdRng::seed_from_u64(0);
165
166        (0..chunk_count)
167            .map(|_| {
168                BoolArray::from_iter((0..len).map(|_| match rng.random_range::<u8, _>(0..=2) {
169                    0 => Some(false),
170                    1 => Some(true),
171                    2 => None,
172                    _ => unreachable!(),
173                }))
174                .into_array()
175            })
176            .collect::<ChunkedArray>()
177            .into_array()
178    }
179
180    #[test]
181    fn tests() {
182        let len = 1000;
183        let chunk_count = 10;
184        let chunk = make_opt_bool_chunks(len, chunk_count);
185
186        let mut builder = builder_with_capacity(chunk.dtype(), len * chunk_count);
187        chunk.clone().append_to_builder(builder.as_mut());
188
189        let canon_into = builder.finish().to_bool();
190        let into_canon = chunk.to_bool();
191
192        assert_eq!(canon_into.validity(), into_canon.validity());
193        assert_eq!(canon_into.bit_buffer(), into_canon.bit_buffer());
194    }
195
196    #[test]
197    fn test_append_scalar() {
198        let mut builder = BoolBuilder::with_capacity(Nullability::Nullable, 10);
199
200        // Test appending true value.
201        let true_scalar = Scalar::bool(true, Nullability::Nullable);
202        builder.append_scalar(&true_scalar).unwrap();
203
204        // Test appending false value.
205        let false_scalar = Scalar::bool(false, Nullability::Nullable);
206        builder.append_scalar(&false_scalar).unwrap();
207
208        // Test appending null value.
209        let null_scalar = Scalar::null(DType::Bool(Nullability::Nullable));
210        builder.append_scalar(&null_scalar).unwrap();
211
212        let array = builder.finish_into_bool();
213        let expected = BoolArray::from_iter([Some(true), Some(false), None]);
214        assert_arrays_eq!(&array, &expected);
215
216        // Test wrong dtype error.
217        let mut builder = BoolBuilder::with_capacity(Nullability::NonNullable, 10);
218        let wrong_scalar = Scalar::from(42i32);
219        assert!(builder.append_scalar(&wrong_scalar).is_err());
220    }
221}