Skip to main content

arrow_array/array/
fixed_size_list_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::{FixedSizeListBuilder, PrimitiveBuilder};
20use crate::iterator::FixedSizeListIter;
21use crate::{Array, ArrayAccessor, ArrayRef, ArrowPrimitiveType, make_array};
22use arrow_buffer::ArrowNativeType;
23use arrow_buffer::buffer::NullBuffer;
24use arrow_data::{ArrayData, ArrayDataBuilder};
25use arrow_schema::{ArrowError, DataType, FieldRef};
26use std::any::Any;
27use std::sync::Arc;
28
29/// An array of [fixed length lists], similar to JSON arrays
30/// (e.g. `["A", "B"]`).
31///
32/// Lists are represented using a `values` child
33/// array where each list has a fixed size of `value_length`.
34///
35/// Use [`FixedSizeListBuilder`] to construct a [`FixedSizeListArray`].
36///
37/// # Representation
38///
39/// A [`FixedSizeListArray`] can represent a list of values of any other
40/// supported Arrow type. Each element of the `FixedSizeListArray` itself is
41/// a list which may contain NULL and non-null values,
42/// or may itself be NULL.
43///
44/// For example, this `FixedSizeListArray` stores lists of strings:
45///
46/// ```text
47/// ┌─────────────┐
48/// │    [A,B]    │
49/// ├─────────────┤
50/// │    NULL     │
51/// ├─────────────┤
52/// │   [C,NULL]  │
53/// └─────────────┘
54/// ```
55///
56/// The `values` of this `FixedSizeListArray`s are stored in a child
57/// [`StringArray`] where logical null values take up `values_length` slots in the array
58/// as shown in the following diagram. The logical values
59/// are shown on the left, and the actual `FixedSizeListArray` encoding on the right
60///
61/// ```text
62///                                 ┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐
63///                                                         ┌ ─ ─ ─ ─ ─ ─ ─ ─┐
64///  ┌─────────────┐                │     ┌───┐               ┌───┐ ┌──────┐      │
65///  │   [A,B]     │                      │ 1 │             │ │ 1 │ │  A   │ │ 0
66///  ├─────────────┤                │     ├───┤               ├───┤ ├──────┤      │
67///  │    NULL     │                      │ 0 │             │ │ 1 │ │  B   │ │ 1
68///  ├─────────────┤                │     ├───┤               ├───┤ ├──────┤      │
69///  │  [C,NULL]   │                      │ 1 │             │ │ 0 │ │ ???? │ │ 2
70///  └─────────────┘                │     └───┘               ├───┤ ├──────┤      │
71///                                                         | │ 0 │ │ ???? │ │ 3
72///  Logical Values                 │   Validity              ├───┤ ├──────┤      │
73///                                     (nulls)             │ │ 1 │ │  C   │ │ 4
74///                                 │                         ├───┤ ├──────┤      │
75///                                                         │ │ 0 │ │ ???? │ │ 5
76///                                 │                         └───┘ └──────┘      │
77///                                                         │     Values     │
78///                                 │   FixedSizeListArray        (Array)         │
79///                                                         └ ─ ─ ─ ─ ─ ─ ─ ─┘
80///                                 └ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘
81/// ```
82///
83/// # Example
84///
85/// ```
86/// # use std::sync::Arc;
87/// # use arrow_array::{Array, FixedSizeListArray, Int32Array};
88/// # use arrow_data::ArrayData;
89/// # use arrow_schema::{DataType, Field};
90/// # use arrow_buffer::Buffer;
91/// // Construct a value array
92/// let value_data = ArrayData::builder(DataType::Int32)
93///     .len(9)
94///     .add_buffer(Buffer::from_slice_ref(&[0, 1, 2, 3, 4, 5, 6, 7, 8]))
95///     .build()
96///     .unwrap();
97/// let list_data_type = DataType::FixedSizeList(
98///     Arc::new(Field::new_list_field(DataType::Int32, false)),
99///     3,
100/// );
101/// let list_data = ArrayData::builder(list_data_type.clone())
102///     .len(3)
103///     .add_child_data(value_data.clone())
104///     .build()
105///     .unwrap();
106/// let list_array = FixedSizeListArray::from(list_data);
107/// let list0 = list_array.value(0);
108/// let list1 = list_array.value(1);
109/// let list2 = list_array.value(2);
110///
111/// assert_eq!( &[0, 1, 2], list0.as_any().downcast_ref::<Int32Array>().unwrap().values());
112/// assert_eq!( &[3, 4, 5], list1.as_any().downcast_ref::<Int32Array>().unwrap().values());
113/// assert_eq!( &[6, 7, 8], list2.as_any().downcast_ref::<Int32Array>().unwrap().values());
114/// ```
115///
116/// [`StringArray`]: crate::array::StringArray
117/// [fixed size arrays](https://arrow.apache.org/docs/format/Columnar.html#fixed-size-list-layout)
118#[derive(Clone)]
119pub struct FixedSizeListArray {
120    data_type: DataType, // Must be DataType::FixedSizeList(value_length)
121    values: ArrayRef,
122    nulls: Option<NullBuffer>,
123    value_length: i32,
124    len: usize,
125}
126
127impl FixedSizeListArray {
128    /// Create a new [`FixedSizeListArray`] with `size` element size, panicking on failure.
129    ///
130    /// Note that if `size == 0` and `nulls` is `None` (a degenerate, non-nullable
131    /// `FixedSizeListArray`), this function will set the length of the array to 0.
132    ///
133    /// If you would like to have a degenerate, non-nullable `FixedSizeListArray` with arbitrary
134    /// length, use the [`try_new_with_length()`] constructor.
135    ///
136    /// [`try_new_with_length()`]: Self::try_new_with_length
137    ///
138    /// # Panics
139    ///
140    /// Panics if [`Self::try_new`] returns an error
141    pub fn new(field: FieldRef, size: i32, values: ArrayRef, nulls: Option<NullBuffer>) -> Self {
142        Self::try_new(field, size, values, nulls).unwrap()
143    }
144
145    /// Create a new [`FixedSizeListArray`] from the provided parts, returning an error on failure.
146    ///
147    /// Note that if `size == 0` and `nulls` is `None` (a degenerate, non-nullable
148    /// `FixedSizeListArray`), this function will set the length of the array to 0.
149    ///
150    /// If you would like to have a degenerate, non-nullable `FixedSizeListArray` with arbitrary
151    /// length, use the [`try_new_with_length()`] constructor.
152    ///
153    /// [`try_new_with_length()`]: Self::try_new_with_length
154    ///
155    /// # Errors
156    ///
157    /// * `size < 0`
158    /// * `values.len() != nulls.len() * size` if `nulls` is `Some`
159    /// * `values.data_type() != field.data_type()`
160    /// * `!field.is_nullable() && !nulls.expand(size).contains(values.logical_nulls())`
161    pub fn try_new(
162        field: FieldRef,
163        size: i32,
164        values: ArrayRef,
165        nulls: Option<NullBuffer>,
166    ) -> Result<Self, ArrowError> {
167        let s = size.to_usize().ok_or_else(|| {
168            ArrowError::InvalidArgumentError(format!("Size cannot be negative, got {size}"))
169        })?;
170
171        if s == 0 {
172            // Note that for degenerate (`size == 0`) and non-nullable `FixedSizeList`s, we will set
173            // the length to 0 (`_or_default`).
174            let len = nulls.as_ref().map(|x| x.len()).unwrap_or_default();
175
176            Self::try_new_with_length(field, size, values, nulls, len)
177        } else {
178            if values.len() % s != 0 {
179                return Err(ArrowError::InvalidArgumentError(format!(
180                    "Incorrect length of values buffer for FixedSizeListArray, \
181                     expected a multiple of {s} got {}",
182                    values.len(),
183                )));
184            }
185
186            let len = values.len() / s;
187
188            // Check that the null buffer length is correct (if it exists).
189            if let Some(null_buffer) = &nulls {
190                if s * null_buffer.len() != values.len() {
191                    return Err(ArrowError::InvalidArgumentError(format!(
192                        "Incorrect length of values buffer for FixedSizeListArray, \
193                            expected {} got {}",
194                        s * null_buffer.len(),
195                        values.len(),
196                    )));
197                }
198            }
199
200            Self::try_new_with_length(field, size, values, nulls, len)
201        }
202    }
203
204    /// Create a new [`FixedSizeListArray`] from the provided parts, returning an error on failure.
205    ///
206    /// This method exists to allow the construction of arbitrary length degenerate (`size == 0`)
207    /// and non-nullable `FixedSizeListArray`s. If you want a nullable `FixedSizeListArray`, then
208    /// you can use [`try_new()`] instead.
209    ///
210    /// [`try_new()`]: Self::try_new
211    ///
212    /// # Errors
213    ///
214    /// * `size < 0`
215    /// * `nulls.len() != len` if `nulls` is `Some`
216    /// * `values.len() != len * size`
217    /// * `values.data_type() != field.data_type()`
218    /// * `!field.is_nullable() && !nulls.expand(size).contains(values.logical_nulls())`
219    pub fn try_new_with_length(
220        field: FieldRef,
221        size: i32,
222        values: ArrayRef,
223        nulls: Option<NullBuffer>,
224        len: usize,
225    ) -> Result<Self, ArrowError> {
226        let s = size.to_usize().ok_or_else(|| {
227            ArrowError::InvalidArgumentError(format!("Size cannot be negative, got {size}"))
228        })?;
229
230        if let Some(null_buffer) = &nulls {
231            if null_buffer.len() != len {
232                return Err(ArrowError::InvalidArgumentError(format!(
233                    "Invalid null buffer for FixedSizeListArray, expected {len} found {}",
234                    null_buffer.len()
235                )));
236            }
237        }
238
239        if s == 0 && !values.is_empty() {
240            return Err(ArrowError::InvalidArgumentError(format!(
241                "An degenerate FixedSizeListArray should have no underlying values, found {} values",
242                values.len()
243            )));
244        }
245
246        if values.len() != len * s {
247            return Err(ArrowError::InvalidArgumentError(format!(
248                "Incorrect length of values buffer for FixedSizeListArray, expected {} got {}",
249                len * s,
250                values.len(),
251            )));
252        }
253
254        if field.data_type() != values.data_type() {
255            return Err(ArrowError::InvalidArgumentError(format!(
256                "FixedSizeListArray expected data type {} got {} for {:?}",
257                field.data_type(),
258                values.data_type(),
259                field.name()
260            )));
261        }
262
263        if let Some(a) = values.logical_nulls() {
264            let nulls_valid = field.is_nullable()
265                || nulls
266                    .as_ref()
267                    .map(|n| n.expand(size as _).contains(&a))
268                    .unwrap_or_default()
269                || (nulls.is_none() && a.null_count() == 0);
270
271            if !nulls_valid {
272                return Err(ArrowError::InvalidArgumentError(format!(
273                    "Found unmasked nulls for non-nullable FixedSizeListArray field {:?}",
274                    field.name()
275                )));
276            }
277        }
278
279        let data_type = DataType::FixedSizeList(field, size);
280        Ok(Self {
281            data_type,
282            values,
283            value_length: size,
284            nulls,
285            len,
286        })
287    }
288
289    /// Create a new [`FixedSizeListArray`] of length `len` where all values are null
290    ///
291    /// # Panics
292    ///
293    /// Panics if
294    ///
295    /// * `size < 0`
296    /// * `size * len` would overflow `usize`
297    pub fn new_null(field: FieldRef, size: i32, len: usize) -> Self {
298        let capacity = size.to_usize().unwrap().checked_mul(len).unwrap();
299        Self {
300            values: make_array(ArrayData::new_null(field.data_type(), capacity)),
301            data_type: DataType::FixedSizeList(field, size),
302            nulls: Some(NullBuffer::new_null(len)),
303            value_length: size,
304            len,
305        }
306    }
307
308    /// Deconstruct this array into its constituent parts
309    pub fn into_parts(self) -> (FieldRef, i32, ArrayRef, Option<NullBuffer>) {
310        let f = match self.data_type {
311            DataType::FixedSizeList(f, _) => f,
312            _ => unreachable!(),
313        };
314        (f, self.value_length, self.values, self.nulls)
315    }
316
317    /// Returns a reference to the values of this list.
318    pub fn values(&self) -> &ArrayRef {
319        &self.values
320    }
321
322    /// Returns a clone of the value type of this list.
323    pub fn value_type(&self) -> DataType {
324        self.values.data_type().clone()
325    }
326
327    /// Returns ith value of this list array.
328    ///
329    /// Note: This method does not check for nulls and the value is arbitrary
330    /// (but still well-defined) if [`is_null`](Self::is_null) returns true for the index.
331    ///
332    /// # Panics
333    /// Panics if index `i` is out of bounds
334    pub fn value(&self, i: usize) -> ArrayRef {
335        self.values
336            .slice(self.value_offset_at(i), self.value_length() as usize)
337    }
338
339    /// Returns the offset for value at index `i`.
340    ///
341    /// Note this doesn't do any bound checking, for performance reason.
342    #[inline]
343    pub fn value_offset(&self, i: usize) -> i32 {
344        self.value_offset_at(i) as i32
345    }
346
347    /// Returns the length for an element.
348    ///
349    /// All elements have the same length as the array is a fixed size.
350    #[inline]
351    pub const fn value_length(&self) -> i32 {
352        self.value_length
353    }
354
355    #[inline]
356    const fn value_offset_at(&self, i: usize) -> usize {
357        i * self.value_length as usize
358    }
359
360    /// Returns a zero-copy slice of this array with the indicated offset and length.
361    pub fn slice(&self, offset: usize, len: usize) -> Self {
362        assert!(
363            offset.saturating_add(len) <= self.len,
364            "the length + offset of the sliced FixedSizeListArray cannot exceed the existing length"
365        );
366        let size = self.value_length as usize;
367
368        Self {
369            data_type: self.data_type.clone(),
370            values: self.values.slice(offset * size, len * size),
371            nulls: self.nulls.as_ref().map(|n| n.slice(offset, len)),
372            value_length: self.value_length,
373            len,
374        }
375    }
376
377    /// Creates a [`FixedSizeListArray`] from an iterator of primitive values
378    /// # Example
379    /// ```
380    /// # use arrow_array::FixedSizeListArray;
381    /// # use arrow_array::types::Int32Type;
382    ///
383    /// let data = vec![
384    ///    Some(vec![Some(0), Some(1), Some(2)]),
385    ///    None,
386    ///    Some(vec![Some(3), None, Some(5)]),
387    ///    Some(vec![Some(6), Some(7), Some(45)]),
388    /// ];
389    /// let list_array = FixedSizeListArray::from_iter_primitive::<Int32Type, _, _>(data, 3);
390    /// println!("{:?}", list_array);
391    /// ```
392    pub fn from_iter_primitive<T, P, I>(iter: I, length: i32) -> Self
393    where
394        T: ArrowPrimitiveType,
395        P: IntoIterator<Item = Option<<T as ArrowPrimitiveType>::Native>>,
396        I: IntoIterator<Item = Option<P>>,
397    {
398        let l = length as usize;
399        let iter = iter.into_iter();
400        let size_hint = iter.size_hint().0;
401        let mut builder = FixedSizeListBuilder::with_capacity(
402            PrimitiveBuilder::<T>::with_capacity(size_hint * l),
403            length,
404            size_hint,
405        );
406
407        for i in iter {
408            match i {
409                Some(p) => {
410                    for t in p {
411                        builder.values().append_option(t);
412                    }
413                    builder.append(true);
414                }
415                None => {
416                    builder.values().append_nulls(l);
417                    builder.append(false)
418                }
419            }
420        }
421        builder.finish()
422    }
423
424    /// constructs a new iterator
425    pub fn iter(&self) -> FixedSizeListIter<'_> {
426        FixedSizeListIter::new(self)
427    }
428}
429
430impl From<ArrayData> for FixedSizeListArray {
431    fn from(data: ArrayData) -> Self {
432        let value_length = match data.data_type() {
433            DataType::FixedSizeList(_, len) => *len,
434            data_type => {
435                panic!(
436                    "FixedSizeListArray data should contain a FixedSizeList data type, got {data_type}"
437                )
438            }
439        };
440
441        let size = value_length as usize;
442        let values =
443            make_array(data.child_data()[0].slice(data.offset() * size, data.len() * size));
444        Self {
445            data_type: data.data_type().clone(),
446            values,
447            nulls: data.nulls().cloned(),
448            value_length,
449            len: data.len(),
450        }
451    }
452}
453
454impl From<FixedSizeListArray> for ArrayData {
455    fn from(array: FixedSizeListArray) -> Self {
456        let builder = ArrayDataBuilder::new(array.data_type)
457            .len(array.len)
458            .nulls(array.nulls)
459            .child_data(vec![array.values.to_data()]);
460
461        unsafe { builder.build_unchecked() }
462    }
463}
464
465/// SAFETY: Correctly implements the contract of Arrow Arrays
466unsafe impl Array for FixedSizeListArray {
467    fn as_any(&self) -> &dyn Any {
468        self
469    }
470
471    fn to_data(&self) -> ArrayData {
472        self.clone().into()
473    }
474
475    fn into_data(self) -> ArrayData {
476        self.into()
477    }
478
479    fn data_type(&self) -> &DataType {
480        &self.data_type
481    }
482
483    fn slice(&self, offset: usize, length: usize) -> ArrayRef {
484        Arc::new(self.slice(offset, length))
485    }
486
487    fn len(&self) -> usize {
488        self.len
489    }
490
491    fn is_empty(&self) -> bool {
492        self.len == 0
493    }
494
495    fn shrink_to_fit(&mut self) {
496        self.values.shrink_to_fit();
497        if let Some(nulls) = &mut self.nulls {
498            nulls.shrink_to_fit();
499        }
500    }
501
502    fn offset(&self) -> usize {
503        0
504    }
505
506    fn nulls(&self) -> Option<&NullBuffer> {
507        self.nulls.as_ref()
508    }
509
510    fn logical_null_count(&self) -> usize {
511        // More efficient that the default implementation
512        self.null_count()
513    }
514
515    fn get_buffer_memory_size(&self) -> usize {
516        let mut size = self.values.get_buffer_memory_size();
517        if let Some(n) = self.nulls.as_ref() {
518            size += n.buffer().capacity();
519        }
520        size
521    }
522
523    fn get_array_memory_size(&self) -> usize {
524        let mut size = std::mem::size_of::<Self>() + self.values.get_array_memory_size();
525        if let Some(n) = self.nulls.as_ref() {
526            size += n.buffer().capacity();
527        }
528        size
529    }
530}
531
532impl ArrayAccessor for FixedSizeListArray {
533    type Item = ArrayRef;
534
535    fn value(&self, index: usize) -> Self::Item {
536        FixedSizeListArray::value(self, index)
537    }
538
539    unsafe fn value_unchecked(&self, index: usize) -> Self::Item {
540        FixedSizeListArray::value(self, index)
541    }
542}
543
544impl std::fmt::Debug for FixedSizeListArray {
545    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
546        write!(f, "FixedSizeListArray<{}>\n[\n", self.value_length())?;
547        print_long_array(self, f, |array, index, f| {
548            std::fmt::Debug::fmt(&array.value(index), f)
549        })?;
550        write!(f, "]")
551    }
552}
553
554impl ArrayAccessor for &FixedSizeListArray {
555    type Item = ArrayRef;
556
557    fn value(&self, index: usize) -> Self::Item {
558        FixedSizeListArray::value(self, index)
559    }
560
561    unsafe fn value_unchecked(&self, index: usize) -> Self::Item {
562        FixedSizeListArray::value(self, index)
563    }
564}
565
566#[cfg(test)]
567mod tests {
568    use arrow_buffer::{BooleanBuffer, Buffer, bit_util};
569    use arrow_schema::Field;
570
571    use crate::cast::AsArray;
572    use crate::types::Int32Type;
573    use crate::{Int32Array, new_empty_array};
574
575    use super::*;
576
577    #[test]
578    fn test_fixed_size_list_array() {
579        // Construct a value array
580        let value_data = ArrayData::builder(DataType::Int32)
581            .len(9)
582            .add_buffer(Buffer::from_slice_ref([0, 1, 2, 3, 4, 5, 6, 7, 8]))
583            .build()
584            .unwrap();
585
586        // Construct a list array from the above two
587        let list_data_type =
588            DataType::FixedSizeList(Arc::new(Field::new_list_field(DataType::Int32, false)), 3);
589        let list_data = ArrayData::builder(list_data_type.clone())
590            .len(3)
591            .add_child_data(value_data.clone())
592            .build()
593            .unwrap();
594        let list_array = FixedSizeListArray::from(list_data);
595
596        assert_eq!(value_data, list_array.values().to_data());
597        assert_eq!(DataType::Int32, list_array.value_type());
598        assert_eq!(3, list_array.len());
599        assert_eq!(0, list_array.null_count());
600        assert_eq!(6, list_array.value_offset(2));
601        assert_eq!(3, list_array.value_length());
602        assert_eq!(0, list_array.value(0).as_primitive::<Int32Type>().value(0));
603        for i in 0..3 {
604            assert!(list_array.is_valid(i));
605            assert!(!list_array.is_null(i));
606        }
607
608        // Now test with a non-zero offset
609        let list_data = ArrayData::builder(list_data_type)
610            .len(2)
611            .offset(1)
612            .add_child_data(value_data.clone())
613            .build()
614            .unwrap();
615        let list_array = FixedSizeListArray::from(list_data);
616
617        assert_eq!(value_data.slice(3, 6), list_array.values().to_data());
618        assert_eq!(DataType::Int32, list_array.value_type());
619        assert_eq!(2, list_array.len());
620        assert_eq!(0, list_array.null_count());
621        assert_eq!(3, list_array.value(0).as_primitive::<Int32Type>().value(0));
622        assert_eq!(3, list_array.value_offset(1));
623        assert_eq!(3, list_array.value_length());
624    }
625
626    #[test]
627    #[should_panic(expected = "assertion failed: (offset + length) <= self.len()")]
628    // Different error messages, so skip for now
629    // https://github.com/apache/arrow-rs/issues/1545
630    #[cfg(not(feature = "force_validate"))]
631    fn test_fixed_size_list_array_unequal_children() {
632        // Construct a value array
633        let value_data = ArrayData::builder(DataType::Int32)
634            .len(8)
635            .add_buffer(Buffer::from_slice_ref([0, 1, 2, 3, 4, 5, 6, 7]))
636            .build()
637            .unwrap();
638
639        // Construct a list array from the above two
640        let list_data_type =
641            DataType::FixedSizeList(Arc::new(Field::new_list_field(DataType::Int32, false)), 3);
642        let list_data = unsafe {
643            ArrayData::builder(list_data_type)
644                .len(3)
645                .add_child_data(value_data)
646                .build_unchecked()
647        };
648        drop(FixedSizeListArray::from(list_data));
649    }
650
651    #[test]
652    fn test_fixed_size_list_array_slice() {
653        // Construct a value array
654        let value_data = ArrayData::builder(DataType::Int32)
655            .len(10)
656            .add_buffer(Buffer::from_slice_ref([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))
657            .build()
658            .unwrap();
659
660        // Set null buts for the nested array:
661        //  [[0, 1], null, null, [6, 7], [8, 9]]
662        // 01011001 00000001
663        let mut null_bits: [u8; 1] = [0; 1];
664        bit_util::set_bit(&mut null_bits, 0);
665        bit_util::set_bit(&mut null_bits, 3);
666        bit_util::set_bit(&mut null_bits, 4);
667
668        // Construct a fixed size list array from the above two
669        let list_data_type =
670            DataType::FixedSizeList(Arc::new(Field::new_list_field(DataType::Int32, false)), 2);
671        let list_data = ArrayData::builder(list_data_type)
672            .len(5)
673            .add_child_data(value_data.clone())
674            .null_bit_buffer(Some(Buffer::from(null_bits)))
675            .build()
676            .unwrap();
677        let list_array = FixedSizeListArray::from(list_data);
678
679        assert_eq!(value_data, list_array.values().to_data());
680        assert_eq!(DataType::Int32, list_array.value_type());
681        assert_eq!(5, list_array.len());
682        assert_eq!(2, list_array.null_count());
683        assert_eq!(6, list_array.value_offset(3));
684        assert_eq!(2, list_array.value_length());
685
686        let sliced_array = list_array.slice(1, 4);
687        assert_eq!(4, sliced_array.len());
688        assert_eq!(2, sliced_array.null_count());
689
690        for i in 0..sliced_array.len() {
691            if bit_util::get_bit(&null_bits, 1 + i) {
692                assert!(sliced_array.is_valid(i));
693            } else {
694                assert!(sliced_array.is_null(i));
695            }
696        }
697
698        // Check offset and length for each non-null value.
699        let sliced_list_array = sliced_array
700            .as_any()
701            .downcast_ref::<FixedSizeListArray>()
702            .unwrap();
703        assert_eq!(2, sliced_list_array.value_length());
704        assert_eq!(4, sliced_list_array.value_offset(2));
705        assert_eq!(6, sliced_list_array.value_offset(3));
706    }
707
708    #[test]
709    #[should_panic(expected = "the offset of the new Buffer cannot exceed the existing length")]
710    fn test_fixed_size_list_array_index_out_of_bound() {
711        // Construct a value array
712        let value_data = ArrayData::builder(DataType::Int32)
713            .len(10)
714            .add_buffer(Buffer::from_slice_ref([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))
715            .build()
716            .unwrap();
717
718        // Set null buts for the nested array:
719        //  [[0, 1], null, null, [6, 7], [8, 9]]
720        // 01011001 00000001
721        let mut null_bits: [u8; 1] = [0; 1];
722        bit_util::set_bit(&mut null_bits, 0);
723        bit_util::set_bit(&mut null_bits, 3);
724        bit_util::set_bit(&mut null_bits, 4);
725
726        // Construct a fixed size list array from the above two
727        let list_data_type =
728            DataType::FixedSizeList(Arc::new(Field::new_list_field(DataType::Int32, false)), 2);
729        let list_data = ArrayData::builder(list_data_type)
730            .len(5)
731            .add_child_data(value_data)
732            .null_bit_buffer(Some(Buffer::from(null_bits)))
733            .build()
734            .unwrap();
735        let list_array = FixedSizeListArray::from(list_data);
736
737        list_array.value(10);
738    }
739
740    #[test]
741    fn test_fixed_size_list_constructors() {
742        let values = Arc::new(Int32Array::from_iter([
743            Some(1),
744            Some(2),
745            None,
746            None,
747            Some(3),
748            Some(4),
749        ]));
750
751        let field = Arc::new(Field::new_list_field(DataType::Int32, true));
752        let list = FixedSizeListArray::new(field.clone(), 2, values.clone(), None);
753        assert_eq!(list.len(), 3);
754
755        let nulls = NullBuffer::new_null(3);
756        let list = FixedSizeListArray::new(field.clone(), 2, values.clone(), Some(nulls));
757        assert_eq!(list.len(), 3);
758
759        let list = FixedSizeListArray::new(field.clone(), 3, values.clone(), None);
760        assert_eq!(list.len(), 2);
761
762        let err = FixedSizeListArray::try_new(field.clone(), 4, values.clone(), None).unwrap_err();
763        assert_eq!(
764            err.to_string(),
765            "Invalid argument error: Incorrect length of values buffer for FixedSizeListArray, \
766             expected a multiple of 4 got 6",
767        );
768
769        let err =
770            FixedSizeListArray::try_new_with_length(field.clone(), 4, values.clone(), None, 1)
771                .unwrap_err();
772        assert_eq!(
773            err.to_string(),
774            "Invalid argument error: Incorrect length of values buffer for FixedSizeListArray, expected 4 got 6"
775        );
776
777        let err = FixedSizeListArray::try_new(field.clone(), -1, values.clone(), None).unwrap_err();
778        assert_eq!(
779            err.to_string(),
780            "Invalid argument error: Size cannot be negative, got -1"
781        );
782
783        let nulls = NullBuffer::new_null(2);
784        let err = FixedSizeListArray::try_new(field, 2, values.clone(), Some(nulls)).unwrap_err();
785        assert_eq!(
786            err.to_string(),
787            "Invalid argument error: Incorrect length of values buffer for FixedSizeListArray, expected 4 got 6"
788        );
789
790        let field = Arc::new(Field::new_list_field(DataType::Int32, false));
791        let err = FixedSizeListArray::try_new(field.clone(), 2, values.clone(), None).unwrap_err();
792        assert_eq!(
793            err.to_string(),
794            "Invalid argument error: Found unmasked nulls for non-nullable FixedSizeListArray field \"item\""
795        );
796
797        // Valid as nulls in child masked by parent
798        let nulls = NullBuffer::new(BooleanBuffer::new(Buffer::from([0b0000101]), 0, 3));
799        FixedSizeListArray::new(field, 2, values.clone(), Some(nulls));
800
801        let field = Arc::new(Field::new_list_field(DataType::Int64, true));
802        let err = FixedSizeListArray::try_new(field, 2, values, None).unwrap_err();
803        assert_eq!(
804            err.to_string(),
805            "Invalid argument error: FixedSizeListArray expected data type Int64 got Int32 for \"item\""
806        );
807    }
808
809    #[test]
810    fn degenerate_fixed_size_list() {
811        let field = Arc::new(Field::new_list_field(DataType::Int32, true));
812        let nulls = NullBuffer::new_null(2);
813        let values = new_empty_array(&DataType::Int32);
814        let list = FixedSizeListArray::new(field.clone(), 0, values.clone(), Some(nulls.clone()));
815        assert_eq!(list.len(), 2);
816
817        // Test invalid null buffer length.
818        let err = FixedSizeListArray::try_new_with_length(
819            field.clone(),
820            0,
821            values.clone(),
822            Some(nulls),
823            5,
824        )
825        .unwrap_err();
826        assert_eq!(
827            err.to_string(),
828            "Invalid argument error: Invalid null buffer for FixedSizeListArray, expected 5 found 2"
829        );
830
831        // Test non-empty values for degenerate list.
832        let non_empty_values = Arc::new(Int32Array::from(vec![1, 2, 3]));
833        let err =
834            FixedSizeListArray::try_new_with_length(field.clone(), 0, non_empty_values, None, 3)
835                .unwrap_err();
836        assert_eq!(
837            err.to_string(),
838            "Invalid argument error: An degenerate FixedSizeListArray should have no underlying values, found 3 values"
839        );
840    }
841
842    #[test]
843    fn test_fixed_size_list_new_null_len() {
844        let field = Arc::new(Field::new_list_field(DataType::Int32, true));
845        let array = FixedSizeListArray::new_null(field, 2, 5);
846        assert_eq!(array.len(), 5);
847    }
848}