Skip to main content

arrow_array/array/
byte_view_array.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use crate::array::print_long_array;
19use crate::builder::{ArrayBuilder, GenericByteViewBuilder};
20use crate::iterator::ArrayIter;
21use crate::types::bytes::ByteArrayNativeType;
22use crate::types::{BinaryViewType, ByteViewType, StringViewType};
23use crate::{Array, ArrayAccessor, ArrayRef, GenericByteArray, OffsetSizeTrait, Scalar};
24use arrow_buffer::{ArrowNativeType, Buffer, NullBuffer, ScalarBuffer};
25use arrow_data::{ArrayData, ArrayDataBuilder, ByteView, MAX_INLINE_VIEW_LEN};
26use arrow_schema::{ArrowError, DataType};
27use core::str;
28use num_traits::ToPrimitive;
29use std::any::Any;
30use std::cmp::Ordering;
31use std::fmt::Debug;
32use std::marker::PhantomData;
33use std::sync::Arc;
34
35use super::ByteArrayType;
36
37/// [Variable-size Binary View Layout]: An array of variable length bytes views.
38///
39/// This array type is used to store variable length byte data (e.g. Strings, Binary)
40/// and has efficient operations such as `take`, `filter`, and comparison.
41///
42/// [Variable-size Binary View Layout]: https://arrow.apache.org/docs/format/Columnar.html#variable-size-binary-view-layout
43///
44/// This is different from [`GenericByteArray`], which also stores variable
45/// length byte data, as it represents strings with an offset and length. `take`
46/// and `filter` like operations are implemented by manipulating the "views"
47/// (`u128`) without modifying the bytes. Each view also stores an inlined
48/// prefix which speed up comparisons.
49///
50/// # See Also
51///
52/// * [`StringViewArray`] for storing utf8 encoded string data
53/// * [`BinaryViewArray`] for storing bytes
54/// * [`ByteView`] to interpret `u128`s layout of the views.
55///
56/// [`ByteView`]: arrow_data::ByteView
57///
58/// # Layout: "views" and buffers
59///
60/// A `GenericByteViewArray` stores variable length byte strings. An array of
61/// `N` elements is stored as `N` fixed length "views" and a variable number
62/// of variable length "buffers".
63///
64/// Each view is a `u128` value whose layout is different depending on the
65/// length of the string stored at that location:
66///
67/// ```text
68///                         ┌──────┬────────────────────────┐
69///                         │length│      string value      │
70///    Strings (len <= 12)  │      │    (padded with 0)     │
71///                         └──────┴────────────────────────┘
72///                          0    31                      127
73///
74///                         ┌───────┬───────┬───────┬───────┐
75///                         │length │prefix │  buf  │offset │
76///    Strings (len > 12)   │       │       │ index │       │
77///                         └───────┴───────┴───────┴───────┘
78///                          0    31       63      95    127
79/// ```
80///
81/// * Strings with length <= 12 ([`MAX_INLINE_VIEW_LEN`]) are stored directly in
82///   the view. See [`Self::inline_value`] to access the inlined prefix from a
83///   short view.
84///
85/// * Strings with length > 12: The first four bytes are stored inline in the
86///   view and the entire string is stored in one of the buffers. See [`ByteView`]
87///   to access the fields of the these views.
88///
89/// As with other arrays, the optimized kernels in [`arrow_compute`] are likely
90/// the easiest and fastest way to work with this data. However, it is possible
91/// to access the views and buffers directly for more control.
92///
93/// For example
94///
95/// ```rust
96/// # use arrow_array::StringViewArray;
97/// # use arrow_array::Array;
98/// use arrow_data::ByteView;
99/// let array = StringViewArray::from(vec![
100///   "hello",
101///   "this string is longer than 12 bytes",
102///   "this string is also longer than 12 bytes"
103/// ]);
104///
105/// // ** Examine the first view (short string) **
106/// assert!(array.is_valid(0)); // Check for nulls
107/// let short_view: u128 = array.views()[0]; // "hello"
108/// // get length of the string
109/// let len = short_view as u32;
110/// assert_eq!(len, 5); // strings less than 12 bytes are stored in the view
111/// // SAFETY: `view` is a valid view
112/// let value = unsafe {
113///   StringViewArray::inline_value(&short_view, len as usize)
114/// };
115/// assert_eq!(value, b"hello");
116///
117/// // ** Examine the third view (long string) **
118/// assert!(array.is_valid(12)); // Check for nulls
119/// let long_view: u128 = array.views()[2]; // "this string is also longer than 12 bytes"
120/// let len = long_view as u32;
121/// assert_eq!(len, 40); // strings longer than 12 bytes are stored in the buffer
122/// let view = ByteView::from(long_view); // use ByteView to access the fields
123/// assert_eq!(view.length, 40);
124/// assert_eq!(view.buffer_index, 0);
125/// assert_eq!(view.offset, 35); // data starts after the first long string
126/// // Views for long strings store a 4 byte prefix
127/// let prefix = view.prefix.to_le_bytes();
128/// assert_eq!(&prefix, b"this");
129/// let value = array.value(2); // get the string value (see `value` implementation for how to access the bytes directly)
130/// assert_eq!(value, "this string is also longer than 12 bytes");
131/// ```
132///
133/// [`MAX_INLINE_VIEW_LEN`]: arrow_data::MAX_INLINE_VIEW_LEN
134/// [`arrow_compute`]: https://docs.rs/arrow/latest/arrow/compute/index.html
135///
136/// Unlike [`GenericByteArray`], there are no constraints on the offsets other
137/// than they must point into a valid buffer. However, they can be out of order,
138/// non continuous and overlapping.
139///
140/// For example, in the following diagram, the strings "FishWasInTownToday" and
141/// "CrumpleFacedFish" are both longer than 12 bytes and thus are stored in a
142/// separate buffer while the string "LavaMonster" is stored inlined in the
143/// view. In this case, the same bytes for "Fish" are used to store both strings.
144///
145/// [`ByteView`]: arrow_data::ByteView
146///
147/// ```text
148///                                                                            ┌───┐
149///                         ┌──────┬──────┬──────┬──────┐               offset │...│
150/// "FishWasInTownTodayYay" │  21  │ Fish │  0   │ 115  │─ ─              103  │Mr.│
151///                         └──────┴──────┴──────┴──────┘   │      ┌ ─ ─ ─ ─ ▶ │Cru│
152///                         ┌──────┬──────┬──────┬──────┐                      │mpl│
153/// "CrumpleFacedFish"      │  16  │ Crum │  0   │ 103  │─ ─│─ ─ ─ ┘           │eFa│
154///                         └──────┴──────┴──────┴──────┘                      │ced│
155///                         ┌──────┬────────────────────┐   └ ─ ─ ─ ─ ─ ─ ─ ─ ▶│Fis│
156/// "LavaMonster"           │  11  │   LavaMonster      │                      │hWa│
157///                         └──────┴────────────────────┘               offset │sIn│
158///                                                                       115  │Tow│
159///                                                                            │nTo│
160///                                                                            │day│
161///                                  u128 "views"                              │Yay│
162///                                                                   buffer 0 │...│
163///                                                                            └───┘
164/// ```
165pub struct GenericByteViewArray<T: ByteViewType + ?Sized> {
166    data_type: DataType,
167    views: ScalarBuffer<u128>,
168    buffers: Arc<[Buffer]>,
169    phantom: PhantomData<T>,
170    nulls: Option<NullBuffer>,
171}
172
173impl<T: ByteViewType + ?Sized> Clone for GenericByteViewArray<T> {
174    fn clone(&self) -> Self {
175        Self {
176            data_type: T::DATA_TYPE,
177            views: self.views.clone(),
178            buffers: self.buffers.clone(),
179            nulls: self.nulls.clone(),
180            phantom: Default::default(),
181        }
182    }
183}
184
185impl<T: ByteViewType + ?Sized> GenericByteViewArray<T> {
186    /// Create a new [`GenericByteViewArray`] from the provided parts, panicking on failure
187    ///
188    /// # Panics
189    ///
190    /// Panics if [`GenericByteViewArray::try_new`] returns an error
191    pub fn new<U>(views: ScalarBuffer<u128>, buffers: U, nulls: Option<NullBuffer>) -> Self
192    where
193        U: Into<Arc<[Buffer]>>,
194    {
195        Self::try_new(views, buffers, nulls).unwrap()
196    }
197
198    /// Create a new [`GenericByteViewArray`] from the provided parts, returning an error on failure
199    ///
200    /// # Errors
201    ///
202    /// * `views.len() != nulls.len()`
203    /// * [ByteViewType::validate] fails
204    pub fn try_new<U>(
205        views: ScalarBuffer<u128>,
206        buffers: U,
207        nulls: Option<NullBuffer>,
208    ) -> Result<Self, ArrowError>
209    where
210        U: Into<Arc<[Buffer]>>,
211    {
212        let buffers: Arc<[Buffer]> = buffers.into();
213
214        T::validate(&views, &buffers)?;
215
216        if let Some(n) = nulls.as_ref()
217            && n.len() != views.len()
218        {
219            return Err(ArrowError::InvalidArgumentError(format!(
220                "Incorrect length of null buffer for {}ViewArray, expected {} got {}",
221                T::PREFIX,
222                views.len(),
223                n.len(),
224            )));
225        }
226
227        Ok(Self {
228            data_type: T::DATA_TYPE,
229            views,
230            buffers,
231            nulls,
232            phantom: Default::default(),
233        })
234    }
235
236    /// Create a new [`GenericByteViewArray`] from the provided parts, without validation
237    ///
238    /// # Safety
239    ///
240    /// Safe if [`Self::try_new`] would not error
241    pub unsafe fn new_unchecked(
242        views: ScalarBuffer<u128>,
243        buffers: Arc<[Buffer]>,
244        nulls: Option<NullBuffer>,
245    ) -> Self {
246        if cfg!(feature = "force_validate") {
247            return Self::new(views, buffers, nulls);
248        }
249
250        Self {
251            data_type: T::DATA_TYPE,
252            phantom: Default::default(),
253            views,
254            buffers,
255            nulls,
256        }
257    }
258
259    /// Create a new [`GenericByteViewArray`] of length `len` where all values are null
260    pub fn new_null(len: usize) -> Self {
261        Self {
262            data_type: T::DATA_TYPE,
263            views: vec![0; len].into(),
264            buffers: vec![].into(),
265            nulls: Some(NullBuffer::new_null(len)),
266            phantom: Default::default(),
267        }
268    }
269
270    /// Create a new [`Scalar`] from `value`
271    pub fn new_scalar(value: impl AsRef<T::Native>) -> Scalar<Self> {
272        Scalar::new(Self::from_iter_values(std::iter::once(value)))
273    }
274
275    /// Creates a [`GenericByteViewArray`] based on an iterator of values without nulls
276    pub fn from_iter_values<Ptr, I>(iter: I) -> Self
277    where
278        Ptr: AsRef<T::Native>,
279        I: IntoIterator<Item = Ptr>,
280    {
281        let iter = iter.into_iter();
282        let mut builder = GenericByteViewBuilder::<T>::with_capacity(iter.size_hint().0);
283        for v in iter {
284            builder.append_value(v);
285        }
286        builder.finish()
287    }
288
289    /// Deconstruct this array into its constituent parts
290    pub fn into_parts(self) -> (ScalarBuffer<u128>, Arc<[Buffer]>, Option<NullBuffer>) {
291        (self.views, self.buffers, self.nulls)
292    }
293
294    /// Returns the views buffer
295    #[inline]
296    pub fn views(&self) -> &ScalarBuffer<u128> {
297        &self.views
298    }
299
300    /// Returns the shared collection of buffers storing non-inline string or binary data.
301    ///
302    /// The returned `Arc` can be cloned to share the buffers with another array without
303    /// allocating a new collection or cloning the individual buffers. To consume this
304    /// array and take ownership of its buffers, use [`Self::into_parts`].
305    #[inline]
306    pub fn data_buffers(&self) -> &Arc<[Buffer]> {
307        &self.buffers
308    }
309
310    /// Returns the element at index `i`
311    ///
312    /// Note: This method does not check for nulls and the value is arbitrary
313    /// (but still well-defined) if [`is_null`](Self::is_null) returns true for the index.
314    ///
315    /// # Panics
316    /// Panics if index `i` is out of bounds.
317    pub fn value(&self, i: usize) -> &T::Native {
318        assert!(
319            i < self.len(),
320            "Trying to access an element at index {} from a {}ViewArray of length {}",
321            i,
322            T::PREFIX,
323            self.len()
324        );
325
326        unsafe { self.value_unchecked(i) }
327    }
328
329    /// Returns the element at index `i` without bounds checking
330    ///
331    /// Note: This method does not check for nulls and the value is arbitrary
332    /// if [`is_null`](Self::is_null) returns true for the index.
333    ///
334    /// # Safety
335    ///
336    /// Caller is responsible for ensuring that the index is within the bounds
337    /// of the array
338    pub unsafe fn value_unchecked(&self, idx: usize) -> &T::Native {
339        let v = unsafe { self.views.get_unchecked(idx) };
340        let len = *v as u32;
341        let b = if len <= MAX_INLINE_VIEW_LEN {
342            unsafe { Self::inline_value(v, len as usize) }
343        } else {
344            let view = ByteView::from(*v);
345            let data = unsafe { self.buffers.get_unchecked(view.buffer_index as usize) };
346            let offset = view.offset as usize;
347            unsafe { data.get_unchecked(offset..offset + len as usize) }
348        };
349        unsafe { T::Native::from_bytes_unchecked(b) }
350    }
351
352    /// Returns the first `len` bytes the inline value of the view.
353    ///
354    /// # Safety
355    /// - The `view` must be a valid element from `Self::views()` that adheres to the view layout.
356    /// - The `len` must be the length of the inlined value. It should never be larger than [`MAX_INLINE_VIEW_LEN`].
357    #[inline(always)]
358    pub unsafe fn inline_value(view: &u128, len: usize) -> &[u8] {
359        debug_assert!(len <= MAX_INLINE_VIEW_LEN as usize);
360        unsafe {
361            std::slice::from_raw_parts(
362                std::ptr::from_ref::<u128>(view)
363                    .cast::<u8>()
364                    .wrapping_add(4),
365                len,
366            )
367        }
368    }
369
370    /// Constructs a new iterator for iterating over the values of this array
371    pub fn iter(&self) -> ArrayIter<&Self> {
372        ArrayIter::new(self)
373    }
374
375    /// Returns an iterator over the bytes of this array, including null values
376    pub fn bytes_iter(&self) -> impl Iterator<Item = &[u8]> {
377        self.views.iter().map(move |v| {
378            let len = *v as u32;
379            if len <= MAX_INLINE_VIEW_LEN {
380                unsafe { Self::inline_value(v, len as usize) }
381            } else {
382                let view = ByteView::from(*v);
383                let data = &self.buffers[view.buffer_index as usize];
384                let offset = view.offset as usize;
385                unsafe { data.get_unchecked(offset..offset + len as usize) }
386            }
387        })
388    }
389
390    /// Returns an iterator over the first `prefix_len` bytes of each array
391    /// element, including null values.
392    ///
393    /// If `prefix_len` is larger than the element's length, the iterator will
394    /// return an empty slice (`&[]`).
395    pub fn prefix_bytes_iter(&self, prefix_len: usize) -> impl Iterator<Item = &[u8]> {
396        self.views().into_iter().map(move |v| {
397            let len = (*v as u32) as usize;
398
399            if len < prefix_len {
400                return &[] as &[u8];
401            }
402
403            if prefix_len <= 4 || len as u32 <= MAX_INLINE_VIEW_LEN {
404                unsafe { StringViewArray::inline_value(v, prefix_len) }
405            } else {
406                let view = ByteView::from(*v);
407                let data = unsafe {
408                    self.data_buffers()
409                        .get_unchecked(view.buffer_index as usize)
410                };
411                let offset = view.offset as usize;
412                unsafe { data.get_unchecked(offset..offset + prefix_len) }
413            }
414        })
415    }
416
417    /// Returns an iterator over the last `suffix_len` bytes of each array
418    /// element, including null values.
419    ///
420    /// Note that for [`StringViewArray`] the last bytes may start in the middle
421    /// of a UTF-8 codepoint, and thus may not be a valid `&str`.
422    ///
423    /// If `suffix_len` is larger than the element's length, the iterator will
424    /// return an empty slice (`&[]`).
425    pub fn suffix_bytes_iter(&self, suffix_len: usize) -> impl Iterator<Item = &[u8]> {
426        self.views().into_iter().map(move |v| {
427            let len = (*v as u32) as usize;
428
429            if len < suffix_len {
430                return &[] as &[u8];
431            }
432
433            if len as u32 <= MAX_INLINE_VIEW_LEN {
434                unsafe { &StringViewArray::inline_value(v, len)[len - suffix_len..] }
435            } else {
436                let view = ByteView::from(*v);
437                let data = unsafe {
438                    self.data_buffers()
439                        .get_unchecked(view.buffer_index as usize)
440                };
441                let offset = view.offset as usize;
442                unsafe { data.get_unchecked(offset + len - suffix_len..offset + len) }
443            }
444        })
445    }
446
447    /// Return an iterator over the length of each array element, including null values.
448    ///
449    /// Null values length would equal to the underlying bytes length and NOT 0
450    ///
451    /// Example of getting 0 for null values
452    /// ```rust
453    /// # use arrow_array::StringViewArray;
454    /// # use arrow_array::Array;
455    /// use arrow_data::ByteView;
456    ///
457    /// fn lengths_with_zero_for_nulls(view: &StringViewArray) -> impl Iterator<Item = u32> {
458    ///     view.lengths()
459    ///         .enumerate()
460    ///         .map(|(index, length)| if view.is_null(index) { 0 } else { length })
461    /// }
462    /// ```
463    pub fn lengths(&self) -> impl ExactSizeIterator<Item = u32> + Clone {
464        self.views().iter().map(|v| *v as u32)
465    }
466
467    /// Returns a zero-copy slice of this array with the indicated offset and length.
468    ///
469    /// # Panics
470    /// Panics if `offset + length > self.len()`
471    pub fn slice(&self, offset: usize, length: usize) -> Self {
472        Self {
473            data_type: T::DATA_TYPE,
474            views: self.views.slice(offset, length),
475            buffers: self.buffers.clone(),
476            nulls: self.nulls.as_ref().map(|n| n.slice(offset, length)),
477            phantom: Default::default(),
478        }
479    }
480
481    /// Returns a "compacted" version of this array
482    ///
483    /// The original array will *not* be modified
484    ///
485    /// # Garbage Collection
486    ///
487    /// Before GC:
488    /// ```text
489    ///                                        ┌──────┐
490    ///                                        │......│
491    ///                                        │......│
492    /// ┌────────────────────┐       ┌ ─ ─ ─ ▶ │Data1 │   Large buffer
493    /// │       View 1       │─ ─ ─ ─          │......│  with data that
494    /// ├────────────────────┤                 │......│ is not referred
495    /// │       View 2       │─ ─ ─ ─ ─ ─ ─ ─▶ │Data2 │ to by View 1 or
496    /// └────────────────────┘                 │......│      View 2
497    ///                                        │......│
498    ///    2 views, refer to                   │......│
499    ///   small portions of a                  └──────┘
500    ///      large buffer
501    /// ```
502    ///
503    /// After GC:
504    ///
505    /// ```text
506    /// ┌────────────────────┐                 ┌─────┐    After gc, only
507    /// │       View 1       │─ ─ ─ ─ ─ ─ ─ ─▶ │Data1│     data that is
508    /// ├────────────────────┤       ┌ ─ ─ ─ ▶ │Data2│    pointed to by
509    /// │       View 2       │─ ─ ─ ─          └─────┘     the views is
510    /// └────────────────────┘                                 left
511    ///
512    ///
513    ///         2 views
514    /// ```
515    /// This method will compact the data buffers by recreating the view array and only include the data
516    /// that is pointed to by the views.
517    ///
518    /// Note that it will copy the array regardless of whether the original array is compact.
519    /// Use with caution as this can be an expensive operation, only use it when you are sure that the view
520    /// array is significantly smaller than when it is originally created, e.g., after filtering or slicing.
521    ///
522    /// Note: this function does not attempt to canonicalize / deduplicate values. For this
523    /// feature see  [`GenericByteViewBuilder::with_deduplicate_strings`].
524    pub fn gc(&self) -> Self {
525        // A single Arrow data buffer is addressed by a `u32` offset, so no
526        // output buffer may exceed `i32::MAX` bytes. Above that the data is
527        // split across multiple buffers (the "slow path").
528        self.gc_with_max_buffer_size(i32::MAX as usize)
529    }
530
531    /// Like [`Self::gc`], but with a configurable maximum output buffer size.
532    ///
533    /// `gc` always uses `i32::MAX` (the largest a single Arrow buffer can be).
534    /// This method exists so the multi-buffer split path — which `gc` only
535    /// reaches once a column references more than 2 GiB of non-inline data —
536    /// can be exercised in tests with a small threshold and tiny inputs.
537    fn gc_with_max_buffer_size(&self, max_buffer_size: usize) -> Self {
538        // 1) Read basic properties once
539        let len = self.len(); // number of elements
540        let nulls = self.nulls().cloned(); // reuse & clone existing null bitmap
541
542        // 1.5) Fast path: if there are no buffers, just reuse original views and no data blocks
543        if self.data_buffers().is_empty() {
544            return unsafe {
545                GenericByteViewArray::new_unchecked(
546                    self.views().clone(),
547                    Arc::from([]), // empty data blocks
548                    nulls,
549                )
550            };
551        }
552
553        // 2) Calculate total size of all non-inline data and detect if any exists
554        let total_large = self.total_buffer_bytes_used();
555
556        // 2.5) Fast path: if there is no non-inline data, avoid buffer allocation & processing
557        if total_large == 0 {
558            // Views are inline-only or all null; just reuse original views and no data blocks
559            return unsafe {
560                GenericByteViewArray::new_unchecked(
561                    self.views().clone(),
562                    Arc::from([]), // empty data blocks
563                    nulls,
564                )
565            };
566        }
567
568        let (views_buf, data_blocks) = if total_large <= max_buffer_size {
569            // fast path, the entire data fits in a single buffer
570            // 3) Allocate exactly capacity for all non-inline data
571            let mut data_buf = Vec::with_capacity(total_large);
572
573            // 4) Iterate over views and process each inline/non-inline view
574            let views_buf: Vec<u128> = (0..len)
575                .map(|i| unsafe { self.copy_view_to_buffer(i, 0, &mut data_buf) })
576                .collect();
577            let data_block = Buffer::from_vec(data_buf);
578            let data_blocks = vec![data_block];
579            (views_buf, data_blocks)
580        } else {
581            // slow path: the non-inline data does not fit in a single buffer
582            // (a buffer offset is a `u32`, so no buffer may exceed `i32::MAX`),
583            // so it must be split across several buffers.
584
585            // A contiguous run of views destined for one output buffer.
586            struct GcCopyGroup {
587                total_buffer_bytes: usize,
588                total_len: usize,
589            }
590
591            // First pass: partition the views into contiguous groups, each of
592            // which fits within one output buffer. `total_len` counts *all*
593            // views in the group, not just the non-inline ones — this is
594            // essential for correctness, as gc must preserve the row count.
595            // Inline views reference no buffer and are copied unchanged, but
596            // they still belong to a group and must be accounted for. Recording
597            // each group's exact byte size lets the second pass size every
598            // buffer precisely, with no over-reservation or trailing waste.
599            let mut groups: Vec<GcCopyGroup> = Vec::new();
600            // Bytes accumulated for the current group. A view length is a `u32`
601            // and a single value may be larger than `max_buffer_size`, so the
602            // running sum is kept in `u64` to stay within the addressable
603            // buffer-offset space without wrapping.
604            let mut current_length: u64 = 0;
605            let mut current_elements = 0;
606
607            for view in self.views() {
608                let view_len = *view as u32;
609                if view_len > MAX_INLINE_VIEW_LEN {
610                    // Seal the current group before adding this view would push
611                    // its buffer past `max_buffer_size`.
612                    if current_length + view_len as u64 > max_buffer_size as u64 {
613                        groups.push(GcCopyGroup {
614                            total_buffer_bytes: current_length as usize,
615                            total_len: current_elements,
616                        });
617                        current_length = 0;
618                        current_elements = 0;
619                    }
620                    current_length += view_len as u64;
621                }
622                current_elements += 1;
623            }
624            if current_elements != 0 {
625                groups.push(GcCopyGroup {
626                    total_buffer_bytes: current_length as usize,
627                    total_len: current_elements,
628                });
629            }
630            debug_assert!(i32::try_from(groups.len()).is_ok());
631
632            // Second pass: copy each group into an exactly-sized buffer.
633            let mut views_buf = Vec::with_capacity(len);
634            let mut data_blocks = Vec::with_capacity(groups.len());
635            let mut current_view_idx = 0;
636
637            for (group_idx, group) in groups.iter().enumerate() {
638                let mut data_buf = Vec::with_capacity(group.total_buffer_bytes);
639
640                // Directly push views to avoid an intermediate Vec allocation.
641                let new_views =
642                    (current_view_idx..current_view_idx + group.total_len).map(|view_idx| {
643                        // SAFETY: `view_idx` came from iterating a valid range
644                        // within `len`, and every view refers to valid data.
645                        unsafe {
646                            self.copy_view_to_buffer(view_idx, group_idx as i32, &mut data_buf)
647                        }
648                    });
649                views_buf.extend(new_views);
650
651                data_blocks.push(Buffer::from_vec(data_buf));
652                current_view_idx += group.total_len;
653            }
654            (views_buf, data_blocks)
655        };
656
657        // 5) Wrap up views buffer
658        let views_scalar = ScalarBuffer::from(views_buf);
659
660        // SAFETY: views_scalar, data_blocks, and nulls are correctly aligned and sized
661        unsafe { GenericByteViewArray::new_unchecked(views_scalar, data_blocks.into(), nulls) }
662    }
663
664    /// Copy the i‑th view into `data_buf` if it refers to an out‑of‑line buffer.
665    ///
666    /// # Safety
667    ///
668    /// - `i < self.len()`.
669    /// - Every element in `self.views()` must currently refer to a valid slice
670    ///   inside one of `self.buffers`.
671    /// - `data_buf` must be ready to have additional bytes appended.
672    /// - After this call, the returned view will have its
673    ///   `buffer_index` reset to `buffer_idx` and its `offset` updated so that it points
674    ///   into the bytes just appended at the end of `data_buf`.
675    #[inline(always)]
676    unsafe fn copy_view_to_buffer(
677        &self,
678        i: usize,
679        buffer_idx: i32,
680        data_buf: &mut Vec<u8>,
681    ) -> u128 {
682        // SAFETY: `i < self.len()` ensures this is in‑bounds.
683        let raw_view = unsafe { *self.views().get_unchecked(i) };
684        let mut bv = ByteView::from(raw_view);
685
686        // Inline‑small views stay as‑is.
687        if bv.length <= MAX_INLINE_VIEW_LEN {
688            raw_view
689        } else {
690            // SAFETY: `bv.buffer_index` and `bv.offset..bv.offset+bv.length`
691            // must both lie within valid ranges for `self.buffers`.
692            let buffer = unsafe { self.buffers.get_unchecked(bv.buffer_index as usize) };
693            let start = bv.offset as usize;
694            let end = start + bv.length as usize;
695            let slice = unsafe { buffer.get_unchecked(start..end) };
696
697            // Copy out‑of‑line data into our single “0” buffer.
698            let new_offset = data_buf.len() as u32;
699            data_buf.extend_from_slice(slice);
700
701            bv.buffer_index = buffer_idx as u32;
702            bv.offset = new_offset;
703            bv.into()
704        }
705    }
706
707    /// Returns the total number of bytes of all non-null values in this array.
708    ///
709    /// Unlike [`Self::total_buffer_bytes_used`], this method includes inlined strings
710    /// (those with length ≤ [`MAX_INLINE_VIEW_LEN`]), making it suitable as a
711    /// capacity hint when pre-allocating output buffers.
712    ///
713    /// Null values are excluded from the sum.
714    ///
715    /// # Example
716    ///
717    /// ```rust
718    /// # use arrow_array::StringViewArray;
719    /// let array = StringViewArray::from_iter(vec![
720    ///     Some("hello"),   // 5 bytes, inlined
721    ///     None,            // excluded
722    ///     Some("large payload over 12 bytes"),  // 27 bytes, non-inlined
723    /// ]);
724    /// assert_eq!(array.total_bytes_len(), 5 + 27);
725    /// ```
726    pub fn total_bytes_len(&self) -> usize {
727        match self.nulls() {
728            None => self.views().iter().map(|v| (*v as u32) as usize).sum(),
729            Some(nulls) => self
730                .views()
731                .iter()
732                .zip(nulls.iter())
733                .map(|(v, is_valid)| if is_valid { (*v as u32) as usize } else { 0 })
734                .sum(),
735        }
736    }
737
738    /// Returns the total number of bytes used by all non inlined views in all
739    /// buffers.
740    ///
741    /// Note this does not account for views that point at the same underlying
742    /// data in buffers
743    ///
744    /// For example, if the array has three strings views:
745    /// * View with length = 9 (inlined)
746    /// * View with length = 32 (non inlined)
747    /// * View with length = 16 (non inlined)
748    ///
749    /// Then this method would report 48
750    pub fn total_buffer_bytes_used(&self) -> usize {
751        self.views()
752            .iter()
753            .map(|v| {
754                let len = *v as u32;
755                if len > MAX_INLINE_VIEW_LEN {
756                    len as usize
757                } else {
758                    0
759                }
760            })
761            .sum()
762    }
763
764    /// Compare two [`GenericByteViewArray`] at index `left_idx` and `right_idx`
765    ///
766    /// Comparing two ByteView types are non-trivial.
767    /// It takes a bit of patience to understand why we don't just compare two &[u8] directly.
768    ///
769    /// ByteView types give us the following two advantages, and we need to be careful not to lose them:
770    /// (1) For string/byte smaller than [`MAX_INLINE_VIEW_LEN`] bytes, the entire data is inlined in the view.
771    ///     Meaning that reading one array element requires only one memory access
772    ///     (two memory access required for StringArray, one for offset buffer, the other for value buffer).
773    ///
774    /// (2) For string/byte larger than [`MAX_INLINE_VIEW_LEN`] bytes, we can still be faster than (for certain operations) StringArray/ByteArray,
775    ///     thanks to the inlined 4 bytes.
776    ///     Consider equality check:
777    ///     If the first four bytes of the two strings are different, we can return false immediately (with just one memory access).
778    ///
779    /// If we directly compare two &[u8], we materialize the entire string (i.e., make multiple memory accesses), which might be unnecessary.
780    /// - Most of the time (eq, ord), we only need to look at the first 4 bytes to know the answer,
781    ///   e.g., if the inlined 4 bytes are different, we can directly return unequal without looking at the full string.
782    ///
783    /// # Order check flow
784    /// (1) if both string are smaller than [`MAX_INLINE_VIEW_LEN`] bytes, we can directly compare the data inlined to the view.
785    /// (2) if any of the string is larger than [`MAX_INLINE_VIEW_LEN`] bytes, we need to compare the full string.
786    ///     (2.1) if the inlined 4 bytes are different, we can return the result immediately.
787    ///     (2.2) o.w., we need to compare the full string.
788    ///
789    /// # Safety
790    /// The left/right_idx must within range of each array
791    pub unsafe fn compare_unchecked(
792        left: &GenericByteViewArray<T>,
793        left_idx: usize,
794        right: &GenericByteViewArray<T>,
795        right_idx: usize,
796    ) -> Ordering {
797        let l_view = unsafe { left.views().get_unchecked(left_idx) };
798        let l_byte_view = ByteView::from(*l_view);
799
800        let r_view = unsafe { right.views().get_unchecked(right_idx) };
801        let r_byte_view = ByteView::from(*r_view);
802
803        let l_len = l_byte_view.length;
804        let r_len = r_byte_view.length;
805
806        if l_len <= 12 && r_len <= 12 {
807            return Self::inline_key_fast(*l_view).cmp(&Self::inline_key_fast(*r_view));
808        }
809
810        // one of the string is larger than 12 bytes,
811        // we then try to compare the inlined data first
812
813        // Note: In theory, ByteView is only used for string which is larger than 12 bytes,
814        // but we can still use it to get the inlined prefix for shorter strings.
815        // The prefix is always the first 4 bytes of the view, for both short and long strings.
816        let l_inlined_be = l_byte_view.prefix.swap_bytes();
817        let r_inlined_be = r_byte_view.prefix.swap_bytes();
818        if l_inlined_be != r_inlined_be {
819            return l_inlined_be.cmp(&r_inlined_be);
820        }
821
822        // unfortunately, we need to compare the full data
823        let l_full_data: &[u8] = unsafe { left.value_unchecked(left_idx).as_ref() };
824        let r_full_data: &[u8] = unsafe { right.value_unchecked(right_idx).as_ref() };
825
826        l_full_data.cmp(r_full_data)
827    }
828
829    /// Builds a 128-bit composite key for an inline value:
830    ///
831    /// - High 96 bits: the inline data in big-endian byte order (for correct lexicographical sorting).
832    /// - Low  32 bits: the length in big-endian byte order, acting as a tiebreaker so shorter strings
833    ///   (or those with fewer meaningful bytes) always numerically sort before longer ones.
834    ///
835    /// This function extracts the length and the 12-byte inline string data from the raw
836    /// little-endian `u128` representation, converts them to big-endian ordering, and packs them
837    /// into a single `u128` value suitable for fast, branchless comparisons.
838    ///
839    /// # Why include length?
840    ///
841    /// A pure 96-bit content comparison can’t distinguish between two values whose inline bytes
842    /// compare equal—either because one is a true prefix of the other or because zero-padding
843    /// hides extra bytes. By tucking the 32-bit length into the lower bits, a single `u128` compare
844    /// handles both content and length in one go.
845    ///
846    /// Example: comparing "bar" (3 bytes) vs "bar\0" (4 bytes)
847    ///
848    /// | String     | Bytes 0–4 (length LE) | Bytes 4–16 (data + padding)    |
849    /// |------------|-----------------------|---------------------------------|
850    /// | `"bar"`   | `03 00 00 00`         | `62 61 72` + 9 × `00`           |
851    /// | `"bar\0"`| `04 00 00 00`         | `62 61 72 00` + 8 × `00`        |
852    ///
853    /// Both inline parts become `62 61 72 00…00`, so they tie on content. The length field
854    /// then differentiates:
855    ///
856    /// ```text
857    /// key("bar")   = 0x0000000000000000000062617200000003
858    /// key("bar\0") = 0x0000000000000000000062617200000004
859    /// ⇒ key("bar") < key("bar\0")
860    /// ```
861    /// - `raw` is treated as a 128-bit integer with its bits laid out as follows:
862    ///   - bits 0–31: length (little-endian)
863    ///   - bits 32–127: data (little-endian)
864    ///
865    /// # Inlining and Endianness
866    ///
867    /// This function uses platform-independent bitwise operations to construct a 128-bit key:
868    /// - `raw.swap_bytes() << 32` effectively clears the length bits and shifts the 12-byte inline data
869    ///   into the high 96 bits in Big-Endian order. This ensures the first byte of the string
870    ///   is the most significant byte of the resulting `u128`.
871    /// - `raw as u32` extracts the length as a numeric integer, which is then placed in the low 32 bits.
872    #[inline(always)]
873    pub fn inline_key_fast(raw: u128) -> u128 {
874        (raw.swap_bytes() << 32) | (raw as u32 as u128)
875    }
876}
877
878impl<T: ByteViewType + ?Sized> Debug for GenericByteViewArray<T> {
879    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
880        write!(f, "{}ViewArray\n[\n", T::PREFIX)?;
881        print_long_array(self, f, |array, index, f| {
882            std::fmt::Debug::fmt(&array.value(index), f)
883        })?;
884        write!(f, "]")
885    }
886}
887
888/// SAFETY: Correctly implements the contract of Arrow Arrays
889unsafe impl<T: ByteViewType + ?Sized> Array for GenericByteViewArray<T> {
890    fn as_any(&self) -> &dyn Any {
891        self
892    }
893
894    fn to_data(&self) -> ArrayData {
895        self.clone().into()
896    }
897
898    fn into_data(self) -> ArrayData {
899        self.into()
900    }
901
902    fn data_type(&self) -> &DataType {
903        &self.data_type
904    }
905
906    fn slice(&self, offset: usize, length: usize) -> ArrayRef {
907        Arc::new(self.slice(offset, length))
908    }
909
910    fn len(&self) -> usize {
911        self.views.len()
912    }
913
914    fn is_empty(&self) -> bool {
915        self.views.is_empty()
916    }
917
918    fn shrink_to_fit(&mut self) {
919        self.views.shrink_to_fit();
920
921        // The goal of `shrink_to_fit` is to minimize the space used by any of
922        // its allocations. The use of `Arc::get_mut` over `Arc::make_mut` is
923        // because if the reference count is greater than 1, `Arc::make_mut`
924        // will first clone its contents. So, any large allocations will first
925        // be cloned before being shrunk, leaving the pre-cloned allocations
926        // intact, before adding the extra (used) space of the new clones.
927        if let Some(buffers) = Arc::get_mut(&mut self.buffers) {
928            buffers.iter_mut().for_each(|b| b.shrink_to_fit());
929        }
930
931        // With the assumption that this is a best-effort function, no attempt
932        // is made to shrink `self.buffers`, which it can't because it's type
933        // does not expose a `shrink_to_fit` method.
934
935        if let Some(nulls) = &mut self.nulls {
936            nulls.shrink_to_fit();
937        }
938    }
939
940    fn offset(&self) -> usize {
941        0
942    }
943
944    fn nulls(&self) -> Option<&NullBuffer> {
945        self.nulls.as_ref()
946    }
947
948    fn logical_null_count(&self) -> usize {
949        // More efficient that the default implementation
950        self.null_count()
951    }
952
953    fn get_buffer_memory_size(&self) -> usize {
954        let mut sum = self.buffers.iter().map(|b| b.capacity()).sum::<usize>();
955        sum += self.views.inner().capacity();
956        if let Some(x) = &self.nulls {
957            sum += x.buffer().capacity()
958        }
959        sum
960    }
961
962    fn get_array_memory_size(&self) -> usize {
963        std::mem::size_of::<Self>() + self.get_buffer_memory_size()
964    }
965
966    #[cfg(feature = "pool")]
967    fn claim(&self, pool: &dyn arrow_buffer::MemoryPool) {
968        self.views.claim(pool);
969        for buffer in self.buffers.iter() {
970            buffer.claim(pool);
971        }
972        if let Some(nulls) = &self.nulls {
973            nulls.claim(pool);
974        }
975    }
976}
977
978impl<'a, T: ByteViewType + ?Sized> ArrayAccessor for &'a GenericByteViewArray<T> {
979    type Item = &'a T::Native;
980
981    fn value(&self, index: usize) -> Self::Item {
982        GenericByteViewArray::value(self, index)
983    }
984
985    unsafe fn value_unchecked(&self, index: usize) -> Self::Item {
986        unsafe { GenericByteViewArray::value_unchecked(self, index) }
987    }
988}
989
990impl<'a, T: ByteViewType + ?Sized> IntoIterator for &'a GenericByteViewArray<T> {
991    type Item = Option<&'a T::Native>;
992    type IntoIter = ArrayIter<Self>;
993
994    fn into_iter(self) -> Self::IntoIter {
995        ArrayIter::new(self)
996    }
997}
998
999impl<T: ByteViewType + ?Sized> From<ArrayData> for GenericByteViewArray<T> {
1000    fn from(data: ArrayData) -> Self {
1001        let (data_type, len, nulls, offset, buffers, _child_data) = data.into_parts();
1002        assert_eq!(
1003            data_type,
1004            T::DATA_TYPE,
1005            "Mismatched data type, expected {}, got {data_type}",
1006            T::DATA_TYPE
1007        );
1008        let mut buffers = buffers.into_iter();
1009        // first buffer is views, remaining are data buffers
1010        let views = ScalarBuffer::new(buffers.next().unwrap(), offset, len);
1011        Self {
1012            data_type,
1013            views,
1014            buffers: Arc::from_iter(buffers),
1015            nulls,
1016            phantom: Default::default(),
1017        }
1018    }
1019}
1020
1021/// Efficiently convert a [`GenericByteArray`] to a [`GenericByteViewArray`]
1022///
1023/// For example this method can convert a [`StringArray`] to a
1024/// [`StringViewArray`].
1025///
1026/// If the offsets are all less than u32::MAX, the new [`GenericByteViewArray`]
1027/// is built without copying the underlying string data (views are created
1028/// directly into the existing buffer)
1029///
1030/// [`StringArray`]: crate::StringArray
1031impl<FROM, V> From<&GenericByteArray<FROM>> for GenericByteViewArray<V>
1032where
1033    FROM: ByteArrayType,
1034    FROM::Offset: OffsetSizeTrait + ToPrimitive,
1035    V: ByteViewType<Native = FROM::Native>,
1036{
1037    fn from(byte_array: &GenericByteArray<FROM>) -> Self {
1038        let offsets = byte_array.offsets();
1039
1040        let can_reuse_buffer = match offsets.last() {
1041            Some(offset) => offset.as_usize() < u32::MAX as usize,
1042            None => true,
1043        };
1044
1045        if can_reuse_buffer {
1046            // build views directly pointing to the existing buffer
1047            let len = byte_array.len();
1048            let mut views_builder = GenericByteViewBuilder::<V>::with_capacity(len);
1049            let str_values_buf = byte_array.values().clone();
1050            let block = views_builder.append_block(str_values_buf);
1051            for (i, w) in offsets.windows(2).enumerate() {
1052                let offset = w[0].as_usize();
1053                let end = w[1].as_usize();
1054                let length = end - offset;
1055
1056                if byte_array.is_null(i) {
1057                    views_builder.append_null();
1058                } else {
1059                    // Safety: the input was a valid array so it valid UTF8 (if string). And
1060                    // all offsets were valid
1061                    unsafe {
1062                        views_builder.append_view_unchecked(block, offset as u32, length as u32)
1063                    }
1064                }
1065            }
1066            assert_eq!(views_builder.len(), len);
1067            views_builder.finish()
1068        } else {
1069            // Otherwise, create a new buffer for large strings
1070            // TODO: the original buffer could still be used
1071            // by making multiple slices of u32::MAX length
1072            GenericByteViewArray::<V>::from_iter(byte_array.iter())
1073        }
1074    }
1075}
1076
1077impl<T: ByteViewType + ?Sized> From<GenericByteViewArray<T>> for ArrayData {
1078    fn from(array: GenericByteViewArray<T>) -> Self {
1079        let len = array.len();
1080
1081        let mut buffers = array.buffers.to_vec();
1082        buffers.insert(0, array.views.into_inner());
1083
1084        let builder = ArrayDataBuilder::new(T::DATA_TYPE)
1085            .len(len)
1086            .buffers(buffers)
1087            .nulls(array.nulls);
1088
1089        unsafe { builder.build_unchecked() }
1090    }
1091}
1092
1093impl<'a, Ptr, T> FromIterator<&'a Option<Ptr>> for GenericByteViewArray<T>
1094where
1095    Ptr: AsRef<T::Native> + 'a,
1096    T: ByteViewType + ?Sized,
1097{
1098    fn from_iter<I: IntoIterator<Item = &'a Option<Ptr>>>(iter: I) -> Self {
1099        iter.into_iter()
1100            .map(|o| o.as_ref().map(|p| p.as_ref()))
1101            .collect()
1102    }
1103}
1104
1105impl<Ptr, T: ByteViewType + ?Sized> FromIterator<Option<Ptr>> for GenericByteViewArray<T>
1106where
1107    Ptr: AsRef<T::Native>,
1108{
1109    fn from_iter<I: IntoIterator<Item = Option<Ptr>>>(iter: I) -> Self {
1110        let iter = iter.into_iter();
1111        let mut builder = GenericByteViewBuilder::<T>::with_capacity(iter.size_hint().0);
1112        builder.extend(iter);
1113        builder.finish()
1114    }
1115}
1116
1117/// A [`GenericByteViewArray`] of `[u8]`
1118///
1119/// See [`GenericByteViewArray`] for format and layout details.
1120///
1121/// # Example
1122/// ```
1123/// use arrow_array::BinaryViewArray;
1124/// let array = BinaryViewArray::from_iter_values(vec![b"hello" as &[u8], b"world", b"lulu", b"large payload over 12 bytes"]);
1125/// assert_eq!(array.value(0), b"hello");
1126/// assert_eq!(array.value(3), b"large payload over 12 bytes");
1127/// ```
1128pub type BinaryViewArray = GenericByteViewArray<BinaryViewType>;
1129
1130impl BinaryViewArray {
1131    /// Convert the [`BinaryViewArray`] to [`StringViewArray`]
1132    /// If items not utf8 data, validate will fail and error returned.
1133    pub fn to_string_view(self) -> Result<StringViewArray, ArrowError> {
1134        StringViewType::validate(self.views(), self.data_buffers())?;
1135        unsafe { Ok(self.to_string_view_unchecked()) }
1136    }
1137
1138    /// Convert the [`BinaryViewArray`] to [`StringViewArray`]
1139    /// # Safety
1140    /// Caller is responsible for ensuring that items in array are utf8 data.
1141    pub unsafe fn to_string_view_unchecked(self) -> StringViewArray {
1142        unsafe { StringViewArray::new_unchecked(self.views, self.buffers, self.nulls) }
1143    }
1144}
1145
1146impl From<Vec<&[u8]>> for BinaryViewArray {
1147    fn from(v: Vec<&[u8]>) -> Self {
1148        Self::from_iter_values(v)
1149    }
1150}
1151
1152impl From<Vec<Option<&[u8]>>> for BinaryViewArray {
1153    fn from(v: Vec<Option<&[u8]>>) -> Self {
1154        v.into_iter().collect()
1155    }
1156}
1157
1158/// A [`GenericByteViewArray`] that stores utf8 data
1159///
1160/// See [`GenericByteViewArray`] for format and layout details.
1161///
1162/// # Example
1163/// ```
1164/// use arrow_array::StringViewArray;
1165/// let array = StringViewArray::from_iter_values(vec!["hello", "world", "lulu", "large payload over 12 bytes"]);
1166/// assert_eq!(array.value(0), "hello");
1167/// assert_eq!(array.value(3), "large payload over 12 bytes");
1168/// ```
1169pub type StringViewArray = GenericByteViewArray<StringViewType>;
1170
1171impl StringViewArray {
1172    /// Convert the [`StringViewArray`] to [`BinaryViewArray`]
1173    pub fn to_binary_view(self) -> BinaryViewArray {
1174        unsafe { BinaryViewArray::new_unchecked(self.views, self.buffers, self.nulls) }
1175    }
1176
1177    /// Returns true if all data within this array is ASCII
1178    pub fn is_ascii(&self) -> bool {
1179        // Alternative (but incorrect): directly check the underlying buffers
1180        // (1) Our string view might be sparse, i.e., a subset of the buffers,
1181        //      so even if the buffer is not ascii, we can still be ascii.
1182        // (2) It is quite difficult to know the range of each buffer (unlike StringArray)
1183        // This means that this operation is quite expensive, shall we cache the result?
1184        //  i.e. track `is_ascii` in the builder.
1185        self.iter().all(|v| match v {
1186            Some(v) => v.is_ascii(),
1187            None => true,
1188        })
1189    }
1190}
1191
1192impl From<Vec<&str>> for StringViewArray {
1193    fn from(v: Vec<&str>) -> Self {
1194        Self::from_iter_values(v)
1195    }
1196}
1197
1198impl From<Vec<Option<&str>>> for StringViewArray {
1199    fn from(v: Vec<Option<&str>>) -> Self {
1200        v.into_iter().collect()
1201    }
1202}
1203
1204impl From<Vec<String>> for StringViewArray {
1205    fn from(v: Vec<String>) -> Self {
1206        Self::from_iter_values(v)
1207    }
1208}
1209
1210impl From<Vec<Option<String>>> for StringViewArray {
1211    fn from(v: Vec<Option<String>>) -> Self {
1212        v.into_iter().collect()
1213    }
1214}
1215
1216#[cfg(test)]
1217mod tests {
1218    use crate::builder::{BinaryViewBuilder, StringViewBuilder};
1219    use crate::types::BinaryViewType;
1220    use crate::{
1221        Array, BinaryViewArray, GenericBinaryArray, GenericByteViewArray, StringViewArray,
1222    };
1223    use arrow_buffer::{Buffer, NullBuffer, ScalarBuffer};
1224    use arrow_data::{ArrayDataBuilder, ByteView, MAX_INLINE_VIEW_LEN};
1225    use arrow_schema::DataType;
1226    use rand::prelude::StdRng;
1227    use rand::{RngExt, SeedableRng};
1228    use std::str::from_utf8;
1229
1230    const BLOCK_SIZE: u32 = 8;
1231
1232    #[test]
1233    fn try_new_string() {
1234        let array = StringViewArray::from_iter_values(vec![
1235            "hello",
1236            "world",
1237            "lulu",
1238            "large payload over 12 bytes",
1239        ]);
1240        assert_eq!(array.value(0), "hello");
1241        assert_eq!(array.value(3), "large payload over 12 bytes");
1242    }
1243
1244    #[test]
1245    fn try_new_binary() {
1246        let array = BinaryViewArray::from_iter_values(vec![
1247            b"hello".as_slice(),
1248            b"world".as_slice(),
1249            b"lulu".as_slice(),
1250            b"large payload over 12 bytes".as_slice(),
1251        ]);
1252        assert_eq!(array.value(0), b"hello");
1253        assert_eq!(array.value(3), b"large payload over 12 bytes");
1254    }
1255
1256    #[test]
1257    fn try_new_empty_string() {
1258        // test empty array
1259        let array = {
1260            let mut builder = StringViewBuilder::new();
1261            builder.finish()
1262        };
1263        assert!(array.is_empty());
1264    }
1265
1266    #[test]
1267    fn try_new_empty_binary() {
1268        // test empty array
1269        let array = {
1270            let mut builder = BinaryViewBuilder::new();
1271            builder.finish()
1272        };
1273        assert!(array.is_empty());
1274    }
1275
1276    #[test]
1277    fn test_append_string() {
1278        // test builder append
1279        let array = {
1280            let mut builder = StringViewBuilder::new();
1281            builder.append_value("hello");
1282            builder.append_null();
1283            builder.append_option(Some("large payload over 12 bytes"));
1284            builder.finish()
1285        };
1286        assert_eq!(array.value(0), "hello");
1287        assert!(array.is_null(1));
1288        assert_eq!(array.value(2), "large payload over 12 bytes");
1289    }
1290
1291    #[test]
1292    fn test_append_binary() {
1293        // test builder append
1294        let array = {
1295            let mut builder = BinaryViewBuilder::new();
1296            builder.append_value(b"hello");
1297            builder.append_null();
1298            builder.append_option(Some(b"large payload over 12 bytes"));
1299            builder.finish()
1300        };
1301        assert_eq!(array.value(0), b"hello");
1302        assert!(array.is_null(1));
1303        assert_eq!(array.value(2), b"large payload over 12 bytes");
1304    }
1305
1306    #[test]
1307    fn test_in_progress_recreation() {
1308        let array = {
1309            // make a builder with small block size.
1310            let mut builder = StringViewBuilder::new().with_fixed_block_size(14);
1311            builder.append_value("large payload over 12 bytes");
1312            builder.append_option(Some("another large payload over 12 bytes that double than the first one, so that we can trigger the in_progress in builder re-created"));
1313            builder.finish()
1314        };
1315        assert_eq!(array.value(0), "large payload over 12 bytes");
1316        assert_eq!(
1317            array.value(1),
1318            "another large payload over 12 bytes that double than the first one, so that we can trigger the in_progress in builder re-created"
1319        );
1320        assert_eq!(2, array.buffers.len());
1321    }
1322
1323    #[test]
1324    #[should_panic(expected = "Invalid buffer index at 0: got index 3 but only has 1 buffers")]
1325    fn new_with_invalid_view_data() {
1326        let v = "large payload over 12 bytes";
1327        let view = ByteView::new(13, &v.as_bytes()[0..4])
1328            .with_buffer_index(3)
1329            .with_offset(1);
1330        let views = ScalarBuffer::from(vec![view.into()]);
1331        let buffers = vec![Buffer::from_slice_ref(v)];
1332        StringViewArray::new(views, buffers, None);
1333    }
1334
1335    #[test]
1336    #[should_panic(
1337        expected = "Encountered non-UTF-8 data at index 0: invalid utf-8 sequence of 1 bytes from index 0"
1338    )]
1339    fn new_with_invalid_utf8_data() {
1340        let v: Vec<u8> = vec![
1341            // invalid UTF8
1342            0xf0, 0x80, 0x80, 0x80, // more bytes to make it larger than 12
1343            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1344        ];
1345        let view = ByteView::new(v.len() as u32, &v[0..4]);
1346        let views = ScalarBuffer::from(vec![view.into()]);
1347        let buffers = vec![Buffer::from_slice_ref(v)];
1348        StringViewArray::new(views, buffers, None);
1349    }
1350
1351    #[test]
1352    #[should_panic(expected = "View at index 0 contained non-zero padding for string of length 1")]
1353    fn new_with_invalid_zero_padding() {
1354        let mut data = [0; 12];
1355        data[0] = b'H';
1356        data[11] = 1; // no zero padding
1357
1358        let mut view_buffer = [0; 16];
1359        view_buffer[0..4].copy_from_slice(&1u32.to_le_bytes());
1360        view_buffer[4..].copy_from_slice(&data);
1361
1362        let view = ByteView::from(u128::from_le_bytes(view_buffer));
1363        let views = ScalarBuffer::from(vec![view.into()]);
1364        let buffers = vec![];
1365        StringViewArray::new(views, buffers, None);
1366    }
1367
1368    #[test]
1369    #[should_panic(expected = "Mismatch between embedded prefix and data")]
1370    fn test_mismatch_between_embedded_prefix_and_data() {
1371        let input_str_1 = "Hello, Rustaceans!";
1372        let input_str_2 = "Hallo, Rustaceans!";
1373        let length = input_str_1.len() as u32;
1374        assert!(input_str_1.len() > 12);
1375
1376        let mut view_buffer = [0; 16];
1377        view_buffer[0..4].copy_from_slice(&length.to_le_bytes());
1378        view_buffer[4..8].copy_from_slice(&input_str_1.as_bytes()[0..4]);
1379        view_buffer[8..12].copy_from_slice(&0u32.to_le_bytes());
1380        view_buffer[12..].copy_from_slice(&0u32.to_le_bytes());
1381        let view = ByteView::from(u128::from_le_bytes(view_buffer));
1382        let views = ScalarBuffer::from(vec![view.into()]);
1383        let buffers = vec![Buffer::from_slice_ref(input_str_2.as_bytes())];
1384
1385        StringViewArray::new(views, buffers, None);
1386    }
1387
1388    #[test]
1389    fn test_gc() {
1390        let test_data = [
1391            Some("longer than 12 bytes"),
1392            Some("short"),
1393            Some("t"),
1394            Some("longer than 12 bytes"),
1395            None,
1396            Some("short"),
1397        ];
1398
1399        let array = {
1400            let mut builder = StringViewBuilder::new().with_fixed_block_size(8); // create multiple buffers
1401            test_data.into_iter().for_each(|v| builder.append_option(v));
1402            builder.finish()
1403        };
1404        assert!(array.buffers.len() > 1);
1405
1406        fn check_gc(to_test: &StringViewArray) {
1407            let gc = to_test.gc();
1408            assert_ne!(to_test.data_buffers().len(), gc.data_buffers().len());
1409
1410            to_test.iter().zip(gc.iter()).for_each(|(a, b)| {
1411                assert_eq!(a, b);
1412            });
1413            assert_eq!(to_test.len(), gc.len());
1414        }
1415
1416        check_gc(&array);
1417        check_gc(&array.slice(1, 3));
1418        check_gc(&array.slice(2, 1));
1419        check_gc(&array.slice(2, 2));
1420        check_gc(&array.slice(3, 1));
1421    }
1422
1423    /// 1) Empty array: no elements, expect gc to return empty with no data buffers
1424    #[test]
1425    fn test_gc_empty_array() {
1426        let array = StringViewBuilder::new()
1427            .with_fixed_block_size(BLOCK_SIZE)
1428            .finish();
1429        let gced = array.gc();
1430        // length and null count remain zero
1431        assert_eq!(gced.len(), 0);
1432        assert_eq!(gced.null_count(), 0);
1433        // no underlying data buffers should be allocated
1434        assert!(
1435            gced.data_buffers().is_empty(),
1436            "Expected no data buffers for empty array"
1437        );
1438    }
1439
1440    /// 2) All inline values (<= INLINE_LEN): capacity-only data buffer, same values
1441    #[test]
1442    fn test_gc_all_inline() {
1443        let mut builder = StringViewBuilder::new().with_fixed_block_size(BLOCK_SIZE);
1444        // append many short strings, each exactly INLINE_LEN long
1445        for _ in 0..100 {
1446            let s = "A".repeat(MAX_INLINE_VIEW_LEN as usize);
1447            builder.append_option(Some(&s));
1448        }
1449        let array = builder.finish();
1450        let gced = array.gc();
1451        // Since all views fit inline, data buffer is empty
1452        assert_eq!(
1453            gced.data_buffers().len(),
1454            0,
1455            "Should have no data buffers for inline values"
1456        );
1457        assert_eq!(gced.len(), 100);
1458        // verify element-wise equality
1459        array.iter().zip(gced.iter()).for_each(|(orig, got)| {
1460            assert_eq!(orig, got, "Inline value mismatch after gc");
1461        });
1462    }
1463
1464    /// 3) All large values (> INLINE_LEN): each must be copied into the new data buffer
1465    #[test]
1466    fn test_gc_all_large() {
1467        let mut builder = StringViewBuilder::new().with_fixed_block_size(BLOCK_SIZE);
1468        let large_str = "X".repeat(MAX_INLINE_VIEW_LEN as usize + 5);
1469        // append multiple large strings
1470        for _ in 0..50 {
1471            builder.append_option(Some(&large_str));
1472        }
1473        let array = builder.finish();
1474        let gced = array.gc();
1475        // New data buffers should be populated (one or more blocks)
1476        assert!(
1477            !gced.data_buffers().is_empty(),
1478            "Expected data buffers for large values"
1479        );
1480        assert_eq!(gced.len(), 50);
1481        // verify that every large string emerges unchanged
1482        array.iter().zip(gced.iter()).for_each(|(orig, got)| {
1483            assert_eq!(orig, got, "Large view mismatch after gc");
1484        });
1485    }
1486
1487    /// 4) All null elements: ensure null bitmap handling path is correct
1488    #[test]
1489    fn test_gc_all_nulls() {
1490        let mut builder = StringViewBuilder::new().with_fixed_block_size(BLOCK_SIZE);
1491        for _ in 0..20 {
1492            builder.append_null();
1493        }
1494        let array = builder.finish();
1495        let gced = array.gc();
1496        // length and null count match
1497        assert_eq!(gced.len(), 20);
1498        assert_eq!(gced.null_count(), 20);
1499        // data buffers remain empty for null-only array
1500        assert!(
1501            gced.data_buffers().is_empty(),
1502            "No data should be stored for nulls"
1503        );
1504    }
1505
1506    /// 5) Random mix of inline, large, and null values with slicing tests
1507    #[test]
1508    fn test_gc_random_mixed_and_slices() {
1509        let mut rng = StdRng::seed_from_u64(42);
1510        let mut builder = StringViewBuilder::new().with_fixed_block_size(BLOCK_SIZE);
1511        // Keep a Vec of original Option<String> for later comparison
1512        let mut original: Vec<Option<String>> = Vec::new();
1513
1514        for _ in 0..200 {
1515            if rng.random_bool(0.1) {
1516                // 10% nulls
1517                builder.append_null();
1518                original.push(None);
1519            } else {
1520                // random length between 0 and twice the inline limit
1521                let len = rng.random_range(0..(MAX_INLINE_VIEW_LEN * 2));
1522                let s = "A".repeat(len as usize);
1523                builder.append_option(Some(&s));
1524                original.push(Some(s));
1525            }
1526        }
1527
1528        let array = builder.finish();
1529        // Test multiple slice ranges to ensure offset logic is correct
1530        for (offset, slice_len) in &[(0, 50), (10, 100), (150, 30)] {
1531            let sliced = array.slice(*offset, *slice_len);
1532            let gced = sliced.gc();
1533            // Build expected slice of Option<&str>
1534            let expected: Vec<Option<&str>> = original[*offset..(*offset + *slice_len)]
1535                .iter()
1536                .map(|opt| opt.as_deref())
1537                .collect();
1538
1539            assert_eq!(gced.len(), *slice_len, "Slice length mismatch");
1540            // Compare element-wise
1541            gced.iter().zip(expected.iter()).for_each(|(got, expect)| {
1542                assert_eq!(got, *expect, "Value mismatch in mixed slice after gc");
1543            });
1544        }
1545    }
1546
1547    #[test]
1548    #[cfg_attr(miri, ignore)] // Takes too long
1549    fn test_gc_huge_array() {
1550        // Construct multiple 128 MiB BinaryView entries so total > 4 GiB
1551        let block_len: usize = 128 * 1024 * 1024; // 128 MiB per view
1552        let num_views: usize = 36;
1553
1554        // Create a single 128 MiB data block with a simple byte pattern
1555        let buffer = Buffer::from_vec(vec![0xAB; block_len]);
1556        let buffer2 = Buffer::from_vec(vec![0xFF; block_len]);
1557
1558        // Append this block and then add many views pointing to it
1559        let mut builder = BinaryViewBuilder::new();
1560        let block_id = builder.append_block(buffer);
1561        for _ in 0..num_views / 2 {
1562            builder
1563                .try_append_view(block_id, 0, block_len as u32)
1564                .expect("append view into 128MiB block");
1565        }
1566        let block_id2 = builder.append_block(buffer2);
1567        for _ in 0..num_views / 2 {
1568            builder
1569                .try_append_view(block_id2, 0, block_len as u32)
1570                .expect("append view into 128MiB block");
1571        }
1572
1573        let array = builder.finish();
1574        let total = array.total_buffer_bytes_used();
1575        assert!(
1576            total > u32::MAX as usize,
1577            "Expected total non-inline bytes to exceed 4 GiB, got {total}"
1578        );
1579
1580        // Run gc and verify correctness
1581        let gced = array.gc();
1582        assert_eq!(gced.len(), num_views, "Length mismatch after gc");
1583        assert_eq!(gced.null_count(), 0, "Null count mismatch after gc");
1584        assert_ne!(
1585            gced.data_buffers().len(),
1586            1,
1587            "gc with huge buffer should not consolidate data into a single buffer"
1588        );
1589
1590        // Element-wise equality check across the entire array
1591        array.iter().zip(gced.iter()).for_each(|(orig, got)| {
1592            assert_eq!(orig, got, "Value mismatch after gc on huge array");
1593        });
1594    }
1595
1596    #[test]
1597    fn test_gc_slow_path_preserves_inline_views() {
1598        // Regression test for a bug in the multi-buffer "slow path" of `gc()`,
1599        // which `gc` only reaches once a view column references more than
1600        // `i32::MAX` (~2.1 GiB) of non-inline data. That path grouped and copied
1601        // only the *non-inline* views and dropped every inline (short) view,
1602        // returning an array shorter than its input. GC is a pure size
1603        // optimisation and must never change the row count.
1604        //
1605        // Rather than allocate gigabytes to hit the real threshold, drive the
1606        // same code via `gc_with_max_buffer_size` with a tiny cap so a handful
1607        // of bytes forces the buffer split. Inline, large, and null views are
1608        // interspersed so the split lands between and around inline entries.
1609        let long = "this is definitely longer than twelve bytes"; // non-inline
1610        let test_data = [
1611            Some("short"),      // inline
1612            Some(long),         // large
1613            Some("s"),          // inline
1614            Some(long),         // large
1615            None,               // null
1616            Some(long),         // large
1617            Some("also short"), // inline
1618            Some(long),         // large
1619            Some("tail"),       // inline
1620        ];
1621        let array: StringViewArray = test_data.into_iter().collect();
1622
1623        // Cap each output buffer at ~1.5 large values so the copy is forced to
1624        // split across several buffers, exercising the slow path with no
1625        // multi-GiB allocation.
1626        let max_buffer_size = long.len() + long.len() / 2;
1627        let gced = array.gc_with_max_buffer_size(max_buffer_size);
1628
1629        // The core invariant: gc preserves the row count.
1630        assert_eq!(gced.len(), array.len(), "gc changed the row count");
1631        // The split must actually have happened, otherwise we'd be exercising
1632        // the fast path and could not catch the inline-dropping regression.
1633        assert!(
1634            gced.data_buffers().len() > 1,
1635            "expected output split across multiple buffers, got {}",
1636            gced.data_buffers().len()
1637        );
1638        // No output buffer may exceed the cap.
1639        for buf in gced.data_buffers().iter() {
1640            assert!(buf.len() <= max_buffer_size, "buffer exceeded max size");
1641        }
1642        // Every value (inline, large, and null) is unchanged and in order.
1643        array
1644            .iter()
1645            .zip(gced.iter())
1646            .enumerate()
1647            .for_each(|(i, (a, b))| {
1648                assert_eq!(a, b, "value mismatch at index {i} after gc");
1649            });
1650        // The result round-trips through full validation.
1651        gced.to_data().validate_full().unwrap();
1652    }
1653
1654    #[test]
1655    fn test_gc_slow_path_value_larger_than_buffer() {
1656        // A single non-inline value can be larger than one output buffer (a view
1657        // length is a `u32`, so a value may exceed `max_buffer_size`). Such a
1658        // value cannot be split, so it occupies a buffer of its own; gc must
1659        // still preserve every row and produce a valid array.
1660        //
1661        // The real threshold needs a multi-GiB value, so drive the same logic
1662        // with a `max_buffer_size` smaller than the value length.
1663        let big = "x".repeat(64); // non-inline, larger than the cap below
1664        let test_data = [
1665            Some("short"),      // inline
1666            Some(big.as_str()), // larger than max_buffer_size
1667            None,               // null
1668            Some("tail"),       // inline
1669            Some(big.as_str()), // larger than max_buffer_size
1670        ];
1671        let array: StringViewArray = test_data.into_iter().collect();
1672
1673        let max_buffer_size = 16; // smaller than a single `big` value
1674        let gced = array.gc_with_max_buffer_size(max_buffer_size);
1675
1676        // Row count is preserved.
1677        assert_eq!(gced.len(), array.len(), "gc changed the row count");
1678        // Each oversized value lands in its own buffer, so the copy splits.
1679        assert!(
1680            gced.data_buffers().len() > 1,
1681            "expected output split across multiple buffers, got {}",
1682            gced.data_buffers().len()
1683        );
1684        // Every value is unchanged and in order.
1685        array
1686            .iter()
1687            .zip(gced.iter())
1688            .enumerate()
1689            .for_each(|(i, (a, b))| {
1690                assert_eq!(a, b, "value mismatch at index {i} after gc");
1691            });
1692        // The result round-trips through full validation.
1693        gced.to_data().validate_full().unwrap();
1694    }
1695
1696    #[test]
1697    fn test_eq() {
1698        let test_data = [
1699            Some("longer than 12 bytes"),
1700            None,
1701            Some("short"),
1702            Some("again, this is longer than 12 bytes"),
1703        ];
1704
1705        let array1 = {
1706            let mut builder = StringViewBuilder::new().with_fixed_block_size(8);
1707            test_data.into_iter().for_each(|v| builder.append_option(v));
1708            builder.finish()
1709        };
1710        let array2 = {
1711            // create a new array with the same data but different layout
1712            let mut builder = StringViewBuilder::new().with_fixed_block_size(100);
1713            test_data.into_iter().for_each(|v| builder.append_option(v));
1714            builder.finish()
1715        };
1716        assert_eq!(array1, array1.clone());
1717        assert_eq!(array2, array2.clone());
1718        assert_eq!(array1, array2);
1719    }
1720
1721    /// Integration tests for `inline_key_fast` covering:
1722    ///
1723    /// 1. Monotonic ordering across increasing lengths and lexical variations.
1724    /// 2. Cross-check against `GenericBinaryArray` comparison to ensure semantic equivalence.
1725    ///
1726    /// This also includes a specific test for the “bar” vs. “bar\0” case, demonstrating why
1727    /// the length field is required even when all inline bytes fit in 12 bytes.
1728    ///
1729    /// The test includes strings that verify correct byte order (prevent reversal bugs),
1730    /// and length-based tie-breaking in the composite key.
1731    ///
1732    /// The test confirms that `inline_key_fast` produces keys which sort consistently
1733    /// with the expected lexicographical order of the raw byte arrays.
1734    #[test]
1735    fn test_inline_key_fast_various_lengths_and_lexical() {
1736        /// Helper to create a raw u128 value representing an inline ByteView:
1737        /// - `length`: number of meaningful bytes (must be ≤ 12)
1738        /// - `data`: the actual inline data bytes
1739        ///
1740        /// The first 4 bytes encode length in little-endian,
1741        /// the following 12 bytes contain the inline string data (unpadded).
1742        fn make_raw_inline(length: u32, data: &[u8]) -> u128 {
1743            assert!(length as usize <= 12, "Inline length must be ≤ 12");
1744            assert!(
1745                data.len() == length as usize,
1746                "Data length must match `length`"
1747            );
1748
1749            let mut raw_bytes = [0u8; 16];
1750            raw_bytes[0..4].copy_from_slice(&length.to_le_bytes()); // length stored little-endian
1751            raw_bytes[4..(4 + data.len())].copy_from_slice(data); // inline data
1752            u128::from_le_bytes(raw_bytes)
1753        }
1754
1755        // Test inputs: various lengths and lexical orders,
1756        // plus special cases for byte order and length tie-breaking
1757        let test_inputs: Vec<&[u8]> = vec![
1758            b"a",
1759            b"aa",
1760            b"aaa",
1761            b"aab",
1762            b"abcd",
1763            b"abcde",
1764            b"abcdef",
1765            b"abcdefg",
1766            b"abcdefgh",
1767            b"abcdefghi",
1768            b"abcdefghij",
1769            b"abcdefghijk",
1770            b"abcdefghijkl",
1771            // Tests for byte-order reversal bug:
1772            // Without the fix, "backend one" would compare as "eno dnekcab",
1773            // causing incorrect sort order relative to "backend two".
1774            b"backend one",
1775            b"backend two",
1776            // Tests length-tiebreaker logic:
1777            // "bar" (3 bytes) and "bar\0" (4 bytes) have identical inline data,
1778            // so only the length differentiates their ordering.
1779            b"bar",
1780            b"bar\0",
1781            // Additional lexical and length tie-breaking cases with same prefix, in correct lex order:
1782            b"than12Byt",
1783            b"than12Bytes",
1784            b"than12Bytes\0",
1785            b"than12Bytesx",
1786            b"than12Bytex",
1787            b"than12Bytez",
1788            // Additional lexical tests
1789            b"xyy",
1790            b"xyz",
1791            b"xza",
1792        ];
1793
1794        // Create a GenericBinaryArray for cross-comparison of lex order
1795        let array: GenericBinaryArray<i32> =
1796            GenericBinaryArray::from(test_inputs.iter().map(|s| Some(*s)).collect::<Vec<_>>());
1797
1798        for i in 0..array.len() - 1 {
1799            let v1 = array.value(i);
1800            let v2 = array.value(i + 1);
1801
1802            // Assert the array's natural lexical ordering is correct
1803            assert!(v1 < v2, "Array compare failed: {v1:?} !< {v2:?}");
1804
1805            // Assert the keys produced by inline_key_fast reflect the same ordering
1806            let key1 = GenericByteViewArray::<BinaryViewType>::inline_key_fast(make_raw_inline(
1807                v1.len() as u32,
1808                v1,
1809            ));
1810            let key2 = GenericByteViewArray::<BinaryViewType>::inline_key_fast(make_raw_inline(
1811                v2.len() as u32,
1812                v2,
1813            ));
1814
1815            assert!(
1816                key1 < key2,
1817                "Key compare failed: key({v1:?})=0x{key1:032x} !< key({v2:?})=0x{key2:032x}",
1818            );
1819        }
1820    }
1821
1822    #[test]
1823    fn empty_array_should_return_empty_lengths_iterator() {
1824        let empty = GenericByteViewArray::<BinaryViewType>::from(Vec::<&[u8]>::new());
1825
1826        let mut lengths_iter = empty.lengths();
1827        assert_eq!(lengths_iter.len(), 0);
1828        assert_eq!(lengths_iter.next(), None);
1829    }
1830
1831    #[test]
1832    fn array_lengths_should_return_correct_length_for_both_inlined_and_non_inlined() {
1833        let cases = GenericByteViewArray::<BinaryViewType>::from(vec![
1834            // Not inlined as longer than 12 bytes
1835            b"Supercalifragilisticexpialidocious" as &[u8],
1836            // Inlined as shorter than 12 bytes
1837            b"Hello",
1838            // Empty value
1839            b"",
1840            // Exactly 12 bytes
1841            b"abcdefghijkl",
1842        ]);
1843
1844        let mut lengths_iter = cases.lengths();
1845
1846        assert_eq!(lengths_iter.len(), cases.len());
1847
1848        let cases_iter = cases.iter();
1849
1850        for case in cases_iter {
1851            let case_value = case.unwrap();
1852            let length = lengths_iter.next().expect("Should have a length");
1853
1854            assert_eq!(case_value.len(), length as usize);
1855        }
1856
1857        assert_eq!(lengths_iter.next(), None, "Should not have more lengths");
1858    }
1859
1860    #[test]
1861    fn array_lengths_should_return_the_underlying_length_for_null_values() {
1862        let cases = GenericByteViewArray::<BinaryViewType>::from(vec![
1863            // Not inlined as longer than 12 bytes
1864            b"Supercalifragilisticexpialidocious" as &[u8],
1865            // Inlined as shorter than 12 bytes
1866            b"Hello",
1867            // Empty value
1868            b"",
1869            // Exactly 12 bytes
1870            b"abcdefghijkl",
1871        ]);
1872
1873        let (views, buffer, _) = cases.clone().into_parts();
1874
1875        // Keeping the values but just adding nulls on top
1876        let cases_with_all_nulls = GenericByteViewArray::<BinaryViewType>::new(
1877            views,
1878            buffer,
1879            Some(NullBuffer::new_null(cases.len())),
1880        );
1881
1882        let lengths_iter = cases.lengths();
1883        let mut all_nulls_lengths_iter = cases_with_all_nulls.lengths();
1884
1885        assert_eq!(lengths_iter.len(), all_nulls_lengths_iter.len());
1886
1887        for expected_length in lengths_iter {
1888            let actual_length = all_nulls_lengths_iter.next().expect("Should have a length");
1889
1890            assert_eq!(expected_length, actual_length);
1891        }
1892
1893        assert_eq!(
1894            all_nulls_lengths_iter.next(),
1895            None,
1896            "Should not have more lengths"
1897        );
1898    }
1899
1900    #[test]
1901    fn array_lengths_on_sliced_should_only_return_lengths_for_sliced_data() {
1902        let array = GenericByteViewArray::<BinaryViewType>::from(vec![
1903            b"aaaaaaaaaaaaaaaaaaaaaaaaaaa" as &[u8],
1904            b"Hello",
1905            b"something great",
1906            b"is",
1907            b"coming soon!",
1908            b"when you find what it is",
1909            b"let me know",
1910            b"cause",
1911            b"I",
1912            b"have no idea",
1913            b"what it",
1914            b"is",
1915        ]);
1916
1917        let sliced_array = array.slice(2, array.len() - 3);
1918
1919        let mut lengths_iter = sliced_array.lengths();
1920
1921        assert_eq!(lengths_iter.len(), sliced_array.len());
1922
1923        let values_iter = sliced_array.iter();
1924
1925        for value in values_iter {
1926            let value = value.unwrap();
1927            let length = lengths_iter.next().expect("Should have a length");
1928
1929            assert_eq!(value.len(), length as usize);
1930        }
1931
1932        assert_eq!(lengths_iter.next(), None, "Should not have more lengths");
1933    }
1934
1935    #[should_panic(expected = "Mismatched data type, expected Utf8View, got BinaryView")]
1936    #[test]
1937    fn invalid_casting_from_array_data() {
1938        // Should not be able to cast to StringViewArray due to invalid UTF-8
1939        let array_data = binary_view_array_with_invalid_utf8_data().into_data();
1940        let _ = StringViewArray::from(array_data);
1941    }
1942
1943    #[should_panic(expected = "invalid utf-8 sequence")]
1944    #[test]
1945    fn invalid_array_data() {
1946        let (views, buffers, nulls) = binary_view_array_with_invalid_utf8_data().into_parts();
1947
1948        // manually try and add invalid array data with Utf8View data type
1949        let mut builder = ArrayDataBuilder::new(DataType::Utf8View)
1950            .add_buffer(views.into_inner())
1951            .len(3);
1952        for buffer in buffers.iter() {
1953            builder = builder.add_buffer(buffer.clone())
1954        }
1955        builder = builder.nulls(nulls);
1956
1957        let data = builder.build().unwrap(); // should fail validation
1958        let _arr = StringViewArray::from(data);
1959    }
1960
1961    /// Returns a BinaryViewArray with one invalid UTF-8 value
1962    fn binary_view_array_with_invalid_utf8_data() -> BinaryViewArray {
1963        let array = GenericByteViewArray::<BinaryViewType>::from(vec![
1964            b"aaaaaaaaaaaaaaaaaaaaaaaaaaa" as &[u8],
1965            &[
1966                0xf0, 0x80, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1967                0x00, 0x00,
1968            ],
1969            b"good",
1970        ]);
1971        assert!(from_utf8(array.value(0)).is_ok());
1972        assert!(from_utf8(array.value(1)).is_err()); // value 1 is invalid utf8
1973        assert!(from_utf8(array.value(2)).is_ok());
1974        array
1975    }
1976
1977    #[test]
1978    fn test_total_bytes_len() {
1979        // inlined: "hello"=5, "world"=5, "lulu"=4 → 14
1980        // non-inlined: "large payload over 12 bytes"=27
1981        // null: should not count
1982        let mut builder = StringViewBuilder::new();
1983        builder.append_value("hello");
1984        builder.append_value("world");
1985        builder.append_value("lulu");
1986        builder.append_null();
1987        builder.append_value("large payload over 12 bytes");
1988        let array = builder.finish();
1989        assert_eq!(array.total_bytes_len(), 5 + 5 + 4 + 27);
1990    }
1991
1992    #[test]
1993    fn test_total_bytes_len_empty() {
1994        let array = StringViewArray::from_iter::<Vec<Option<&str>>>(vec![]);
1995        assert_eq!(array.total_bytes_len(), 0);
1996    }
1997
1998    #[test]
1999    fn test_total_bytes_len_all_nulls() {
2000        let array = StringViewArray::new_null(5);
2001        assert_eq!(array.total_bytes_len(), 0);
2002    }
2003
2004    #[test]
2005    fn test_total_bytes_len_binary_view() {
2006        let array = BinaryViewArray::from_iter(vec![
2007            Some(b"hi".as_ref()),
2008            None,
2009            Some(b"large payload over 12 bytes".as_ref()),
2010        ]);
2011        assert_eq!(array.total_bytes_len(), 2 + 27);
2012    }
2013}