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/builder/fixed_size_list_builder.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::builder::ArrayBuilder;
19
use crate::{ArrayRef, FixedSizeListArray};
20
use arrow_buffer::NullBufferBuilder;
21
use arrow_schema::{Field, FieldRef};
22
use std::any::Any;
23
use std::sync::Arc;
24
25
///  Builder for [`FixedSizeListArray`]
26
/// ```
27
/// use arrow_array::{builder::{Int32Builder, FixedSizeListBuilder}, Array, Int32Array};
28
/// let values_builder = Int32Builder::new();
29
/// let mut builder = FixedSizeListBuilder::new(values_builder, 3);
30
///
31
/// //  [[0, 1, 2], null, [3, null, 5], [6, 7, null]]
32
/// builder.values().append_value(0);
33
/// builder.values().append_value(1);
34
/// builder.values().append_value(2);
35
/// builder.append(true);
36
/// builder.values().append_null();
37
/// builder.values().append_null();
38
/// builder.values().append_null();
39
/// builder.append(false);
40
/// builder.values().append_value(3);
41
/// builder.values().append_null();
42
/// builder.values().append_value(5);
43
/// builder.append(true);
44
/// builder.values().append_value(6);
45
/// builder.values().append_value(7);
46
/// builder.values().append_null();
47
/// builder.append(true);
48
/// let list_array = builder.finish();
49
/// assert_eq!(
50
///     *list_array.value(0),
51
///     Int32Array::from(vec![Some(0), Some(1), Some(2)])
52
/// );
53
/// assert!(list_array.is_null(1));
54
/// assert_eq!(
55
///     *list_array.value(2),
56
///     Int32Array::from(vec![Some(3), None, Some(5)])
57
/// );
58
/// assert_eq!(
59
///     *list_array.value(3),
60
///     Int32Array::from(vec![Some(6), Some(7), None])
61
/// )
62
/// ```
63
///
64
#[derive(Debug)]
65
pub struct FixedSizeListBuilder<T: ArrayBuilder> {
66
    null_buffer_builder: NullBufferBuilder,
67
    values_builder: T,
68
    list_len: i32,
69
    field: Option<FieldRef>,
70
}
71
72
impl<T: ArrayBuilder> FixedSizeListBuilder<T> {
73
    /// Creates a new [`FixedSizeListBuilder`] from a given values array builder
74
    /// `value_length` is the number of values within each array
75
    pub fn new(values_builder: T, value_length: i32) -> Self {
76
        let capacity = values_builder
77
            .len()
78
            .checked_div(value_length as _)
79
            .unwrap_or_default();
80
81
        Self::with_capacity(values_builder, value_length, capacity)
82
    }
83
84
    /// Creates a new [`FixedSizeListBuilder`] from a given values array builder
85
    /// `value_length` is the number of values within each array
86
    /// `capacity` is the number of items to pre-allocate space for in this builder
87
11
    pub fn with_capacity(values_builder: T, value_length: i32, capacity: usize) -> Self {
88
11
        Self {
89
11
            null_buffer_builder: NullBufferBuilder::new(capacity),
90
11
            values_builder,
91
11
            list_len: value_length,
92
11
            field: None,
93
11
        }
94
11
    }
95
96
    /// Override the field passed to [`FixedSizeListArray::new`]
97
    ///
98
    /// By default, a nullable field is created with the name `item`
99
    ///
100
    /// Note: [`Self::finish`] and [`Self::finish_cloned`] will panic if the
101
    /// field's data type does not match that of `T`
102
0
    pub fn with_field(self, field: impl Into<FieldRef>) -> Self {
103
0
        Self {
104
0
            field: Some(field.into()),
105
0
            ..self
106
0
        }
107
0
    }
108
}
109
110
impl<T: ArrayBuilder> ArrayBuilder for FixedSizeListBuilder<T>
111
where
112
    T: 'static,
113
{
114
    /// Returns the builder as a non-mutable `Any` reference.
115
0
    fn as_any(&self) -> &dyn Any {
116
0
        self
117
0
    }
118
119
    /// Returns the builder as a mutable `Any` reference.
120
0
    fn as_any_mut(&mut self) -> &mut dyn Any {
121
0
        self
122
0
    }
123
124
    /// Returns the boxed builder as a box of `Any`.
125
0
    fn into_box_any(self: Box<Self>) -> Box<dyn Any> {
126
0
        self
127
0
    }
128
129
    /// Returns the number of array slots in the builder
130
11
    fn len(&self) -> usize {
131
11
        self.null_buffer_builder.len()
132
11
    }
133
134
    /// Builds the array and reset this builder.
135
0
    fn finish(&mut self) -> ArrayRef {
136
0
        Arc::new(self.finish())
137
0
    }
138
139
    /// Builds the array without resetting the builder.
140
0
    fn finish_cloned(&self) -> ArrayRef {
141
0
        Arc::new(self.finish_cloned())
142
0
    }
143
}
144
145
impl<T: ArrayBuilder> FixedSizeListBuilder<T>
146
where
147
    T: 'static,
