Coverage Report

Created: 2025-11-17 14:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/Users/andrewlamb/Software/arrow-rs/arrow-array/src/array/fixed_size_list_array.rs
Line
Count
Source
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
18
use crate::array::print_long_array;
19
use crate::builder::{FixedSizeListBuilder, PrimitiveBuilder};
20
use crate::iterator::FixedSizeListIter;
21
use crate::{Array, ArrayAccessor, ArrayRef, ArrowPrimitiveType, make_array};
22
use arrow_buffer::ArrowNativeType;
23
use arrow_buffer::buffer::NullBuffer;
24
use arrow_data::{ArrayData, ArrayDataBuilder};
25
use arrow_schema::{ArrowError, DataType, FieldRef};
26
use std::any::Any;
27
use 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)]
119
pub 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
127
impl 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
11
    pub fn new(field: FieldRef, size: i32, values: ArrayRef, nulls: Option<NullBuffer>) -> Self {
142
11
        Self::try_new(field, size, values, nulls).unwrap()
143
11
    }
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
12
    pub fn try_new(
162
12
        field: FieldRef,
163
12
        size: i32,
164
12
        values: ArrayRef,
165
12
        nulls: Option<NullBuffer>,
166
12
    ) -> Result<Self, ArrowError> {
167
12
        let s = size.to_usize().ok_or_else(|| 
{0
168
0
            ArrowError::InvalidArgumentError(format!("Size cannot be negative, got {size}"))
169
0
        })?;
170
171
12
        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
0
            let len = nulls.as_ref().map(|x| x.len()).unwrap_or_default();
175
176
0
            Self::try_new_with_length(field, size, values, nulls, len)
177
        } else {
178
12
            if values.len() % s != 0 {
179
0
                return Err(ArrowError::InvalidArgumentError(format!(
180
0
                    "Incorrect length of values buffer for FixedSizeListArray, \
181
0
                     expected a multiple of {s} got {}",
182
0
                    values.len(),
183
0
                )));
184
12
            }
185
186
12
            let len = values.len() / s;
187
188
            // Check that the null buffer length is correct (if it exists).
189
12
            if let Some(
null_buffer6
) = &nulls {
190
6
                if s * null_buffer.len() != values.len() {
191
0
                    return Err(ArrowError::InvalidArgumentError(format!(
192
0
                        "Incorrect length of values buffer for FixedSizeListArray, \
193
0
                            expected {} got {}",
194
0
                        s * null_buffer.len(),
195
0
                        values.len(),
196
0
                    )));
197
6
                }
198
6
            }
199
200
12
            Self::try_new_with_length(field, size, values, nulls, len)
201
        }
202
12
    }
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
12
    pub fn try_new_with_length(
220
12
        field: FieldRef,
221
12
        size: i32,
222
12
        values: ArrayRef,
223
12
        nulls: Option<NullBuffer>,
224
12
        len: usize,
225
12
    ) -> Result<Self, ArrowError> {
226
12
        let s = size.to_usize().ok_or_else(|| 
{0
227
0
            ArrowError::InvalidArgumentError(format!("Size cannot be negative, got {size}"))
228
0
        })?;
229
230
12
        if let Some(
null_buffer6
) = &nulls {
231
6
            if null_buffer.len() != len {
232
0
                return Err(ArrowError::InvalidArgumentError(format!(
233
0
                    "Invalid null buffer for FixedSizeListArray, expected {len} found {}",
234
0
                    null_buffer.len()
235
0
                )));
236
6
            }
237
6
        }
238
239
12
        if s == 0 && 
!values.is_empty()0
{
240
0
            return Err(ArrowError::InvalidArgumentError(format!(
241
0
                "An degenerate FixedSizeListArray should have no underlying values, found {} values",
242
0
                values.len()
243
0
            )));
244
12
        }
245
246
12
        if values.len() != len * s {
247
0
            return Err(ArrowError::InvalidArgumentError(format!(
248
0
                "Incorrect length of values buffer for FixedSizeListArray, expected {} got {}",
249
0
                len * s,
250
0
                values.len(),
251
0
            )));
252
12
        }
253
254
12
        if field.data_type() != values.data_type() {
255
0
            return Err(ArrowError::InvalidArgumentError(format!(
256
0
                "FixedSizeListArray expected data type {} got {} for {:?}",
257
0
                field.data_type(),
258
0
                values.data_type(),
259
0
                field.name()
260
0
            )));
261
12
        }
262
263
12
        if let Some(
a8
) = values.logical_nulls() {
264
8
            let nulls_valid = field.is_nullable()
265
0
                || nulls
266
0
                    .as_ref()
267
0
                    .map(|n| n.expand(size as _).contains(&a))
268
0
                    .unwrap_or_default()
269
0
                || (nulls.is_none() && a.null_count() == 0);
270
271
8
            if !nulls_valid {
272
0
                return Err(ArrowError::InvalidArgumentError(format!(
273
0
                    "Found unmasked nulls for non-nullable FixedSizeListArray field {:?}",
274
0
                    field.name()
275
0
                )));
276
8
            }
277
4
        }
278
279
12
        let data_type = DataType::FixedSizeList(field, size);
280
12
        Ok(Self {
281
12
            data_type,
282
12
            values,
283
12
            value_length: size,
284
12
            nulls,
285
12
            len,
286
12
        })
287
12
    }
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
0
    pub fn new_null(field: FieldRef, size: i32, len: usize) -> Self {
298
0
        let capacity = size.to_usize().unwrap().checked_mul(len).unwrap();
299
0
        Self {
300
0
            values: make_array(ArrayData::new_null(field.data_type(), capacity)),
301
0
            data_type: DataType::FixedSizeList(field, size),
302
0
            nulls: Some(NullBuffer::new_null(len)),
303
0
            value_length: size,
304
0
            len,
305
0
        }
306
0
    }
