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