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