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