Skip to main content

vortex_array/builders/
list.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4use std::any::Any;
5use std::sync::Arc;
6
7use num_traits::AsPrimitive;
8use vortex_error::VortexExpect;
9use vortex_error::VortexResult;
10use vortex_error::vortex_bail;
11use vortex_error::vortex_ensure;
12use vortex_error::vortex_panic;
13use vortex_mask::Mask;
14
15use crate::ArrayRef;
16use crate::Canonical;
17use crate::ExecutionCtx;
18use crate::IntoArray;
19use crate::array::ArrayView;
20use crate::arrays::List;
21use crate::arrays::ListArray;
22use crate::arrays::ListView;
23use crate::arrays::ListViewArray;
24use crate::arrays::PrimitiveArray;
25use crate::arrays::list::ListArraySlotsExt;
26use crate::arrays::listview::ListViewArraySlotsExt;
27use crate::builders::ArrayBuilder;
28use crate::builders::DEFAULT_BUILDER_CAPACITY;
29use crate::builders::LazyBitBufferBuilder;
30use crate::builders::PrimitiveBuilder;
31use crate::builders::builder_with_capacity;
32use crate::dtype::DType;
33use crate::dtype::IntegerPType;
34use crate::dtype::Nullability;
35use crate::dtype::Nullability::NonNullable;
36use crate::dtype::OffsetBuilderPType;
37use crate::match_each_integer_ptype;
38use crate::scalar::ListScalar;
39use crate::scalar::Scalar;
40
41/// The builder for building a [`ListArray`], parametrized by the [`OffsetBuilderPType`] of the
42/// `offsets` builder.
43pub struct ListBuilder<O: OffsetBuilderPType> {
44    /// The [`DType`] of the [`ListArray`]. This **must** be a [`DType::List`].
45    dtype: DType,
46
47    /// The builder for the underlying elements of the [`ListArray`].
48    elements_builder: Box<dyn ArrayBuilder>,
49
50    /// The builder for the `offsets` into the `elements` array.
51    offsets_builder: PrimitiveBuilder<O>,
52
53    /// The null map builder of the [`ListArray`].
54    nulls: LazyBitBufferBuilder,
55}
56
57impl<O: OffsetBuilderPType> ListBuilder<O> {
58    /// Creates a new `ListBuilder` with a capacity of [`DEFAULT_BUILDER_CAPACITY`].
59    pub fn new(value_dtype: Arc<DType>, nullability: Nullability) -> Self {
60        Self::with_capacity(
61            value_dtype,
62            nullability,
63            // We arbitrarily choose 2 times the number of list scalars for the capacity of the
64            // elements builder since we cannot know this ahead of time.
65            DEFAULT_BUILDER_CAPACITY * 2,
66            DEFAULT_BUILDER_CAPACITY,
67        )
68    }
69
70    /// Create a new [`ListArray`] builder with a with the given `capacity`, as well as an initial
71    /// capacity for the `elements` builder (since we cannot know that ahead of time solely based on
72    /// the outer array `capacity`).
73    ///
74    /// # Notes
75    ///
76    /// The number of offsets is one more than the length (# of list scalars) in the array.
77    pub fn with_capacity(
78        value_dtype: Arc<DType>,
79        nullability: Nullability,
80        elements_capacity: usize,
81        capacity: usize,
82    ) -> Self {
83        let elements_builder = builder_with_capacity(value_dtype.as_ref(), elements_capacity);
84        let mut offsets_builder = PrimitiveBuilder::<O>::with_capacity(NonNullable, capacity + 1);
85
86        // The first offset is always 0 and represents an empty list.
87        offsets_builder.append_zero();
88
89        Self {
90            elements_builder,
91            offsets_builder,
92            nulls: LazyBitBufferBuilder::new(capacity),
93            dtype: DType::List(value_dtype, nullability),
94        }
95    }
96
97    /// Appends an array as a single non-null list entry to the builder.
98    ///
99    /// The input `array` must have the same dtype as the element dtype of this list builder.
100    ///
101    /// Note that the list entry will be non-null but the elements themselves are allowed to be null
102    /// (only if the elements [`DType`] in nullable, of course).
103    pub fn append_array_as_list(
104        &mut self,
105        array: &ArrayRef,
106        ctx: &mut ExecutionCtx,
107    ) -> VortexResult<()> {
108        vortex_ensure!(
109            array.dtype() == self.element_dtype(),
110            "Array dtype {:?} does not match list element dtype {:?}",
111            array.dtype(),
112            self.element_dtype()
113        );
114
115        self.elements_builder.reserve_exact(array.len());
116        array.append_to_builder(self.elements_builder.as_mut(), ctx)?;
117        self.nulls.append_non_null();
118        self.offsets_builder.append_value(
119            O::from_usize(self.elements_builder.len())
120                .vortex_expect("Failed to convert from usize to O"),
121        );
122
123        Ok(())
124    }
125
126    /// Appends a list `value` to the builder.
127    pub fn append_value(&mut self, value: ListScalar) -> VortexResult<()> {
128        match value.elements() {
129            None => {
130                if self.dtype.nullability() == NonNullable {
131                    vortex_bail!("Cannot append null value to non-nullable list");
132                }
133                self.append_null();
134            }
135            Some(elements) => {
136                for scalar in elements {
137                    // TODO(connor): This is slow, we should be able to append multiple values at
138                    // once, or the list scalar should hold an Array
139                    self.elements_builder.append_scalar(&scalar)?;
140                }
141
142                self.nulls.append_non_null();
143                self.offsets_builder.append_value(
144                    O::from_usize(self.elements_builder.len())
145                        .vortex_expect("Failed to convert from usize to O"),
146                );
147            }
148        }
149
150        Ok(())
151    }
152
153    /// Finishes the builder directly into a [`ListArray`].
154    pub fn finish_into_list(&mut self) -> ListArray {
155        assert_eq!(
156            self.offsets_builder.len(),
157            self.nulls.len() + 1,
158            "offsets length must be one more than nulls length."
159        );
160
161        ListArray::try_new(
162            self.elements_builder.finish(),
163            self.offsets_builder.finish(),
164            self.nulls.finish_with_nullability(self.dtype.nullability()),
165        )
166        .vortex_expect("Buffer, offsets, and validity must have same length.")
167    }
168
169    /// The [`DType`] of the inner elements. Note that this is **not** the same as the [`DType`] of
170    /// the outer `List`.
171    pub fn element_dtype(&self) -> &DType {
172        let DType::List(element_dtype, _) = &self.dtype else {
173            vortex_panic!("`ListBuilder` has an incorrect dtype: {}", self.dtype);
174        };
175
176        element_dtype
177    }
178
179    /// Appends the values of a [`List`]-encoded `array` to this builder.
180    ///
181    /// List encodings dispatch here through
182    /// [`match_each_list_builder!`](crate::match_each_list_builder) because the concrete list
183    /// builders are generic over their offset integer type, which cannot be named through a
184    /// `dyn ArrayBuilder`.
185    pub fn append_list_array(
186        &mut self,
187        array: ArrayView<'_, List>,
188        ctx: &mut ExecutionCtx,
189    ) -> VortexResult<()> {
190        if array.is_empty() {
191            return Ok(());
192        }
193
194        self.nulls
195            .append_validity_mask(&array.validity()?.execute_mask(array.len(), ctx)?);
196
197        let num_lists = array.len();
198        let offsets = array.offsets().clone().execute::<PrimitiveArray>(ctx)?;
199        match_each_integer_ptype!(offsets.ptype(), |OffsetType| {
200            let offsets = offsets.as_slice::<OffsetType>();
201            let first: usize = offsets[0].as_();
202            let last: usize = offsets[num_lists].as_();
203
204            // Lists in a `ListArray` are contiguous, so the referenced elements can be appended
205            // in bulk and the offsets rebased onto this builder's elements.
206            let elements_base = self.elements_builder.len();
207            if last > first {
208                self.elements_builder.reserve_exact(last - first);
209                array
210                    .elements()
211                    .slice(first..last)?
212                    .append_to_builder(self.elements_builder.as_mut(), ctx)?;
213            }
214
215            self.offsets_builder.reserve_exact(num_lists);
216            let mut offsets_range = self.offsets_builder.uninit_range(num_lists);
217            for i in 0..num_lists {
218                let end: usize = offsets[i + 1].as_();
219                offsets_range.set_value(
220                    i,
221                    O::from_usize(end - first + elements_base)
222                        .vortex_expect("Failed to convert offset"),
223                );
224            }
225            // SAFETY: We have initialized all `num_lists` values, and since the `offsets` array is
226            // non-nullable, we are done.
227            unsafe { offsets_range.finish() };
228        });
229        Ok(())
230    }
231
232    /// Appends the values of a [`ListView`]-encoded `array` to this builder.
233    ///
234    /// See [`append_list_array`](Self::append_list_array); this is the same hook for the canonical
235    /// [`ListViewArray`] encoding.
236    pub fn append_listview_array(
237        &mut self,
238        array: ArrayView<'_, ListView>,
239        ctx: &mut ExecutionCtx,
240    ) -> VortexResult<()> {
241        if array.is_empty() {
242            return Ok(());
243        }
244
245        self.nulls
246            .append_validity_mask(&array.validity()?.execute_mask(array.len(), ctx)?);
247
248        // Note that `ListViewArray` has `n` offsets and sizes, not `n+1` offsets like `ListArray`.
249        let elements = array.elements();
250        let offsets = array.offsets().clone().execute::<PrimitiveArray>(ctx)?;
251        let sizes = array.sizes().clone().execute::<PrimitiveArray>(ctx)?;
252
253        match_each_integer_ptype!(offsets.ptype(), |OffsetType| {
254            match_each_integer_ptype!(sizes.ptype(), |SizeType| {
255                extend_from_listview(
256                    self,
257                    elements,
258                    offsets.as_slice::<OffsetType>(),
259                    sizes.as_slice::<SizeType>(),
260                    ctx,
261                )?
262            })
263        });
264        Ok(())
265    }
266}
267
268/// Appends `ListViewArray`-layout lists (`n` offsets and sizes) into a [`ListBuilder`], converting
269/// into the `ListArray` (`n + 1` offsets) layout.
270fn extend_from_listview<O, OffsetType, SizeType>(
271    builder: &mut ListBuilder<O>,
272    new_elements: &ArrayRef,
273    new_offsets: &[OffsetType],
274    new_sizes: &[SizeType],
275    ctx: &mut ExecutionCtx,
276) -> VortexResult<()>
277where
278    O: OffsetBuilderPType,
279    OffsetType: IntegerPType,
280    SizeType: IntegerPType,
281{
282    let num_lists = new_offsets.len();
283    debug_assert_eq!(num_lists, new_sizes.len());
284
285    let total_elements: usize = new_sizes.iter().map(|size| size.as_()).sum();
286    builder.elements_builder.reserve_exact(total_elements);
287
288    let mut curr_offset = builder.elements_builder.len();
289    builder.offsets_builder.reserve_exact(num_lists);
290    let mut offsets_range = builder.offsets_builder.uninit_range(num_lists);
291
292    // We need to append each list individually, converting from `ListViewArray` format to
293    // the `ListArray` format that `ListBuilder` expects.
294    for i in 0..new_offsets.len() {
295        let offset: usize = new_offsets[i].as_();
296        let size: usize = new_sizes[i].as_();
297
298        if size > 0 {
299            let list_elements = new_elements
300                .slice(offset..offset + size)
301                .vortex_expect("list builder slice");
302            list_elements.append_to_builder(builder.elements_builder.as_mut(), ctx)?;
303            curr_offset += size;
304        }
305
306        let new_offset = O::from_usize(curr_offset).vortex_expect("Failed to convert offset");
307
308        offsets_range.set_value(i, new_offset);
309    }
310
311    // SAFETY: We have initialized all `num_lists` values, and since the `offsets` array is
312    // non-nullable, we are done.
313    unsafe { offsets_range.finish() };
314    Ok(())
315}
316
317impl<O: OffsetBuilderPType> ArrayBuilder for ListBuilder<O> {
318    fn as_any(&self) -> &dyn Any {
319        self
320    }
321
322    fn as_any_mut(&mut self) -> &mut dyn Any {
323        self
324    }
325
326    fn dtype(&self) -> &DType {
327        &self.dtype
328    }
329
330    fn len(&self) -> usize {
331        self.nulls.len()
332    }
333
334    fn append_zeros(&mut self, n: usize) {
335        let curr_len = self.elements_builder.len();
336        for _ in 0..n {
337            self.offsets_builder.append_value(
338                O::from_usize(curr_len).vortex_expect("Failed to convert from usize to <O>"),
339            )
340        }
341        self.nulls.append_n_non_nulls(n);
342    }
343
344    unsafe fn append_nulls_unchecked(&mut self, n: usize) {
345        let curr_len = self.elements_builder.len();
346        for _ in 0..n {
347            // A list with a null element is can be a list with a zero-span offset and a validity
348            // bit set
349            self.offsets_builder.append_value(
350                O::from_usize(curr_len).vortex_expect("Failed to convert from usize to <O>"),
351            )
352        }
353        self.nulls.append_n_nulls(n);
354    }
355
356    fn append_scalar(&mut self, scalar: &Scalar) -> VortexResult<()> {
357        vortex_ensure!(
358            scalar.dtype() == self.dtype(),
359            "ListBuilder expected scalar with dtype {}, got {}",
360            self.dtype(),
361            scalar.dtype()
362        );
363
364        self.append_value(scalar.as_list())
365    }
366
367    fn reserve_exact(&mut self, additional: usize) {
368        self.elements_builder.reserve_exact(additional);
369        self.offsets_builder.reserve_exact(additional);
370        self.nulls.reserve_exact(additional);
371    }
372
373    unsafe fn set_validity_unchecked(&mut self, validity: Mask) {
374        self.nulls = LazyBitBufferBuilder::from_validity_mask(validity);
375    }
376
377    fn finish(&mut self) -> ArrayRef {
378        self.finish_into_list().into_array()
379    }
380
381    fn finish_into_canonical(&mut self, ctx: &mut ExecutionCtx) -> Canonical {
382        let listview = self
383            .finish()
384            .execute::<ListViewArray>(ctx)
385            .vortex_expect("list builder should canonicalize to listview");
386        Canonical::List(listview)
387    }
388}
389
390#[cfg(test)]
391mod tests {
392    use std::sync::Arc;
393
394    use Nullability::NonNullable;
395    use Nullability::Nullable;
396    use vortex_buffer::buffer;
397    use vortex_error::VortexExpect;
398    use vortex_error::VortexResult;
399
400    use crate::IntoArray;
401    use crate::array_session;
402    use crate::arrays::ChunkedArray;
403    use crate::arrays::ListViewArray;
404    use crate::arrays::PrimitiveArray;
405    use crate::arrays::list::ListArraySlotsExt;
406    use crate::arrays::listview::ListViewArrayExt;
407    use crate::arrays::listview::ListViewArraySlotsExt;
408    use crate::assert_arrays_eq;
409    use crate::builders::ArrayBuilder;
410    use crate::builders::ListViewBuilder;
411    use crate::builders::builder_with_capacity;
412    use crate::builders::list::ListArray;
413    use crate::builders::list::ListBuilder;
414    use crate::dtype::DType;
415    use crate::dtype::Nullability;
416    use crate::dtype::OffsetBuilderPType;
417    use crate::dtype::PType::I32;
418    use crate::executor::VortexSessionExecute;
419    use crate::scalar::Scalar;
420    use crate::validity::Validity;
421
422    #[test]
423    fn test_empty() {
424        let mut builder =
425            ListBuilder::<u32>::with_capacity(Arc::new(I32.into()), NonNullable, 0, 0);
426
427        let list = builder.finish();
428        assert_eq!(list.len(), 0);
429    }
430
431    #[test]
432    fn test_values() {
433        let dtype: Arc<DType> = Arc::new(I32.into());
434        let mut builder = ListBuilder::<u32>::with_capacity(Arc::clone(&dtype), NonNullable, 0, 0);
435
436        builder
437            .append_value(
438                Scalar::list(
439                    Arc::clone(&dtype),
440                    vec![1i32.into(), 2i32.into(), 3i32.into()],
441                    NonNullable,
442                )
443                .as_list(),
444            )
445            .unwrap();
446
447        builder
448            .append_value(
449                Scalar::list(
450                    dtype,
451                    vec![4i32.into(), 5i32.into(), 6i32.into()],
452                    NonNullable,
453                )
454                .as_list(),
455            )
456            .unwrap();
457
458        let list = builder.finish();
459        assert_eq!(list.len(), 2);
460
461        let mut ctx = array_session().create_execution_ctx();
462        let list_array = list.execute::<ListViewArray>(&mut ctx).unwrap();
463
464        assert_eq!(list_array.list_elements_at(0).unwrap().len(), 3);
465        assert_eq!(list_array.list_elements_at(1).unwrap().len(), 3);
466    }
467
468    #[test]
469    fn test_append_empty_list() {
470        let dtype: Arc<DType> = Arc::new(I32.into());
471        let mut builder = ListBuilder::<u32>::with_capacity(Arc::clone(&dtype), NonNullable, 0, 0);
472
473        assert!(
474            builder
475                .append_value(Scalar::list_empty(dtype, NonNullable).as_list())
476                .is_ok()
477        )
478    }
479
480    #[test]
481    fn test_nullable_values() {
482        let dtype: Arc<DType> = Arc::new(I32.into());
483        let mut builder = ListBuilder::<u32>::with_capacity(Arc::clone(&dtype), Nullable, 0, 0);
484
485        builder
486            .append_value(
487                Scalar::list(
488                    Arc::clone(&dtype),
489                    vec![1i32.into(), 2i32.into(), 3i32.into()],
490                    NonNullable,
491                )
492                .as_list(),
493            )
494            .unwrap();
495
496        builder
497            .append_value(Scalar::list_empty(Arc::clone(&dtype), NonNullable).as_list())
498            .unwrap();
499
500        builder
501            .append_value(
502                Scalar::list(
503                    dtype,
504                    vec![4i32.into(), 5i32.into(), 6i32.into()],
505                    NonNullable,
506                )
507                .as_list(),
508            )
509            .unwrap();
510
511        let list = builder.finish();
512        assert_eq!(list.len(), 3);
513
514        let mut ctx = array_session().create_execution_ctx();
515        let list_array = list.execute::<ListViewArray>(&mut ctx).unwrap();
516
517        assert_eq!(list_array.list_elements_at(0).unwrap().len(), 3);
518        assert_eq!(list_array.list_elements_at(1).unwrap().len(), 0);
519        assert_eq!(list_array.list_elements_at(2).unwrap().len(), 3);
520    }
521
522    fn test_extend_builder_gen<O: OffsetBuilderPType>() {
523        let list = ListArray::from_iter_opt_slow::<O, _, _>(
524            [Some(vec![0, 1, 2]), None, Some(vec![4, 5])],
525            Arc::new(I32.into()),
526        )
527        .unwrap()
528        .into_array();
529        assert_eq!(list.len(), 3);
530
531        let mut ctx = array_session().create_execution_ctx();
532
533        let mut builder = ListBuilder::<O>::with_capacity(Arc::new(I32.into()), Nullable, 18, 9);
534        list.append_to_builder(&mut builder, &mut ctx).unwrap();
535        list.append_to_builder(&mut builder, &mut ctx).unwrap();
536        list.slice(0..0)
537            .unwrap()
538            .append_to_builder(&mut builder, &mut ctx)
539            .unwrap();
540        list.slice(1..3)
541            .unwrap()
542            .append_to_builder(&mut builder, &mut ctx)
543            .unwrap();
544
545        let expected = ListArray::from_iter_opt_slow::<O, _, _>(
546            [
547                Some(vec![0, 1, 2]),
548                None,
549                Some(vec![4, 5]),
550                Some(vec![0, 1, 2]),
551                None,
552                Some(vec![4, 5]),
553                None,
554                Some(vec![4, 5]),
555            ],
556            Arc::new(DType::Primitive(I32, NonNullable)),
557        )
558        .unwrap()
559        .into_array()
560        .execute::<ListViewArray>(&mut ctx)
561        .unwrap();
562
563        let actual = builder.finish_into_canonical(&mut ctx).into_listview();
564
565        assert_arrays_eq!(actual.elements(), expected.elements(), &mut ctx);
566
567        assert_arrays_eq!(actual.offsets(), expected.offsets(), &mut ctx);
568
569        assert!(
570            actual
571                .validity()
572                .vortex_expect("list validity should be derivable")
573                .mask_eq(
574                    &expected
575                        .validity()
576                        .vortex_expect("list validity should be derivable"),
577                    actual.len(),
578                    &mut ctx,
579                )
580                .unwrap(),
581        );
582    }
583
584    /// `append_to_builder` must handle any list builder kind without assuming the offset/size
585    /// integer types produced by `builder_with_capacity`. It appends a `List`-encoded array and a
586    /// `ListView`-encoded array into `ListViewBuilder`s and `ListBuilder`s with assorted (and
587    /// non-`u64`) offset/size types.
588    #[test]
589    fn test_append_to_builder_any_list_builder() -> VortexResult<()> {
590        let mut ctx = array_session().create_execution_ctx();
591
592        let list = ListArray::from_iter_opt_slow::<u64, _, _>(
593            [Some(vec![0, 1, 2]), None, Some(vec![4, 5])],
594            Arc::new(I32.into()),
595        )?
596        .into_array();
597        let listview = list
598            .clone()
599            .execute::<ListViewArray>(&mut ctx)?
600            .into_array();
601        let elem_dtype = || Arc::new(I32.into());
602
603        // `builder_with_capacity` produces a `ListViewBuilder` for `DType::List`; appending the
604        // `List`-encoded array must dispatch into it instead of bailing.
605        let mut listview_builder = builder_with_capacity(list.dtype(), list.len());
606        list.append_to_builder(listview_builder.as_mut(), &mut ctx)?;
607        assert_arrays_eq!(listview_builder.finish(), list, &mut ctx);
608
609        // A `ListViewBuilder` with non-`u64` (including signed) offset and size types must work
610        // for both source encodings.
611        let mut lv_u64_u32 =
612            ListViewBuilder::<u64, u32>::with_capacity(elem_dtype(), Nullable, 8, 4);
613        list.append_to_builder(&mut lv_u64_u32, &mut ctx)?;
614        assert_arrays_eq!(lv_u64_u32.finish(), list, &mut ctx);
615
616        let mut lv_i64_i32 =
617            ListViewBuilder::<i64, i32>::with_capacity(elem_dtype(), Nullable, 8, 4);
618        list.append_to_builder(&mut lv_i64_i32, &mut ctx)?;
619        assert_arrays_eq!(lv_i64_i32.finish(), list, &mut ctx);
620
621        let mut lv_u32_u32 =
622            ListViewBuilder::<u32, u32>::with_capacity(elem_dtype(), Nullable, 8, 4);
623        listview.append_to_builder(&mut lv_u32_u32, &mut ctx)?;
624        assert_arrays_eq!(lv_u32_u32.finish(), list, &mut ctx);
625
626        // Both source encodings appended into `ListBuilder`s with non-`u64` (including signed)
627        // offset types.
628        let mut list_builder = ListBuilder::<u32>::with_capacity(elem_dtype(), Nullable, 8, 4);
629        list.append_to_builder(&mut list_builder, &mut ctx)?;
630        assert_arrays_eq!(list_builder.finish(), list, &mut ctx);
631
632        let mut list_builder_i32 = ListBuilder::<i32>::with_capacity(elem_dtype(), Nullable, 8, 4);
633        listview.append_to_builder(&mut list_builder_i32, &mut ctx)?;
634        assert_arrays_eq!(list_builder_i32.finish(), list, &mut ctx);
635
636        Ok(())
637    }
638
639    #[test]
640    fn test_append_list_arrays_grow_builder() -> VortexResult<()> {
641        let mut ctx = array_session().create_execution_ctx();
642        let dtype: Arc<DType> = Arc::new(I32.into());
643
644        // Enough lists to exceed the offsets capacity of a zero-capacity builder, so appending
645        // must grow the builder rather than panic in `uninit_range`.
646        let lists: Vec<Option<Vec<i32>>> =
647            (0..100).map(|i| (i % 10 != 0).then(|| vec![i])).collect();
648        let source = ListArray::from_iter_opt_slow::<u32, _, _>(lists.clone(), Arc::clone(&dtype))?;
649        let expected = ListArray::from_iter_opt_slow::<u32, _, _>(
650            lists.iter().cloned().chain(lists.iter().cloned()),
651            Arc::clone(&dtype),
652        )?;
653
654        // Appending twice checks growth from a non-empty builder and offset rebasing.
655        let mut builder = ListBuilder::<u32>::with_capacity(Arc::clone(&dtype), Nullable, 0, 0);
656        builder.append_list_array(source.as_view(), &mut ctx)?;
657        builder.append_list_array(source.as_view(), &mut ctx)?;
658        assert_arrays_eq!(builder.finish(), expected, &mut ctx);
659
660        let source_listview = source.into_array().execute::<ListViewArray>(&mut ctx)?;
661        let mut builder = ListBuilder::<u32>::with_capacity(dtype, Nullable, 0, 0);
662        builder.append_listview_array(source_listview.as_view(), &mut ctx)?;
663        builder.append_listview_array(source_listview.as_view(), &mut ctx)?;
664        assert_arrays_eq!(builder.finish(), expected, &mut ctx);
665
666        Ok(())
667    }
668
669    #[test]
670    fn test_extend_builder() {
671        test_extend_builder_gen::<i32>();
672        test_extend_builder_gen::<i64>();
673
674        test_extend_builder_gen::<u32>();
675        test_extend_builder_gen::<u64>();
676    }
677
678    #[test]
679    pub fn test_array_with_gap() {
680        let one_trailing_unused_element = ListArray::try_new(
681            buffer![1, 2, 3, 4].into_array(),
682            buffer![0, 3].into_array(),
683            Validity::NonNullable,
684        )
685        .unwrap();
686
687        let second_array = ListArray::try_new(
688            buffer![5, 6].into_array(),
689            buffer![0, 2].into_array(),
690            Validity::NonNullable,
691        )
692        .unwrap();
693
694        let chunked_list = ChunkedArray::try_new(
695            vec![
696                one_trailing_unused_element.clone().into_array(),
697                second_array.clone().into_array(),
698            ],
699            DType::List(Arc::new(DType::Primitive(I32, NonNullable)), NonNullable),
700        );
701
702        let mut ctx = array_session().create_execution_ctx();
703        let canon_values = chunked_list
704            .unwrap()
705            .as_array()
706            .clone()
707            .execute::<ListViewArray>(&mut ctx)
708            .unwrap();
709
710        assert_eq!(
711            one_trailing_unused_element
712                .execute_scalar(0, &mut array_session().create_execution_ctx())
713                .unwrap(),
714            canon_values
715                .execute_scalar(0, &mut array_session().create_execution_ctx())
716                .unwrap()
717        );
718        assert_eq!(
719            second_array
720                .execute_scalar(0, &mut array_session().create_execution_ctx())
721                .unwrap(),
722            canon_values
723                .execute_scalar(1, &mut array_session().create_execution_ctx())
724                .unwrap()
725        );
726    }
727
728    #[test]
729    fn test_append_scalar() {
730        let dtype: Arc<DType> = Arc::new(I32.into());
731        let mut builder = ListBuilder::<u64>::with_capacity(Arc::clone(&dtype), Nullable, 20, 10);
732
733        // Test appending a valid list.
734        let list_scalar1 =
735            Scalar::list(Arc::clone(&dtype), vec![1i32.into(), 2i32.into()], Nullable);
736        builder.append_scalar(&list_scalar1).unwrap();
737
738        // Test appending another list.
739        let list_scalar2 = Scalar::list(
740            Arc::clone(&dtype),
741            vec![3i32.into(), 4i32.into(), 5i32.into()],
742            Nullable,
743        );
744        builder.append_scalar(&list_scalar2).unwrap();
745
746        // Test appending null value.
747        let null_scalar = Scalar::null(DType::List(Arc::clone(&dtype), Nullable));
748        builder.append_scalar(&null_scalar).unwrap();
749
750        let array = builder.finish_into_list();
751        assert_eq!(array.len(), 3);
752
753        let mut ctx = array_session().create_execution_ctx();
754
755        // Check actual values using scalar_at.
756
757        let scalar0 = array.execute_scalar(0, &mut ctx).unwrap();
758        let list0 = scalar0.as_list();
759        assert_eq!(list0.len(), 2);
760        if let Some(list0_items) = list0.elements() {
761            assert_eq!(list0_items[0].as_primitive().typed_value::<i32>(), Some(1));
762            assert_eq!(list0_items[1].as_primitive().typed_value::<i32>(), Some(2));
763        }
764
765        let scalar1 = array.execute_scalar(1, &mut ctx).unwrap();
766        let list1 = scalar1.as_list();
767        assert_eq!(list1.len(), 3);
768        if let Some(list1_items) = list1.elements() {
769            assert_eq!(list1_items[0].as_primitive().typed_value::<i32>(), Some(3));
770            assert_eq!(list1_items[1].as_primitive().typed_value::<i32>(), Some(4));
771            assert_eq!(list1_items[2].as_primitive().typed_value::<i32>(), Some(5));
772        }
773
774        let scalar2 = array.execute_scalar(2, &mut ctx).unwrap();
775        let list2 = scalar2.as_list();
776        assert!(list2.is_null()); // This should be null.
777
778        // Check validity.
779        assert!(
780            array
781                .validity()
782                .vortex_expect("list validity should be derivable")
783                .execute_is_valid(0, &mut ctx)
784                .unwrap()
785        );
786        assert!(
787            array
788                .validity()
789                .vortex_expect("list validity should be derivable")
790                .execute_is_valid(1, &mut ctx)
791                .unwrap()
792        );
793        assert!(
794            !array
795                .validity()
796                .vortex_expect("list validity should be derivable")
797                .execute_is_valid(2, &mut ctx)
798                .unwrap()
799        );
800
801        // Test wrong dtype error.
802        let mut builder = ListBuilder::<u64>::with_capacity(dtype, NonNullable, 20, 10);
803        let wrong_scalar = Scalar::from(42i32);
804        assert!(builder.append_scalar(&wrong_scalar).is_err());
805    }
806
807    #[test]
808    fn test_append_array_as_list() {
809        let dtype: Arc<DType> = Arc::new(I32.into());
810        let mut ctx = array_session().create_execution_ctx();
811        let mut builder =
812            ListBuilder::<u32>::with_capacity(Arc::clone(&dtype), NonNullable, 20, 10);
813
814        // Append a primitive array as a single list entry.
815        let arr1 = buffer![1i32, 2, 3].into_array();
816        builder.append_array_as_list(&arr1, &mut ctx).unwrap();
817
818        // Interleave with a list scalar.
819        builder
820            .append_value(
821                Scalar::list(
822                    Arc::clone(&dtype),
823                    vec![10i32.into(), 11i32.into()],
824                    NonNullable,
825                )
826                .as_list(),
827            )
828            .unwrap();
829
830        // Append another primitive array as a single list entry.
831        let arr2 = buffer![4i32, 5].into_array();
832        builder.append_array_as_list(&arr2, &mut ctx).unwrap();
833
834        // Append an empty array as a single list entry (empty list).
835        let arr3 = buffer![0i32; 0].into_array();
836        builder.append_array_as_list(&arr3, &mut ctx).unwrap();
837
838        // Interleave with another list scalar (empty list).
839        builder
840            .append_value(Scalar::list_empty(Arc::clone(&dtype), NonNullable).as_list())
841            .unwrap();
842
843        let list = builder.finish_into_list();
844        assert_eq!(list.len(), 5);
845
846        // Verify elements array: [1, 2, 3, 10, 11, 4, 5].
847        assert_arrays_eq!(
848            list.elements(),
849            PrimitiveArray::from_iter([1i32, 2, 3, 10, 11, 4, 5]),
850            &mut ctx
851        );
852
853        // Verify offsets array.
854        assert_arrays_eq!(
855            list.offsets(),
856            PrimitiveArray::from_iter([0u32, 3, 5, 7, 7, 7]),
857            &mut ctx
858        );
859
860        // Test dtype mismatch error.
861        let mut builder = ListBuilder::<u32>::with_capacity(dtype, NonNullable, 20, 10);
862        let wrong_dtype_arr = buffer![1i64, 2, 3].into_array();
863        assert!(
864            builder
865                .append_array_as_list(&wrong_dtype_arr, &mut ctx)
866                .is_err()
867        );
868    }
869}