1use std::mem::MaybeUninit;
5use std::ops::BitAnd;
6use std::ops::BitOr;
7use std::ops::Not;
8
9use vortex_buffer::Buffer;
10use vortex_buffer::BufferMut;
11use vortex_error::VortexExpect;
12use vortex_error::VortexResult;
13use vortex_mask::Mask;
14
15use crate::ArrayRef;
16use crate::ExecutionCtx;
17use crate::IntoArray;
18use crate::array::ArrayView;
19use crate::arrays::Chunked;
20use crate::arrays::ChunkedArray;
21use crate::arrays::ListView;
22use crate::arrays::ListViewArray;
23use crate::arrays::chunked::ChunkedArrayExt;
24use crate::arrays::listview::ListViewArraySlotsExt;
25use crate::builtins::ArrayBuiltins;
26use crate::dtype::DType;
27use crate::dtype::Nullability;
28use crate::dtype::PType;
29use crate::scalar_fn::fns::zip::ZipKernel;
30use crate::validity::Validity;
31
32impl ZipKernel for ListView {
42 fn zip(
43 if_true: ArrayView<'_, ListView>,
44 if_false: &ArrayRef,
45 mask: &ArrayRef,
46 ctx: &mut ExecutionCtx,
47 ) -> VortexResult<Option<ArrayRef>> {
48 let Some(if_false) = if_false.as_opt::<ListView>() else {
49 return Ok(None);
50 };
51
52 let mask = mask.clone().fill_null(false)?.execute::<Mask>(ctx)?;
54 match &mask {
55 Mask::AllTrue(_) | Mask::AllFalse(_) => return Ok(None),
57 Mask::Values(_) => {}
58 }
59
60 let len = if_true.len();
61
62 let result_elements_dtype = if_true
63 .elements()
64 .dtype()
65 .union_nullability(if_false.elements().dtype().nullability());
66
67 let true_elements = if_true.elements().cast(result_elements_dtype.clone())?;
70 let false_elements = if_false.elements().cast(result_elements_dtype.clone())?;
71
72 let false_shift = true_elements.len() as u64;
74
75 let mut chunks = Vec::with_capacity(2);
79 push_element_chunks(true_elements, &mut chunks);
80 push_element_chunks(false_elements, &mut chunks);
81 let elements = ChunkedArray::try_new(chunks, result_elements_dtype)?.into_array();
82
83 let true_offsets = to_u64(if_true.offsets(), ctx)?;
84 let true_sizes = to_u64(if_true.sizes(), ctx)?;
85 let false_offsets = to_u64(if_false.offsets(), ctx)?;
86 let false_sizes = to_u64(if_false.sizes(), ctx)?;
87
88 let mut offsets = BufferMut::<u64>::with_capacity(len);
89 let mut sizes = BufferMut::<u64>::with_capacity(len);
90 {
91 let true_offsets = true_offsets.as_slice();
92 let true_sizes = true_sizes.as_slice();
93 let false_offsets = false_offsets.as_slice();
94 let false_sizes = false_sizes.as_slice();
95
96 let offsets_out = offsets.spare_capacity_mut();
97 let sizes_out = sizes.spare_capacity_mut();
98
99 let mask_bits = mask
105 .values()
106 .vortex_expect("mask is Mask::Values")
107 .bit_buffer();
108 let unaligned = mask_bits.unaligned_chunks();
109 let lead = unaligned.lead_padding();
112
113 let mut select_block = |word: u64, base: usize, n: usize| {
114 let end = base + n;
115 select_column(
118 word,
119 &true_offsets[base..end],
120 &false_offsets[base..end],
121 false_shift,
122 &mut offsets_out[base..end],
123 );
124 select_column(
125 word,
126 &true_sizes[base..end],
127 &false_sizes[base..end],
128 0,
129 &mut sizes_out[base..end],
130 );
131 };
132
133 let mut base = 0;
134 if let Some(prefix) = unaligned.prefix() {
135 let n = (64 - lead).min(len);
136 select_block(prefix >> lead, base, n);
137 base += n;
138 }
139 for &word in unaligned.chunks() {
140 select_block(word, base, 64);
141 base += 64;
142 }
143 if let Some(suffix) = unaligned.suffix() {
144 select_block(suffix, base, len - base);
145 }
146 }
147
148 unsafe {
150 offsets.set_len(len);
151 sizes.set_len(len);
152 }
153
154 let validity = zip_validity(if_true.validity()?, if_false.validity()?, &mask, ctx)?;
155
156 Ok(Some(
157 ListViewArray::try_new(
158 elements,
159 offsets.freeze().into_array(),
160 sizes.freeze().into_array(),
161 validity,
162 ctx,
163 )?
164 .into_array(),
165 ))
166 }
167}
168
169#[inline]
176fn select_column(
177 word: u64,
178 true_vals: &[u64],
179 false_vals: &[u64],
180 false_add: u64,
181 out: &mut [MaybeUninit<u64>],
182) {
183 let n = out.len();
184 let true_vals = &true_vals[..n];
185 let false_vals = &false_vals[..n];
186 for j in 0..n {
187 let lane = 0u64.wrapping_sub((word >> j) & 1);
189 out[j].write((true_vals[j] & lane) | ((false_vals[j] + false_add) & !lane));
190 }
191}
192
193fn push_element_chunks(array: ArrayRef, chunks: &mut Vec<ArrayRef>) {
196 match array.as_opt::<Chunked>() {
197 Some(chunked) => chunks.extend(chunked.iter_chunks().cloned()),
198 None => chunks.push(array),
199 }
200}
201
202fn to_u64(array: &ArrayRef, ctx: &mut ExecutionCtx) -> VortexResult<Buffer<u64>> {
204 array
205 .clone()
206 .cast(DType::Primitive(PType::U64, Nullability::NonNullable))?
207 .execute::<Buffer<u64>>(ctx)
208}
209
210fn zip_validity(
213 if_true: Validity,
214 if_false: Validity,
215 mask: &Mask,
216 ctx: &mut ExecutionCtx,
217) -> VortexResult<Validity> {
218 Ok(match (&if_true, &if_false) {
219 (Validity::NonNullable, Validity::NonNullable) => Validity::NonNullable,
220 (Validity::AllValid, Validity::AllValid) => Validity::AllValid,
221 (Validity::AllInvalid, Validity::AllInvalid) => Validity::AllInvalid,
222 _ => {
223 let true_mask = if_true.execute_mask(mask.len(), ctx)?;
224 let false_mask = if_false.execute_mask(mask.len(), ctx)?;
225 let combined = true_mask
226 .bitand(mask)
227 .bitor(&false_mask.bitand(&mask.not()));
228 Validity::from_mask(combined, if_true.nullability() | if_false.nullability())
229 }
230 })
231}
232
233#[cfg(test)]
234mod tests {
235 #![allow(
236 clippy::cast_possible_truncation,
237 reason = "test fixtures use small indices that fit the target widths"
238 )]
239
240 use vortex_buffer::Buffer;
241 use vortex_buffer::buffer;
242 use vortex_error::VortexResult;
243 use vortex_mask::Mask;
244
245 use crate::ArrayRef;
246 use crate::IntoArray;
247 use crate::VortexSessionExecute;
248 use crate::array_session;
249 use crate::arrays::BoolArray;
250 use crate::arrays::Chunked;
251 use crate::arrays::ChunkedArray;
252 use crate::arrays::ListView;
253 use crate::arrays::ListViewArray;
254 use crate::arrays::chunked::ChunkedArrayExt;
255 use crate::arrays::listview::ListViewArraySlotsExt;
256 use crate::assert_arrays_eq;
257 use crate::builtins::ArrayBuiltins;
258 use crate::dtype::DType;
259 use crate::dtype::Nullability;
260 use crate::dtype::PType;
261 use crate::validity::Validity;
262
263 fn list_view(
264 elements: ArrayRef,
265 offsets: ArrayRef,
266 sizes: ArrayRef,
267 validity: Validity,
268 ) -> ArrayRef {
269 let mut ctx = array_session().create_execution_ctx();
270 ListViewArray::try_new(elements, offsets, sizes, validity, &mut ctx)
271 .unwrap()
272 .into_array()
273 }
274
275 #[test]
277 fn zip_selects_lists() -> VortexResult<()> {
278 let mut ctx = array_session().create_execution_ctx();
279 let if_true = list_view(
281 buffer![1i32, 2, 3, 4, 5, 6].into_array(),
282 buffer![0u32, 2, 3].into_array(),
283 buffer![2u32, 1, 3].into_array(),
284 Validity::NonNullable,
285 );
286 let if_false = list_view(
288 buffer![10i32, 20, 21, 30].into_array(),
289 buffer![0u32, 1, 3].into_array(),
290 buffer![1u32, 2, 1].into_array(),
291 Validity::NonNullable,
292 );
293 let mask = Mask::from_iter([true, false, true]);
294
295 let result = mask
296 .into_array()
297 .zip(if_true, if_false)?
298 .execute::<ArrayRef>(&mut ctx)?;
299
300 assert!(result.is::<ListView>());
302
303 let expected = list_view(
305 buffer![1i32, 2, 20, 21, 4, 5, 6].into_array(),
306 buffer![0u32, 2, 4].into_array(),
307 buffer![2u32, 2, 3].into_array(),
308 Validity::NonNullable,
309 );
310 assert_arrays_eq!(result, expected, &mut ctx);
311 Ok(())
312 }
313
314 #[test]
316 fn zip_selects_validity() -> VortexResult<()> {
317 let mut ctx = array_session().create_execution_ctx();
318 let if_true = list_view(
320 buffer![1i32, 2].into_array(),
321 buffer![0u32, 1, 1].into_array(),
322 buffer![1u32, 0, 1].into_array(),
323 Validity::Array(BoolArray::from_iter([true, false, true]).into_array()),
324 );
325 let if_false = list_view(
327 buffer![10i32, 20].into_array(),
328 buffer![0u32, 1, 2].into_array(),
329 buffer![1u32, 1, 0].into_array(),
330 Validity::Array(BoolArray::from_iter([true, true, false]).into_array()),
331 );
332 let mask = Mask::from_iter([false, true, true]);
334
335 let result = mask.into_array().zip(if_true, if_false)?;
336
337 let expected = list_view(
339 buffer![10i32, 2].into_array(),
340 buffer![0u32, 1, 1].into_array(),
341 buffer![1u32, 0, 1].into_array(),
342 Validity::Array(BoolArray::from_iter([true, false, true]).into_array()),
343 );
344 assert_arrays_eq!(result, expected, &mut ctx);
345 Ok(())
346 }
347
348 #[test]
351 fn zip_out_of_order_offsets_and_widening() -> VortexResult<()> {
352 let mut ctx = array_session().create_execution_ctx();
353 let if_true = list_view(
355 buffer![7i32, 8, 9, 5, 6].into_array(),
356 buffer![3u32, 0, 1].into_array(),
357 buffer![2u32, 1, 2].into_array(),
358 Validity::NonNullable,
359 );
360 let if_false = list_view(
362 buffer![100i32, 200, 201].into_array(),
363 buffer![0u32, 1, 1].into_array(),
364 buffer![1u32, 0, 2].into_array(),
365 Validity::Array(BoolArray::from_iter([true, false, true]).into_array()),
366 );
367 let mask = Mask::from_iter([true, true, false]);
368
369 let result = mask
370 .into_array()
371 .zip(if_true, if_false)?
372 .execute::<ArrayRef>(&mut ctx)?;
373 assert!(result.is::<ListView>());
374
375 let expected = list_view(
377 buffer![5i32, 6, 7, 200, 201].into_array(),
378 buffer![0u32, 2, 3].into_array(),
379 buffer![2u32, 1, 2].into_array(),
380 Validity::AllValid,
381 );
382 assert_arrays_eq!(result, expected, &mut ctx);
383 Ok(())
384 }
385
386 #[test]
389 fn zip_spans_multiple_mask_chunks() -> VortexResult<()> {
390 let mut ctx = array_session().create_execution_ctx();
391 let len = 130usize;
393 let true_elements: Vec<i32> = (0..len as i32).collect();
394 let false_elements: Vec<i32> = (0..len as i32).map(|i| 1000 + i).collect();
395 let offsets: Vec<u64> = (0..len as u64).collect();
396 let sizes: Vec<u64> = vec![1; len];
397
398 let if_true = list_view(
399 true_elements
400 .iter()
401 .copied()
402 .collect::<Buffer<i32>>()
403 .into_array(),
404 offsets
405 .iter()
406 .copied()
407 .collect::<Buffer<u64>>()
408 .into_array(),
409 sizes.iter().copied().collect::<Buffer<u64>>().into_array(),
410 Validity::NonNullable,
411 );
412 let if_false = list_view(
413 false_elements
414 .iter()
415 .copied()
416 .collect::<Buffer<i32>>()
417 .into_array(),
418 offsets
419 .iter()
420 .copied()
421 .collect::<Buffer<u64>>()
422 .into_array(),
423 sizes.iter().copied().collect::<Buffer<u64>>().into_array(),
424 Validity::NonNullable,
425 );
426
427 let mask_bits: Vec<bool> = (0..len).map(|i| i.is_multiple_of(3) || i == 64).collect();
429 let mask = Mask::from_iter(mask_bits.iter().copied());
430
431 let result = mask
432 .into_array()
433 .zip(if_true, if_false)?
434 .execute::<ArrayRef>(&mut ctx)?;
435 assert!(result.is::<ListView>());
436
437 let expected_elements: Vec<i32> = (0..len)
439 .map(|i| {
440 if mask_bits[i] {
441 i as i32
442 } else {
443 1000 + i as i32
444 }
445 })
446 .collect();
447 let expected = list_view(
448 expected_elements
449 .iter()
450 .copied()
451 .collect::<Buffer<i32>>()
452 .into_array(),
453 offsets
454 .iter()
455 .copied()
456 .collect::<Buffer<u64>>()
457 .into_array(),
458 sizes.iter().copied().collect::<Buffer<u64>>().into_array(),
459 Validity::NonNullable,
460 );
461 assert_arrays_eq!(result, expected, &mut ctx);
462 Ok(())
463 }
464
465 #[test]
469 fn zip_handles_offset_mask() -> VortexResult<()> {
470 let mut ctx = array_session().create_execution_ctx();
471 let len = 200usize;
475 let true_elements: Vec<i32> = (0..len as i32).collect();
476 let false_elements: Vec<i32> = (0..len as i32).map(|i| 1000 + i).collect();
477 let offsets: Vec<u64> = (0..len as u64).collect();
478 let sizes: Vec<u64> = vec![1; len];
479
480 let single_element_view = |elements: &[i32]| {
481 list_view(
482 elements
483 .iter()
484 .copied()
485 .collect::<Buffer<i32>>()
486 .into_array(),
487 offsets
488 .iter()
489 .copied()
490 .collect::<Buffer<u64>>()
491 .into_array(),
492 sizes.iter().copied().collect::<Buffer<u64>>().into_array(),
493 Validity::NonNullable,
494 )
495 };
496 let if_true = single_element_view(&true_elements);
497 let if_false = single_element_view(&false_elements);
498
499 let offset = 3usize;
503 let mask_bits: Vec<bool> = (0..offset + len)
504 .map(|i| i.is_multiple_of(3) || i == offset + 64)
505 .collect();
506 let mask = BoolArray::from_iter(mask_bits.iter().copied())
507 .into_array()
508 .slice(offset..offset + len)?;
509
510 let result = mask.zip(if_true, if_false)?.execute::<ArrayRef>(&mut ctx)?;
511 assert!(result.is::<ListView>());
512
513 let expected_elements: Vec<i32> = (0..len)
515 .map(|i| {
516 if mask_bits[offset + i] {
517 i as i32
518 } else {
519 1000 + i as i32
520 }
521 })
522 .collect();
523 let expected = single_element_view(&expected_elements);
524 assert_arrays_eq!(result, expected, &mut ctx);
525 Ok(())
526 }
527
528 #[test]
531 fn zip_flattens_chunked_elements() -> VortexResult<()> {
532 let mut ctx = array_session().create_execution_ctx();
533 let chunked_elements = ChunkedArray::try_new(
535 vec![buffer![1i32, 2].into_array(), buffer![3i32].into_array()],
536 DType::Primitive(PType::I32, Nullability::NonNullable),
537 )?
538 .into_array();
539 let if_true = list_view(
540 chunked_elements,
541 buffer![0u32, 2].into_array(),
542 buffer![2u32, 1].into_array(),
543 Validity::NonNullable,
544 );
545 let if_false = list_view(
547 buffer![10i32, 20].into_array(),
548 buffer![0u32, 1].into_array(),
549 buffer![1u32, 1].into_array(),
550 Validity::NonNullable,
551 );
552 let mask = Mask::from_iter([true, false]);
553
554 let result = mask
555 .into_array()
556 .zip(if_true, if_false)?
557 .execute::<ArrayRef>(&mut ctx)?;
558
559 let result_lv = result
561 .as_opt::<ListView>()
562 .expect("zip keeps the list-view encoding");
563 let chunked = result_lv
564 .elements()
565 .as_opt::<Chunked>()
566 .expect("zip concatenates elements into a chunked array");
567 assert!(
568 chunked.iter_chunks().all(|chunk| !chunk.is::<Chunked>()),
569 "chunked elements must be flattened, not nested",
570 );
571
572 let expected = list_view(
574 buffer![1i32, 2, 20].into_array(),
575 buffer![0u32, 2].into_array(),
576 buffer![2u32, 1].into_array(),
577 Validity::NonNullable,
578 );
579 assert_arrays_eq!(result, expected, &mut ctx);
580 Ok(())
581 }
582}