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