307
308
    /// Deconstruct this array into its constituent parts
309
0
    pub fn into_parts(self) -> (FieldRef, i32, ArrayRef, Option<NullBuffer>) {
310
0
        let f = match self.data_type {
311
0
            DataType::FixedSizeList(f, _) => f,
312
0
            _ => unreachable!(),
313
        };
314
0
        (f, self.value_length, self.values, self.nulls)
315
0
    }
316
317
    /// Returns a reference to the values of this list.
318
5
    pub fn values(&self) -> &ArrayRef {
319
5
        &self.values
320
5
    }
321
322
    /// Returns a clone of the value type of this list.
323
0
    pub fn value_type(&self) -> DataType {
324
0
        self.values.data_type().clone()
325
0
    }
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
5
    pub fn value(&self, i: usize) -> ArrayRef {
335
5
        self.values
336
5
            .slice(self.value_offset_at(i), self.value_length() as usize)
337
5
    }
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
20
    pub fn value_offset(&self, i: usize) -> i32 {
344
20
        self.value_offset_at(i) as i32
345
20
    }
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
5
    pub const fn value_length(&self) -> i32 {
352
5
        self.value_length
353
5
    }
354
355
    #[inline]
356
25
    const fn value_offset_at(&self, i: usize) -> usize {
357
25
        i * self.value_length as usize
358
25
    }
359
360
    /// Returns a zero-copy slice of this array with the indicated offset and length.
361
0
    pub fn slice(&self, offset: usize, len: usize) -> Self {
362
0
        assert!(
363
0
            offset.saturating_add(len) <= self.len,
364
0
            "the length + offset of the sliced FixedSizeListArray cannot exceed the existing length"
365
        );
366
0
        let size = self.value_length as usize;
367
368
        Self {
369
0
            data_type: self.data_type.clone(),
370
0
            values: self.values.slice(offset * size, len * size),
371
0
            nulls: self.nulls.as_ref().map(|n| n.slice(offset, len)),
372
0
            value_length: self.value_length,
373
0
            len,
374
        }
375
0
    }
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
11
    pub fn from_iter_primitive<T, P, I>(iter: I, length: i32) -> Self
393
11
    where
394
11
        T: ArrowPrimitiveType,
395
11
        P: IntoIterator<Item = Option<<T as ArrowPrimitiveType>::Native>>,
396
11
        I: IntoIterator<Item = Option<P>>,
