Skip to main content

vortex_array/arrays/list/compute/
take.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4use 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
36// TODO(connor)[ListView]: Re-revert to the version where we simply convert to a `ListView` and call
37// the `ListView::take` compute function once `ListView` is more stable.
38
39impl TakeExecute for List {
40    /// Take implementation for [`ListArray`].
41    ///
42    /// Unlike `ListView`, `ListArray` must rebuild the elements array to maintain its invariant
43    /// that lists are stored contiguously and in-order (`offset[i+1] >= offset[i]`). Taking
44    /// non-contiguous indices would violate this requirement.
45    #[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        // This is an over-approximation of the total number of elements in the resulting array.
64        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    // SAFETY: valid source rows contribute ranges derived from list offsets; null index/source
132    // rows contribute zero-length placeholder ranges. `current_offset` is the sum of all generated
133    // element lengths, and multiplier 1 preserves contiguous ranges.
134    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    // SAFETY: offsets are rebuilt from the gathered element ranges and have one entry per output
145    // row plus the leading zero; validity is produced by the usual take-validity path.
146    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        // SAFETY: output offsets are rebuilt from valid monotonic source offsets; output elements
367        // are exactly the gathered child ranges referenced by those offsets; validity has one bit
368        // per output row.
369        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        // SAFETY: output offsets are rebuilt from valid monotonic source offsets; output elements
433        // are exactly the gathered child ranges referenced by those offsets; validity has one bit
434        // per output row.
435        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    // SAFETY: element ranges are derived from validated source list offsets, and total_elements is
624    // the sum of the gathered element range lengths. Multiplier 1 preserves contiguous ranges.
625    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    // SAFETY: element ranges come only from valid source list rows. Source list construction
677    // validated those row offsets, and null source rows produce no element range.
678    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    // SAFETY: element ranges are derived from validated source list offsets, and total_elements is
741    // the sum of the gathered element range lengths. Multiplier 1 preserves contiguous ranges.
742    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    // SAFETY: element ranges come only from valid source list rows. Source list construction
796    // validated those row offsets, and null source rows produce no element range.
797    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            &mut ctx,
881        )
882        .unwrap()
883        .into_array();
884
885        let idx =
886            PrimitiveArray::from_option_iter(vec![Some(0), None, Some(1), Some(3)]).into_array();
887
888        let result = list.take(idx).unwrap();
889
890        assert_eq!(
891            result.dtype(),
892            &DType::List(
893                Arc::new(DType::Primitive(I32, Nullability::NonNullable)),
894                Nullability::Nullable
895            )
896        );
897
898        let result = result.execute::<ListViewArray>(&mut ctx).unwrap();
899
900        assert_eq!(result.len(), 4);
901
902        let element_dtype: Arc<DType> = Arc::new(I32.into());
903
904        assert!(
905            result
906                .is_valid(0, &mut array_session().create_execution_ctx())
907                .unwrap()
908        );
909        assert_eq!(
910            result
911                .execute_scalar(0, &mut array_session().create_execution_ctx())
912                .unwrap(),
913            Scalar::list(
914                Arc::clone(&element_dtype),
915                vec![0i32.into(), 5.into()],
916                Nullability::Nullable
917            )
918        );
919
920        assert!(
921            result
922                .is_invalid(1, &mut array_session().create_execution_ctx())
923                .unwrap()
924        );
925
926        assert!(
927            result
928                .is_valid(2, &mut array_session().create_execution_ctx())
929                .unwrap()
930        );
931        assert_eq!(
932            result
933                .execute_scalar(2, &mut array_session().create_execution_ctx())
934                .unwrap(),
935            Scalar::list(
936                Arc::clone(&element_dtype),
937                vec![3i32.into()],
938                Nullability::Nullable
939            )
940        );
941
942        assert!(
943            result
944                .is_valid(3, &mut array_session().create_execution_ctx())
945                .unwrap()
946        );
947        assert_eq!(
948            result
949                .execute_scalar(3, &mut array_session().create_execution_ctx())
950                .unwrap(),
951            Scalar::list(element_dtype, vec![], Nullability::Nullable)
952        );
953    }
954
955    #[test]
956    fn null_index_ignores_out_of_bounds_payload() {
957        let mut ctx = array_session().create_execution_ctx();
958        let list = ListArray::try_new(
959            buffer![1i32, 2, 3, 4].into_array(),
960            buffer![0u32, 2, 4].into_array(),
961            Validity::NonNullable,
962            &mut ctx,
963        )
964        .unwrap()
965        .into_array();
966
967        let idx = PrimitiveArray::new(
968            buffer![1u32, 99, 0],
969            Validity::from_iter([true, false, true]),
970        )
971        .into_array();
972        let result = list.take(idx).unwrap();
973
974        let expected = ListArray::new(
975            buffer![3i32, 4, 1, 2].into_array(),
976            buffer![0u32, 2, 2, 4].into_array(),
977            Validity::from_iter([true, false, true]),
978            &mut ctx,
979        );
980        assert_arrays_eq!(result, expected, &mut ctx);
981    }
982
983    #[test]
984    fn null_source_row_uses_valid_empty_output_range() {
985        let mut ctx = array_session().create_execution_ctx();
986        let list = ListArray::new(
987            buffer![1i32, 2, 7, 8].into_array(),
988            buffer![0u32, 2, 4].into_array(),
989            Validity::from_iter([true, false]),
990            &mut ctx,
991        )
992        .into_array();
993
994        let idx = buffer![0u32, 1].into_array();
995        let result = list.take(idx).unwrap();
996
997        let expected = ListArray::new(
998            buffer![1i32, 2].into_array(),
999            buffer![0u32, 2, 2].into_array(),
1000            Validity::from_iter([true, false]),
1001            &mut ctx,
1002        );
1003        assert_arrays_eq!(result, expected, &mut ctx);
1004    }
1005
1006    #[test]
1007    fn change_validity() {
1008        let mut ctx = array_session().create_execution_ctx();
1009        let list = ListArray::try_new(
1010            buffer![0i32, 5, 3, 4].into_array(),
1011            buffer![0, 2, 3].into_array(),
1012            Validity::NonNullable,
1013            &mut ctx,
1014        )
1015        .unwrap()
1016        .into_array();
1017
1018        let idx = PrimitiveArray::from_option_iter(vec![Some(0), Some(1), None]).into_array();
1019        // since idx is nullable, the final list will also be nullable
1020
1021        let result = list.take(idx).unwrap();
1022        assert_eq!(
1023            result.dtype(),
1024            &DType::List(
1025                Arc::new(DType::Primitive(I32, Nullability::NonNullable)),
1026                Nullability::Nullable
1027            )
1028        );
1029        let expected = ListArray::new(
1030            buffer![0i32, 5, 3].into_array(),
1031            buffer![0u32, 2, 3, 3].into_array(),
1032            Validity::from_iter([true, true, false]),
1033            &mut ctx,
1034        );
1035        assert_arrays_eq!(
1036            result,
1037            expected,
1038            &mut array_session().create_execution_ctx()
1039        );
1040    }
1041
1042    #[test]
1043    fn non_nullable_take() {
1044        let mut ctx = array_session().create_execution_ctx();
1045        let list = ListArray::try_new(
1046            buffer![0i32, 5, 3, 4].into_array(),
1047            buffer![0, 2, 3, 3, 4].into_array(),
1048            Validity::NonNullable,
1049            &mut ctx,
1050        )
1051        .unwrap()
1052        .into_array();
1053
1054        let idx = buffer![1, 0, 2].into_array();
1055
1056        let result = list.take(idx).unwrap();
1057
1058        assert_eq!(
1059            result.dtype(),
1060            &DType::List(
1061                Arc::new(DType::Primitive(I32, Nullability::NonNullable)),
1062                Nullability::NonNullable
1063            )
1064        );
1065
1066        let result = result.execute::<ListViewArray>(&mut ctx).unwrap();
1067
1068        assert_eq!(result.len(), 3);
1069
1070        let element_dtype: Arc<DType> = Arc::new(I32.into());
1071
1072        assert!(
1073            result
1074                .is_valid(0, &mut array_session().create_execution_ctx())
1075                .unwrap()
1076        );
1077        assert_eq!(
1078            result
1079                .execute_scalar(0, &mut array_session().create_execution_ctx())
1080                .unwrap(),
1081            Scalar::list(
1082                Arc::clone(&element_dtype),
1083                vec![3i32.into()],
1084                Nullability::NonNullable
1085            )
1086        );
1087
1088        assert!(
1089            result
1090                .is_valid(1, &mut array_session().create_execution_ctx())
1091                .unwrap()
1092        );
1093        assert_eq!(
1094            result
1095                .execute_scalar(1, &mut array_session().create_execution_ctx())
1096                .unwrap(),
1097            Scalar::list(
1098                Arc::clone(&element_dtype),
1099                vec![0i32.into(), 5.into()],
1100                Nullability::NonNullable
1101            )
1102        );
1103
1104        assert!(
1105            result
1106                .is_valid(2, &mut array_session().create_execution_ctx())
1107                .unwrap()
1108        );
1109        assert_eq!(
1110            result
1111                .execute_scalar(2, &mut array_session().create_execution_ctx())
1112                .unwrap(),
1113            Scalar::list(element_dtype, vec![], Nullability::NonNullable)
1114        );
1115    }
1116
1117    #[test]
1118    fn piecewise_sequence_take() {
1119        let mut ctx = array_session().create_execution_ctx();
1120        let list = ListArray::try_new(
1121            buffer![0i32, 1, 2, 3, 4, 5, 6].into_array(),
1122            buffer![0u32, 2, 5, 5, 7].into_array(),
1123            Validity::NonNullable,
1124            &mut ctx,
1125        )
1126        .unwrap()
1127        .into_array();
1128        let idx = PiecewiseSequenceArray::try_new(
1129            buffer![1u64, 0].into_array(),
1130            buffer![2u64, 1].into_array(),
1131            ConstantArray::new(1u64, 2).into_array(),
1132            3,
1133        )
1134        .unwrap()
1135        .into_array();
1136
1137        let result = list
1138            .take(idx)
1139            .unwrap()
1140            .execute::<ListViewArray>(&mut ctx)
1141            .unwrap();
1142
1143        let element_dtype: Arc<DType> = Arc::new(I32.into());
1144        assert_eq!(
1145            result.execute_scalar(0, &mut ctx).unwrap(),
1146            Scalar::list(
1147                Arc::clone(&element_dtype),
1148                vec![2i32.into(), 3.into(), 4.into()],
1149                Nullability::NonNullable
1150            )
1151        );
1152        assert_eq!(
1153            result.execute_scalar(1, &mut ctx).unwrap(),
1154            Scalar::list(Arc::clone(&element_dtype), vec![], Nullability::NonNullable)
1155        );
1156        assert_eq!(
1157            result.execute_scalar(2, &mut ctx).unwrap(),
1158            Scalar::list(
1159                element_dtype,
1160                vec![0i32.into(), 1.into()],
1161                Nullability::NonNullable
1162            )
1163        );
1164    }
1165
1166    #[test]
1167    fn piecewise_sequence_take_nullable_list_constant_lengths() -> VortexResult<()> {
1168        let mut ctx = array_session().create_execution_ctx();
1169        let list = ListArray::try_new(
1170            buffer![0i32, 1, 99, 100, 2, 3, 4, 5].into_array(),
1171            buffer![0u32, 2, 4, 7, 8].into_array(),
1172            Validity::Array(BoolArray::from_iter([true, false, true, true]).into_array()),
1173            &mut ctx,
1174        )?
1175        .into_array();
1176        let idx = PiecewiseSequenceArray::try_new(
1177            buffer![0u64].into_array(),
1178            ConstantArray::new(4u64, 1).into_array(),
1179            ConstantArray::new(1u64, 1).into_array(),
1180            4,
1181        )?
1182        .into_array();
1183
1184        let result = list.take(idx)?.execute::<ListViewArray>(&mut ctx)?;
1185        assert_eq!(result.offset_at(0, &mut ctx), 0);
1186        assert_eq!(result.size_at(0, &mut ctx), 2);
1187        assert_eq!(result.offset_at(1, &mut ctx), 2);
1188        assert_eq!(result.size_at(1, &mut ctx), 0);
1189        assert_eq!(result.offset_at(2, &mut ctx), 2);
1190        assert_eq!(result.size_at(2, &mut ctx), 3);
1191        assert_eq!(result.offset_at(3, &mut ctx), 5);
1192        assert_eq!(result.size_at(3, &mut ctx), 1);
1193
1194        let element_dtype: Arc<DType> = Arc::new(I32.into());
1195        assert_eq!(
1196            result.execute_scalar(0, &mut ctx)?,
1197            Scalar::list(
1198                Arc::clone(&element_dtype),
1199                vec![0i32.into(), 1.into()],
1200                Nullability::Nullable
1201            )
1202        );
1203        assert!(result.is_invalid(1, &mut ctx)?);
1204        assert_eq!(
1205            result.execute_scalar(2, &mut ctx)?,
1206            Scalar::list(
1207                Arc::clone(&element_dtype),
1208                vec![2i32.into(), 3.into(), 4.into()],
1209                Nullability::Nullable
1210            )
1211        );
1212        assert_eq!(
1213            result.execute_scalar(3, &mut ctx)?,
1214            Scalar::list(element_dtype, vec![5i32.into()], Nullability::Nullable)
1215        );
1216        Ok(())
1217    }
1218
1219    #[test]
1220    fn piecewise_sequence_take_nullable_list_array_lengths() -> VortexResult<()> {
1221        let mut ctx = array_session().create_execution_ctx();
1222        let list = ListArray::try_new(
1223            buffer![0i32, 1, 99, 100, 2, 3, 4, 5].into_array(),
1224            buffer![0u32, 2, 4, 7, 8].into_array(),
1225            Validity::Array(BoolArray::from_iter([true, false, true, true]).into_array()),
1226            &mut ctx,
1227        )?
1228        .into_array();
1229        let idx = PiecewiseSequenceArray::try_new(
1230            buffer![1u64, 0].into_array(),
1231            buffer![2u64, 1].into_array(),
1232            ConstantArray::new(1u64, 2).into_array(),
1233            3,
1234        )?
1235        .into_array();
1236
1237        let result = list.take(idx)?.execute::<ListViewArray>(&mut ctx)?;
1238        assert_eq!(result.offset_at(0, &mut ctx), 0);
1239        assert_eq!(result.size_at(0, &mut ctx), 0);
1240        assert_eq!(result.offset_at(1, &mut ctx), 0);
1241        assert_eq!(result.size_at(1, &mut ctx), 3);
1242        assert_eq!(result.offset_at(2, &mut ctx), 3);
1243        assert_eq!(result.size_at(2, &mut ctx), 2);
1244
1245        let element_dtype: Arc<DType> = Arc::new(I32.into());
1246        assert!(result.is_invalid(0, &mut ctx)?);
1247        assert_eq!(
1248            result.execute_scalar(1, &mut ctx)?,
1249            Scalar::list(
1250                Arc::clone(&element_dtype),
1251                vec![2i32.into(), 3.into(), 4.into()],
1252                Nullability::Nullable
1253            )
1254        );
1255        assert_eq!(
1256            result.execute_scalar(2, &mut ctx)?,
1257            Scalar::list(
1258                element_dtype,
1259                vec![0i32.into(), 1.into()],
1260                Nullability::Nullable
1261            )
1262        );
1263        Ok(())
1264    }
1265
1266    #[test]
1267    fn test_take_empty_array() {
1268        let mut ctx = array_session().create_execution_ctx();
1269        let list = ListArray::try_new(
1270            buffer![0i32, 5, 3, 4].into_array(),
1271            buffer![0].into_array(),
1272            Validity::NonNullable,
1273            &mut ctx,
1274        )
1275        .unwrap()
1276        .into_array();
1277
1278        let idx = PrimitiveArray::empty::<i32>(Nullability::Nullable).into_array();
1279
1280        let result = list.take(idx).unwrap();
1281        assert_eq!(
1282            result.dtype(),
1283            &DType::List(
1284                Arc::new(DType::Primitive(I32, Nullability::NonNullable)),
1285                Nullability::Nullable
1286            )
1287        );
1288        assert_eq!(result.len(), 0,);
1289    }
1290
1291    #[rstest]
1292    #[case(ListArray::try_new(
1293        buffer![0i32, 1, 2, 3, 4, 5].into_array(),
1294        buffer![0, 2, 3, 5, 5, 6].into_array(),
1295        Validity::NonNullable,
1296        &mut array_session().create_execution_ctx(),
1297    ).unwrap())]
1298    #[case(ListArray::try_new(
1299        buffer![10i32, 20, 30, 40, 50].into_array(),
1300        buffer![0, 2, 3, 4, 5].into_array(),
1301        Validity::Array(BoolArray::from_iter(vec![true, false, true, true]).into_array()),
1302        &mut array_session().create_execution_ctx(),
1303    ).unwrap())]
1304    #[case(ListArray::try_new(
1305        buffer![1i32, 2, 3].into_array(),
1306        buffer![0, 0, 2, 2, 3].into_array(), // First and third are empty
1307        Validity::NonNullable,
1308        &mut array_session().create_execution_ctx(),
1309    ).unwrap())]
1310    #[case(ListArray::try_new(
1311        buffer![42i32, 43].into_array(),
1312        buffer![0, 2].into_array(),
1313        Validity::NonNullable,
1314        &mut array_session().create_execution_ctx(),
1315    ).unwrap())]
1316    #[case({
1317        let elements = buffer![0i32..200].into_array();
1318        let mut offsets = vec![0u64];
1319        for i in 1..=50 {
1320            offsets.push(offsets[i - 1] + (i as u64 % 5)); // Variable length lists
1321        }
1322        ListArray::try_new(
1323            elements,
1324            PrimitiveArray::from_iter(offsets).into_array(),
1325            Validity::NonNullable,
1326            &mut array_session().create_execution_ctx(),
1327        ).unwrap()
1328    })]
1329    #[case(ListArray::try_new(
1330        PrimitiveArray::from_option_iter([Some(1i32), None, Some(3), Some(4), None]).into_array(),
1331        buffer![0, 2, 3, 5].into_array(),
1332        Validity::NonNullable,
1333        &mut array_session().create_execution_ctx(),
1334    ).unwrap())]
1335    fn test_take_list_conformance(#[case] list: ListArray) {
1336        test_take_conformance(
1337            &list.into_array(),
1338            &mut array_session().create_execution_ctx(),
1339        );
1340    }
1341
1342    #[test]
1343    fn test_u64_offset_accumulation_non_nullable() {
1344        let mut ctx = array_session().create_execution_ctx();
1345        let elements = buffer![0i32; 200].into_array();
1346        let offsets = buffer![0u8, 200].into_array();
1347        let list = ListArray::try_new(elements, offsets, Validity::NonNullable, &mut ctx)
1348            .unwrap()
1349            .into_array();
1350
1351        // Take the same large list twice - would overflow u8 but works with u64.
1352        let idx = buffer![0u8, 0].into_array();
1353        let result = list.take(idx).unwrap();
1354
1355        assert_eq!(result.len(), 2);
1356
1357        let result_view = result.execute::<ListViewArray>(&mut ctx).unwrap();
1358        assert_eq!(result_view.len(), 2);
1359        assert!(
1360            result_view
1361                .is_valid(0, &mut array_session().create_execution_ctx())
1362                .unwrap()
1363        );
1364        assert!(
1365            result_view
1366                .is_valid(1, &mut array_session().create_execution_ctx())
1367                .unwrap()
1368        );
1369        let expected = ListArray::new(
1370            buffer![0i32; 400].into_array(),
1371            buffer![0u32, 200, 400].into_array(),
1372            Validity::NonNullable,
1373            &mut ctx,
1374        );
1375        assert_arrays_eq!(result_view, expected, &mut ctx);
1376    }
1377
1378    #[test]
1379    fn test_u64_offset_accumulation_nullable() {
1380        let mut ctx = array_session().create_execution_ctx();
1381        let elements = buffer![0i32; 150].into_array();
1382        let offsets = buffer![0u8, 150, 150].into_array();
1383        let validity = BoolArray::from_iter(vec![true, false]).into_array();
1384        let list = ListArray::try_new(elements, offsets, Validity::Array(validity), &mut ctx)
1385            .unwrap()
1386            .into_array();
1387
1388        // Take the same large list twice - would overflow u8 but works with u64.
1389        let idx = PrimitiveArray::from_option_iter(vec![Some(0u8), None, Some(0u8)]).into_array();
1390        let result = list.take(idx).unwrap();
1391
1392        assert_eq!(result.len(), 3);
1393
1394        let result_view = result.execute::<ListViewArray>(&mut ctx).unwrap();
1395        assert_eq!(result_view.len(), 3);
1396        assert!(
1397            result_view
1398                .is_valid(0, &mut array_session().create_execution_ctx())
1399                .unwrap()
1400        );
1401        assert!(
1402            result_view
1403                .is_invalid(1, &mut array_session().create_execution_ctx())
1404                .unwrap()
1405        );
1406        assert!(
1407            result_view
1408                .is_valid(2, &mut array_session().create_execution_ctx())
1409                .unwrap()
1410        );
1411        let expected = ListArray::new(
1412            buffer![0i32; 300].into_array(),
1413            buffer![0u32, 150, 150, 300].into_array(),
1414            Validity::from_iter([true, false, true]),
1415            &mut ctx,
1416        );
1417        assert_arrays_eq!(result_view, expected, &mut ctx);
1418    }
1419
1420    /// Regression test for validity length mismatch bug.
1421    ///
1422    /// When source array has `Validity::Array(...)` and indices are non-nullable,
1423    /// the result validity must have length equal to indices.len(), not source.len().
1424    #[test]
1425    fn test_take_validity_length_mismatch_regression() {
1426        let mut ctx = array_session().create_execution_ctx();
1427        // Source array with explicit validity array (length 2).
1428        let list = ListArray::try_new(
1429            buffer![1i32, 2, 3, 4].into_array(),
1430            buffer![0, 2, 4].into_array(),
1431            Validity::Array(BoolArray::from_iter(vec![true, true]).into_array()),
1432            &mut ctx,
1433        )
1434        .unwrap()
1435        .into_array();
1436
1437        // Take more indices than source length (4 vs 2) with non-nullable indices.
1438        let idx = buffer![0u32, 1, 0, 1].into_array();
1439
1440        // This should not panic - result should have length 4.
1441        let result = list.take(idx).unwrap();
1442        assert_eq!(result.len(), 4);
1443        let expected = ListArray::new(
1444            buffer![1i32, 2, 3, 4, 1, 2, 3, 4].into_array(),
1445            buffer![0u32, 2, 4, 6, 8].into_array(),
1446            Validity::AllValid,
1447            &mut ctx,
1448        );
1449        assert_arrays_eq!(
1450            result,
1451            expected,
1452            &mut array_session().create_execution_ctx()
1453        );
1454    }
1455}