1use itertools::Itertools;
10use vortex_array::ArrayRef;
11use vortex_array::ExecutionCtx;
12use vortex_array::IntoArray;
13use vortex_array::arrays::BoolArray;
14use vortex_array::arrays::ConstantArray;
15use vortex_array::arrays::PrimitiveArray;
16use vortex_array::arrays::bool::BoolArrayExt;
17use vortex_array::dtype::DType;
18use vortex_array::dtype::Nullability;
19use vortex_array::match_each_unsigned_integer_ptype;
20use vortex_array::scalar::Scalar;
21use vortex_array::validity::Validity;
22use vortex_buffer::BitBuffer;
23use vortex_buffer::BitBufferMut;
24use vortex_error::VortexResult;
25use vortex_mask::Mask;
26
27use crate::iter::trimmed_ends_iter;
28
29const PREFILL_RUN_THRESHOLD: usize = 32;
32
33pub fn runend_decode_bools(
35 ends: PrimitiveArray,
36 values: BoolArray,
37 offset: usize,
38 length: usize,
39 ctx: &mut ExecutionCtx,
40) -> VortexResult<ArrayRef> {
41 let validity = values
42 .as_ref()
43 .validity()?
44 .execute_mask(values.as_ref().len(), ctx)?;
45 let values_buf = values.to_bit_buffer();
46 let nullability = values.dtype().nullability();
47
48 let num_runs = values_buf.len();
50 if offset == 0 && num_runs < PREFILL_RUN_THRESHOLD {
51 return Ok(match_each_unsigned_integer_ptype!(ends.ptype(), |E| {
52 decode_few_runs_no_offset(
53 ends.as_slice::<E>(),
54 &values_buf,
55 validity,
56 nullability,
57 length,
58 )
59 }));
60 }
61
62 Ok(match_each_unsigned_integer_ptype!(ends.ptype(), |E| {
63 runend_decode_typed_bool(
64 trimmed_ends_iter(ends.as_slice::<E>(), offset, length),
65 &values_buf,
66 validity,
67 nullability,
68 length,
69 )
70 }))
71}
72
73pub fn runend_decode_typed_bool(
81 run_ends: impl Iterator<Item = usize>,
82 values: &BitBuffer,
83 values_validity: Mask,
84 values_nullability: Nullability,
85 length: usize,
86) -> ArrayRef {
87 match values_validity {
88 Mask::AllTrue(_) => {
89 decode_bool_non_nullable(run_ends, values, values_nullability, length).into_array()
90 }
91 Mask::AllFalse(_) => {
92 ConstantArray::new(Scalar::null(DType::Bool(Nullability::Nullable)), length)
93 .into_array()
94 }
95 Mask::Values(mask) => {
96 decode_bool_nullable(run_ends, values, mask.bit_buffer(), length).into_array()
97 }
98 }
99}
100
101#[allow(clippy::inline_always)]
105#[inline(always)]
106fn decode_few_runs_no_offset<E: vortex_array::dtype::IntegerPType>(
107 ends: &[E],
108 values: &BitBuffer,
109 validity: Mask,
110 nullability: Nullability,
111 length: usize,
112) -> ArrayRef {
113 match validity {
114 Mask::AllTrue(_) => {
115 let mut decoded = BitBufferMut::with_capacity(length);
116 let mut prev_end = 0usize;
117 for (i, &end) in ends.iter().enumerate() {
118 let end = end.as_().min(length);
119 decoded.append_n(values.value(i), end - prev_end);
120 prev_end = end;
121 }
122 BoolArray::new(decoded.freeze(), nullability.into()).into_array()
123 }
124 Mask::AllFalse(_) => {
125 ConstantArray::new(Scalar::null(DType::Bool(Nullability::Nullable)), length)
126 .into_array()
127 }
128 Mask::Values(mask) => {
129 let validity_buf = mask.bit_buffer();
130 let mut decoded = BitBufferMut::with_capacity(length);
131 let mut decoded_validity = BitBufferMut::with_capacity(length);
132 let mut prev_end = 0usize;
133 for (i, &end) in ends.iter().enumerate() {
134 let end = end.as_().min(length);
135 let run_len = end - prev_end;
136 let is_valid = validity_buf.value(i);
137 if is_valid {
138 decoded_validity.append_n(true, run_len);
139 decoded.append_n(values.value(i), run_len);
140 } else {
141 decoded_validity.append_n(false, run_len);
142 decoded.append_n(false, run_len);
143 }
144 prev_end = end;
145 }
146 BoolArray::new(decoded.freeze(), Validity::from(decoded_validity.freeze())).into_array()
147 }
148 }
149}
150
151fn decode_bool_non_nullable(
153 run_ends: impl Iterator<Item = usize>,
154 values: &BitBuffer,
155 nullability: Nullability,
156 length: usize,
157) -> BoolArray {
158 let num_runs = values.len();
159
160 if num_runs < PREFILL_RUN_THRESHOLD {
162 let mut decoded = BitBufferMut::with_capacity(length);
163 for (end, value) in run_ends.zip(values.iter()) {
164 decoded.append_n(value, end - decoded.len());
165 }
166 return BoolArray::new(decoded.freeze(), nullability.into());
167 }
168
169 let prefill = values.true_count() > num_runs - values.true_count();
171 let mut decoded = BitBufferMut::full(prefill, length);
172 let mut current_pos = 0usize;
173
174 for (end, value) in run_ends.zip_eq(values.iter()) {
175 if end > current_pos && value != prefill {
176 unsafe { decoded.fill_range_unchecked(current_pos, end, value) };
178 }
179 current_pos = end;
180 }
181 BoolArray::new(decoded.freeze(), nullability.into())
182}
183
184fn decode_bool_nullable(
186 run_ends: impl Iterator<Item = usize>,
187 values: &BitBuffer,
188 validity_mask: &BitBuffer,
189 length: usize,
190) -> BoolArray {
191 let num_runs = values.len();
192
193 if num_runs < PREFILL_RUN_THRESHOLD {
195 return decode_nullable_sequential(run_ends, values, validity_mask, length);
196 }
197
198 let prefill_decoded = values.true_count() > num_runs - values.true_count();
200 let prefill_valid = validity_mask.true_count() > num_runs - validity_mask.true_count();
201
202 let mut decoded = BitBufferMut::full(prefill_decoded, length);
203 let mut decoded_validity = BitBufferMut::full(prefill_valid, length);
204 let mut current_pos = 0usize;
205
206 for (end, (value, is_valid)) in run_ends.zip_eq(values.iter().zip(validity_mask.iter())) {
207 if end > current_pos {
208 if is_valid != prefill_valid {
210 unsafe { decoded_validity.fill_range_unchecked(current_pos, end, is_valid) };
211 }
212 let want_decoded = is_valid && value;
214 if want_decoded != prefill_decoded {
215 unsafe { decoded.fill_range_unchecked(current_pos, end, want_decoded) };
216 }
217 current_pos = end;
218 }
219 }
220 BoolArray::new(decoded.freeze(), Validity::from(decoded_validity.freeze()))
221}
222
223#[allow(clippy::inline_always)]
225#[inline(always)]
226fn decode_nullable_sequential(
227 run_ends: impl Iterator<Item = usize>,
228 values: &BitBuffer,
229 validity_mask: &BitBuffer,
230 length: usize,
231) -> BoolArray {
232 let mut decoded = BitBufferMut::with_capacity(length);
233 let mut decoded_validity = BitBufferMut::with_capacity(length);
234
235 for (end, (value, is_valid)) in run_ends.zip(values.iter().zip(validity_mask.iter())) {
236 let run_len = end - decoded.len();
237 if is_valid {
238 decoded_validity.append_n(true, run_len);
239 decoded.append_n(value, run_len);
240 } else {
241 decoded_validity.append_n(false, run_len);
242 decoded.append_n(false, run_len);
243 }
244 }
245
246 BoolArray::new(decoded.freeze(), Validity::from(decoded_validity.freeze()))
247}
248
249#[cfg(test)]
250mod tests {
251 use std::sync::LazyLock;
252
253 use vortex_array::VortexSessionExecute;
254 use vortex_array::arrays::BoolArray;
255 use vortex_array::arrays::PrimitiveArray;
256 use vortex_array::arrays::bool::BoolArrayExt;
257 use vortex_array::assert_arrays_eq;
258 use vortex_array::validity::Validity;
259 use vortex_buffer::BitBuffer;
260 use vortex_error::VortexResult;
261 use vortex_session::VortexSession;
262
263 use super::runend_decode_bools;
264
265 static SESSION: LazyLock<VortexSession> = LazyLock::new(|| {
266 let session = vortex_array::array_session();
267 crate::initialize(&session);
268 session
269 });
270
271 #[test]
272 fn decode_bools_alternating() -> VortexResult<()> {
273 let mut ctx = SESSION.create_execution_ctx();
274 let ends = PrimitiveArray::from_iter([2u32, 5, 10]);
276 let values = BoolArray::from(BitBuffer::from(vec![true, false, true]));
277 let decoded = runend_decode_bools(ends, values, 0, 10, &mut ctx)?;
278
279 let expected = BoolArray::from(BitBuffer::from(vec![
280 true, true, false, false, false, true, true, true, true, true,
281 ]));
282 assert_arrays_eq!(decoded, expected, &mut ctx);
283 Ok(())
284 }
285
286 #[test]
287 fn decode_bools_mostly_true() -> VortexResult<()> {
288 let mut ctx = SESSION.create_execution_ctx();
289 let ends = PrimitiveArray::from_iter([5u32, 6, 10]);
291 let values = BoolArray::from(BitBuffer::from(vec![true, false, true]));
292 let decoded = runend_decode_bools(ends, values, 0, 10, &mut ctx)?;
293
294 let expected = BoolArray::from(BitBuffer::from(vec![
295 true, true, true, true, true, false, true, true, true, true,
296 ]));
297 assert_arrays_eq!(decoded, expected, &mut ctx);
298 Ok(())
299 }
300
301 #[test]
302 fn decode_bools_mostly_false() -> VortexResult<()> {
303 let mut ctx = SESSION.create_execution_ctx();
304 let ends = PrimitiveArray::from_iter([5u32, 6, 10]);
306 let values = BoolArray::from(BitBuffer::from(vec![false, true, false]));
307 let decoded = runend_decode_bools(ends, values, 0, 10, &mut ctx)?;
308
309 let expected = BoolArray::from(BitBuffer::from(vec![
310 false, false, false, false, false, true, false, false, false, false,
311 ]));
312 assert_arrays_eq!(decoded, expected, &mut ctx);
313 Ok(())
314 }
315
316 #[test]
317 fn decode_bools_all_true_single_run() -> VortexResult<()> {
318 let mut ctx = SESSION.create_execution_ctx();
319 let ends = PrimitiveArray::from_iter([10u32]);
320 let values = BoolArray::from(BitBuffer::from(vec![true]));
321 let decoded = runend_decode_bools(ends, values, 0, 10, &mut ctx)?;
322
323 let expected = BoolArray::from(BitBuffer::from(vec![
324 true, true, true, true, true, true, true, true, true, true,
325 ]));
326 assert_arrays_eq!(decoded, expected, &mut ctx);
327 Ok(())
328 }
329
330 #[test]
331 fn decode_bools_all_false_single_run() -> VortexResult<()> {
332 let mut ctx = SESSION.create_execution_ctx();
333 let ends = PrimitiveArray::from_iter([10u32]);
334 let values = BoolArray::from(BitBuffer::from(vec![false]));
335 let decoded = runend_decode_bools(ends, values, 0, 10, &mut ctx)?;
336
337 let expected = BoolArray::from(BitBuffer::from(vec![
338 false, false, false, false, false, false, false, false, false, false,
339 ]));
340 assert_arrays_eq!(decoded, expected, &mut ctx);
341 Ok(())
342 }
343
344 #[test]
345 fn decode_bools_with_offset() -> VortexResult<()> {
346 let mut ctx = SESSION.create_execution_ctx();
347 let ends = PrimitiveArray::from_iter([2u32, 5, 10]);
349 let values = BoolArray::from(BitBuffer::from(vec![true, false, true]));
350 let decoded = runend_decode_bools(ends, values, 2, 6, &mut ctx)?;
351
352 let expected =
353 BoolArray::from(BitBuffer::from(vec![false, false, false, true, true, true]));
354 assert_arrays_eq!(decoded, expected, &mut ctx);
355 Ok(())
356 }
357
358 #[test]
359 fn decode_bools_nullable() -> VortexResult<()> {
360 use vortex_array::validity::Validity;
361
362 let mut ctx = SESSION.create_execution_ctx();
363 let ends = PrimitiveArray::from_iter([2u32, 5, 10]);
365 let values = BoolArray::new(
366 BitBuffer::from(vec![true, false, true]),
367 Validity::from(BitBuffer::from(vec![true, false, true])),
368 );
369 let decoded = runend_decode_bools(ends, values, 0, 10, &mut ctx)?;
370
371 let expected = BoolArray::new(
373 BitBuffer::from(vec![
374 true, true, false, false, false, true, true, true, true, true,
375 ]),
376 Validity::from(BitBuffer::from(vec![
377 true, true, false, false, false, true, true, true, true, true,
378 ])),
379 );
380 assert_arrays_eq!(decoded, expected, &mut ctx);
381 Ok(())
382 }
383
384 #[test]
385 fn decode_bools_nullable_few_runs() -> VortexResult<()> {
386 let mut ctx = SESSION.create_execution_ctx();
387 let ends = PrimitiveArray::from_iter([2000u32, 4000, 6000, 8000, 10000]);
389 let values = BoolArray::new(
390 BitBuffer::from(vec![true, false, true, false, true]),
391 Validity::from(BitBuffer::from(vec![true, false, true, false, true])),
392 );
393 let decoded = runend_decode_bools(ends, values, 0, 10000, &mut ctx)?
394 .execute::<BoolArray>(&mut ctx)?;
395
396 assert_eq!(decoded.len(), 10000);
398 assert!(
400 decoded
401 .as_ref()
402 .validity()?
403 .execute_mask(decoded.as_ref().len(), &mut ctx)?
404 .value(0)
405 );
406 assert!(decoded.to_bit_buffer().value(0));
407 assert!(
409 !decoded
410 .as_ref()
411 .validity()?
412 .execute_mask(decoded.as_ref().len(), &mut ctx)?
413 .value(2000)
414 );
415 assert!(
417 decoded
418 .as_ref()
419 .validity()?
420 .execute_mask(decoded.as_ref().len(), &mut ctx)?
421 .value(4000)
422 );
423 assert!(decoded.to_bit_buffer().value(4000));
424 Ok(())
425 }
426}