1use itertools::Itertools as _;
5use vortex_buffer::BufferMut;
6use vortex_error::VortexExpect;
7use vortex_error::VortexResult;
8use vortex_error::vortex_ensure_eq;
9use vortex_error::vortex_err;
10use vortex_mask::Mask;
11
12use crate::ArrayRef;
13use crate::Columnar;
14use crate::IntoArray;
15use crate::array::ArrayView;
16use crate::arrays::ConstantArray;
17use crate::arrays::List;
18use crate::arrays::ListArray;
19use crate::arrays::PiecewiseSequence;
20use crate::arrays::PiecewiseSequenceArray;
21use crate::arrays::Primitive;
22use crate::arrays::PrimitiveArray;
23use crate::arrays::dict::TakeExecute;
24use crate::arrays::list::ListArrayExt;
25use crate::arrays::list::ListArraySlotsExt;
26use crate::arrays::piecewise_sequence::constant_unsigned_usize;
27use crate::arrays::piecewise_sequence::maybe_contiguous_slices;
28use crate::arrays::primitive::PrimitiveArrayExt;
29use crate::dtype::IntegerPType;
30use crate::dtype::UnsignedPType;
31use crate::executor::ExecutionCtx;
32use crate::match_each_unsigned_integer_ptype;
33use crate::match_smallest_offset_type;
34use crate::validity::Validity;
35
36impl TakeExecute for List {
40 #[expect(clippy::cognitive_complexity)]
46 fn take(
47 array: ArrayView<'_, List>,
48 indices: &ArrayRef,
49 ctx: &mut ExecutionCtx,
50 ) -> VortexResult<Option<ArrayRef>> {
51 if let Some(piecewise_indices) = indices.as_opt::<PiecewiseSequence>()
52 && let Some(taken) = take_slices(array, piecewise_indices, indices, ctx)?
53 {
54 return Ok(Some(taken));
55 }
56
57 let new_validity = array.validity()?.take(indices)?;
58 let indices = indices.clone().execute::<PrimitiveArray>(ctx)?;
59 let indices = indices.reinterpret_cast(indices.ptype().to_unsigned());
60 let offsets = array.offsets().clone().execute::<PrimitiveArray>(ctx)?;
61 let offsets = offsets.reinterpret_cast(offsets.ptype().to_unsigned());
62 let validity_mask = new_validity.execute_mask(indices.len(), ctx)?;
63 let total_approx = array.elements().len().saturating_mul(indices.len());
65
66 match_each_unsigned_integer_ptype!(offsets.ptype(), |O| {
67 match_each_unsigned_integer_ptype!(indices.ptype(), |I| {
68 match_smallest_offset_type!(total_approx, |OutOffset| {
69 take_with_piecewise_elements::<I, O, OutOffset>(
70 array,
71 offsets.as_view(),
72 indices.as_view(),
73 new_validity,
74 &validity_mask,
75 )
76 .map(Some)
77 })
78 })
79 })
80 }
81}
82
83fn take_with_piecewise_elements<I: IntegerPType, O: IntegerPType, OutOffset: IntegerPType>(
84 array: ArrayView<'_, List>,
85 offsets_array: ArrayView<'_, Primitive>,
86 indices_array: ArrayView<'_, Primitive>,
87 new_validity: Validity,
88 validity_mask: &Mask,
89) -> VortexResult<ArrayRef> {
90 let offsets: &[O] = offsets_array.as_slice();
91 let indices: &[I] = indices_array.as_slice();
92
93 let offsets_capacity = indices
94 .len()
95 .checked_add(1)
96 .ok_or_else(|| vortex_err!("List take offsets length overflow"))?;
97 let mut new_offsets = BufferMut::<OutOffset>::with_capacity(offsets_capacity);
98 let mut element_starts = BufferMut::<u64>::with_capacity(indices.len());
99 let mut element_lengths = BufferMut::<u64>::with_capacity(indices.len());
100
101 let mut current_offset = 0usize;
102 new_offsets.push(OutOffset::zero());
103
104 for (&data_idx, is_valid) in indices.iter().zip_eq(validity_mask.iter()) {
105 if !is_valid {
106 new_offsets.push(new_offset_value::<OutOffset>(current_offset));
107 element_starts.push(0);
108 element_lengths.push(0);
109 continue;
110 }
111
112 let data_idx: usize = data_idx.as_();
113
114 let start = offsets[data_idx];
115 let stop = offsets[data_idx + 1];
116 let start: usize = start.as_();
117 let stop: usize = stop.as_();
118 let length = stop - start;
119
120 current_offset = current_offset
121 .checked_add(length)
122 .ok_or_else(|| vortex_err!("List take output elements length overflow"))?;
123 new_offsets.push(new_offset_value::<OutOffset>(current_offset));
124 element_starts.push(start as u64);
125 element_lengths.push(length as u64);
126 }
127
128 let new_offsets = PrimitiveArray::new(new_offsets.freeze(), Validity::NonNullable).into_array();
129 let multipliers = ConstantArray::new(1u64, element_starts.len()).into_array();
130
131 let element_indices = unsafe {
135 PiecewiseSequenceArray::new_unchecked(
136 element_starts.into_array(),
137 element_lengths.into_array(),
138 multipliers,
139 current_offset,
140 )
141 };
142 let new_elements = array.elements().take(element_indices.into_array())?;
143
144 Ok(unsafe { ListArray::new_unchecked(new_elements, new_offsets, new_validity) }.into_array())
147}
148
149fn take_slices(
150 array: ArrayView<'_, List>,
151 indices: ArrayView<'_, PiecewiseSequence>,
152 indices_ref: &ArrayRef,
153 ctx: &mut ExecutionCtx,
154) -> VortexResult<Option<ArrayRef>> {
155 let Some((starts, lengths)) = maybe_contiguous_slices(indices, ctx)? else {
156 return Ok(None);
157 };
158 let data_validity = array
159 .list_validity()
160 .execute_mask(array.as_ref().len(), ctx)?;
161 let offsets = array.offsets().clone().execute::<PrimitiveArray>(ctx)?;
162 let offsets = offsets.reinterpret_cast(offsets.ptype().to_unsigned());
163 let output_len = indices_ref.len();
164
165 let taken = match lengths {
166 Columnar::Constant(lengths) => {
167 let length = constant_unsigned_usize(&lengths);
168 take_slices_constant_start_dispatch(
169 array,
170 &starts,
171 length,
172 &offsets,
173 indices_ref,
174 output_len,
175 &data_validity,
176 )?
177 }
178 Columnar::Canonical(lengths) => {
179 let lengths = lengths.into_primitive();
180 take_slices_start_dispatch(
181 array,
182 &starts,
183 &lengths,
184 &offsets,
185 indices_ref,
186 output_len,
187 &data_validity,
188 )?
189 }
190 };
191 Ok(Some(taken))
192}
193
194fn take_slices_constant_start_dispatch(
195 array: ArrayView<'_, List>,
196 starts: &PrimitiveArray,
197 length: usize,
198 offsets: &PrimitiveArray,
199 indices_ref: &ArrayRef,
200 output_len: usize,
201 data_validity: &Mask,
202) -> VortexResult<ArrayRef> {
203 match_each_unsigned_integer_ptype!(starts.ptype(), |S| {
204 take_slices_constant_offset_dispatch::<S>(
205 array,
206 starts,
207 length,
208 offsets,
209 indices_ref,
210 output_len,
211 data_validity,
212 )
213 })
214}
215
216fn take_slices_constant_offset_dispatch<S>(
217 array: ArrayView<'_, List>,
218 starts: &PrimitiveArray,
219 length: usize,
220 offsets: &PrimitiveArray,
221 indices_ref: &ArrayRef,
222 output_len: usize,
223 data_validity: &Mask,
224) -> VortexResult<ArrayRef>
225where
226 S: UnsignedPType,
227{
228 match_each_unsigned_integer_ptype!(offsets.ptype(), |O| {
229 take_slices_constant_length::<S, O>(
230 array,
231 starts.as_slice::<S>(),
232 length,
233 offsets.as_slice::<O>(),
234 indices_ref,
235 output_len,
236 data_validity,
237 )
238 })
239}
240
241fn take_slices_start_dispatch(
242 array: ArrayView<'_, List>,
243 starts: &PrimitiveArray,
244 lengths: &PrimitiveArray,
245 offsets: &PrimitiveArray,
246 indices_ref: &ArrayRef,
247 output_len: usize,
248 data_validity: &Mask,
249) -> VortexResult<ArrayRef> {
250 match_each_unsigned_integer_ptype!(starts.ptype(), |S| {
251 take_slices_length_dispatch::<S>(
252 array,
253 starts,
254 lengths,
255 offsets,
256 indices_ref,
257 output_len,
258 data_validity,
259 )
260 })
261}
262
263fn take_slices_length_dispatch<S>(
264 array: ArrayView<'_, List>,
265 starts: &PrimitiveArray,
266 lengths: &PrimitiveArray,
267 offsets: &PrimitiveArray,
268 indices_ref: &ArrayRef,
269 output_len: usize,
270 data_validity: &Mask,
271) -> VortexResult<ArrayRef>
272where
273 S: UnsignedPType,
274{
275 match_each_unsigned_integer_ptype!(lengths.ptype(), |L| {
276 take_slices_offset_dispatch::<S, L>(
277 array,
278 starts,
279 lengths,
280 offsets,
281 indices_ref,
282 output_len,
283 data_validity,
284 )
285 })
286}
287
288fn take_slices_offset_dispatch<S, L>(
289 array: ArrayView<'_, List>,
290 starts: &PrimitiveArray,
291 lengths: &PrimitiveArray,
292 offsets: &PrimitiveArray,
293 indices_ref: &ArrayRef,
294 output_len: usize,
295 data_validity: &Mask,
296) -> VortexResult<ArrayRef>
297where
298 S: UnsignedPType,
299 L: UnsignedPType,
300{
301 match_each_unsigned_integer_ptype!(offsets.ptype(), |O| {
302 take_slices_typed::<S, L, O>(
303 array,
304 starts.as_slice::<S>(),
305 lengths.as_slice::<L>(),
306 offsets.as_slice::<O>(),
307 indices_ref,
308 output_len,
309 data_validity,
310 )
311 })
312}
313
314fn take_slices_constant_length<S, Offset>(
315 array: ArrayView<'_, List>,
316 starts: &[S],
317 length: usize,
318 offsets: &[Offset],
319 indices_ref: &ArrayRef,
320 output_len: usize,
321 data_validity: &Mask,
322) -> VortexResult<ArrayRef>
323where
324 S: UnsignedPType,
325 Offset: UnsignedPType,
326{
327 let computed_len = starts
328 .len()
329 .checked_mul(length)
330 .ok_or_else(|| vortex_err!("PiecewiseSequenceArray output length overflows usize"))?;
331 vortex_ensure_eq!(
332 computed_len,
333 output_len,
334 "PiecewiseSequenceArray expanded length does not match declared length",
335 );
336 let all_valid = data_validity.all_true();
337 let total_elements = if all_valid {
338 piecewise_list_elements_len_constant(offsets, starts, length)?
339 } else {
340 piecewise_list_elements_len_constant_validity(offsets, starts, length, data_validity)?
341 };
342 let validity = array.validity()?.take(indices_ref)?;
343
344 match_smallest_offset_type!(total_elements, |OutOffset| {
345 let gathered = if all_valid {
346 gather_piecewise_list_constant_length::<S, Offset, OutOffset>(
347 array.elements(),
348 offsets,
349 starts,
350 length,
351 output_len,
352 total_elements,
353 )?
354 } else {
355 gather_piecewise_list_constant_length_validity::<S, Offset, OutOffset>(
356 array.elements(),
357 offsets,
358 starts,
359 length,
360 output_len,
361 total_elements,
362 data_validity,
363 )?
364 };
365
366 Ok(
370 unsafe { ListArray::new_unchecked(gathered.elements, gathered.offsets, validity) }
371 .into_array(),
372 )
373 })
374}
375
376fn take_slices_typed<S, L, Offset>(
377 array: ArrayView<'_, List>,
378 starts: &[S],
379 lengths: &[L],
380 offsets: &[Offset],
381 indices_ref: &ArrayRef,
382 output_len: usize,
383 data_validity: &Mask,
384) -> VortexResult<ArrayRef>
385where
386 S: UnsignedPType,
387 L: UnsignedPType,
388 Offset: UnsignedPType,
389{
390 let mut computed_len = 0usize;
391 for &length in lengths {
392 let length: usize = length.as_();
393 computed_len = computed_len
394 .checked_add(length)
395 .ok_or_else(|| vortex_err!("PiecewiseSequenceArray output length overflows usize"))?;
396 }
397 vortex_ensure_eq!(
398 computed_len,
399 output_len,
400 "PiecewiseSequenceArray expanded length does not match declared length",
401 );
402 let all_valid = data_validity.all_true();
403 let total_elements = if all_valid {
404 piecewise_list_elements_len(offsets, starts, lengths)?
405 } else {
406 piecewise_list_elements_len_validity(offsets, starts, lengths, data_validity)?
407 };
408
409 match_smallest_offset_type!(total_elements, |OutOffset| {
410 let gathered = if all_valid {
411 gather_piecewise_list::<S, L, Offset, OutOffset>(
412 array.elements(),
413 offsets,
414 starts,
415 lengths,
416 output_len,
417 total_elements,
418 )?
419 } else {
420 gather_piecewise_list_validity::<S, L, Offset, OutOffset>(
421 array.elements(),
422 offsets,
423 starts,
424 lengths,
425 output_len,
426 total_elements,
427 data_validity,
428 )?
429 };
430 let validity = array.validity()?.take(indices_ref)?;
431
432 Ok(
436 unsafe { ListArray::new_unchecked(gathered.elements, gathered.offsets, validity) }
437 .into_array(),
438 )
439 })
440}
441
442struct GatheredList {
443 elements: ArrayRef,
444 offsets: ArrayRef,
445}
446
447struct ValidPieceGather<OutOffset> {
448 new_offsets: BufferMut<OutOffset>,
449 element_starts: BufferMut<u64>,
450 element_lengths: BufferMut<u64>,
451 output_elements: usize,
452}
453
454fn piecewise_list_elements_len_constant<S, Offset>(
455 offsets: &[Offset],
456 starts: &[S],
457 length: usize,
458) -> VortexResult<usize>
459where
460 S: UnsignedPType,
461 Offset: UnsignedPType,
462{
463 if length == 0 {
464 return Ok(0);
465 }
466
467 let mut total = 0usize;
468 for start in starts {
469 let start: usize = start.as_();
470 let offset_range = &offsets[start..][..=length];
471 let element_start: usize = offset_range[0].as_();
472 let element_end: usize = offset_range[length].as_();
473 total = total
474 .checked_add(element_end - element_start)
475 .ok_or_else(|| vortex_err!("List take output elements length overflow"))?;
476 }
477 Ok(total)
478}
479
480fn piecewise_list_elements_len_constant_validity<S, Offset>(
481 offsets: &[Offset],
482 starts: &[S],
483 length: usize,
484 data_validity: &Mask,
485) -> VortexResult<usize>
486where
487 S: UnsignedPType,
488 Offset: UnsignedPType,
489{
490 if length == 0 {
491 return Ok(0);
492 }
493
494 let mut total = 0usize;
495 for start in starts {
496 let start: usize = start.as_();
497 let additional = valid_piece_elements_len(offsets, data_validity, start, length)?;
498 total = total
499 .checked_add(additional)
500 .ok_or_else(|| vortex_err!("List take output elements length overflow"))?;
501 }
502 Ok(total)
503}
504
505fn piecewise_list_elements_len<S, L, Offset>(
506 offsets: &[Offset],
507 starts: &[S],
508 lengths: &[L],
509) -> VortexResult<usize>
510where
511 S: UnsignedPType,
512 L: UnsignedPType,
513 Offset: UnsignedPType,
514{
515 let mut total = 0usize;
516 for (&start, &length) in starts.iter().zip_eq(lengths) {
517 let start: usize = start.as_();
518 let length: usize = length.as_();
519 let offset_range = &offsets[start..][..=length];
520 let element_start: usize = offset_range[0].as_();
521 let element_end: usize = offset_range[length].as_();
522 total = total
523 .checked_add(element_end - element_start)
524 .ok_or_else(|| vortex_err!("List take output elements length overflow"))?;
525 }
526 Ok(total)
527}
528
529fn piecewise_list_elements_len_validity<S, L, Offset>(
530 offsets: &[Offset],
531 starts: &[S],
532 lengths: &[L],
533 data_validity: &Mask,
534) -> VortexResult<usize>
535where
536 S: UnsignedPType,
537 L: UnsignedPType,
538 Offset: UnsignedPType,
539{
540 let mut total = 0usize;
541 for (&start, &length) in starts.iter().zip_eq(lengths) {
542 let start: usize = start.as_();
543 let length: usize = length.as_();
544 let additional = valid_piece_elements_len(offsets, data_validity, start, length)?;
545 total = total
546 .checked_add(additional)
547 .ok_or_else(|| vortex_err!("List take output elements length overflow"))?;
548 }
549 Ok(total)
550}
551
552fn valid_piece_elements_len<Offset>(
553 offsets: &[Offset],
554 data_validity: &Mask,
555 start: usize,
556 length: usize,
557) -> VortexResult<usize>
558where
559 Offset: UnsignedPType,
560{
561 let offset_range = &offsets[start..][..=length];
562 let mut total = 0usize;
563 for (data_idx, window) in (start..).zip(offset_range.windows(2)) {
564 if !data_validity.value(data_idx) {
565 continue;
566 }
567 let element_start: usize = window[0].as_();
568 let element_end: usize = window[1].as_();
569 total = total
570 .checked_add(element_end - element_start)
571 .ok_or_else(|| vortex_err!("List take output elements length overflow"))?;
572 }
573 Ok(total)
574}
575
576fn gather_piecewise_list_constant_length<S, Offset, OutOffset>(
577 elements: &ArrayRef,
578 offsets: &[Offset],
579 starts: &[S],
580 length: usize,
581 output_len: usize,
582 total_elements: usize,
583) -> VortexResult<GatheredList>
584where
585 S: UnsignedPType,
586 Offset: UnsignedPType,
587 OutOffset: IntegerPType,
588{
589 let offsets_capacity = output_len
590 .checked_add(1)
591 .ok_or_else(|| vortex_err!("List take offsets length overflow"))?;
592 let mut new_offsets = BufferMut::<OutOffset>::with_capacity(offsets_capacity);
593 let mut element_starts = BufferMut::<u64>::with_capacity(starts.len());
594 let mut element_lengths = BufferMut::<u64>::with_capacity(starts.len());
595 let mut output_elements = 0usize;
596
597 new_offsets.push(OutOffset::zero());
598 for start in starts {
599 let start: usize = start.as_();
600 if length == 0 {
601 continue;
602 }
603
604 let offset_range = &offsets[start..][..=length];
605 let element_start: usize = offset_range[0].as_();
606 let element_end: usize = offset_range[length].as_();
607 for &offset in &offset_range[1..] {
608 let offset: usize = offset.as_();
609 let relative = offset - element_start;
610 let output_offset = output_elements + relative;
611 new_offsets.push(new_offset_value::<OutOffset>(output_offset));
612 }
613
614 let element_length = element_end - element_start;
615 element_starts.push(element_start as u64);
616 element_lengths.push(element_length as u64);
617 output_elements += element_length;
618 }
619 debug_assert_eq!(output_elements, total_elements);
620
621 let offsets = PrimitiveArray::new(new_offsets.freeze(), Validity::NonNullable).into_array();
622 let multipliers = ConstantArray::new(1u64, element_starts.len()).into_array();
623 let element_indices = unsafe {
626 PiecewiseSequenceArray::new_unchecked(
627 element_starts.into_array(),
628 element_lengths.into_array(),
629 multipliers,
630 total_elements,
631 )
632 };
633 let elements = elements.take(element_indices.into_array())?;
634
635 Ok(GatheredList { elements, offsets })
636}
637
638fn gather_piecewise_list_constant_length_validity<S, Offset, OutOffset>(
639 elements: &ArrayRef,
640 offsets: &[Offset],
641 starts: &[S],
642 length: usize,
643 output_len: usize,
644 total_elements: usize,
645 data_validity: &Mask,
646) -> VortexResult<GatheredList>
647where
648 S: UnsignedPType,
649 Offset: UnsignedPType,
650 OutOffset: IntegerPType,
651{
652 let offsets_capacity = output_len
653 .checked_add(1)
654 .ok_or_else(|| vortex_err!("List take offsets length overflow"))?;
655 let mut gather = ValidPieceGather {
656 new_offsets: BufferMut::<OutOffset>::with_capacity(offsets_capacity),
657 element_starts: BufferMut::<u64>::with_capacity(output_len),
658 element_lengths: BufferMut::<u64>::with_capacity(output_len),
659 output_elements: 0,
660 };
661
662 gather.new_offsets.push(OutOffset::zero());
663 for start in starts {
664 let start: usize = start.as_();
665 if length == 0 {
666 continue;
667 }
668
669 gather_valid_piece(offsets, data_validity, start, length, &mut gather);
670 }
671 debug_assert_eq!(gather.output_elements, total_elements);
672
673 let offsets =
674 PrimitiveArray::new(gather.new_offsets.freeze(), Validity::NonNullable).into_array();
675 let multipliers = ConstantArray::new(1u64, gather.element_starts.len()).into_array();
676 let element_indices = unsafe {
679 PiecewiseSequenceArray::new_unchecked(
680 gather.element_starts.into_array(),
681 gather.element_lengths.into_array(),
682 multipliers,
683 total_elements,
684 )
685 };
686 let elements = elements.take(element_indices.into_array())?;
687
688 Ok(GatheredList { elements, offsets })
689}
690
691fn gather_piecewise_list<S, L, Offset, OutOffset>(
692 elements: &ArrayRef,
693 offsets: &[Offset],
694 starts: &[S],
695 lengths: &[L],
696 output_len: usize,
697 total_elements: usize,
698) -> VortexResult<GatheredList>
699where
700 S: UnsignedPType,
701 L: UnsignedPType,
702 Offset: UnsignedPType,
703 OutOffset: IntegerPType,
704{
705 let offsets_capacity = output_len
706 .checked_add(1)
707 .ok_or_else(|| vortex_err!("List take offsets length overflow"))?;
708 let mut new_offsets = BufferMut::<OutOffset>::with_capacity(offsets_capacity);
709 let mut element_starts = BufferMut::<u64>::with_capacity(starts.len());
710 let mut element_lengths = BufferMut::<u64>::with_capacity(lengths.len());
711 let mut output_elements = 0usize;
712
713 new_offsets.push(OutOffset::zero());
714 for (&start, &length) in starts.iter().zip_eq(lengths) {
715 let start: usize = start.as_();
716 let length: usize = length.as_();
717 if length == 0 {
718 continue;
719 }
720
721 let offset_range = &offsets[start..][..=length];
722 let element_start: usize = offset_range[0].as_();
723 let element_end: usize = offset_range[length].as_();
724 for &offset in &offset_range[1..] {
725 let offset: usize = offset.as_();
726 let relative = offset - element_start;
727 let output_offset = output_elements + relative;
728 new_offsets.push(new_offset_value::<OutOffset>(output_offset));
729 }
730
731 let element_length = element_end - element_start;
732 element_starts.push(element_start as u64);
733 element_lengths.push(element_length as u64);
734 output_elements += element_length;
735 }
736 debug_assert_eq!(output_elements, total_elements);
737
738 let offsets = PrimitiveArray::new(new_offsets.freeze(), Validity::NonNullable).into_array();
739 let multipliers = ConstantArray::new(1u64, element_starts.len()).into_array();
740 let element_indices = unsafe {
743 PiecewiseSequenceArray::new_unchecked(
744 element_starts.into_array(),
745 element_lengths.into_array(),
746 multipliers,
747 total_elements,
748 )
749 };
750 let elements = elements.take(element_indices.into_array())?;
751
752 Ok(GatheredList { elements, offsets })
753}
754
755fn gather_piecewise_list_validity<S, L, Offset, OutOffset>(
756 elements: &ArrayRef,
757 offsets: &[Offset],
758 starts: &[S],
759 lengths: &[L],
760 output_len: usize,
761 total_elements: usize,
762 data_validity: &Mask,
763) -> VortexResult<GatheredList>
764where
765 S: UnsignedPType,
766 L: UnsignedPType,
767 Offset: UnsignedPType,
768 OutOffset: IntegerPType,
769{
770 let offsets_capacity = output_len
771 .checked_add(1)
772 .ok_or_else(|| vortex_err!("List take offsets length overflow"))?;
773 let mut gather = ValidPieceGather {
774 new_offsets: BufferMut::<OutOffset>::with_capacity(offsets_capacity),
775 element_starts: BufferMut::<u64>::with_capacity(output_len),
776 element_lengths: BufferMut::<u64>::with_capacity(output_len),
777 output_elements: 0,
778 };
779
780 gather.new_offsets.push(OutOffset::zero());
781 for (&start, &length) in starts.iter().zip_eq(lengths) {
782 let start: usize = start.as_();
783 let length: usize = length.as_();
784 if length == 0 {
785 continue;
786 }
787
788 gather_valid_piece(offsets, data_validity, start, length, &mut gather);
789 }
790 debug_assert_eq!(gather.output_elements, total_elements);
791
792 let offsets =
793 PrimitiveArray::new(gather.new_offsets.freeze(), Validity::NonNullable).into_array();
794 let multipliers = ConstantArray::new(1u64, gather.element_starts.len()).into_array();
795 let element_indices = unsafe {
798 PiecewiseSequenceArray::new_unchecked(
799 gather.element_starts.into_array(),
800 gather.element_lengths.into_array(),
801 multipliers,
802 total_elements,
803 )
804 };
805 let elements = elements.take(element_indices.into_array())?;
806
807 Ok(GatheredList { elements, offsets })
808}
809
810fn gather_valid_piece<Offset, OutOffset>(
811 offsets: &[Offset],
812 data_validity: &Mask,
813 start: usize,
814 length: usize,
815 gather: &mut ValidPieceGather<OutOffset>,
816) where
817 Offset: UnsignedPType,
818 OutOffset: IntegerPType,
819{
820 let offset_range = &offsets[start..][..=length];
821 for (data_idx, window) in (start..).zip(offset_range.windows(2)) {
822 if !data_validity.value(data_idx) {
823 gather
824 .new_offsets
825 .push(new_offset_value::<OutOffset>(gather.output_elements));
826 continue;
827 }
828
829 let element_start: usize = window[0].as_();
830 let element_end: usize = window[1].as_();
831 let element_length = element_end - element_start;
832 if element_length != 0 {
833 gather.element_starts.push(element_start as u64);
834 gather.element_lengths.push(element_length as u64);
835 gather.output_elements += element_length;
836 }
837 gather
838 .new_offsets
839 .push(new_offset_value::<OutOffset>(gather.output_elements));
840 }
841}
842
843fn new_offset_value<T: IntegerPType>(value: usize) -> T {
844 T::from_usize(value).vortex_expect("output offset fits selected offset type")
845}
846
847#[cfg(test)]
848mod test {
849 use std::sync::Arc;
850
851 use rstest::rstest;
852 use vortex_buffer::buffer;
853 use vortex_error::VortexResult;
854
855 use crate::IntoArray as _;
856 use crate::VortexSessionExecute;
857 use crate::array_session;
858 use crate::arrays::BoolArray;
859 use crate::arrays::ConstantArray;
860 use crate::arrays::ListArray;
861 use crate::arrays::ListViewArray;
862 use crate::arrays::PiecewiseSequenceArray;
863 use crate::arrays::PrimitiveArray;
864 use crate::arrays::listview::ListViewArrayExt;
865 use crate::assert_arrays_eq;
866 use crate::compute::conformance::take::test_take_conformance;
867 use crate::dtype::DType;
868 use crate::dtype::Nullability;
869 use crate::dtype::PType::I32;
870 use crate::scalar::Scalar;
871 use crate::validity::Validity;
872
873 #[test]
874 fn nullable_take() {
875 let mut ctx = array_session().create_execution_ctx();
876 let list = ListArray::try_new(
877 buffer![0i32, 5, 3, 4].into_array(),
878 buffer![0, 2, 3, 4, 4].into_array(),
879 Validity::Array(BoolArray::from_iter(vec![true, true, false, true]).into_array()),
880 )
881 .unwrap()
882 .into_array();
883
884 let idx =
885 PrimitiveArray::from_option_iter(vec![Some(0), None, Some(1), Some(3)]).into_array();
886
887 let result = list.take(idx).unwrap();
888
889 assert_eq!(
890 result.dtype(),
891 &DType::List(
892 Arc::new(DType::Primitive(I32, Nullability::NonNullable)),
893 Nullability::Nullable
894 )
895 );
896
897 let result = result.execute::<ListViewArray>(&mut ctx).unwrap();
898
899 assert_eq!(result.len(), 4);
900
901 let element_dtype: Arc<DType> = Arc::new(I32.into());
902
903 assert!(
904 result
905 .is_valid(0, &mut array_session().create_execution_ctx())
906 .unwrap()
907 );
908 assert_eq!(
909 result
910 .execute_scalar(0, &mut array_session().create_execution_ctx())
911 .unwrap(),
912 Scalar::list(
913 Arc::clone(&element_dtype),
914 vec![0i32.into(), 5.into()],
915 Nullability::Nullable
916 )
917 );
918
919 assert!(
920 result
921 .is_invalid(1, &mut array_session().create_execution_ctx())
922 .unwrap()
923 );
924
925 assert!(
926 result
927 .is_valid(2, &mut array_session().create_execution_ctx())
928 .unwrap()
929 );
930 assert_eq!(
931 result
932 .execute_scalar(2, &mut array_session().create_execution_ctx())
933 .unwrap(),
934 Scalar::list(
935 Arc::clone(&element_dtype),
936 vec![3i32.into()],
937 Nullability::Nullable
938 )
939 );
940
941 assert!(
942 result
943 .is_valid(3, &mut array_session().create_execution_ctx())
944 .unwrap()
945 );
946 assert_eq!(
947 result
948 .execute_scalar(3, &mut array_session().create_execution_ctx())
949 .unwrap(),
950 Scalar::list(element_dtype, vec![], Nullability::Nullable)
951 );
952 }
953
954 #[test]
955 fn null_index_ignores_out_of_bounds_payload() {
956 let mut ctx = array_session().create_execution_ctx();
957 let list = ListArray::try_new(
958 buffer![1i32, 2, 3, 4].into_array(),
959 buffer![0u32, 2, 4].into_array(),
960 Validity::NonNullable,
961 )
962 .unwrap()
963 .into_array();
964
965 let idx = PrimitiveArray::new(
966 buffer![1u32, 99, 0],
967 Validity::from_iter([true, false, true]),
968 )
969 .into_array();
970 let result = list.take(idx).unwrap();
971
972 let expected = ListArray::new(
973 buffer![3i32, 4, 1, 2].into_array(),
974 buffer![0u32, 2, 2, 4].into_array(),
975 Validity::from_iter([true, false, true]),
976 );
977 assert_arrays_eq!(result, expected, &mut ctx);
978 }
979
980 #[test]
981 fn null_source_row_uses_valid_empty_output_range() {
982 let mut ctx = array_session().create_execution_ctx();
983 let list = ListArray::new(
984 buffer![1i32, 2, 7, 8].into_array(),
985 buffer![0u32, 2, 4].into_array(),
986 Validity::from_iter([true, false]),
987 )
988 .into_array();
989
990 let idx = buffer![0u32, 1].into_array();
991 let result = list.take(idx).unwrap();
992
993 let expected = ListArray::new(
994 buffer![1i32, 2].into_array(),
995 buffer![0u32, 2, 2].into_array(),
996 Validity::from_iter([true, false]),
997 );
998 assert_arrays_eq!(result, expected, &mut ctx);
999 }
1000
1001 #[test]
1002 fn change_validity() {
1003 let list = ListArray::try_new(
1004 buffer![0i32, 5, 3, 4].into_array(),
1005 buffer![0, 2, 3].into_array(),
1006 Validity::NonNullable,
1007 )
1008 .unwrap()
1009 .into_array();
1010
1011 let idx = PrimitiveArray::from_option_iter(vec![Some(0), Some(1), None]).into_array();
1012 let result = list.take(idx).unwrap();
1015 assert_eq!(
1016 result.dtype(),
1017 &DType::List(
1018 Arc::new(DType::Primitive(I32, Nullability::NonNullable)),
1019 Nullability::Nullable
1020 )
1021 );
1022 let expected = ListArray::new(
1023 buffer![0i32, 5, 3].into_array(),
1024 buffer![0u32, 2, 3, 3].into_array(),
1025 Validity::from_iter([true, true, false]),
1026 );
1027 assert_arrays_eq!(
1028 result,
1029 expected,
1030 &mut array_session().create_execution_ctx()
1031 );
1032 }
1033
1034 #[test]
1035 fn non_nullable_take() {
1036 let mut ctx = array_session().create_execution_ctx();
1037 let list = ListArray::try_new(
1038 buffer![0i32, 5, 3, 4].into_array(),
1039 buffer![0, 2, 3, 3, 4].into_array(),
1040 Validity::NonNullable,
1041 )
1042 .unwrap()
1043 .into_array();
1044
1045 let idx = buffer![1, 0, 2].into_array();
1046
1047 let result = list.take(idx).unwrap();
1048
1049 assert_eq!(
1050 result.dtype(),
1051 &DType::List(
1052 Arc::new(DType::Primitive(I32, Nullability::NonNullable)),
1053 Nullability::NonNullable
1054 )
1055 );
1056
1057 let result = result.execute::<ListViewArray>(&mut ctx).unwrap();
1058
1059 assert_eq!(result.len(), 3);
1060
1061 let element_dtype: Arc<DType> = Arc::new(I32.into());
1062
1063 assert!(
1064 result
1065 .is_valid(0, &mut array_session().create_execution_ctx())
1066 .unwrap()
1067 );
1068 assert_eq!(
1069 result
1070 .execute_scalar(0, &mut array_session().create_execution_ctx())
1071 .unwrap(),
1072 Scalar::list(
1073 Arc::clone(&element_dtype),
1074 vec![3i32.into()],
1075 Nullability::NonNullable
1076 )
1077 );
1078
1079 assert!(
1080 result
1081 .is_valid(1, &mut array_session().create_execution_ctx())
1082 .unwrap()
1083 );
1084 assert_eq!(
1085 result
1086 .execute_scalar(1, &mut array_session().create_execution_ctx())
1087 .unwrap(),
1088 Scalar::list(
1089 Arc::clone(&element_dtype),
1090 vec![0i32.into(), 5.into()],
1091 Nullability::NonNullable
1092 )
1093 );
1094
1095 assert!(
1096 result
1097 .is_valid(2, &mut array_session().create_execution_ctx())
1098 .unwrap()
1099 );
1100 assert_eq!(
1101 result
1102 .execute_scalar(2, &mut array_session().create_execution_ctx())
1103 .unwrap(),
1104 Scalar::list(element_dtype, vec![], Nullability::NonNullable)
1105 );
1106 }
1107
1108 #[test]
1109 fn piecewise_sequence_take() {
1110 let mut ctx = array_session().create_execution_ctx();
1111 let list = ListArray::try_new(
1112 buffer![0i32, 1, 2, 3, 4, 5, 6].into_array(),
1113 buffer![0u32, 2, 5, 5, 7].into_array(),
1114 Validity::NonNullable,
1115 )
1116 .unwrap()
1117 .into_array();
1118 let idx = PiecewiseSequenceArray::try_new(
1119 buffer![1u64, 0].into_array(),
1120 buffer![2u64, 1].into_array(),
1121 ConstantArray::new(1u64, 2).into_array(),
1122 3,
1123 )
1124 .unwrap()
1125 .into_array();
1126
1127 let result = list
1128 .take(idx)
1129 .unwrap()
1130 .execute::<ListViewArray>(&mut ctx)
1131 .unwrap();
1132
1133 let element_dtype: Arc<DType> = Arc::new(I32.into());
1134 assert_eq!(
1135 result.execute_scalar(0, &mut ctx).unwrap(),
1136 Scalar::list(
1137 Arc::clone(&element_dtype),
1138 vec![2i32.into(), 3.into(), 4.into()],
1139 Nullability::NonNullable
1140 )
1141 );
1142 assert_eq!(
1143 result.execute_scalar(1, &mut ctx).unwrap(),
1144 Scalar::list(Arc::clone(&element_dtype), vec![], Nullability::NonNullable)
1145 );
1146 assert_eq!(
1147 result.execute_scalar(2, &mut ctx).unwrap(),
1148 Scalar::list(
1149 element_dtype,
1150 vec![0i32.into(), 1.into()],
1151 Nullability::NonNullable
1152 )
1153 );
1154 }
1155
1156 #[test]
1157 fn piecewise_sequence_take_nullable_list_constant_lengths() -> VortexResult<()> {
1158 let mut ctx = array_session().create_execution_ctx();
1159 let list = ListArray::try_new(
1160 buffer![0i32, 1, 99, 100, 2, 3, 4, 5].into_array(),
1161 buffer![0u32, 2, 4, 7, 8].into_array(),
1162 Validity::Array(BoolArray::from_iter([true, false, true, true]).into_array()),
1163 )?
1164 .into_array();
1165 let idx = PiecewiseSequenceArray::try_new(
1166 buffer![0u64].into_array(),
1167 ConstantArray::new(4u64, 1).into_array(),
1168 ConstantArray::new(1u64, 1).into_array(),
1169 4,
1170 )?
1171 .into_array();
1172
1173 let result = list.take(idx)?.execute::<ListViewArray>(&mut ctx)?;
1174 assert_eq!(result.offset_at(0), 0);
1175 assert_eq!(result.size_at(0), 2);
1176 assert_eq!(result.offset_at(1), 2);
1177 assert_eq!(result.size_at(1), 0);
1178 assert_eq!(result.offset_at(2), 2);
1179 assert_eq!(result.size_at(2), 3);
1180 assert_eq!(result.offset_at(3), 5);
1181 assert_eq!(result.size_at(3), 1);
1182
1183 let element_dtype: Arc<DType> = Arc::new(I32.into());
1184 assert_eq!(
1185 result.execute_scalar(0, &mut ctx)?,
1186 Scalar::list(
1187 Arc::clone(&element_dtype),
1188 vec![0i32.into(), 1.into()],
1189 Nullability::Nullable
1190 )
1191 );
1192 assert!(result.is_invalid(1, &mut ctx)?);
1193 assert_eq!(
1194 result.execute_scalar(2, &mut ctx)?,
1195 Scalar::list(
1196 Arc::clone(&element_dtype),
1197 vec![2i32.into(), 3.into(), 4.into()],
1198 Nullability::Nullable
1199 )
1200 );
1201 assert_eq!(
1202 result.execute_scalar(3, &mut ctx)?,
1203 Scalar::list(element_dtype, vec![5i32.into()], Nullability::Nullable)
1204 );
1205 Ok(())
1206 }
1207
1208 #[test]
1209 fn piecewise_sequence_take_nullable_list_array_lengths() -> VortexResult<()> {
1210 let mut ctx = array_session().create_execution_ctx();
1211 let list = ListArray::try_new(
1212 buffer![0i32, 1, 99, 100, 2, 3, 4, 5].into_array(),
1213 buffer![0u32, 2, 4, 7, 8].into_array(),
1214 Validity::Array(BoolArray::from_iter([true, false, true, true]).into_array()),
1215 )?
1216 .into_array();
1217 let idx = PiecewiseSequenceArray::try_new(
1218 buffer![1u64, 0].into_array(),
1219 buffer![2u64, 1].into_array(),
1220 ConstantArray::new(1u64, 2).into_array(),
1221 3,
1222 )?
1223 .into_array();
1224
1225 let result = list.take(idx)?.execute::<ListViewArray>(&mut ctx)?;
1226 assert_eq!(result.offset_at(0), 0);
1227 assert_eq!(result.size_at(0), 0);
1228 assert_eq!(result.offset_at(1), 0);
1229 assert_eq!(result.size_at(1), 3);
1230 assert_eq!(result.offset_at(2), 3);
1231 assert_eq!(result.size_at(2), 2);
1232
1233 let element_dtype: Arc<DType> = Arc::new(I32.into());
1234 assert!(result.is_invalid(0, &mut ctx)?);
1235 assert_eq!(
1236 result.execute_scalar(1, &mut ctx)?,
1237 Scalar::list(
1238 Arc::clone(&element_dtype),
1239 vec![2i32.into(), 3.into(), 4.into()],
1240 Nullability::Nullable
1241 )
1242 );
1243 assert_eq!(
1244 result.execute_scalar(2, &mut ctx)?,
1245 Scalar::list(
1246 element_dtype,
1247 vec![0i32.into(), 1.into()],
1248 Nullability::Nullable
1249 )
1250 );
1251 Ok(())
1252 }
1253
1254 #[test]
1255 fn test_take_empty_array() {
1256 let list = ListArray::try_new(
1257 buffer![0i32, 5, 3, 4].into_array(),
1258 buffer![0].into_array(),
1259 Validity::NonNullable,
1260 )
1261 .unwrap()
1262 .into_array();
1263
1264 let idx = PrimitiveArray::empty::<i32>(Nullability::Nullable).into_array();
1265
1266 let result = list.take(idx).unwrap();
1267 assert_eq!(
1268 result.dtype(),
1269 &DType::List(
1270 Arc::new(DType::Primitive(I32, Nullability::NonNullable)),
1271 Nullability::Nullable
1272 )
1273 );
1274 assert_eq!(result.len(), 0,);
1275 }
1276
1277 #[rstest]
1278 #[case(ListArray::try_new(
1279 buffer![0i32, 1, 2, 3, 4, 5].into_array(),
1280 buffer![0, 2, 3, 5, 5, 6].into_array(),
1281 Validity::NonNullable,
1282 ).unwrap())]
1283 #[case(ListArray::try_new(
1284 buffer![10i32, 20, 30, 40, 50].into_array(),
1285 buffer![0, 2, 3, 4, 5].into_array(),
1286 Validity::Array(BoolArray::from_iter(vec![true, false, true, true]).into_array()),
1287 ).unwrap())]
1288 #[case(ListArray::try_new(
1289 buffer![1i32, 2, 3].into_array(),
1290 buffer![0, 0, 2, 2, 3].into_array(), Validity::NonNullable,
1292 ).unwrap())]
1293 #[case(ListArray::try_new(
1294 buffer![42i32, 43].into_array(),
1295 buffer![0, 2].into_array(),
1296 Validity::NonNullable,
1297 ).unwrap())]
1298 #[case({
1299 let elements = buffer![0i32..200].into_array();
1300 let mut offsets = vec![0u64];
1301 for i in 1..=50 {
1302 offsets.push(offsets[i - 1] + (i as u64 % 5)); }
1304 ListArray::try_new(
1305 elements,
1306 PrimitiveArray::from_iter(offsets).into_array(),
1307 Validity::NonNullable,
1308 ).unwrap()
1309 })]
1310 #[case(ListArray::try_new(
1311 PrimitiveArray::from_option_iter([Some(1i32), None, Some(3), Some(4), None]).into_array(),
1312 buffer![0, 2, 3, 5].into_array(),
1313 Validity::NonNullable,
1314 ).unwrap())]
1315 fn test_take_list_conformance(#[case] list: ListArray) {
1316 test_take_conformance(
1317 &list.into_array(),
1318 &mut array_session().create_execution_ctx(),
1319 );
1320 }
1321
1322 #[test]
1323 fn test_u64_offset_accumulation_non_nullable() {
1324 let mut ctx = array_session().create_execution_ctx();
1325 let elements = buffer![0i32; 200].into_array();
1326 let offsets = buffer![0u8, 200].into_array();
1327 let list = ListArray::try_new(elements, offsets, Validity::NonNullable)
1328 .unwrap()
1329 .into_array();
1330
1331 let idx = buffer![0u8, 0].into_array();
1333 let result = list.take(idx).unwrap();
1334
1335 assert_eq!(result.len(), 2);
1336
1337 let result_view = result.execute::<ListViewArray>(&mut ctx).unwrap();
1338 assert_eq!(result_view.len(), 2);
1339 assert!(
1340 result_view
1341 .is_valid(0, &mut array_session().create_execution_ctx())
1342 .unwrap()
1343 );
1344 assert!(
1345 result_view
1346 .is_valid(1, &mut array_session().create_execution_ctx())
1347 .unwrap()
1348 );
1349 let expected = ListArray::new(
1350 buffer![0i32; 400].into_array(),
1351 buffer![0u32, 200, 400].into_array(),
1352 Validity::NonNullable,
1353 );
1354 assert_arrays_eq!(result_view, expected, &mut ctx);
1355 }
1356
1357 #[test]
1358 fn test_u64_offset_accumulation_nullable() {
1359 let mut ctx = array_session().create_execution_ctx();
1360 let elements = buffer![0i32; 150].into_array();
1361 let offsets = buffer![0u8, 150, 150].into_array();
1362 let validity = BoolArray::from_iter(vec![true, false]).into_array();
1363 let list = ListArray::try_new(elements, offsets, Validity::Array(validity))
1364 .unwrap()
1365 .into_array();
1366
1367 let idx = PrimitiveArray::from_option_iter(vec![Some(0u8), None, Some(0u8)]).into_array();
1369 let result = list.take(idx).unwrap();
1370
1371 assert_eq!(result.len(), 3);
1372
1373 let result_view = result.execute::<ListViewArray>(&mut ctx).unwrap();
1374 assert_eq!(result_view.len(), 3);
1375 assert!(
1376 result_view
1377 .is_valid(0, &mut array_session().create_execution_ctx())
1378 .unwrap()
1379 );
1380 assert!(
1381 result_view
1382 .is_invalid(1, &mut array_session().create_execution_ctx())
1383 .unwrap()
1384 );
1385 assert!(
1386 result_view
1387 .is_valid(2, &mut array_session().create_execution_ctx())
1388 .unwrap()
1389 );
1390 let expected = ListArray::new(
1391 buffer![0i32; 300].into_array(),
1392 buffer![0u32, 150, 150, 300].into_array(),
1393 Validity::from_iter([true, false, true]),
1394 );
1395 assert_arrays_eq!(result_view, expected, &mut ctx);
1396 }
1397
1398 #[test]
1403 fn test_take_validity_length_mismatch_regression() {
1404 let list = ListArray::try_new(
1406 buffer![1i32, 2, 3, 4].into_array(),
1407 buffer![0, 2, 4].into_array(),
1408 Validity::Array(BoolArray::from_iter(vec![true, true]).into_array()),
1409 )
1410 .unwrap()
1411 .into_array();
1412
1413 let idx = buffer![0u32, 1, 0, 1].into_array();
1415
1416 let result = list.take(idx).unwrap();
1418 assert_eq!(result.len(), 4);
1419 let expected = ListArray::new(
1420 buffer![1i32, 2, 3, 4, 1, 2, 3, 4].into_array(),
1421 buffer![0u32, 2, 4, 6, 8].into_array(),
1422 Validity::AllValid,
1423 );
1424 assert_arrays_eq!(
1425 result,
1426 expected,
1427 &mut array_session().create_execution_ctx()
1428 );
1429 }
1430}