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        )
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        // since idx is nullable, the final list will also be nullable
1013
1014        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(), // First and third are empty
1291        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)); // Variable length lists
1303        }
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        // Take the same large list twice - would overflow u8 but works with u64.
1332        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        // Take the same large list twice - would overflow u8 but works with u64.
1368        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    /// Regression test for validity length mismatch bug.
1399    ///
1400    /// When source array has `Validity::Array(...)` and indices are non-nullable,
1401    /// the result validity must have length equal to indices.len(), not source.len().
1402    #[test]
1403    fn test_take_validity_length_mismatch_regression() {
1404        // Source array with explicit validity array (length 2).
1405        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        // Take more indices than source length (4 vs 2) with non-nullable indices.
1414        let idx = buffer![0u32, 1, 0, 1].into_array();
1415
1416        // This should not panic - result should have length 4.
1417        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}