397
    {
398
11
        let l = length as usize;
399
11
        let iter = iter.into_iter();
400
11
        let size_hint = iter.size_hint().0;
401
11
        let mut builder = FixedSizeListBuilder::with_capacity(
402
11
            PrimitiveBuilder::<T>::with_capacity(size_hint * l),
403
11
            length,
404
11
            size_hint,
405
        );
406
407
55
        for 
i44
in iter {
408
44
            match i {
409
36
                Some(p) => {
410
112
                    for 
t76
in p {
411
76
                        builder.values().append_option(t);
412
76
                    }
413
36
                    builder.append(true);
414
                }
415
                None => {
416
8
                    builder.values().append_nulls(l);
417
8
                    builder.append(false)
418
                }
419
            }
420
        }
421
11
        builder.finish()
422
11
    }
423
424
    /// constructs a new iterator
425
0
    pub fn iter(&self) -> FixedSizeListIter<'_> {
426
0
        FixedSizeListIter::new(self)
427
0
    }
428
}
429
430
impl From<ArrayData> for FixedSizeListArray {
431
10
    fn from(data: ArrayData) -> Self {
432
10
        let value_length = match data.data_type() {
433
10
            DataType::FixedSizeList(_, len) => *len,
434
0
            data_type => {
435
0
                panic!(
436
0
                    "FixedSizeListArray data should contain a FixedSizeList data type, got {data_type}"
437
                )
438
            }
439
        };
440
441
10
        let size = value_length as usize;
442
10
        let values =
443
10
            make_array(data.child_data()[0].slice(data.offset() * size, data.len() * size));
444
10
        Self {
445
10
            data_type: data.data_type().clone(),
446
10
            values,
447
10
            nulls: data.nulls().cloned(),
448
10
            value_length,
449
10
            len: data.len(),
450
10
        }
451
10
    }
452
}
453
454
impl From<FixedSizeListArray> for ArrayData {
455
14
    fn from(array: FixedSizeListArray) -> Self {
456
14
        let builder = ArrayDataBuilder::new(array.data_type)
457
14
            .len(array.len)
458
14
            .nulls(array.nulls)
459
14
            .child_data(vec![array.values.to_data()]);
460
461
14
        unsafe { builder.build_unchecked() }
462
14
    }
463
}
464
465
impl Array for FixedSizeListArray {
466
5
    fn as_any(&self) -> &dyn Any {
467
5
        self
468
5
    }
469
470
14
    fn to_data(&self) -> ArrayData {
471
14
        self.clone().into()
472
14
    }
473
474
0
    fn into_data(self) -> ArrayData {
475
0
        self.into()
476
0
    }
477
478
18
    fn data_type(&self) -> &DataType {
479
18
        &self.data_type
480
18
    }
481
482
0
    fn slice(&self, offset: usize, length: usize) -> ArrayRef {
483
0
        Arc::new(self.slice(offset, length))
484
0
    }
485
486
12
    fn len(&self) -> usize {
487
12
        self.len
488
12
    }
489
490
0
    fn is_empty(&self) -> bool {
491
0
        self.len == 0
492
0
    }
493
494
0
    fn shrink_to_fit(&mut self) {
495
0
        self.values.shrink_to_fit();
496
0
        if let Some(nulls) = &mut self.nulls {
497
0
            nulls.shrink_to_fit();
498
0
        }
499
0
    }
500
501
0
    fn offset(&self) -> usize {
502
0
        0
503
0
    }
504
505
13
    fn nulls(&self) -> Option<&NullBuffer> {
506
13
        self.nulls.as_ref()
507
13
    }
508
509
0
    fn logical_null_count(&self) -> usize {
510
        // More efficient that the default implementation
511
0
        self.null_count()
512
0
    }
513
514
0
    fn get_buffer_memory_size(&self) -> usize {
515
0
        let mut size = self.values.get_buffer_memory_size();
516
0
        if let Some(n) = self.nulls.as_ref() {
517
0
            size += n.buffer().capacity();
518
0
        }
519
0
        size
520
0
    }
521
522
0
    fn get_array_memory_size(&self) -> usize {
523
0
        let mut size = std::mem::size_of::<Self>() + self.values.get_array_memory_size();
524
0
        if let Some(n) = self.nulls.as_ref() {
525
0
            size += n.buffer().capacity();
526
0
        }
527
0
        size
528
0
    }
529
}
530
531
impl ArrayAccessor for FixedSizeListArray {
532
    type Item = ArrayRef;
533
534
0
    fn value(&self, index: usize) -> Self::Item {
535
0
        FixedSizeListArray::value(self, index)
536
0
    }
537
538
0
    unsafe fn value_unchecked(&self, index: usize) -> Self::Item {
539
0
        FixedSizeListArray::value(self, index)
540
0
    }
541
}
542
543
impl std::fmt::Debug for FixedSizeListArray {
544
0
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
545
0
        write!(f, "FixedSizeListArray<{}>\n[\n", self.value_length())?;
546
0
        print_long_array(self, f, |array, index, f| {
547
0
            std::fmt::Debug::fmt(&array.value(index), f)
548
0
        })?;
549
0
        write!(f, "]")
550
0
    }