148
{
149
    /// Returns the child array builder as a mutable reference.
150
    ///
151
    /// This mutable reference can be used to append values into the child array builder,
152
    /// but you must call [`append`](#method.append) to delimit each distinct list value.
153
84
    pub fn values(&mut self) -> &mut T {
154
84
        &mut self.values_builder
155
84
    }
156
157
    /// Returns the length of the list
158
    pub fn value_length(&self) -> i32 {
159
        self.list_len
160
    }
161
162
    /// Finish the current fixed-length list array slot
163
    #[inline]
164
44
    pub fn append(&mut self, is_valid: bool) {
165
44
        self.null_buffer_builder.append(is_valid);
166
44
    }
167
168
    /// Builds the [`FixedSizeListBuilder`] and reset this builder.
169
11
    pub fn finish(&mut self) -> FixedSizeListArray {
170
11
        let len = self.len();
171
11
        let values = self.values_builder.finish();
172
11
        let nulls = self.null_buffer_builder.finish();
173
174
11
        assert_eq!(
175
11
            values.len(),
176
11
            len * self.list_len as usize,
177
0
            "Length of the child array ({}) must be the multiple of the value length ({}) and the array length ({}).",
178
0
            values.len(),
179
            self.list_len,
180
            len,
181
        );
182
183
11
        let field = self
184
11
            .field
185
11
            .clone()
186
11
            .unwrap_or_else(|| Arc::new(Field::new_list_field(values.data_type().clone(), true)));
187
188
11
        FixedSizeListArray::new(field, self.list_len, values, nulls)
189
11
    }
190
191
    /// Builds the [`FixedSizeListBuilder`] without resetting the builder.
192
0
    pub fn finish_cloned(&self) -> FixedSizeListArray {
193
0
        let len = self.len();
194
0
        let values = self.values_builder.finish_cloned();
195
0
        let nulls = self.null_buffer_builder.finish_cloned();
196
197
0
        assert_eq!(
198
0
            values.len(),
199
0
            len * self.list_len as usize,
200
0
            "Length of the child array ({}) must be the multiple of the value length ({}) and the array length ({}).",
201
0
            values.len(),
202
            self.list_len,
203
            len,
204
        );
205
206
0
        let field = self
207
0
            .field
208
0
            .clone()
209
0
            .unwrap_or_else(|| Arc::new(Field::new_list_field(values.data_type().clone(), true)));
210
211
0
        FixedSizeListArray::new(field, self.list_len, values, nulls)
212
0
    }
213
214
    /// Returns the current null buffer as a slice
215
    pub fn validity_slice(&self) -> Option<&[u8]> {
216
        self.null_buffer_builder.as_slice()
217
    }
218
}
219
220
#[cfg(test)]
221
mod tests {
222
    use super::*;
223
    use arrow_schema::DataType;
224
225
    use crate::Array;
226
    use crate::Int32Array;
227
    use crate::builder::Int32Builder;
228
229
    fn make_list_builder(
230
        include_null_element: bool,
231
        include_null_in_values: bool,
232
    ) -> FixedSizeListBuilder<crate::builder::PrimitiveBuilder<crate::types::Int32Type>> {
233
        let values_builder = Int32Builder::new();
234
        let mut builder = FixedSizeListBuilder::new(values_builder, 3);
235
236
        builder.values().append_value(0);
237
        builder.values().append_value(1);
238
        builder.values().append_value(2);
239
        builder.append(true);
240
241
        builder.values().append_value(2);
242
        builder.values().append_value(3);
243
        builder.values().append_value(4);
244
        builder.append(true);
245
246
        if include_null_element {
247
            builder.values().append_null();
248
            builder.values().append_null();
249
            builder.values().append_null();
250
            builder.append(false);
251
        } else {
252
            builder.values().append_value(2);
253
            builder.values().append_value(3);
254
            builder.values().append_value(4);
255
            builder.append(true);
256
        }
257
258
        if include_null_in_values {
259
            builder.values().append_value(3);
260
            builder.values().append_null();
261
            builder.values().append_value(5);
262
            builder.append(true);
263
        } else {
264
            builder.values().append_value(3);
265
            builder.values().append_value(4);
266
            builder.values().append_value(5);
267
            builder.append(true);
268
        }
269
270
        builder
271
    }
272
273
    #[test]
274
    fn test_fixed_size_list_array_builder() {
275
        let mut builder = make_list_builder(true, true);
276
277
        let list_array = builder.finish();
278
279
        assert_eq!(DataType::Int32, list_array.value_type());
280
        assert_eq!(4, list_array.len());
281
        assert_eq!(1, list_array.null_count());
282
        assert_eq!(6, list_array.value_offset(2));
283
        assert_eq!(3, list_array.value_length());
284
    }
285
286
    #[test]
287
    fn test_fixed_size_list_array_builder_with_field() {
288
        let builder = make_list_builder(false, false);
289
        let mut builder = builder.with_field(Field::new("list_element", DataType::Int32, false));
290
        let list_array = builder.finish();
291
292
        assert_eq!(DataType::Int32, list_array.value_type());
293
        assert_eq!(4, list_array.len());
294
        assert_eq!(0, list_array.null_count());
295
        assert_eq!(6, list_array.value_offset(2));
296
        assert_eq!(3, list_array.value_length());
297
    }
298
299
    #[test]
300
    fn test_fixed_size_list_array_builder_with_field_and_null() {
301
        let builder = make_list_builder(true, false);
302
        let mut builder = builder.with_field(Field::new("list_element", DataType::Int32, false));
303
        let list_array = builder.finish();
304
305
        assert_eq!(DataType::Int32, list_array.value_type());
306
        assert_eq!(4, list_array.len());
307
        assert_eq!(1, list_array.null_count());
308
        assert_eq!(6, list_array.value_offset(2));
309
        assert_eq!(3, list_array.value_length());
310
    }
311
312
    #[test]
313
    #[should_panic(expected = "Found unmasked nulls for non-nullable FixedSizeListArray")]
314
    fn test_fixed_size_list_array_builder_with_field_null_panic() {
315
        let builder = make_list_builder(true, true);
316
        let mut builder = builder.with_field(Field::new("list_item", DataType::Int32, false));
317
318
        builder.finish();
319
    }
320
321
    #[test]
322
    #[should_panic(expected = "FixedSizeListArray expected data type Int64 got Int32")]
323
    fn test_fixed_size_list_array_builder_with_field_type_panic() {
324
        let values_builder = Int32Builder::new();
325
        let builder = FixedSizeListBuilder::new(values_builder, 3);
326
        let mut builder = builder.with_field(Field::new("list_item", DataType::Int64, true));
327
328
        //  [[0, 1, 2], null, [3, null, 5], [6, 7, null]]
329
        builder.values().append_value(0);
330
        builder.values().append_value(1);
331
        builder.values().append_value(2);
332
        builder.append(true);
333
        builder.values().append_null();
334
        builder.values().append_null();
335
        builder.values().append_null();
336
        builder.append(false);
337
        builder.values().append_value(3);
338
        builder.values().append_value(4);
339
        builder.values().append_value(5);
340
        builder.append(true);
341
342
        builder.finish();
343
    }
344
345
    #[test]
346
    fn test_fixed_size_list_array_builder_cloned_with_field() {
347
        let builder = make_list_builder(true, true);
348
        let builder = builder.with_field(Field::new("list_element", DataType::Int32, true));
349
350
        let list_array = builder.finish_cloned();
351
352
        assert_eq!(DataType::Int32, list_array.value_type());
353
        assert_eq!(4, list_array.len());
354
        assert_eq!(1, list_array.null_count());
355
        assert_eq!(6, list_array.value_offset(2));
356
        assert_eq!(3, list_array.value_length());
357
    }
358
359
    #[test]
360
    #[should_panic(expected = "Found unmasked nulls for non-nullable FixedSizeListArray")]
361
    fn test_fixed_size_list_array_builder_cloned_with_field_null_panic() {
362
        let builder = make_list_builder(true, true);
363
        let builder = builder.with_field(Field::new("list_item", DataType::Int32, false));
364
365
        builder.finish_cloned();
366
    }
367
368
    #[test]
369
    fn test_fixed_size_list_array_builder_cloned_with_field_and_null() {
370
        let builder = make_list_builder(true, false);
371
        let mut builder = builder.with_field(Field::new("list_element", DataType::Int32, false));
372
        let list_array = builder.finish();
373
374
        assert_eq!(DataType::Int32, list_array.value_type());
375
        assert_eq!(4, list_array.len());
376
        assert_eq!(1, list_array.null_count());
377
        assert_eq!(6, list_array.value_offset(2));
378
        assert_eq!(3, list_array.value_length());
379
    }
380
381
    #[test]
382
    #[should_panic(expected = "FixedSizeListArray expected data type Int64 got Int32")]
383
    fn test_fixed_size_list_array_builder_cloned_with_field_type_panic() {
384
        let builder = make_list_builder(false, false);
385
        let builder = builder.with_field(Field::new("list_item", DataType::Int64, true));
386
387
        builder.finish_cloned();
388
    }
389
390
    #[test]
391
    fn test_fixed_size_list_array_builder_finish_cloned() {
392
        let mut builder = make_list_builder(true, true);
393
394
        let mut list_array = builder.finish_cloned();
395
396
        assert_eq!(DataType::Int32, list_array.value_type());
397
        assert_eq!(4, list_array.len());
398
        assert_eq!(1, list_array.null_count());
399
        assert_eq!(3, list_array.value_length());
400
401
        builder.values().append_value(6);
402
        builder.values().append_value(7);
403
        builder.values().append_null();
404
        builder.append(true);
405
        builder.values().append_null();
406
        builder.values().append_null();
407
        builder.values().append_null();
408
        builder.append(false);
409
        list_array = builder.finish();
410
411
        assert_eq!(DataType::Int32, list_array.value_type());
412
        assert_eq!(6, list_array.len());
413
        assert_eq!(2, list_array.null_count());
414
        assert_eq!(6, list_array.value_offset(2));
415
        assert_eq!(3, list_array.value_length());
416
    }
417
418
    #[test]
419
    fn test_fixed_size_list_array_builder_with_field_empty() {
420
        let values_builder = Int32Array::builder(0);
421
        let mut builder = FixedSizeListBuilder::new(values_builder, 3).with_field(Field::new(
422
            "list_item",
423
            DataType::Int32,
424
            false,
425
        ));
426
        assert!(builder.is_empty());
427
        let arr = builder.finish();
428
        assert_eq!(0, arr.len());
429
        assert_eq!(0, builder.len());
430
    }
431
432
    #[test]
433
    fn test_fixed_size_list_array_builder_cloned_with_field_empty() {
434
        let values_builder = Int32Array::builder(0);
435
        let builder = FixedSizeListBuilder::new(values_builder, 3).with_field(Field::new(
436
            "list_item",
437
            DataType::Int32,
438
            false,
439
        ));
440
        assert!(builder.is_empty());
441
        let arr = builder.finish_cloned();
442
        assert_eq!(0, arr.len());
443
        assert_eq!(0, builder.len());
444
    }
445
446
    #[test]
447
    fn test_fixed_size_list_array_builder_empty() {
448
        let values_builder = Int32Array::builder(5);
449
        let mut builder = FixedSizeListBuilder::new(values_builder, 3);
450
        assert!(builder.is_empty());
451
        let arr = builder.finish();
452
        assert_eq!(0, arr.len());
453
        assert_eq!(0, builder.len());
454
    }
455
456
    #[test]
457
    fn test_fixed_size_list_array_builder_finish() {
458
        let values_builder = Int32Array::builder(5);
459
        let mut builder = FixedSizeListBuilder::new(values_builder, 3);
460
461
        builder.values().append_slice(&[1, 2, 3]);
462
        builder.append(true);
463
        builder.values().append_slice(&[4, 5, 6]);
464
        builder.append(true);
465
466
        let mut arr = builder.finish();
467
        assert_eq!(2, arr.len());
468
        assert_eq!(0, builder.len());
469
470
        builder.values().append_slice(&[7, 8, 9]);
471
        builder.append(true);
472
        arr = builder.finish();
473
        assert_eq!(1, arr.len());
474
        assert_eq!(0, builder.len());
475
    }
476
477
    #[test]
478
    #[should_panic(
479
        expected = "Length of the child array (10) must be the multiple of the value length (3) and the array length (3)."
480
    )]
481
    fn test_fixed_size_list_array_builder_fail() {
482
        let values_builder = Int32Array::builder(5);
483
        let mut builder = FixedSizeListBuilder::new(values_builder, 3);
484
485
        builder.values().append_slice(&[1, 2, 3]);
486
        builder.append(true);
487
        builder.values().append_slice(&[4, 5, 6]);
488
        builder.append(true);
489
        builder.values().append_slice(&[7, 8, 9, 10]);
490
        builder.append(true);
491
492
        builder.finish();
493
    }
494
}