Skip to main content

vortex_array/arrays/varbin/compute/
take.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4use std::ptr;
5
6use itertools::Itertools as _;
7use vortex_buffer::BitBufferMut;
8use vortex_buffer::BufferMut;
9use vortex_buffer::ByteBufferMut;
10use vortex_error::VortexExpect;
11use vortex_error::VortexResult;
12use vortex_error::vortex_ensure;
13use vortex_error::vortex_err;
14use vortex_error::vortex_panic;
15use vortex_mask::Mask;
16
17use crate::ArrayRef;
18use crate::Columnar;
19use crate::IntoArray;
20use crate::array::ArrayView;
21use crate::arrays::PiecewiseSequence;
22use crate::arrays::PrimitiveArray;
23use crate::arrays::VarBin;
24use crate::arrays::VarBinArray;
25use crate::arrays::dict::TakeExecute;
26use crate::arrays::piecewise_sequence::constant_unsigned_usize;
27use crate::arrays::piecewise_sequence::maybe_contiguous_slices;
28use crate::arrays::primitive::PrimitiveArrayExt;
29use crate::arrays::varbin::VarBinArrayExt;
30use crate::dtype::DType;
31use crate::dtype::IntegerPType;
32use crate::dtype::PType;
33use crate::dtype::UnsignedPType;
34use crate::executor::ExecutionCtx;
35use crate::match_each_unsigned_integer_ptype;
36use crate::validity::Validity;
37
38/// The widened offset type used for a taken `VarBinArray`: offsets are widened to at least 32 bits
39/// (to avoid overflow) while preserving signedness, so a signed result stays Arrow-compatible.
40fn taken_offset_ptype(offsets_ptype: PType) -> PType {
41    match offsets_ptype {
42        PType::U8 | PType::U16 | PType::U32 => PType::U32,
43        PType::U64 => PType::U64,
44        PType::I8 | PType::I16 | PType::I32 => PType::I32,
45        PType::I64 => PType::I64,
46        _ => unreachable!("invalid PType for offsets"),
47    }
48}
49
50impl TakeExecute for VarBin {
51    fn take(
52        array: ArrayView<'_, VarBin>,
53        indices: &ArrayRef,
54        ctx: &mut ExecutionCtx,
55    ) -> VortexResult<Option<ArrayRef>> {
56        if let Some(piecewise_indices) = indices.as_opt::<PiecewiseSequence>()
57            && let Some(taken) = take_contiguous_ranges(array, piecewise_indices, indices, ctx)?
58        {
59            return Ok(Some(taken));
60        }
61
62        // TODO(joe): Be lazy with execute
63        let offsets = array.offsets().clone().execute::<PrimitiveArray>(ctx)?;
64        let data = array.bytes();
65        let indices = indices.clone().execute::<PrimitiveArray>(ctx)?;
66        let dtype = array
67            .dtype()
68            .clone()
69            .union_nullability(indices.dtype().nullability());
70        let array_validity = array
71            .varbin_validity()
72            .execute_mask(array.as_ref().len(), ctx)?;
73        let indices_validity = indices
74            .as_ref()
75            .validity()?
76            .execute_mask(indices.as_ref().len(), ctx)?;
77
78        // Offsets and indices are non-negative; read them through their unsigned reinterpretations
79        // so we only monomorphize over the 4 unsigned widths each (4x4 instead of 8x8). On take,
80        // offsets get widened to either 32- or 64-bit (to avoid overflow); the built output offsets
81        // are reinterpreted back to `out_offset_ptype` to preserve the result's offset signedness.
82        let out_offset_ptype = taken_offset_ptype(offsets.ptype());
83        let offsets = offsets.reinterpret_cast(offsets.ptype().to_unsigned());
84        let indices = indices.reinterpret_cast(indices.ptype().to_unsigned());
85
86        let array = match_each_unsigned_integer_ptype!(indices.ptype(), |I| {
87            match offsets.ptype() {
88                PType::U8 => take::<I, u8, u32>(
89                    dtype,
90                    offsets.as_slice::<u8>(),
91                    data.as_slice(),
92                    indices.as_slice::<I>(),
93                    array_validity,
94                    indices_validity,
95                    out_offset_ptype,
96                ),
97                PType::U16 => take::<I, u16, u32>(
98                    dtype,
99                    offsets.as_slice::<u16>(),
100                    data.as_slice(),
101                    indices.as_slice::<I>(),
102                    array_validity,
103                    indices_validity,
104                    out_offset_ptype,
105                ),
106                PType::U32 => take::<I, u32, u32>(
107                    dtype,
108                    offsets.as_slice::<u32>(),
109                    data.as_slice(),
110                    indices.as_slice::<I>(),
111                    array_validity,
112                    indices_validity,
113                    out_offset_ptype,
114                ),
115                PType::U64 => take::<I, u64, u64>(
116                    dtype,
117                    offsets.as_slice::<u64>(),
118                    data.as_slice(),
119                    indices.as_slice::<I>(),
120                    array_validity,
121                    indices_validity,
122                    out_offset_ptype,
123                ),
124                _ => unreachable!("invalid PType for offsets"),
125            }
126        });
127
128        Ok(Some(array?.into_array()))
129    }
130}
131
132fn take_contiguous_ranges(
133    array: ArrayView<'_, VarBin>,
134    indices: ArrayView<'_, PiecewiseSequence>,
135    indices_ref: &ArrayRef,
136    ctx: &mut ExecutionCtx,
137) -> VortexResult<Option<ArrayRef>> {
138    let Some((starts, lengths)) = maybe_contiguous_slices(indices, ctx)? else {
139        return Ok(None);
140    };
141    let offsets = array.offsets().clone().execute::<PrimitiveArray>(ctx)?;
142    let out_offset_ptype = taken_offset_ptype(offsets.ptype());
143    let offsets = offsets.reinterpret_cast(offsets.ptype().to_unsigned());
144    let bytes = array.bytes();
145    let data = bytes.as_slice();
146    let dtype = array.dtype().clone();
147    let output_len = indices_ref.len();
148
149    let result = match lengths {
150        Columnar::Constant(lengths) => {
151            let length = constant_unsigned_usize(&lengths);
152            gather_slices_constant_dispatch(
153                &starts,
154                length,
155                &offsets,
156                data,
157                output_len,
158                out_offset_ptype,
159            )?
160        }
161        Columnar::Canonical(lengths) => {
162            let lengths = lengths.into_primitive();
163            gather_slices_dispatch(
164                &starts,
165                &lengths,
166                &offsets,
167                data,
168                output_len,
169                out_offset_ptype,
170            )?
171        }
172    };
173
174    let validity = array.validity()?.take(indices_ref)?;
175
176    // SAFETY: output offsets are built from valid input offsets, start at zero, are monotonically
177    // non-decreasing, and the copied data buffer has exactly the referenced byte length.
178    unsafe {
179        Ok(Some(
180            VarBinArray::new_unchecked(result.offsets, result.data.freeze(), dtype, validity)
181                .into_array(),
182        ))
183    }
184}
185
186fn take<Index: IntegerPType, Offset: IntegerPType, NewOffset: IntegerPType>(
187    dtype: DType,
188    offsets: &[Offset],
189    data: &[u8],
190    indices: &[Index],
191    validity_mask: Mask,
192    indices_validity_mask: Mask,
193    out_offset_ptype: PType,
194) -> VortexResult<VarBinArray> {
195    if !validity_mask.all_true() || !indices_validity_mask.all_true() {
196        return Ok(take_nullable::<Index, Offset, NewOffset>(
197            dtype,
198            offsets,
199            data,
200            indices,
201            validity_mask,
202            indices_validity_mask,
203            out_offset_ptype,
204        ));
205    }
206
207    let mut new_offsets = BufferMut::<NewOffset>::with_capacity(indices.len() + 1);
208    new_offsets.push(NewOffset::zero());
209    let mut current_offset = NewOffset::zero();
210
211    for &idx in indices {
212        let idx = idx
213            .to_usize()
214            .unwrap_or_else(|| vortex_panic!("Failed to convert index to usize: {}", idx));
215        let start = offsets[idx];
216        let stop = offsets[idx + 1];
217
218        current_offset += NewOffset::from(stop - start).vortex_expect("offset type overflow");
219        new_offsets.push(current_offset);
220    }
221
222    let mut new_data = ByteBufferMut::with_capacity(current_offset.as_());
223
224    for idx in indices {
225        let idx = idx
226            .to_usize()
227            .unwrap_or_else(|| vortex_panic!("Failed to convert index to usize: {}", idx));
228        let start = offsets[idx]
229            .to_usize()
230            .vortex_expect("Failed to cast max offset to usize");
231        let stop = offsets[idx + 1]
232            .to_usize()
233            .vortex_expect("Failed to cast max offset to usize");
234        new_data.extend_from_slice(&data[start..stop]);
235    }
236
237    let array_validity = Validity::from(dtype.nullability());
238
239    // Built unsigned; reinterpret back to the signedness-preserving result offset type.
240    let new_offsets = PrimitiveArray::new(new_offsets.freeze(), Validity::NonNullable)
241        .reinterpret_cast(out_offset_ptype)
242        .into_array();
243
244    // Safety:
245    // All variants of VarBinArray are satisfied here.
246    unsafe {
247        Ok(VarBinArray::new_unchecked(
248            new_offsets,
249            new_data.freeze(),
250            dtype,
251            array_validity,
252        ))
253    }
254}
255
256struct GatheredPiecewiseVarBin {
257    offsets: ArrayRef,
258    data: ByteBufferMut,
259}
260
261fn gather_slices_constant_dispatch(
262    starts: &PrimitiveArray,
263    length: usize,
264    offsets: &PrimitiveArray,
265    data: &[u8],
266    output_len: usize,
267    out_offset_ptype: PType,
268) -> VortexResult<GatheredPiecewiseVarBin> {
269    match_each_unsigned_integer_ptype!(starts.ptype(), |S| {
270        gather_slices_constant_start_dispatch::<S>(
271            starts,
272            length,
273            offsets,
274            data,
275            output_len,
276            out_offset_ptype,
277        )
278    })
279}
280
281fn gather_slices_constant_start_dispatch<S>(
282    starts: &PrimitiveArray,
283    length: usize,
284    offsets: &PrimitiveArray,
285    data: &[u8],
286    output_len: usize,
287    out_offset_ptype: PType,
288) -> VortexResult<GatheredPiecewiseVarBin>
289where
290    S: UnsignedPType,
291{
292    match offsets.ptype() {
293        PType::U8 => gather_slices_constant_length::<S, u8, u32>(
294            offsets.as_slice::<u8>(),
295            data,
296            starts.as_slice::<S>(),
297            length,
298            output_len,
299            out_offset_ptype,
300        ),
301        PType::U16 => gather_slices_constant_length::<S, u16, u32>(
302            offsets.as_slice::<u16>(),
303            data,
304            starts.as_slice::<S>(),
305            length,
306            output_len,
307            out_offset_ptype,
308        ),
309        PType::U32 => gather_slices_constant_length::<S, u32, u32>(
310            offsets.as_slice::<u32>(),
311            data,
312            starts.as_slice::<S>(),
313            length,
314            output_len,
315            out_offset_ptype,
316        ),
317        PType::U64 => gather_slices_constant_length::<S, u64, u64>(
318            offsets.as_slice::<u64>(),
319            data,
320            starts.as_slice::<S>(),
321            length,
322            output_len,
323            out_offset_ptype,
324        ),
325        _ => unreachable!("offsets were reinterpreted to an unsigned integer ptype"),
326    }
327}
328
329fn gather_slices_dispatch(
330    starts: &PrimitiveArray,
331    lengths: &PrimitiveArray,
332    offsets: &PrimitiveArray,
333    data: &[u8],
334    output_len: usize,
335    out_offset_ptype: PType,
336) -> VortexResult<GatheredPiecewiseVarBin> {
337    match_each_unsigned_integer_ptype!(starts.ptype(), |S| {
338        gather_slices_start_dispatch::<S>(
339            starts,
340            lengths,
341            offsets,
342            data,
343            output_len,
344            out_offset_ptype,
345        )
346    })
347}
348
349fn gather_slices_start_dispatch<S>(
350    starts: &PrimitiveArray,
351    lengths: &PrimitiveArray,
352    offsets: &PrimitiveArray,
353    data: &[u8],
354    output_len: usize,
355    out_offset_ptype: PType,
356) -> VortexResult<GatheredPiecewiseVarBin>
357where
358    S: UnsignedPType,
359{
360    match_each_unsigned_integer_ptype!(lengths.ptype(), |L| {
361        gather_slices_start_length_dispatch::<S, L>(
362            starts,
363            lengths,
364            offsets,
365            data,
366            output_len,
367            out_offset_ptype,
368        )
369    })
370}
371
372fn gather_slices_start_length_dispatch<S, L>(
373    starts: &PrimitiveArray,
374    lengths: &PrimitiveArray,
375    offsets: &PrimitiveArray,
376    data: &[u8],
377    output_len: usize,
378    out_offset_ptype: PType,
379) -> VortexResult<GatheredPiecewiseVarBin>
380where
381    S: UnsignedPType,
382    L: UnsignedPType,
383{
384    match offsets.ptype() {
385        PType::U8 => gather_slices::<S, L, u8, u32>(
386            offsets.as_slice::<u8>(),
387            data,
388            starts.as_slice::<S>(),
389            lengths.as_slice::<L>(),
390            output_len,
391            out_offset_ptype,
392        ),
393        PType::U16 => gather_slices::<S, L, u16, u32>(
394            offsets.as_slice::<u16>(),
395            data,
396            starts.as_slice::<S>(),
397            lengths.as_slice::<L>(),
398            output_len,
399            out_offset_ptype,
400        ),
401        PType::U32 => gather_slices::<S, L, u32, u32>(
402            offsets.as_slice::<u32>(),
403            data,
404            starts.as_slice::<S>(),
405            lengths.as_slice::<L>(),
406            output_len,
407            out_offset_ptype,
408        ),
409        PType::U64 => gather_slices::<S, L, u64, u64>(
410            offsets.as_slice::<u64>(),
411            data,
412            starts.as_slice::<S>(),
413            lengths.as_slice::<L>(),
414            output_len,
415            out_offset_ptype,
416        ),
417        _ => unreachable!("offsets were reinterpreted to an unsigned integer ptype"),
418    }
419}
420
421fn gather_slices_constant_length<S, Offset, NewOffset>(
422    offsets: &[Offset],
423    data: &[u8],
424    starts: &[S],
425    length: usize,
426    output_len: usize,
427    out_offset_ptype: PType,
428) -> VortexResult<GatheredPiecewiseVarBin>
429where
430    S: UnsignedPType,
431    Offset: IntegerPType,
432    NewOffset: IntegerPType,
433{
434    let computed_len = starts
435        .len()
436        .checked_mul(length)
437        .ok_or_else(|| vortex_err!("PiecewiseSequenceArray output length overflows usize"))?;
438    vortex_ensure!(
439        computed_len == output_len,
440        "PiecewiseSequenceArray expanded length {computed_len} does not match declared length {output_len}"
441    );
442
443    let mut new_offsets = BufferMut::<NewOffset>::with_capacity(output_len + 1);
444    new_offsets.push(NewOffset::zero());
445    let mut output_bytes = 0usize;
446
447    for start in starts {
448        let start = start.as_();
449        if length == 0 {
450            continue;
451        }
452
453        let offset_range = &offsets[start..][..=length];
454        let byte_start = offset_range[0].as_();
455        let byte_end = offset_range[length].as_();
456        vortex_ensure!(
457            byte_start <= byte_end && byte_end <= data.len(),
458            "VarBin offsets range {byte_start}..{byte_end} exceeds data length {}",
459            data.len()
460        );
461
462        for &offset in &offset_range[1..] {
463            let offset = offset.as_();
464            let relative = offset.checked_sub(byte_start).ok_or_else(|| {
465                vortex_err!("VarBin offsets are not monotonic at offset {offset}")
466            })?;
467            let output_offset = output_bytes.checked_add(relative).ok_or_else(|| {
468                vortex_err!("PiecewiseSequence VarBin output byte length overflow")
469            })?;
470            new_offsets.push(new_offset_value::<NewOffset>(output_offset)?);
471        }
472
473        output_bytes = output_bytes
474            .checked_add(byte_end - byte_start)
475            .ok_or_else(|| vortex_err!("PiecewiseSequence VarBin output byte length overflow"))?;
476    }
477
478    let mut new_data = ByteBufferMut::with_capacity(output_bytes);
479    let spare = &mut new_data.spare_capacity_mut()[..output_bytes];
480    let mut cursor = 0usize;
481    for start in starts {
482        let start = start.as_();
483        if length == 0 {
484            continue;
485        }
486
487        let offset_range = &offsets[start..][..=length];
488        let byte_start = offset_range[0].as_();
489        let byte_end = offset_range[length].as_();
490        let src = &data[byte_start..byte_end];
491        // SAFETY: `src` and the checked `spare` range have equal lengths and cannot overlap.
492        unsafe {
493            ptr::copy_nonoverlapping(
494                src.as_ptr(),
495                spare[cursor..][..src.len()].as_mut_ptr().cast::<u8>(),
496                src.len(),
497            );
498        }
499        cursor += src.len();
500    }
501    // SAFETY: the loop initialized the prefix `0..cursor` of the spare capacity.
502    unsafe { new_data.set_len(cursor) };
503    vortex_ensure!(
504        new_data.len() == output_bytes,
505        "PiecewiseSequenceArray gathered byte length {} does not match declared byte length {output_bytes}",
506        new_data.len()
507    );
508
509    let offsets = PrimitiveArray::new(new_offsets.freeze(), Validity::NonNullable)
510        .reinterpret_cast(out_offset_ptype)
511        .into_array();
512    Ok(GatheredPiecewiseVarBin {
513        offsets,
514        data: new_data,
515    })
516}
517
518fn gather_slices<S, L, Offset, NewOffset>(
519    offsets: &[Offset],
520    data: &[u8],
521    starts: &[S],
522    lengths: &[L],
523    output_len: usize,
524    out_offset_ptype: PType,
525) -> VortexResult<GatheredPiecewiseVarBin>
526where
527    S: UnsignedPType,
528    L: UnsignedPType,
529    Offset: IntegerPType,
530    NewOffset: IntegerPType,
531{
532    let mut new_offsets = BufferMut::<NewOffset>::with_capacity(output_len + 1);
533    new_offsets.push(NewOffset::zero());
534    let mut output_bytes = 0usize;
535
536    for (&start, &length) in starts.iter().zip_eq(lengths) {
537        let start = start.as_();
538        let length = length.as_();
539        if length == 0 {
540            continue;
541        }
542
543        let offset_range = &offsets[start..][..=length];
544        let byte_start = offset_range[0].as_();
545        let byte_end = offset_range[length].as_();
546        vortex_ensure!(
547            byte_start <= byte_end && byte_end <= data.len(),
548            "VarBin offsets range {byte_start}..{byte_end} exceeds data length {}",
549            data.len()
550        );
551
552        for &offset in &offset_range[1..] {
553            let offset = offset.as_();
554            let relative = offset.checked_sub(byte_start).ok_or_else(|| {
555                vortex_err!("VarBin offsets are not monotonic at offset {offset}")
556            })?;
557            let output_offset = output_bytes.checked_add(relative).ok_or_else(|| {
558                vortex_err!("PiecewiseSequence VarBin output byte length overflow")
559            })?;
560            new_offsets.push(new_offset_value::<NewOffset>(output_offset)?);
561        }
562
563        output_bytes = output_bytes
564            .checked_add(byte_end - byte_start)
565            .ok_or_else(|| vortex_err!("PiecewiseSequence VarBin output byte length overflow"))?;
566    }
567    vortex_ensure!(
568        new_offsets.len() == output_len + 1,
569        "PiecewiseSequenceArray expanded length {} does not match declared length {output_len}",
570        new_offsets.len() - 1
571    );
572
573    let mut new_data = ByteBufferMut::with_capacity(output_bytes);
574    let spare = &mut new_data.spare_capacity_mut()[..output_bytes];
575    let mut cursor = 0usize;
576    for (&start, &length) in starts.iter().zip_eq(lengths) {
577        let start = start.as_();
578        let length = length.as_();
579        if length == 0 {
580            continue;
581        }
582
583        let offset_range = &offsets[start..][..=length];
584        let byte_start = offset_range[0].as_();
585        let byte_end = offset_range[length].as_();
586        let src = &data[byte_start..byte_end];
587        // SAFETY: `src` and the checked `spare` range have equal lengths and cannot overlap.
588        unsafe {
589            ptr::copy_nonoverlapping(
590                src.as_ptr(),
591                spare[cursor..][..src.len()].as_mut_ptr().cast::<u8>(),
592                src.len(),
593            );
594        }
595        cursor += src.len();
596    }
597    // SAFETY: the loop initialized the prefix `0..cursor` of the spare capacity.
598    unsafe { new_data.set_len(cursor) };
599    vortex_ensure!(
600        new_data.len() == output_bytes,
601        "PiecewiseSequenceArray gathered byte length {} does not match declared byte length {output_bytes}",
602        new_data.len()
603    );
604
605    let offsets = PrimitiveArray::new(new_offsets.freeze(), Validity::NonNullable)
606        .reinterpret_cast(out_offset_ptype)
607        .into_array();
608    Ok(GatheredPiecewiseVarBin {
609        offsets,
610        data: new_data,
611    })
612}
613
614fn new_offset_value<T: IntegerPType>(value: usize) -> VortexResult<T> {
615    T::from(value).ok_or_else(|| {
616        vortex_err!(
617            "PiecewiseSequence VarBin offset value {value} does not fit in {}",
618            T::PTYPE
619        )
620    })
621}
622
623fn take_nullable<Index: IntegerPType, Offset: IntegerPType, NewOffset: IntegerPType>(
624    dtype: DType,
625    offsets: &[Offset],
626    data: &[u8],
627    indices: &[Index],
628    data_validity: Mask,
629    indices_validity: Mask,
630    out_offset_ptype: PType,
631) -> VarBinArray {
632    let mut new_offsets = BufferMut::<NewOffset>::with_capacity(indices.len() + 1);
633    new_offsets.push(NewOffset::zero());
634    let mut current_offset = NewOffset::zero();
635
636    let mut validity_buffer = BitBufferMut::with_capacity(indices.len());
637
638    // Convert indices once and store valid ones with their positions
639    let mut valid_indices = Vec::with_capacity(indices.len());
640
641    // First pass: calculate offsets and validity
642    for (data_idx, index_valid) in indices.iter().zip(indices_validity.iter()) {
643        if !index_valid {
644            validity_buffer.append(false);
645            new_offsets.push(current_offset);
646            continue;
647        }
648        let data_idx_usize = data_idx
649            .to_usize()
650            .unwrap_or_else(|| vortex_panic!("Failed to convert index to usize: {}", data_idx));
651        if data_validity.value(data_idx_usize) {
652            validity_buffer.append(true);
653            let start = offsets[data_idx_usize];
654            let stop = offsets[data_idx_usize + 1];
655            current_offset += NewOffset::from(stop - start).vortex_expect("offset type overflow");
656            new_offsets.push(current_offset);
657            valid_indices.push(data_idx_usize);
658        } else {
659            validity_buffer.append(false);
660            new_offsets.push(current_offset);
661        }
662    }
663
664    let mut new_data = ByteBufferMut::with_capacity(current_offset.as_());
665
666    // Second pass: copy data for valid indices only
667    for data_idx in valid_indices {
668        let start = offsets[data_idx]
669            .to_usize()
670            .vortex_expect("Failed to cast max offset to usize");
671        let stop = offsets[data_idx + 1]
672            .to_usize()
673            .vortex_expect("Failed to cast max offset to usize");
674        new_data.extend_from_slice(&data[start..stop]);
675    }
676
677    let array_validity = Validity::from(validity_buffer.freeze());
678
679    // Built unsigned; reinterpret back to the signedness-preserving result offset type.
680    let new_offsets = PrimitiveArray::new(new_offsets.freeze(), Validity::NonNullable)
681        .reinterpret_cast(out_offset_ptype)
682        .into_array();
683
684    // Safety:
685    // All variants of VarBinArray are satisfied here.
686    unsafe { VarBinArray::new_unchecked(new_offsets, new_data.freeze(), dtype, array_validity) }
687}
688
689#[cfg(test)]
690mod tests {
691    use std::iter;
692
693    use rstest::rstest;
694    use vortex_buffer::ByteBuffer;
695    use vortex_buffer::buffer;
696
697    use crate::IntoArray;
698    use crate::VortexSessionExecute;
699    use crate::array_session;
700    use crate::arrays::VarBinArray;
701    use crate::arrays::VarBinViewArray;
702    use crate::arrays::varbin::compute::take::PrimitiveArray;
703    use crate::assert_arrays_eq;
704    use crate::compute::conformance::take::test_take_conformance;
705    use crate::dtype::DType;
706    use crate::dtype::Nullability;
707    use crate::validity::Validity;
708
709    #[test]
710    fn test_null_take() {
711        let arr = VarBinArray::from_iter([Some("h")], DType::Utf8(Nullability::NonNullable));
712
713        let idx1: PrimitiveArray = (0..1).collect();
714
715        assert_eq!(
716            arr.take(idx1.into_array()).unwrap().dtype(),
717            &DType::Utf8(Nullability::NonNullable)
718        );
719
720        let idx2: PrimitiveArray = PrimitiveArray::from_option_iter(vec![Some(0)]);
721
722        assert_eq!(
723            arr.take(idx2.into_array()).unwrap().dtype(),
724            &DType::Utf8(Nullability::Nullable)
725        );
726    }
727
728    #[rstest]
729    #[case(VarBinArray::from_iter(
730        ["hello", "world", "test", "data", "array"].map(Some),
731        DType::Utf8(Nullability::NonNullable),
732    ))]
733    #[case(VarBinArray::from_iter(
734        [Some("hello"), None, Some("test"), Some("data"), None],
735        DType::Utf8(Nullability::Nullable),
736    ))]
737    #[case(VarBinArray::from_iter(
738        [b"hello".as_slice(), b"world", b"test", b"data", b"array"].map(Some),
739        DType::Binary(Nullability::NonNullable),
740    ))]
741    #[case(VarBinArray::from_iter(["single"].map(Some), DType::Utf8(Nullability::NonNullable)))]
742    fn test_take_varbin_conformance(#[case] array: VarBinArray) {
743        test_take_conformance(
744            &array.into_array(),
745            &mut array_session().create_execution_ctx(),
746        );
747    }
748
749    #[test]
750    fn test_take_overflow() {
751        let mut ctx = array_session().create_execution_ctx();
752        let scream = iter::once("a").cycle().take(128).collect::<String>();
753        let bytes = ByteBuffer::copy_from(scream.as_bytes());
754        let offsets = buffer![0u8, 128u8].into_array();
755
756        let array = VarBinArray::new(
757            offsets,
758            bytes,
759            DType::Utf8(Nullability::NonNullable),
760            Validity::NonNullable,
761        );
762
763        let indices = buffer![0u32; 3].into_array();
764        let taken = array.take(indices).unwrap();
765
766        let expected = VarBinViewArray::from_iter(
767            [Some(scream.clone()), Some(scream.clone()), Some(scream)],
768            DType::Utf8(Nullability::NonNullable),
769        );
770        assert_arrays_eq!(expected, taken, &mut ctx);
771    }
772}