551
}
552
553
impl ArrayAccessor for &FixedSizeListArray {
554
    type Item = ArrayRef;
555
556
0
    fn value(&self, index: usize) -> Self::Item {
557
0
        FixedSizeListArray::value(self, index)
558
0
    }
559
560
0
    unsafe fn value_unchecked(&self, index: usize) -> Self::Item {
561
0
        FixedSizeListArray::value(self, index)
562
0
    }
563
}
564
565
#[cfg(test)]
566
mod tests {
567
    use arrow_buffer::{BooleanBuffer, Buffer, bit_util};
568
    use arrow_schema::Field;
569
570
    use crate::cast::AsArray;
571
    use crate::types::Int32Type;
572
    use crate::{Int32Array, new_empty_array};
573
574
    use super::*;
575
576
    #[test]
577
    fn test_fixed_size_list_array() {
578
        // Construct a value array
579
        let value_data = ArrayData::builder(DataType::Int32)
580
            .len(9)
581
            .add_buffer(Buffer::from_slice_ref([0, 1, 2, 3, 4, 5, 6, 7, 8]))
582
            .build()
583
            .unwrap();
584
585
        // Construct a list array from the above two
586
        let list_data_type =
587
            DataType::FixedSizeList(Arc::new(Field::new_list_field(DataType::Int32, false)), 3);
588
        let list_data = ArrayData::builder(list_data_type.clone())
589
            .len(3)
590
            .add_child_data(value_data.clone())
591
            .build()
592
            .unwrap();
593
        let list_array = FixedSizeListArray::from(list_data);
594
595
        assert_eq!(value_data, list_array.values().to_data());
596
        assert_eq!(DataType::Int32, list_array.value_type());
597
        assert_eq!(3, list_array.len());
598
        assert_eq!(0, list_array.null_count());
599
        assert_eq!(6, list_array.value_offset(2));
600
        assert_eq!(3, list_array.value_length());
601
        assert_eq!(0, list_array.value(0).as_primitive::<Int32Type>().value(0));
602
        for i in 0..3 {
603
            assert!(list_array.is_valid(i));
604
            assert!(!list_array.is_null(i));
605
        }
606
607
        // Now test with a non-zero offset
608
        let list_data = ArrayData::builder(list_data_type)
609
            .len(2)
610
            .offset(1)
611
            .add_child_data(value_data.clone())
612
            .build()
613
            .unwrap();
614
        let list_array = FixedSizeListArray::from(list_data);
615
616
        assert_eq!(value_data.slice(3, 6), list_array.values().to_data());
617
        assert_eq!(DataType::Int32, list_array.value_type());
618
        assert_eq!(2, list_array.len());
619
        assert_eq!(0, list_array.null_count());
620
        assert_eq!(3, list_array.value(0).as_primitive::<Int32Type>().value(0));
621
        assert_eq!(3, list_array.value_offset(1));
622
        assert_eq!(3, list_array.value_length());
623
    }
624
625
    #[test]
626
    #[should_panic(expected = "assertion failed: (offset + length) <= self.len()")]
627
    // Different error messages, so skip for now
628
    // https://github.com/apache/arrow-rs/issues/1545
629
    #[cfg(not(feature = "force_validate"))]
630
    fn test_fixed_size_list_array_unequal_children() {
631
        // Construct a value array
632
        let value_data = ArrayData::builder(DataType::Int32)
633
            .len(8)
634
            .add_buffer(Buffer::from_slice_ref([0, 1, 2, 3, 4, 5, 6, 7]))
635
            .build()
636
            .unwrap();
637
638
        // Construct a list array from the above two
639
        let list_data_type =
640
            DataType::FixedSizeList(Arc::new(Field::new_list_field(DataType::Int32, false)), 3);
641
        let list_data = unsafe {
642
            ArrayData::builder(list_data_type)
643
                .len(3)
644
                .add_child_data(value_data)
645
                .build_unchecked()
646
        };
647
        drop(FixedSizeListArray::from(list_data));
648
    }
649
650
    #[test]
651
    fn test_fixed_size_list_array_slice() {
652
        // Construct a value array
653
        let value_data = ArrayData::builder(DataType::Int32)
654
            .len(10)
655
            .add_buffer(Buffer::from_slice_ref([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))
656
            .build()
657
            .unwrap();
658
659
        // Set null buts for the nested array:
660
        //  [[0, 1], null, null, [6, 7], [8, 9]]
661
        // 01011001 00000001
662
        let mut null_bits: [u8; 1] = [0; 1];
663
        bit_util::set_bit(&mut null_bits, 0);
664
        bit_util::set_bit(&mut null_bits, 3);
665
        bit_util::set_bit(&mut null_bits, 4);
666
667
        // Construct a fixed size list array from the above two
668
        let list_data_type =
669
            DataType::FixedSizeList(Arc::new(Field::new_list_field(DataType::Int32, false)), 2);
670
        let list_data = ArrayData::builder(list_data_type)
671
            .len(5)
672
            .add_child_data(value_data.clone())
673
            .null_bit_buffer(Some(Buffer::from(null_bits)))
674
            .build()
675
            .unwrap();
676
        let list_array = FixedSizeListArray::from(list_data);
677
678
        assert_eq!(value_data, list_array.values().to_data());
679
        assert_eq!(DataType::Int32, list_array.value_type());
680
        assert_eq!(5, list_array.len());
681
        assert_eq!(2, list_array.null_count());
682
        assert_eq!(6, list_array.value_offset(3));
683
        assert_eq!(2, list_array.value_length());
684
685
        let sliced_array = list_array.slice(1, 4);
686
        assert_eq!(4, sliced_array.len());
687
        assert_eq!(2, sliced_array.null_count());
688
689
        for i in 0..sliced_array.len() {
690
            if bit_util::get_bit(&null_bits, 1 + i) {
691
                assert!(sliced_array.is_valid(i));
692
            } else {
693
                assert!(sliced_array.is_null(i));
694
            }
695
        }
696
697
        // Check offset and length for each non-null value.
698
        let sliced_list_array = sliced_array
699
            .as_any()
700
            .downcast_ref::<FixedSizeListArray>()
701
            .unwrap();
702
        assert_eq!(2, sliced_list_array.value_length());
703
        assert_eq!(4, sliced_list_array.value_offset(2));
704
        assert_eq!(6, sliced_list_array.value_offset(3));
705
    }
706
707
    #[test]
708
    #[should_panic(expected = "the offset of the new Buffer cannot exceed the existing length")]
709
    fn test_fixed_size_list_array_index_out_of_bound() {
710
        // Construct a value array
711
        let value_data = ArrayData::builder(DataType::Int32)
712
            .len(10)
713
            .add_buffer(Buffer::from_slice_ref([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]))
714
            .build()
715
            .unwrap();
716
717
        // Set null buts for the nested array:
718
        //  [[0, 1], null, null, [6, 7], [8, 9]]
719
        // 01011001 00000001
720
        let mut null_bits: [u8; 1] = [0; 1];
721
        bit_util::set_bit(&mut null_bits, 0);
722
        bit_util::set_bit(&mut null_bits, 3);
723
        bit_util::set_bit(&mut null_bits, 4);
724
725
        // Construct a fixed size list array from the above two
726
        let list_data_type =
727
            DataType::FixedSizeList(Arc::new(Field::new_list_field(DataType::Int32, false)), 2);
728
        let list_data = ArrayData::builder(list_data_type)
729
            .len(5)
730
            .add_child_data(value_data)
731
            .null_bit_buffer(Some(Buffer::from(null_bits)))
732
            .build()
733
            .unwrap();
734
        let list_array = FixedSizeListArray::from(list_data);
735
736
        list_array.value(10);
737
    }
738
739
    #[test]
740
    fn test_fixed_size_list_constructors() {
741
        let values = Arc::new(Int32Array::from_iter([
742
            Some(1),
743
            Some(2),
744
            None,
745
            None,
746
            Some(3),
747
            Some(4),
748
        ]));
749
750
        let field = Arc::new(Field::new_list_field(DataType::Int32, true));
751
        let list = FixedSizeListArray::new(field.clone(), 2, values.clone(), None);
752
        assert_eq!(list.len(), 3);
753
754
        let nulls = NullBuffer::new_null(3);
755
        let list = FixedSizeListArray::new(field.clone(), 2, values.clone(), Some(nulls));
756
        assert_eq!(list.len(), 3);
757
758
        let list = FixedSizeListArray::new(field.clone(), 3, values.clone(), None);
759
        assert_eq!(list.len(), 2);
760
761
        let err = FixedSizeListArray::try_new(field.clone(), 4, values.clone(), None).unwrap_err();
762
        assert_eq!(
763
            err.to_string(),
764
            "Invalid argument error: Incorrect length of values buffer for FixedSizeListArray, \
765
             expected a multiple of 4 got 6",
766
        );
767
768
        let err =
769
            FixedSizeListArray::try_new_with_length(field.clone(), 4, values.clone(), None, 1)
770
                .unwrap_err();
771
        assert_eq!(
772
            err.to_string(),
773
            "Invalid argument error: Incorrect length of values buffer for FixedSizeListArray, expected 4 got 6"
774
        );
775
776
        let err = FixedSizeListArray::try_new(field.clone(), -1, values.clone(), None).unwrap_err();
777
        assert_eq!(
778
            err.to_string(),
779
            "Invalid argument error: Size cannot be negative, got -1"
780
        );
781
782
        let nulls = NullBuffer::new_null(2);
783
        let err = FixedSizeListArray::try_new(field, 2, values.clone(), Some(nulls)).unwrap_err();
784
        assert_eq!(
785
            err.to_string(),
786
            "Invalid argument error: Incorrect length of values buffer for FixedSizeListArray, expected 4 got 6"
787
        );
788
789
        let field = Arc::new(Field::new_list_field(DataType::Int32, false));
790
        let err = FixedSizeListArray::try_new(field.clone(), 2, values.clone(), None).unwrap_err();
791
        assert_eq!(
792
            err.to_string(),
793
            "Invalid argument error: Found unmasked nulls for non-nullable FixedSizeListArray field \"item\""
794
        );
795
796
        // Valid as nulls in child masked by parent
797
        let nulls = NullBuffer::new(BooleanBuffer::new(Buffer::from([0b0000101]), 0, 3));
798
        FixedSizeListArray::new(field, 2, values.clone(), Some(nulls));
799
800
        let field = Arc::new(Field::new_list_field(DataType::Int64, true));
801
        let err = FixedSizeListArray::try_new(field, 2, values, None).unwrap_err();
802
        assert_eq!(
803
            err.to_string(),
804
            "Invalid argument error: FixedSizeListArray expected data type Int64 got Int32 for \"item\""
805
        );
806
    }
807
808
    #[test]
809
    fn degenerate_fixed_size_list() {
810
        let field = Arc::new(Field::new_list_field(DataType::Int32, true));
811
        let nulls = NullBuffer::new_null(2);
812
        let values = new_empty_array(&DataType::Int32);
813
        let list = FixedSizeListArray::new(field.clone(), 0, values.clone(), Some(nulls.clone()));
814
        assert_eq!(list.len(), 2);
815
816
        // Test invalid null buffer length.
817
        let err = FixedSizeListArray::try_new_with_length(
818
            field.clone(),
819
            0,
820
            values.clone(),
821
            Some(nulls),
822
            5,
823
        )
824
        .unwrap_err();
825
        assert_eq!(
826
            err.to_string(),
827
            "Invalid argument error: Invalid null buffer for FixedSizeListArray, expected 5 found 2"
828
        );
829
830
        // Test non-empty values for degenerate list.
831
        let non_empty_values = Arc::new(Int32Array::from(vec![1, 2, 3]));
832
        let err =
833
            FixedSizeListArray::try_new_with_length(field.clone(), 0, non_empty_values, None, 3)
834
                .unwrap_err();
835
        assert_eq!(
836
            err.to_string(),
837
            "Invalid argument error: An degenerate FixedSizeListArray should have no underlying values, found 3 values"
838
        );
839
    }
840
}