Skip to main content

vortex_array/arrays/varbinview/
array.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4use std::fmt::Display;
5use std::fmt::Formatter;
6use std::mem::size_of;
7use std::sync::Arc;
8
9use smallvec::smallvec;
10use vortex_buffer::Alignment;
11use vortex_buffer::Buffer;
12use vortex_buffer::ByteBuffer;
13use vortex_error::VortexExpect;
14use vortex_error::VortexResult;
15use vortex_error::vortex_bail;
16use vortex_error::vortex_ensure;
17use vortex_error::vortex_err;
18use vortex_error::vortex_panic;
19
20use crate::ArrayRef;
21use crate::ArraySlots;
22use crate::VortexSessionExecute;
23use crate::array::Array;
24use crate::array::ArrayParts;
25use crate::array::TypedArrayRef;
26use crate::array::child_to_validity;
27use crate::array::validity_to_child;
28use crate::array_slots;
29use crate::arrays::VarBinView;
30use crate::arrays::varbinview::BinaryView;
31use crate::buffer::BufferHandle;
32use crate::builders::ArrayBuilder;
33use crate::builders::VarBinViewBuilder;
34use crate::dtype::DType;
35use crate::dtype::Nullability;
36use crate::legacy_session;
37use crate::validity::Validity;
38
39#[array_slots(VarBinView)]
40pub struct VarBinViewSlots {
41    /// The validity bitmap indicating which elements are non-null.
42    pub validity: Option<ArrayRef>,
43}
44
45/// A variable-length binary view array that stores strings and binary data efficiently.
46///
47/// This mirrors the Apache Arrow StringView/BinaryView array encoding and provides
48/// an optimized representation for variable-length data with excellent performance
49/// characteristics for both short and long strings.
50///
51/// ## Data Layout
52///
53/// The array uses a hybrid storage approach with two main components:
54/// - **Views buffer**: Array of 16-byte `BinaryView` entries (one per logical element)
55/// - **Data buffers**: Shared backing storage for strings longer than 12 bytes
56///
57/// ## View Structure
58///
59/// Commonly referred to as "German Strings", each 16-byte view entry contains either:
60/// - **Inlined data**: For strings ≤ 12 bytes, the entire string is stored directly in the view
61/// - **Reference data**: For strings > 12 bytes, contains:
62///   - String length (4 bytes)
63///   - First 4 bytes of string as prefix (4 bytes)
64///   - Buffer index and offset (8 bytes total)
65///
66/// The following ASCII graphic is reproduced verbatim from the Arrow documentation:
67///
68/// ```text
69///                         ┌──────┬────────────────────────┐
70///                         │length│      string value      │
71///    Strings (len <= 12)  │      │    (padded with 0)     │
72///                         └──────┴────────────────────────┘
73///                          0    31                      127
74///
75///                         ┌───────┬───────┬───────┬───────┐
76///                         │length │prefix │  buf  │offset │
77///    Strings (len > 12)   │       │       │ index │       │
78///                         └───────┴───────┴───────┴───────┘
79///                          0    31       63      95    127
80/// ```
81///
82/// # Examples
83///
84/// ```
85/// use vortex_array::arrays::VarBinViewArray;
86/// use vortex_array::dtype::{DType, Nullability};
87/// use vortex_array::IntoArray;
88///
89/// // Create from an Iterator<Item = &str>
90/// let array = VarBinViewArray::from_iter_str([
91///         "inlined",
92///         "this string is outlined"
93/// ]);
94///
95/// assert_eq!(array.len(), 2);
96///
97/// // Access individual strings
98/// let first = array.bytes_at(0);
99/// assert_eq!(first.as_slice(), b"inlined"); // "short"
100///
101/// let second = array.bytes_at(1);
102/// assert_eq!(second.as_slice(), b"this string is outlined"); // Long string
103/// ```
104#[derive(Clone, Debug)]
105pub struct VarBinViewData {
106    pub(super) buffers: Arc<[BufferHandle]>,
107    pub(super) views: BufferHandle,
108}
109
110impl Display for VarBinViewData {
111    fn fmt(&self, _f: &mut Formatter<'_>) -> std::fmt::Result {
112        Ok(())
113    }
114}
115
116pub struct VarBinViewDataParts {
117    pub dtype: DType,
118    pub buffers: Arc<[BufferHandle]>,
119    pub views: BufferHandle,
120    pub validity: Validity,
121}
122
123impl VarBinViewData {
124    fn dtype_parts(dtype: &DType) -> VortexResult<(bool, Nullability)> {
125        match dtype {
126            DType::Utf8(nullability) => Ok((true, *nullability)),
127            DType::Binary(nullability) => Ok((false, *nullability)),
128            _ => vortex_bail!(InvalidArgument: "invalid DType {dtype} for `VarBinViewArray`"),
129        }
130    }
131
132    /// Build the slots vector for this array.
133    pub(super) fn make_slots(validity: &Validity, len: usize) -> ArraySlots {
134        smallvec![validity_to_child(validity, len)]
135    }
136
137    /// Creates a new `VarBinViewArray`.
138    ///
139    /// # Panics
140    ///
141    /// Panics if the provided components do not satisfy the invariants documented
142    /// in `VarBinViewArray::new_unchecked`.
143    pub fn new(
144        views: Buffer<BinaryView>,
145        buffers: Arc<[ByteBuffer]>,
146        dtype: DType,
147        validity: Validity,
148    ) -> Self {
149        Self::try_new(views, buffers, dtype, validity)
150            .vortex_expect("VarBinViewArray construction failed")
151    }
152
153    /// Creates a new `VarBinViewArray` with device or host memory.
154    ///
155    /// # Panics
156    ///
157    /// Panics if the provided components do not satisfy the invariants documented
158    /// in `VarBinViewArray::new_unchecked`.
159    pub fn new_handle(
160        views: BufferHandle,
161        buffers: Arc<[BufferHandle]>,
162        dtype: DType,
163        validity: Validity,
164    ) -> Self {
165        Self::try_new_handle(views, buffers, dtype, validity)
166            .vortex_expect("VarbinViewArray construction failed")
167    }
168
169    /// Constructs a new `VarBinViewArray`.
170    ///
171    /// See `VarBinViewArray::new_unchecked` for more information.
172    ///
173    /// # Errors
174    ///
175    /// Returns an error if the provided components do not satisfy the invariants documented in
176    /// `VarBinViewArray::new_unchecked`.
177    pub fn try_new(
178        views: Buffer<BinaryView>,
179        buffers: Arc<[ByteBuffer]>,
180        dtype: DType,
181        validity: Validity,
182    ) -> VortexResult<Self> {
183        Self::validate(&views, &buffers, &dtype, &validity)?;
184
185        // SAFETY: validate ensures all invariants are met.
186        Ok(unsafe { Self::new_unchecked(views, buffers, dtype, validity) })
187    }
188
189    /// Constructs a new `VarBinViewArray`.
190    ///
191    /// See `VarBinViewArray::new_unchecked` for more information.
192    ///
193    /// # Errors
194    ///
195    /// Returns an error if the provided components do not satisfy the invariants documented in
196    /// `VarBinViewArray::new_unchecked`.
197    pub fn try_new_handle(
198        views: BufferHandle,
199        buffers: Arc<[BufferHandle]>,
200        dtype: DType,
201        validity: Validity,
202    ) -> VortexResult<Self> {
203        let views_nbytes = views.len();
204        vortex_ensure!(
205            views_nbytes.is_multiple_of(size_of::<BinaryView>()),
206            "Expected views buffer length ({views_nbytes}) to be a multiple of {}",
207            size_of::<BinaryView>()
208        );
209
210        // TODO(aduffy): device validation.
211        if let Some(host) = views.as_host_opt() {
212            vortex_ensure!(
213                host.is_aligned(Alignment::of::<BinaryView>()),
214                "Views on host must be 16 byte aligned"
215            );
216        }
217
218        // SAFETY: validate ensures all invariants are met.
219        Ok(unsafe { Self::new_handle_unchecked(views, buffers, dtype, validity) })
220    }
221
222    /// Creates a new `VarBinViewArray` without validation from these components:
223    ///
224    /// * `views` is a buffer of 16-byte view entries (one per logical element).
225    /// * `buffers` contains the backing storage for strings longer than 12 bytes.
226    /// * `dtype` specifies whether this contains UTF-8 strings or binary data.
227    /// * `validity` holds the null values.
228    ///
229    /// # Safety
230    ///
231    /// The caller must ensure all of the following invariants are satisfied:
232    ///
233    /// ## View Requirements
234    ///
235    /// - Views must be properly formatted 16-byte [`BinaryView`] entries.
236    /// - Inlined views (length ≤ 12) must have valid data in the first `length` bytes.
237    /// - Reference views (length > 12) must:
238    ///   - Have a valid buffer index < `buffers.len()`.
239    ///   - Have valid offsets that don't exceed the referenced buffer's bounds.
240    ///   - Have a 4-byte prefix that matches the actual data at the referenced location.
241    ///
242    /// ## Type Requirements
243    ///
244    /// - `dtype` must be either [`DType::Utf8`] or [`DType::Binary`].
245    /// - For [`DType::Utf8`], all string data (both inlined and referenced) must be valid UTF-8.
246    ///
247    /// ## Validity Requirements
248    ///
249    /// - The validity must have the same nullability as the dtype.
250    /// - If validity is an array, its length must match `views.len()`.
251    pub unsafe fn new_unchecked(
252        views: Buffer<BinaryView>,
253        buffers: Arc<[ByteBuffer]>,
254        dtype: DType,
255        validity: Validity,
256    ) -> Self {
257        #[cfg(debug_assertions)]
258        Self::validate(&views, &buffers, &dtype, &validity)
259            .vortex_expect("[Debug Assertion]: Invalid `VarBinViewArray` parameters");
260
261        let handles: Vec<BufferHandle> = buffers
262            .iter()
263            .cloned()
264            .map(BufferHandle::new_host)
265            .collect();
266
267        let handles = Arc::from(handles);
268        let view_handle = BufferHandle::new_host(views.into_byte_buffer());
269        unsafe { Self::new_handle_unchecked(view_handle, handles, dtype, validity) }
270    }
271
272    /// Construct a new array from `BufferHandle`s without validation.
273    ///
274    /// # Safety
275    ///
276    /// See documentation in `new_unchecked`.
277    pub unsafe fn new_handle_unchecked(
278        views: BufferHandle,
279        buffers: Arc<[BufferHandle]>,
280        dtype: DType,
281        _validity: Validity,
282    ) -> Self {
283        let _ =
284            Self::dtype_parts(&dtype).vortex_expect("VarBinViewArray dtype must be utf8 or binary");
285        Self { buffers, views }
286    }
287
288    /// Validates the components that would be used to create a `VarBinViewArray`.
289    ///
290    /// This function checks all the invariants required by `VarBinViewArray::new_unchecked`.
291    pub fn validate(
292        views: &Buffer<BinaryView>,
293        buffers: &Arc<[ByteBuffer]>,
294        dtype: &DType,
295        validity: &Validity,
296    ) -> VortexResult<()> {
297        vortex_ensure!(
298            validity.nullability() == dtype.nullability(),
299            InvalidArgument: "validity {:?} incompatible with nullability {:?}",
300            validity,
301            dtype.nullability()
302        );
303
304        match dtype {
305            DType::Utf8(_) => Self::validate_views(views, buffers, validity, |string| {
306                simdutf8::basic::from_utf8(string).is_ok()
307            })?,
308            DType::Binary(_) => Self::validate_views(views, buffers, validity, |_| true)?,
309            _ => vortex_bail!(InvalidArgument: "invalid DType {dtype} for `VarBinViewArray`"),
310        }
311
312        Ok(())
313    }
314
315    #[allow(clippy::disallowed_methods)]
316    fn validate_views<F>(
317        views: &Buffer<BinaryView>,
318        buffers: &Arc<[ByteBuffer]>,
319        validity: &Validity,
320        validator: F,
321    ) -> VortexResult<()>
322    where
323        F: Fn(&[u8]) -> bool,
324    {
325        let validate_view = |idx: usize, view: &BinaryView| -> VortexResult<()> {
326            if view.is_inlined() {
327                // Validate the inline bytestring
328                let bytes = &view.as_inlined().data[..view.len() as usize];
329                vortex_ensure!(
330                    validator(bytes),
331                    InvalidArgument: "view at index {idx}: inlined bytes failed utf-8 validation"
332                );
333            } else {
334                // Validate the view pointer
335                let view = view.as_view();
336                let buf_index = view.buffer_index as usize;
337                let start_offset = view.offset as usize;
338                let end_offset = start_offset.saturating_add(view.size as usize);
339
340                let buf = buffers.get(buf_index).ok_or_else(||
341                    vortex_err!(InvalidArgument: "view at index {idx} references invalid buffer: {buf_index} out of bounds for VarBinViewData with {} buffers",
342                        buffers.len()))?;
343
344                vortex_ensure!(
345                    start_offset < buf.len(),
346                    InvalidArgument: "start offset {start_offset} out of bounds for buffer {buf_index} with size {}",
347                    buf.len(),
348                );
349
350                vortex_ensure!(
351                    end_offset <= buf.len(),
352                    InvalidArgument: "end offset {end_offset} out of bounds for buffer {buf_index} with size {}",
353                    buf.len(),
354                );
355
356                // Make sure the prefix data matches the buffer data.
357                let bytes = &buf[start_offset..end_offset];
358                vortex_ensure!(
359                    view.prefix == bytes[..4],
360                    InvalidArgument: "VarBinView prefix does not match full string"
361                );
362
363                // Validate the full string
364                vortex_ensure!(
365                    validator(bytes),
366                    InvalidArgument: "view at index {idx}: outlined bytes fails utf-8 validation"
367                );
368            }
369            Ok(())
370        };
371
372        match validity {
373            // Array-backed validity is the only variant that needs an execution context: execute it
374            // into a mask once and zip it with the views, validating only the valid (non-null)
375            // entries.
376            Validity::Array(_) => {
377                let mut ctx = legacy_session().create_execution_ctx();
378                let mask = validity.execute_mask(views.len(), &mut ctx)?;
379                for ((idx, view), valid) in views.iter().enumerate().zip(mask.iter()) {
380                    if valid {
381                        validate_view(idx, view)?;
382                    }
383                }
384            }
385            // Every entry is null, so there is nothing to validate.
386            Validity::AllInvalid => {}
387            // No nulls: validate every view.
388            Validity::NonNullable | Validity::AllValid => {
389                for (idx, view) in views.iter().enumerate() {
390                    validate_view(idx, view)?;
391                }
392            }
393        }
394
395        Ok(())
396    }
397
398    /// Returns the length of this array.
399    pub fn len(&self) -> usize {
400        self.views.len() / size_of::<BinaryView>()
401    }
402
403    /// Returns `true` if this array is empty.
404    pub fn is_empty(&self) -> bool {
405        self.len() == 0
406    }
407
408    /// Access to the primitive views buffer.
409    ///
410    /// Variable-sized binary view buffer contain a "view" child array, with 16-byte entries that
411    /// contain either a pointer into one of the array's owned `buffer`s OR an inlined copy of
412    /// the string (if the string has 12 bytes or fewer).
413    #[inline]
414    pub fn views(&self) -> &[BinaryView] {
415        let host_views = self.views.as_host();
416        let len = host_views.len() / size_of::<BinaryView>();
417
418        // SAFETY: data alignment is checked for host buffers on construction
419        unsafe { std::slice::from_raw_parts(host_views.as_ptr().cast(), len) }
420    }
421
422    /// Return the buffer handle backing the views.
423    pub fn views_handle(&self) -> &BufferHandle {
424        &self.views
425    }
426
427    /// Access value bytes at a given index
428    ///
429    /// Will return a `ByteBuffer` containing the data without performing a copy.
430    #[inline]
431    pub fn bytes_at(&self, index: usize) -> ByteBuffer {
432        let views = self.views();
433        let view = &views[index];
434        // Expect this to be the common case: strings > 12 bytes.
435        if !view.is_inlined() {
436            let view_ref = view.as_view();
437            self.buffer(view_ref.buffer_index as usize)
438                .slice(view_ref.as_range())
439        } else {
440            // Return access to the range of bytes around it.
441            self.views_handle()
442                .as_host()
443                .clone()
444                .into_byte_buffer()
445                .slice_ref(view.as_inlined().value())
446        }
447    }
448
449    /// Access one of the backing data buffers.
450    ///
451    /// # Panics
452    ///
453    /// This method panics if the provided index is out of bounds for the set of buffers provided
454    /// at construction time.
455    #[inline]
456    pub fn buffer(&self, idx: usize) -> &ByteBuffer {
457        if idx >= self.data_buffers().len() {
458            vortex_panic!(
459                "{idx} buffer index out of bounds, there are {} buffers",
460                self.data_buffers().len()
461            );
462        }
463        self.buffers[idx].as_host()
464    }
465
466    /// The underlying raw data buffers, not including the views buffer.
467    #[inline]
468    pub fn data_buffers(&self) -> &Arc<[BufferHandle]> {
469        &self.buffers
470    }
471
472    /// Accumulate an iterable set of values into our type here.
473    #[expect(
474        clippy::same_name_method,
475        reason = "intentionally named from_iter like Iterator::from_iter"
476    )]
477    pub fn from_iter<T: AsRef<[u8]>, I: IntoIterator<Item = Option<T>>>(
478        iter: I,
479        dtype: DType,
480    ) -> Self {
481        let iter = iter.into_iter();
482        let mut builder = VarBinViewBuilder::with_capacity(dtype, iter.size_hint().0);
483
484        for item in iter {
485            match item {
486                None => builder.append_null(),
487                Some(v) => builder.append_value(v),
488            }
489        }
490
491        builder.finish_into_varbinview().into_data()
492    }
493
494    pub fn from_iter_str<T: AsRef<str>, I: IntoIterator<Item = T>>(iter: I) -> Self {
495        let iter = iter.into_iter();
496        let mut builder = VarBinViewBuilder::with_capacity(
497            DType::Utf8(Nullability::NonNullable),
498            iter.size_hint().0,
499        );
500
501        for item in iter {
502            builder.append_value(item.as_ref());
503        }
504
505        builder.finish_into_varbinview().into_data()
506    }
507
508    pub fn from_iter_nullable_str<T: AsRef<str>, I: IntoIterator<Item = Option<T>>>(
509        iter: I,
510    ) -> Self {
511        let iter = iter.into_iter();
512        let mut builder = VarBinViewBuilder::with_capacity(
513            DType::Utf8(Nullability::Nullable),
514            iter.size_hint().0,
515        );
516
517        for item in iter {
518            match item {
519                None => builder.append_null(),
520                Some(v) => builder.append_value(v.as_ref()),
521            }
522        }
523
524        builder.finish_into_varbinview().into_data()
525    }
526
527    pub fn from_iter_bin<T: AsRef<[u8]>, I: IntoIterator<Item = T>>(iter: I) -> Self {
528        let iter = iter.into_iter();
529        let mut builder = VarBinViewBuilder::with_capacity(
530            DType::Binary(Nullability::NonNullable),
531            iter.size_hint().0,
532        );
533
534        for item in iter {
535            builder.append_value(item.as_ref());
536        }
537
538        builder.finish_into_varbinview().into_data()
539    }
540
541    pub fn from_iter_nullable_bin<T: AsRef<[u8]>, I: IntoIterator<Item = Option<T>>>(
542        iter: I,
543    ) -> Self {
544        let iter = iter.into_iter();
545        let mut builder = VarBinViewBuilder::with_capacity(
546            DType::Binary(Nullability::Nullable),
547            iter.size_hint().0,
548        );
549
550        for item in iter {
551            match item {
552                None => builder.append_null(),
553                Some(v) => builder.append_value(v.as_ref()),
554            }
555        }
556
557        builder.finish_into_varbinview().into_data()
558    }
559}
560
561pub trait VarBinViewArrayExt: TypedArrayRef<VarBinView> {
562    fn dtype_parts(&self) -> (bool, Nullability) {
563        match self.as_ref().dtype() {
564            DType::Utf8(nullability) => (true, *nullability),
565            DType::Binary(nullability) => (false, *nullability),
566            _ => unreachable!("VarBinViewArrayExt requires a utf8 or binary dtype"),
567        }
568    }
569
570    fn varbinview_validity(&self) -> Validity {
571        child_to_validity(
572            self.as_ref().slots()[VarBinViewSlots::VALIDITY].as_ref(),
573            self.dtype_parts().1,
574        )
575    }
576}
577impl<T: TypedArrayRef<VarBinView>> VarBinViewArrayExt for T {}
578
579impl Array<VarBinView> {
580    #[inline]
581    fn from_prevalidated_data(dtype: DType, data: VarBinViewData, slots: ArraySlots) -> Self {
582        let len = data.len();
583        unsafe {
584            Array::from_parts_unchecked(
585                ArrayParts::new(VarBinView, dtype, len, data).with_slots(slots),
586            )
587        }
588    }
589
590    /// Construct a `VarBinViewArray` from an iterator of optional byte slices.
591    #[expect(
592        clippy::same_name_method,
593        reason = "intentionally named from_iter like Iterator::from_iter"
594    )]
595    pub fn from_iter<T: AsRef<[u8]>, I: IntoIterator<Item = Option<T>>>(
596        iter: I,
597        dtype: DType,
598    ) -> Self {
599        let iter = iter.into_iter();
600        let mut builder = VarBinViewBuilder::with_capacity(dtype, iter.size_hint().0);
601        for value in iter {
602            match value {
603                Some(value) => builder.append_value(value),
604                None => builder.append_null(),
605            }
606        }
607        builder.finish_into_varbinview()
608    }
609
610    pub fn from_iter_str<T: AsRef<str>, I: IntoIterator<Item = T>>(iter: I) -> Self {
611        let iter = iter.into_iter();
612        let mut builder = VarBinViewBuilder::with_capacity(
613            DType::Utf8(Nullability::NonNullable),
614            iter.size_hint().0,
615        );
616        for value in iter {
617            builder.append_value(value.as_ref());
618        }
619        builder.finish_into_varbinview()
620    }
621
622    pub fn from_iter_nullable_str<T: AsRef<str>, I: IntoIterator<Item = Option<T>>>(
623        iter: I,
624    ) -> Self {
625        let iter = iter.into_iter();
626        let mut builder = VarBinViewBuilder::with_capacity(
627            DType::Utf8(Nullability::Nullable),
628            iter.size_hint().0,
629        );
630        for value in iter {
631            match value {
632                Some(value) => builder.append_value(value.as_ref()),
633                None => builder.append_null(),
634            }
635        }
636        builder.finish_into_varbinview()
637    }
638
639    pub fn from_iter_bin<T: AsRef<[u8]>, I: IntoIterator<Item = T>>(iter: I) -> Self {
640        let iter = iter.into_iter();
641        let mut builder = VarBinViewBuilder::with_capacity(
642            DType::Binary(Nullability::NonNullable),
643            iter.size_hint().0,
644        );
645        for value in iter {
646            builder.append_value(value.as_ref());
647        }
648        builder.finish_into_varbinview()
649    }
650
651    pub fn from_iter_nullable_bin<T: AsRef<[u8]>, I: IntoIterator<Item = Option<T>>>(
652        iter: I,
653    ) -> Self {
654        let iter = iter.into_iter();
655        let mut builder = VarBinViewBuilder::with_capacity(
656            DType::Binary(Nullability::Nullable),
657            iter.size_hint().0,
658        );
659        for value in iter {
660            match value {
661                Some(value) => builder.append_value(value.as_ref()),
662                None => builder.append_null(),
663            }
664        }
665        builder.finish_into_varbinview()
666    }
667
668    /// Creates a new `VarBinViewArray`.
669    pub fn try_new(
670        views: Buffer<BinaryView>,
671        buffers: Arc<[ByteBuffer]>,
672        dtype: DType,
673        validity: Validity,
674    ) -> VortexResult<Self> {
675        let data = VarBinViewData::try_new(views, buffers, dtype.clone(), validity.clone())?;
676        let slots = VarBinViewData::make_slots(&validity, data.len());
677        Ok(Self::from_prevalidated_data(dtype, data, slots))
678    }
679
680    /// Creates a new `VarBinViewArray` without validation.
681    ///
682    /// # Safety
683    ///
684    /// See [`VarBinViewData::new_unchecked`].
685    pub unsafe fn new_unchecked(
686        views: Buffer<BinaryView>,
687        buffers: Arc<[ByteBuffer]>,
688        dtype: DType,
689        validity: Validity,
690    ) -> Self {
691        let data = unsafe {
692            VarBinViewData::new_unchecked(views, buffers, dtype.clone(), validity.clone())
693        };
694        let slots = VarBinViewData::make_slots(&validity, data.len());
695        Self::from_prevalidated_data(dtype, data, slots)
696    }
697
698    /// Creates a new `VarBinViewArray` with device or host memory.
699    pub fn new_handle(
700        views: BufferHandle,
701        buffers: Arc<[BufferHandle]>,
702        dtype: DType,
703        validity: Validity,
704    ) -> Self {
705        let data = VarBinViewData::new_handle(views, buffers, dtype.clone(), validity.clone());
706        let slots = VarBinViewData::make_slots(&validity, data.len());
707        Self::from_prevalidated_data(dtype, data, slots)
708    }
709
710    /// Construct a new array from `BufferHandle`s without validation.
711    ///
712    /// # Safety
713    ///
714    /// See [`VarBinViewData::new_handle_unchecked`].
715    pub unsafe fn new_handle_unchecked(
716        views: BufferHandle,
717        buffers: Arc<[BufferHandle]>,
718        dtype: DType,
719        validity: Validity,
720    ) -> Self {
721        let data = unsafe {
722            VarBinViewData::new_handle_unchecked(views, buffers, dtype.clone(), validity.clone())
723        };
724        let slots = VarBinViewData::make_slots(&validity, data.len());
725        Self::from_prevalidated_data(dtype, data, slots)
726    }
727
728    pub fn into_data_parts(self) -> VarBinViewDataParts {
729        let dtype = self.dtype().clone();
730        let validity = self.varbinview_validity();
731        let data = self.into_data();
732        VarBinViewDataParts {
733            dtype,
734            buffers: data.buffers,
735            views: data.views,
736            validity,
737        }
738    }
739}
740
741impl<'a> FromIterator<Option<&'a [u8]>> for VarBinViewData {
742    fn from_iter<T: IntoIterator<Item = Option<&'a [u8]>>>(iter: T) -> Self {
743        Self::from_iter_nullable_bin(iter)
744    }
745}
746
747impl FromIterator<Option<Vec<u8>>> for VarBinViewData {
748    fn from_iter<T: IntoIterator<Item = Option<Vec<u8>>>>(iter: T) -> Self {
749        Self::from_iter_nullable_bin(iter)
750    }
751}
752
753impl FromIterator<Option<String>> for VarBinViewData {
754    fn from_iter<T: IntoIterator<Item = Option<String>>>(iter: T) -> Self {
755        Self::from_iter_nullable_str(iter)
756    }
757}
758
759impl<'a> FromIterator<Option<&'a str>> for VarBinViewData {
760    fn from_iter<T: IntoIterator<Item = Option<&'a str>>>(iter: T) -> Self {
761        Self::from_iter_nullable_str(iter)
762    }
763}
764
765// --- FromIterator forwarding for Array<VarBinView> ---
766
767impl<'a> FromIterator<Option<&'a [u8]>> for Array<VarBinView> {
768    fn from_iter<T: IntoIterator<Item = Option<&'a [u8]>>>(iter: T) -> Self {
769        Self::from_iter(iter, DType::Binary(Nullability::Nullable))
770    }
771}
772
773impl FromIterator<Option<Vec<u8>>> for Array<VarBinView> {
774    fn from_iter<T: IntoIterator<Item = Option<Vec<u8>>>>(iter: T) -> Self {
775        Self::from_iter(iter, DType::Binary(Nullability::Nullable))
776    }
777}
778
779impl FromIterator<Option<String>> for Array<VarBinView> {
780    fn from_iter<T: IntoIterator<Item = Option<String>>>(iter: T) -> Self {
781        Self::from_iter_nullable_str(iter)
782    }
783}
784
785impl<'a> FromIterator<Option<&'a str>> for Array<VarBinView> {
786    fn from_iter<T: IntoIterator<Item = Option<&'a str>>>(iter: T) -> Self {
787        Self::from_iter_nullable_str(iter)
788    }
789}