vortex_array/arrays/bool/compute/
take.rs1use itertools::Itertools as _;
5use num_traits::AsPrimitive;
6use vortex_buffer::BitBuffer;
7use vortex_buffer::BitBufferMut;
8use vortex_buffer::BitBufferView;
9use vortex_buffer::get_bit;
10use vortex_error::VortexResult;
11use vortex_error::vortex_ensure;
12use vortex_error::vortex_err;
13use vortex_mask::Mask;
14
15use crate::ArrayRef;
16use crate::Columnar;
17use crate::IntoArray;
18use crate::array::ArrayView;
19use crate::arrays::Bool;
20use crate::arrays::BoolArray;
21use crate::arrays::ConstantArray;
22use crate::arrays::PiecewiseSequence;
23use crate::arrays::PrimitiveArray;
24use crate::arrays::bool::BoolArrayExt;
25use crate::arrays::dict::TakeExecute;
26use crate::arrays::piecewise_sequence::constant_unsigned_usize;
27use crate::arrays::piecewise_sequence::maybe_contiguous_slices;
28use crate::builtins::ArrayBuiltins;
29use crate::dtype::UnsignedPType;
30use crate::executor::ExecutionCtx;
31use crate::match_each_integer_ptype;
32use crate::match_each_unsigned_integer_ptype;
33use crate::scalar::Scalar;
34
35impl TakeExecute for Bool {
36 fn take(
37 array: ArrayView<'_, Bool>,
38 indices: &ArrayRef,
39 ctx: &mut ExecutionCtx,
40 ) -> VortexResult<Option<ArrayRef>> {
41 if let Some(piecewise_indices) = indices.as_opt::<PiecewiseSequence>()
42 && let Some(taken) = take_contiguous_ranges(array, piecewise_indices, indices, ctx)?
43 {
44 return Ok(Some(taken));
45 }
46
47 let indices_nulls_zeroed = match indices.validity()?.execute_mask(indices.len(), ctx)? {
48 Mask::AllTrue(_) => indices.clone(),
49 Mask::AllFalse(_) => {
50 return Ok(Some(
51 ConstantArray::new(Scalar::null(array.dtype().as_nullable()), indices.len())
52 .into_array(),
53 ));
54 }
55 Mask::Values(_) => indices
56 .clone()
57 .fill_null(Scalar::from(0).cast(indices.dtype())?)?,
58 };
59 let indices_nulls_zeroed = indices_nulls_zeroed.execute::<PrimitiveArray>(ctx)?;
60 let buffer = match_each_integer_ptype!(indices_nulls_zeroed.ptype(), |I| {
61 take_valid_indices(
62 array.bit_buffer_view(),
63 indices_nulls_zeroed.as_slice::<I>(),
64 )
65 });
66
67 Ok(Some(
68 BoolArray::new(buffer, array.validity()?.take(indices)?).into_array(),
69 ))
70 }
71}
72
73fn take_contiguous_ranges(
74 array: ArrayView<'_, Bool>,
75 indices: ArrayView<'_, PiecewiseSequence>,
76 indices_ref: &ArrayRef,
77 ctx: &mut ExecutionCtx,
78) -> VortexResult<Option<ArrayRef>> {
79 let Some((starts, lengths)) = maybe_contiguous_slices(indices, ctx)? else {
80 return Ok(None);
81 };
82 let source = array.to_bit_buffer();
83 let output_len = indices_ref.len();
84 let buffer = match lengths {
85 Columnar::Constant(lengths) => {
86 let length = constant_unsigned_usize(&lengths);
87 match_each_unsigned_integer_ptype!(starts.ptype(), |S| {
88 take_bit_slices_constant_length(
89 &source,
90 starts.as_slice::<S>(),
91 length,
92 output_len,
93 )?
94 })
95 }
96 Columnar::Canonical(lengths) => {
97 let lengths = lengths.into_primitive();
98 match_each_unsigned_integer_ptype!(starts.ptype(), |S| {
99 match_each_unsigned_integer_ptype!(lengths.ptype(), |L| {
100 take_bit_slices(
101 &source,
102 starts.as_slice::<S>(),
103 lengths.as_slice::<L>(),
104 output_len,
105 )?
106 })
107 })
108 }
109 };
110
111 Ok(Some(
112 BoolArray::new(buffer, array.validity()?.take(indices_ref)?).into_array(),
113 ))
114}
115
116fn take_valid_indices<I: AsPrimitive<usize>>(bools: BitBufferView<'_>, indices: &[I]) -> BitBuffer {
117 if bools.len() <= 4096 {
120 let bools = bools.iter().collect_vec();
121 take_byte_bool(bools, indices)
122 } else {
123 take_bool_impl(bools, indices)
124 }
125}
126
127fn take_byte_bool<I: AsPrimitive<usize>>(bools: Vec<bool>, indices: &[I]) -> BitBuffer {
128 BitBuffer::collect_bool(indices.len(), |idx| {
129 bools[unsafe { indices.get_unchecked(idx).as_() }]
130 })
131}
132
133fn take_bool_impl<I: AsPrimitive<usize>>(bools: BitBufferView<'_>, indices: &[I]) -> BitBuffer {
134 let buffer = bools.inner();
136 BitBuffer::collect_bool(indices.len(), |idx| {
137 let idx = unsafe { indices.get_unchecked(idx).as_() };
139 get_bit(buffer, bools.offset() + idx)
140 })
141}
142
143fn take_bit_slices_constant_length<S>(
144 source: &BitBuffer,
145 starts: &[S],
146 length: usize,
147 output_len: usize,
148) -> VortexResult<BitBuffer>
149where
150 S: UnsignedPType,
151{
152 let computed_len = starts
153 .len()
154 .checked_mul(length)
155 .ok_or_else(|| vortex_err!("PiecewiseSequenceArray output length overflows usize"))?;
156 vortex_ensure!(
157 computed_len == output_len,
158 "PiecewiseSequenceArray expanded length {computed_len} does not match declared length {output_len}"
159 );
160
161 let mut values = BitBufferMut::with_capacity(output_len);
162 for start in starts {
163 let start = start.as_();
164 values.append_buffer(&source.slice(start..).slice(..length));
165 }
166
167 Ok(values.freeze())
168}
169
170fn take_bit_slices<S, L>(
171 source: &BitBuffer,
172 starts: &[S],
173 lengths: &[L],
174 output_len: usize,
175) -> VortexResult<BitBuffer>
176where
177 S: UnsignedPType,
178 L: UnsignedPType,
179{
180 let mut values = BitBufferMut::with_capacity(output_len);
181 for (&start, &length) in starts.iter().zip_eq(lengths) {
182 let start = start.as_();
183 let length = length.as_();
184 values.append_buffer(&source.slice(start..).slice(..length));
185 }
186
187 vortex_ensure!(
188 values.len() == output_len,
189 "PiecewiseSequenceArray expanded length {} does not match declared length {output_len}",
190 values.len()
191 );
192 Ok(values.freeze())
193}
194
195#[cfg(test)]
196mod test {
197 use rstest::rstest;
198 use vortex_buffer::buffer;
199
200 use crate::IntoArray as _;
201 use crate::VortexSessionExecute;
202 use crate::array_session;
203 use crate::arrays::BoolArray;
204 use crate::arrays::PrimitiveArray;
205 use crate::arrays::bool::BoolArrayExt;
206 use crate::assert_arrays_eq;
207 use crate::compute::conformance::take::test_take_conformance;
208 use crate::validity::Validity;
209
210 #[test]
211 fn take_nullable() {
212 let mut ctx = array_session().create_execution_ctx();
213 let reference = BoolArray::from_iter(vec![
214 Some(false),
215 Some(true),
216 Some(false),
217 None,
218 Some(false),
219 ]);
220
221 let b = reference
222 .take(buffer![0, 3, 4].into_array())
223 .unwrap()
224 .execute::<BoolArray>(&mut ctx)
225 .unwrap();
226 assert_eq!(
227 b.to_bit_buffer(),
228 BoolArray::from_iter([Some(false), None, Some(false)]).to_bit_buffer()
229 );
230
231 let all_invalid_indices = PrimitiveArray::from_option_iter([None::<i32>, None, None]);
232 let b = reference.take(all_invalid_indices.into_array()).unwrap();
233 assert_arrays_eq!(b, BoolArray::from_iter([None, None, None]), &mut ctx);
234 }
235
236 #[test]
237 fn test_bool_array_take_with_null_out_of_bounds_indices() {
238 let mut ctx = array_session().create_execution_ctx();
239 let values = BoolArray::from_iter(vec![Some(false), Some(true), None, None, Some(false)]);
240 let indices = PrimitiveArray::new(
241 buffer![0, 3, 100],
242 Validity::Array(BoolArray::from_iter([true, true, false]).into_array()),
243 );
244 let actual = values.take(indices.into_array()).unwrap();
245
246 assert_arrays_eq!(
248 actual,
249 BoolArray::from_iter([Some(false), None, None]),
250 &mut ctx
251 );
252 }
253
254 #[test]
255 fn test_non_null_bool_array_take_with_null_out_of_bounds_indices() {
256 let mut ctx = array_session().create_execution_ctx();
257 let values = BoolArray::from_iter(vec![false, true, false, true, false]);
258 let indices = PrimitiveArray::new(
259 buffer![0, 3, 100],
260 Validity::Array(BoolArray::from_iter([true, true, false]).into_array()),
261 );
262 let actual = values.take(indices.into_array()).unwrap();
263 assert_arrays_eq!(
265 actual,
266 BoolArray::from_iter([Some(false), Some(true), None]),
267 &mut ctx
268 );
269 }
270
271 #[test]
272 fn test_bool_array_take_all_null_indices() {
273 let mut ctx = array_session().create_execution_ctx();
274 let values = BoolArray::from_iter(vec![Some(false), Some(true), None, None, Some(false)]);
275 let indices = PrimitiveArray::new(
276 buffer![0, 3, 100],
277 Validity::Array(BoolArray::from_iter([false, false, false]).into_array()),
278 );
279 let actual = values.take(indices.into_array()).unwrap();
280 assert_arrays_eq!(actual, BoolArray::from_iter([None, None, None]), &mut ctx);
281 }
282
283 #[test]
284 fn test_non_null_bool_array_take_all_null_indices() {
285 let mut ctx = array_session().create_execution_ctx();
286 let values = BoolArray::from_iter(vec![false, true, false, true, false]);
287 let indices = PrimitiveArray::new(
288 buffer![0, 3, 100],
289 Validity::Array(BoolArray::from_iter([false, false, false]).into_array()),
290 );
291 let actual = values.take(indices.into_array()).unwrap();
292 assert_arrays_eq!(actual, BoolArray::from_iter([None, None, None]), &mut ctx);
293 }
294
295 #[rstest]
296 #[case(BoolArray::from_iter([true, false, true, true, false]))]
297 #[case(BoolArray::from_iter([Some(true), None, Some(false), Some(true), None]))]
298 #[case(BoolArray::from_iter([true, false]))]
299 #[case(BoolArray::from_iter([true]))]
300 fn test_take_bool_conformance(#[case] array: BoolArray) {
301 test_take_conformance(
302 &array.into_array(),
303 &mut array_session().create_execution_ctx(),
304 );
305 }
306}