Skip to main content

arrow_array/array/
byte_array.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: See the NOTICE file distributed with this work for additional information regarding copyright ownership
3// SPDX-FileContributor: Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements
4
5use crate::array::{get_offsets_from_buffer, print_long_array};
6use crate::builder::GenericByteBuilder;
7use crate::iterator::ArrayIter;
8use crate::types::ByteArrayType;
9use crate::types::bytes::ByteArrayNativeType;
10use crate::{Array, ArrayAccessor, ArrayRef, OffsetSizeTrait, Scalar};
11use arrow_buffer::{ArrowNativeType, Buffer, MutableBuffer};
12use arrow_buffer::{NullBuffer, OffsetBuffer};
13use arrow_data::{ArrayData, ArrayDataBuilder};
14use arrow_schema::{ArrowError, DataType};
15use std::any::Any;
16use std::sync::Arc;
17
18/// An array of [variable length byte arrays](https://arrow.apache.org/docs/format/Columnar.html#variable-size-binary-layout)
19///
20/// See [`StringArray`] and [`LargeStringArray`] for storing utf8 encoded string data
21///
22/// See [`BinaryArray`] and [`LargeBinaryArray`] for storing arbitrary bytes
23///
24/// # Example: From a Vec
25///
26/// ```
27/// # use arrow_array::{Array, GenericByteArray, types::Utf8Type};
28/// let arr: GenericByteArray<Utf8Type> = vec!["hello", "world", ""].into();
29/// assert_eq!(arr.value_data(), b"helloworld");
30/// assert_eq!(arr.value_offsets(), &[0, 5, 10, 10]);
31/// let values: Vec<_> = arr.iter().collect();
32/// assert_eq!(values, &[Some("hello"), Some("world"), Some("")]);
33/// ```
34///
35/// # Example: From an optional Vec
36///
37/// ```
38/// # use arrow_array::{Array, GenericByteArray, types::Utf8Type};
39/// let arr: GenericByteArray<Utf8Type> = vec![Some("hello"), Some("world"), Some(""), None].into();
40/// assert_eq!(arr.value_data(), b"helloworld");
41/// assert_eq!(arr.value_offsets(), &[0, 5, 10, 10, 10]);
42/// let values: Vec<_> = arr.iter().collect();
43/// assert_eq!(values, &[Some("hello"), Some("world"), Some(""), None]);
44/// ```
45///
46/// # Example: From an iterator of option
47///
48/// ```
49/// # use arrow_array::{Array, GenericByteArray, types::Utf8Type};
50/// let arr: GenericByteArray<Utf8Type> = (0..5).map(|x| (x % 2 == 0).then(|| x.to_string())).collect();
51/// let values: Vec<_> = arr.iter().collect();
52/// assert_eq!(values, &[Some("0"), None, Some("2"), None, Some("4")]);
53/// ```
54///
55/// # Example: Using Builder
56///
57/// ```
58/// # use arrow_array::Array;
59/// # use arrow_array::builder::GenericByteBuilder;
60/// # use arrow_array::types::Utf8Type;
61/// let mut builder = GenericByteBuilder::<Utf8Type>::new();
62/// builder.append_value("hello");
63/// builder.append_null();
64/// builder.append_value("world");
65/// let array = builder.finish();
66/// let values: Vec<_> = array.iter().collect();
67/// assert_eq!(values, &[Some("hello"), None, Some("world")]);
68/// ```
69///
70/// [`StringArray`]: crate::StringArray
71/// [`LargeStringArray`]: crate::LargeStringArray
72/// [`BinaryArray`]: crate::BinaryArray
73/// [`LargeBinaryArray`]: crate::LargeBinaryArray
74pub struct GenericByteArray<T: ByteArrayType> {
75    data_type: DataType,
76    value_offsets: OffsetBuffer<T::Offset>,
77    value_data: Buffer,
78    nulls: Option<NullBuffer>,
79}
80
81impl<T: ByteArrayType> Clone for GenericByteArray<T> {
82    fn clone(&self) -> Self {
83        Self {
84            data_type: T::DATA_TYPE,
85            value_offsets: self.value_offsets.clone(),
86            value_data: self.value_data.clone(),
87            nulls: self.nulls.clone(),
88        }
89    }
90}
91
92impl<T: ByteArrayType> GenericByteArray<T> {
93    /// Data type of the array.
94    pub const DATA_TYPE: DataType = T::DATA_TYPE;
95
96    /// Create a new [`GenericByteArray`] from the provided parts, panicking on failure
97    ///
98    /// # Panics
99    ///
100    /// Panics if [`GenericByteArray::try_new`] returns an error
101    pub fn new(
102        offsets: OffsetBuffer<T::Offset>,
103        values: Buffer,
104        nulls: Option<NullBuffer>,
105    ) -> Self {
106        Self::try_new(offsets, values, nulls).unwrap()
107    }
108
109    /// Create a new [`GenericByteArray`] from the provided parts, returning an error on failure
110    ///
111    /// # Errors
112    ///
113    /// * `offsets.len() - 1 != nulls.len()`
114    /// * Any consecutive pair of `offsets` does not denote a valid slice of `values`
115    pub fn try_new(
116        offsets: OffsetBuffer<T::Offset>,
117        values: Buffer,
118        nulls: Option<NullBuffer>,
119    ) -> Result<Self, ArrowError> {
120        let len = offsets.len() - 1;
121
122        // Verify that each pair of offsets is a valid slices of values
123        T::validate(&offsets, &values)?;
124
125        if let Some(n) = nulls.as_ref()
126            && n.len() != len
127        {
128            return Err(ArrowError::InvalidArgumentError(format!(
129                "Incorrect length of null buffer for {}{}Array, expected {len} got {}",
130                T::Offset::PREFIX,
131                T::PREFIX,
132                n.len(),
133            )));
134        }
135
136        Ok(Self {
137            data_type: T::DATA_TYPE,
138            value_offsets: offsets,
139            value_data: values,
140            nulls,
141        })
142    }
143
144    /// Create a new [`GenericByteArray`] from the provided parts, without validation
145    ///
146    /// # Safety
147    ///
148    /// Safe if [`Self::try_new`] would not error
149    pub unsafe fn new_unchecked(
150        offsets: OffsetBuffer<T::Offset>,
151        values: Buffer,
152        nulls: Option<NullBuffer>,
153    ) -> Self {
154        if cfg!(feature = "force_validate") {
155            return Self::new(offsets, values, nulls);
156        }
157        Self {
158            data_type: T::DATA_TYPE,
159            value_offsets: offsets,
160            value_data: values,
161            nulls,
162        }
163    }
164
165    /// Create a new [`GenericByteArray`] of length `len` where all values are null
166    pub fn new_null(len: usize) -> Self {
167        Self {
168            data_type: T::DATA_TYPE,
169            value_offsets: OffsetBuffer::new_zeroed(len),
170            value_data: MutableBuffer::new(0).into(),
171            nulls: Some(NullBuffer::new_null(len)),
172        }
173    }
174
175    /// Create a new [`Scalar`] from `v`
176    pub fn new_scalar(value: impl AsRef<T::Native>) -> Scalar<Self> {
177        Scalar::new(Self::from_iter_values(std::iter::once(value)))
178    }
179
180    /// Create a new [`GenericByteArray`] where `value` is repeated `repeat_count` times.
181    ///
182    /// # Panics
183    /// This will panic if value's length multiplied by `repeat_count` overflows usize.
184    ///
185    pub fn new_repeated(value: impl AsRef<T::Native>, repeat_count: usize) -> Self {
186        let s: &[u8] = value.as_ref().as_ref();
187        let value_offsets = OffsetBuffer::from_repeated_length(s.len(), repeat_count);
188        let bytes: Buffer = {
189            let mut mutable_buffer = MutableBuffer::with_capacity(0);
190            mutable_buffer.repeat_slice_n_times(s, repeat_count);
191
192            mutable_buffer.into()
193        };
194
195        Self {
196            data_type: T::DATA_TYPE,
197            value_data: bytes,
198            value_offsets,
199            nulls: None,
200        }
201    }
202
203    /// Creates a [`GenericByteArray`] based on an iterator of values without nulls
204    ///
205    /// # Panics
206    /// Panics if the total length of the values exceeds `T::Offset::MAX`
207    pub fn from_iter_values<Ptr, I>(iter: I) -> Self
208    where
209        Ptr: AsRef<T::Native>,
210        I: IntoIterator<Item = Ptr>,
211    {
212        let iter = iter.into_iter();
213        // The size hint is only used to pre-allocate: an iterator is free to yield
214        // a different number of items than it reports.
215        let (lower, upper) = iter.size_hint();
216        let capacity = upper.unwrap_or(lower);
217
218        let mut offsets = MutableBuffer::new(
219            capacity
220                .saturating_add(1)
221                .saturating_mul(std::mem::size_of::<T::Offset>()),
222        );
223        offsets.push(T::Offset::usize_as(0));
224
225        let mut values = MutableBuffer::new(0);
226        for s in iter {
227            let s: &[u8] = s.as_ref().as_ref();
228            values.extend_from_slice(s);
229            offsets.push(T::Offset::usize_as(values.len()));
230        }
231
232        T::Offset::from_usize(values.len()).expect("offset overflow");
233        let offsets = Buffer::from(offsets);
234
235        // Safety: valid by construction
236        let value_offsets = unsafe { OffsetBuffer::new_unchecked(offsets.into()) };
237
238        Self {
239            data_type: T::DATA_TYPE,
240            value_data: values.into(),
241            value_offsets,
242            nulls: None,
243        }
244    }
245
246    /// Deconstruct this array into its constituent parts
247    pub fn into_parts(self) -> (OffsetBuffer<T::Offset>, Buffer, Option<NullBuffer>) {
248        (self.value_offsets, self.value_data, self.nulls)
249    }
250
251    /// Returns the length for value at index `i`.
252    /// # Panics
253    /// Panics if index `i` is out of bounds.
254    #[inline]
255    pub fn value_length(&self, i: usize) -> T::Offset {
256        let offsets = self.value_offsets();
257        offsets[i + 1] - offsets[i]
258    }
259
260    /// Returns a reference to the offsets of this array
261    ///
262    /// Unlike [`Self::value_offsets`] this returns the [`OffsetBuffer`]
263    /// allowing for zero-copy cloning
264    #[inline]
265    pub fn offsets(&self) -> &OffsetBuffer<T::Offset> {
266        &self.value_offsets
267    }
268
269    /// Returns the values of this array
270    ///
271    /// Unlike [`Self::value_data`] this returns the [`Buffer`]
272    /// allowing for zero-copy cloning. Like [`Self::value_data`], it can
273    /// include bytes that are not part of any value.
274    #[inline]
275    pub fn values(&self) -> &Buffer {
276        &self.value_data
277    }
278
279    /// Returns the raw value data
280    ///
281    /// This can include bytes before the first offset or after the last one,
282    /// for example when the array is a slice of a larger array. Those bytes are
283    /// not part of any value, and in a string array they need not be valid UTF-8.
284    pub fn value_data(&self) -> &[u8] {
285        self.value_data.as_slice()
286    }
287
288    /// Returns true if all data within this array is ASCII
289    pub fn is_ascii(&self) -> bool {
290        let offsets = &self.value_offsets;
291        self.value_data()[offsets.first().as_usize()..offsets.last().as_usize()].is_ascii()
292    }
293
294    /// Returns the offset values in the offsets buffer
295    #[inline]
296    pub fn value_offsets(&self) -> &[T::Offset] {
297        &self.value_offsets
298    }
299
300    /// Returns the element at index `i`
301    ///
302    /// Note: This method does not check for nulls and the value is arbitrary
303    /// if [`is_null`](Self::is_null) returns true for the index.
304    ///
305    /// # Safety
306    /// Caller is responsible for ensuring that the index is within the bounds of the array
307    pub unsafe fn value_unchecked(&self, i: usize) -> &T::Native {
308        let end = *unsafe { self.value_offsets().get_unchecked(i + 1) };
309        let start = *unsafe { self.value_offsets().get_unchecked(i) };
310
311        // Soundness
312        // pointer alignment & location is ensured by RawPtrBox
313        // buffer bounds/offset is ensured by the value_offset invariants
314
315        // Safety of `to_isize().unwrap()`
316        // `start` and `end` are &OffsetSize, which is a generic type that implements the
317        // OffsetSizeTrait. Currently, only i32 and i64 implement OffsetSizeTrait,
318        // both of which should cleanly cast to isize on an architecture that supports
319        // 32/64-bit offsets
320        let b = unsafe {
321            std::slice::from_raw_parts(
322                self.value_data
323                    .as_ptr()
324                    .offset(start.to_isize().unwrap_unchecked()),
325                (end - start).to_usize().unwrap_unchecked(),
326            )
327        };
328
329        // SAFETY:
330        // ArrayData is valid
331        unsafe { T::Native::from_bytes_unchecked(b) }
332    }
333
334    /// Returns the element at index `i`
335    ///
336    /// Note: This method does not check for nulls and the value is arbitrary
337    /// (but still well-defined) if [`is_null`](Self::is_null) returns true for the index.
338    ///
339    /// # Panics
340    /// Panics if index `i` is out of bounds.
341    pub fn value(&self, i: usize) -> &T::Native {
342        assert!(
343            i < self.len(),
344            "Trying to access an element at index {} from a {}{}Array of length {}",
345            i,
346            T::Offset::PREFIX,
347            T::PREFIX,
348            self.len()
349        );
350        // SAFETY:
351        // Verified length above
352        unsafe { self.value_unchecked(i) }
353    }
354
355    /// constructs a new iterator
356    pub fn iter(&self) -> ArrayIter<&Self> {
357        ArrayIter::new(self)
358    }
359
360    /// Returns a zero-copy slice of this array with the indicated offset and length.
361    ///
362    /// # Panics
363    /// Panics if `offset + length > self.len()`
364    pub fn slice(&self, offset: usize, length: usize) -> Self {
365        Self {
366            data_type: T::DATA_TYPE,
367            value_offsets: self.value_offsets.slice(offset, length),
368            value_data: self.value_data.clone(),
369            nulls: self.nulls.as_ref().map(|n| n.slice(offset, length)),
370        }
371    }
372
373    /// Converts this array into a [`GenericByteBuilder`] that reuses its buffers,
374    /// if possible.
375    ///
376    /// Returns `Err(self)` if the offsets or values cannot be reused, for
377    /// example because they are shared with another array, or because this is a
378    /// sliced array with a non-zero first offset.
379    ///
380    /// The null buffer is copied if it cannot be reused.
381    pub fn into_builder(self) -> Result<GenericByteBuilder<T>, Self> {
382        let (offsets, values, nulls) = self.into_parts();
383
384        // Puts the parts back together into the original array
385        let rebuild = |offsets: Buffer, values: Buffer, nulls: Option<NullBuffer>| {
386            // SAFETY: the parts come unchanged from `self`
387            unsafe {
388                Self::new_unchecked(OffsetBuffer::new_unchecked(offsets.into()), values, nulls)
389            }
390        };
391
392        // A buffer can be reused only if this array is its only owner and it
393        // starts at the beginning of its allocation. Otherwise, the original
394        // array is returned.
395        let offsets = match offsets.into_inner().into_inner().into_mutable() {
396            Ok(offsets) => offsets,
397            Err(offsets) => return Err(rebuild(offsets, values, nulls)),
398        };
399        let values = match values.into_mutable() {
400            Ok(values) => values,
401            Err(values) => return Err(rebuild(offsets.into(), values, nulls)),
402        };
403
404        // The builder needs a null buffer whose first bit is for the first row.
405        // That is the case when the bit offset is 0, and then the buffer is reused
406        // if this array is its only owner. Otherwise, the bits are copied, which is
407        // cheap as there is one bit per row.
408        let nulls = nulls.map(|nulls| {
409            let bits = nulls.into_inner();
410            if bits.offset() == 0 {
411                let len = bits.len();
412                bits.into_inner()
413                    .into_mutable()
414                    .unwrap_or_else(|buffer| buffer[..len.div_ceil(8)].to_vec().into())
415            } else {
416                // `sliced` returns the bits starting at the first row
417                bits.sliced().to_vec().into()
418            }
419        });
420
421        // SAFETY: the buffers come from a valid array
422        Ok(unsafe { GenericByteBuilder::new_from_buffer(offsets, values, nulls) })
423    }
424}
425
426impl<T: ByteArrayType> std::fmt::Debug for GenericByteArray<T> {
427    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
428        write!(f, "{}{}Array\n[\n", T::Offset::PREFIX, T::PREFIX)?;
429        print_long_array(self, f, &mut |index, f| {
430            std::fmt::Debug::fmt(&self.value(index), f)
431        })?;
432        write!(f, "]")
433    }
434}
435
436/// SAFETY: Correctly implements the contract of Arrow Arrays
437unsafe impl<T: ByteArrayType> Array for GenericByteArray<T> {
438    fn as_any(&self) -> &dyn Any {
439        self
440    }
441
442    fn to_data(&self) -> ArrayData {
443        self.clone().into()
444    }
445
446    fn into_data(self) -> ArrayData {
447        self.into()
448    }
449
450    fn data_type(&self) -> &DataType {
451        &self.data_type
452    }
453
454    fn slice(&self, offset: usize, length: usize) -> ArrayRef {
455        Arc::new(self.slice(offset, length))
456    }
457
458    fn len(&self) -> usize {
459        self.value_offsets.len() - 1
460    }
461
462    fn is_empty(&self) -> bool {
463        self.value_offsets.len() <= 1
464    }
465
466    fn shrink_to_fit(&mut self) {
467        self.value_offsets.shrink_to_fit();
468        self.value_data.shrink_to_fit();
469        if let Some(nulls) = &mut self.nulls {
470            nulls.shrink_to_fit();
471        }
472    }
473
474    fn offset(&self) -> usize {
475        0
476    }
477
478    fn nulls(&self) -> Option<&NullBuffer> {
479        self.nulls.as_ref()
480    }
481
482    fn logical_null_count(&self) -> usize {
483        // More efficient that the default implementation
484        self.null_count()
485    }
486
487    fn get_buffer_memory_size(&self) -> usize {
488        let mut sum = self.value_offsets.inner().inner().capacity();
489        sum += self.value_data.capacity();
490        if let Some(x) = &self.nulls {
491            sum += x.buffer().capacity()
492        }
493        sum
494    }
495
496    fn get_array_memory_size(&self) -> usize {
497        std::mem::size_of::<Self>() + self.get_buffer_memory_size()
498    }
499
500    #[cfg(feature = "pool")]
501    fn claim(&self, pool: &dyn arrow_buffer::MemoryPool) {
502        self.value_offsets.claim(pool);
503        self.value_data.claim(pool);
504        if let Some(nulls) = &self.nulls {
505            nulls.claim(pool);
506        }
507    }
508}
509
510impl<'a, T: ByteArrayType> ArrayAccessor for &'a GenericByteArray<T> {
511    type Item = &'a T::Native;
512
513    fn value(&self, index: usize) -> Self::Item {
514        GenericByteArray::value(self, index)
515    }
516
517    unsafe fn value_unchecked(&self, index: usize) -> Self::Item {
518        unsafe { GenericByteArray::value_unchecked(self, index) }
519    }
520}
521
522impl<T: ByteArrayType> From<ArrayData> for GenericByteArray<T> {
523    fn from(data: ArrayData) -> Self {
524        let (data_type, len, nulls, offset, mut buffers, _child_data) = data.into_parts();
525        assert_eq!(
526            data_type,
527            Self::DATA_TYPE,
528            "{}{}Array expects DataType::{}",
529            T::Offset::PREFIX,
530            T::PREFIX,
531            Self::DATA_TYPE
532        );
533        assert_eq!(
534            buffers.len(),
535            2,
536            "{}{}Array data should contain 2 buffers only (offsets and values)",
537            T::Offset::PREFIX,
538            T::PREFIX,
539        );
540        // buffers are offset then value, so pop in reverse
541        let value_data = buffers.pop().expect("checked above");
542        let offset_buffer = buffers.pop().expect("checked above");
543
544        // SAFETY:
545        // ArrayData is valid, and verified type above
546        let value_offsets = unsafe { get_offsets_from_buffer(offset_buffer, offset, len) };
547        Self {
548            data_type,
549            value_offsets,
550            value_data,
551            nulls,
552        }
553    }
554}
555
556impl<T: ByteArrayType> From<GenericByteArray<T>> for ArrayData {
557    fn from(array: GenericByteArray<T>) -> Self {
558        let len = array.len();
559
560        let offsets = array.value_offsets.into_inner().into_inner();
561        let builder = ArrayDataBuilder::new(array.data_type)
562            .len(len)
563            .buffers(vec![offsets, array.value_data])
564            .nulls(array.nulls);
565
566        unsafe { builder.build_unchecked() }
567    }
568}
569
570impl<'a, T: ByteArrayType> IntoIterator for &'a GenericByteArray<T> {
571    type Item = Option<&'a T::Native>;
572    type IntoIter = ArrayIter<Self>;
573
574    fn into_iter(self) -> Self::IntoIter {
575        ArrayIter::new(self)
576    }
577}
578
579impl<'a, Ptr, T: ByteArrayType> FromIterator<&'a Option<Ptr>> for GenericByteArray<T>
580where
581    Ptr: AsRef<T::Native> + 'a,
582{
583    fn from_iter<I: IntoIterator<Item = &'a Option<Ptr>>>(iter: I) -> Self {
584        iter.into_iter()
585            .map(|o| o.as_ref().map(|p| p.as_ref()))
586            .collect()
587    }
588}
589
590impl<Ptr, T: ByteArrayType> FromIterator<Option<Ptr>> for GenericByteArray<T>
591where
592    Ptr: AsRef<T::Native>,
593{
594    fn from_iter<I: IntoIterator<Item = Option<Ptr>>>(iter: I) -> Self {
595        let iter = iter.into_iter();
596        let mut builder = GenericByteBuilder::with_capacity(iter.size_hint().0, 1024);
597        builder.extend(iter);
598        builder.finish()
599    }
600}
601
602#[cfg(test)]
603mod tests {
604    use crate::{Array, BinaryArray, StringArray};
605    use arrow_buffer::{Buffer, NullBuffer, OffsetBuffer};
606
607    /// `from_iter_values` must work with iterators that report no upper size bound,
608    /// and must not trust the size hint it does get.
609    #[test]
610    fn from_iter_values_untrusted_size_hint() {
611        // No upper bound at all:
612        let no_upper_bound = (0..20).filter(|i| i % 2 == 0).map(|i| format!("v{i}"));
613        assert_eq!(no_upper_bound.size_hint(), (0, Some(20)));
614        let array = StringArray::from_iter_values(no_upper_bound);
615        assert_eq!(array.len(), 10);
616        assert_eq!(array.value(0), "v0");
617        assert_eq!(array.value(9), "v18");
618
619        // Upper bound larger than the number of yielded values:
620        let too_large_upper_bound = (0..).map(|i| format!("v{i}")).take_while(|v| v != "v3");
621        assert_eq!(too_large_upper_bound.size_hint(), (0, None));
622        let array = StringArray::from_iter_values(too_large_upper_bound);
623        assert_eq!(array.len(), 3);
624        assert_eq!(array.value(2), "v2");
625    }
626
627    #[test]
628    fn try_new() {
629        let data = Buffer::from_slice_ref("helloworld");
630        let offsets = OffsetBuffer::new(vec![0, 5, 10].into());
631        StringArray::new(offsets.clone(), data.clone(), None);
632
633        let nulls = NullBuffer::new_null(3);
634        let err =
635            StringArray::try_new(offsets.clone(), data.clone(), Some(nulls.clone())).unwrap_err();
636        assert_eq!(
637            err.to_string(),
638            "Invalid argument error: Incorrect length of null buffer for StringArray, expected 2 got 3"
639        );
640
641        let err = BinaryArray::try_new(offsets.clone(), data.clone(), Some(nulls)).unwrap_err();
642        assert_eq!(
643            err.to_string(),
644            "Invalid argument error: Incorrect length of null buffer for BinaryArray, expected 2 got 3"
645        );
646
647        let non_utf8_data = Buffer::from_slice_ref(b"he\xFFloworld");
648        let err = StringArray::try_new(offsets.clone(), non_utf8_data.clone(), None).unwrap_err();
649        assert_eq!(
650            err.to_string(),
651            "Invalid argument error: Encountered non UTF-8 data: invalid utf-8 sequence of 1 bytes from index 2"
652        );
653
654        BinaryArray::new(offsets, non_utf8_data, None);
655
656        let offsets = OffsetBuffer::new(vec![0, 5, 11].into());
657        let err = StringArray::try_new(offsets.clone(), data.clone(), None).unwrap_err();
658        assert_eq!(
659            err.to_string(),
660            "Invalid argument error: Offset of 11 exceeds length of values 10"
661        );
662
663        let err = BinaryArray::try_new(offsets.clone(), data, None).unwrap_err();
664        assert_eq!(
665            err.to_string(),
666            "Invalid argument error: Maximum offset of 11 is larger than values of length 10"
667        );
668
669        let non_ascii_data = Buffer::from_slice_ref("heìloworld");
670        StringArray::new(offsets.clone(), non_ascii_data.clone(), None);
671        BinaryArray::new(offsets, non_ascii_data.clone(), None);
672
673        let offsets = OffsetBuffer::new(vec![0, 3, 10].into());
674        let err = StringArray::try_new(offsets.clone(), non_ascii_data.clone(), None).unwrap_err();
675        assert_eq!(
676            err.to_string(),
677            "Invalid argument error: Split UTF-8 codepoint at offset 3"
678        );
679
680        BinaryArray::new(offsets, non_ascii_data, None);
681    }
682
683    #[test]
684    fn create_repeated() {
685        let arr = BinaryArray::new_repeated(b"hello", 3);
686        assert_eq!(arr.len(), 3);
687        assert_eq!(arr.value(0), b"hello");
688        assert_eq!(arr.value(1), b"hello");
689        assert_eq!(arr.value(2), b"hello");
690
691        let arr = StringArray::new_repeated("world", 2);
692        assert_eq!(arr.len(), 2);
693        assert_eq!(arr.value(0), "world");
694        assert_eq!(arr.value(1), "world");
695    }
696
697    #[test]
698    #[should_panic(expected = "total length overflow: does not fit in usize")]
699    fn create_repeated_usize_overflow_1() {
700        let _arr = BinaryArray::new_repeated(b"hello", (usize::MAX / "hello".len()) + 1);
701    }
702
703    #[test]
704    #[should_panic(expected = "total length overflow: does not fit in usize")]
705    fn create_repeated_usize_overflow_2() {
706        let _arr = BinaryArray::new_repeated(b"hello", usize::MAX);
707    }
708
709    #[test]
710    #[should_panic(expected = "offset overflow")]
711    fn create_repeated_i32_offset_overflow_1() {
712        let _arr = BinaryArray::new_repeated(b"hello", usize::MAX / "hello".len());
713    }
714
715    #[test]
716    #[should_panic(expected = "offset overflow")]
717    fn create_repeated_i32_offset_overflow_2() {
718        let _arr = BinaryArray::new_repeated(b"hello", ((i32::MAX as usize) / "hello".len()) + 1);
719    }
720}