1use std::sync::Arc;
5
6use vortex_dtype::{IntegerPType, Nullability, match_each_integer_ptype};
7use vortex_error::VortexExpect;
8
9use crate::arrays::{ExtensionArray, FixedSizeListArray, ListArray, ListViewArray, StructArray};
10use crate::builders::{ArrayBuilder, ListBuilder, PrimitiveBuilder};
11use crate::vtable::ValidityHelper;
12use crate::{Array, ArrayRef, Canonical, IntoArray, ToCanonical};
13
14pub fn list_view_from_list(list: ListArray) -> ListViewArray {
19 if list.is_empty() {
22 return Canonical::empty(list.dtype()).into_listview();
23 }
24
25 let list = list
29 .reset_offsets(false)
30 .vortex_expect("TODO(connor)[ListView]: This can't fail");
31
32 let list_offsets = list.offsets().clone();
33
34 let sizes = match_each_integer_ptype!(list_offsets.dtype().as_ptype(), |O| {
37 build_sizes_from_offsets::<O>(&list)
38 });
39
40 debug_assert_eq!(list_offsets.len(), list.len() + 1);
42 let adjusted_offsets = list_offsets.slice(0..list.len());
43
44 unsafe {
48 ListViewArray::new_unchecked(
49 list.elements().clone(),
50 adjusted_offsets,
51 sizes,
52 list.validity().clone(),
53 )
54 .with_zero_copy_to_list(true)
55 }
56}
57
58fn build_sizes_from_offsets<O: IntegerPType>(list: &ListArray) -> ArrayRef {
60 let len = list.len();
61 let mut sizes_builder = PrimitiveBuilder::<O>::with_capacity(Nullability::NonNullable, len);
62
63 let mut sizes_range = sizes_builder.uninit_range(len);
65
66 let offsets = list.offsets().to_primitive();
67 let offsets_slice = offsets.as_slice::<O>();
68 debug_assert_eq!(len + 1, offsets_slice.len());
69 debug_assert!(offsets_slice.is_sorted());
70
71 for i in 0..len {
73 let size = offsets_slice[i + 1] - offsets_slice[i];
74 sizes_range.set_value(i, size);
75 }
76
77 unsafe {
79 sizes_range.finish();
80 }
81
82 sizes_builder.finish_into_primitive().into_array()
83}
84
85pub fn list_from_list_view(list_view: ListViewArray) -> ListArray {
95 if list_view.is_zero_copy_to_list() {
97 let list_offsets = match_each_integer_ptype!(list_view.offsets().dtype().as_ptype(), |O| {
98 unsafe { build_list_offsets_from_list_view::<O>(&list_view) }
101 });
102
103 let new_array = unsafe {
106 ListArray::new_unchecked(
107 list_view.elements().clone(),
108 list_offsets,
109 list_view.validity().clone(),
110 )
111 };
112
113 let new_array = new_array
114 .reset_offsets(false)
115 .vortex_expect("TODO(connor)[ListView]: This can't fail");
116
117 return new_array;
118 }
119
120 let elements_dtype = list_view
121 .dtype()
122 .as_list_element_opt()
123 .vortex_expect("`DType` of `ListView` was somehow not a `List`");
124 let nullability = list_view.dtype().nullability();
125 let len = list_view.len();
126
127 match_each_integer_ptype!(list_view.offsets().dtype().as_ptype(), |O| {
128 let mut builder = ListBuilder::<O>::with_capacity(
129 elements_dtype.clone(),
130 nullability,
131 list_view.elements().len(),
132 len,
133 );
134
135 for i in 0..len {
136 builder
137 .append_scalar(&list_view.scalar_at(i))
138 .vortex_expect(
139 "The `ListView` scalars are `ListScalar`, which the `ListBuilder` must accept",
140 )
141 }
142
143 builder.finish_into_list()
144 })
145}
146
147unsafe fn build_list_offsets_from_list_view<O: IntegerPType>(
157 list_view: &ListViewArray,
158) -> ArrayRef {
159 let len = list_view.len();
160 let mut offsets_builder =
161 PrimitiveBuilder::<O>::with_capacity(Nullability::NonNullable, len + 1);
162
163 let mut offsets_range = offsets_builder.uninit_range(len + 1);
165
166 let offsets = list_view.offsets().to_primitive();
167 let offsets_slice = offsets.as_slice::<O>();
168
169 offsets_range.copy_from_slice(0, offsets_slice);
171
172 let final_offset = if len != 0 {
174 let last_offset = offsets_slice[len - 1];
175
176 let last_size = list_view.size_at(len - 1);
177 let last_size =
178 O::from_usize(last_size).vortex_expect("size somehow did not fit into offsets");
179
180 last_offset + last_size
181 } else {
182 O::zero()
183 };
184
185 offsets_range.set_value(len, final_offset);
186
187 unsafe {
189 offsets_range.finish();
190 }
191
192 offsets_builder.finish_into_primitive().into_array()
193}
194
195pub fn recursive_list_from_list_view(array: ArrayRef) -> ArrayRef {
199 if !array.dtype().is_nested() {
200 return array;
201 }
202
203 let canonical = array.to_canonical();
204
205 match canonical {
206 Canonical::List(listview) => {
207 let converted_elements = recursive_list_from_list_view(listview.elements().clone());
208 debug_assert_eq!(converted_elements.len(), listview.elements().len());
209
210 let listview_with_converted_elements =
212 if !Arc::ptr_eq(&converted_elements, listview.elements()) {
213 unsafe {
216 ListViewArray::new_unchecked(
217 converted_elements,
218 listview.offsets().clone(),
219 listview.sizes().clone(),
220 listview.validity().clone(),
221 )
222 .with_zero_copy_to_list(listview.is_zero_copy_to_list())
223 }
224 } else {
225 listview
226 };
227
228 let list_array = list_from_list_view(listview_with_converted_elements);
230 list_array.into_array()
231 }
232 Canonical::FixedSizeList(fixed_size_list) => {
233 let converted_elements =
234 recursive_list_from_list_view(fixed_size_list.elements().clone());
235
236 if !Arc::ptr_eq(&converted_elements, fixed_size_list.elements()) {
238 FixedSizeListArray::try_new(
239 converted_elements,
240 fixed_size_list.list_size(),
241 fixed_size_list.validity().clone(),
242 fixed_size_list.len(),
243 )
244 .vortex_expect(
245 "FixedSizeListArray reconstruction should not fail with valid components",
246 )
247 .into_array()
248 } else {
249 fixed_size_list.into_array()
250 }
251 }
252 Canonical::Struct(struct_array) => {
253 let fields = struct_array.fields();
254 let mut converted_fields = Vec::with_capacity(fields.len());
255 let mut any_changed = false;
256
257 for field in fields.iter() {
258 let converted_field = recursive_list_from_list_view(field.clone());
259 any_changed |= !Arc::ptr_eq(&converted_field, field);
261 converted_fields.push(converted_field);
262 }
263
264 if any_changed {
265 StructArray::try_new(
266 struct_array.names().clone(),
267 converted_fields,
268 struct_array.len(),
269 struct_array.validity().clone(),
270 )
271 .vortex_expect("StructArray reconstruction should not fail with valid components")
272 .into_array()
273 } else {
274 struct_array.into_array()
275 }
276 }
277 Canonical::Extension(ext_array) => {
278 let converted_storage = recursive_list_from_list_view(ext_array.storage().clone());
279
280 if !Arc::ptr_eq(&converted_storage, ext_array.storage()) {
282 ExtensionArray::new(ext_array.ext_dtype().clone(), converted_storage).into_array()
283 } else {
284 ext_array.into_array()
285 }
286 }
287 _ => unreachable!(),
288 }
289}
290
291#[cfg(test)]
292mod tests {
293 use std::sync::Arc;
294
295 use vortex_buffer::buffer;
296 use vortex_dtype::FieldNames;
297
298 use super::super::tests::common::{
299 create_basic_listview, create_empty_lists_listview, create_nullable_listview,
300 create_overlapping_listview,
301 };
302 use super::recursive_list_from_list_view;
303 use crate::arrays::{
304 BoolArray, FixedSizeListArray, ListArray, ListViewArray, PrimitiveArray, StructArray,
305 list_from_list_view, list_view_from_list,
306 };
307 use crate::validity::Validity;
308 use crate::vtable::ValidityHelper;
309 use crate::{IntoArray, assert_arrays_eq};
310
311 #[test]
312 fn test_list_to_listview_basic() {
313 let elements = buffer![0i32, 1, 2, 3, 4, 5, 6, 7, 8, 9].into_array();
315 let offsets = buffer![0u32, 3, 5, 7, 10].into_array();
316 let list_array =
317 ListArray::try_new(elements.clone(), offsets.clone(), Validity::NonNullable).unwrap();
318
319 let list_view = list_view_from_list(list_array.clone());
320
321 assert_eq!(list_view.len(), 4);
323 assert_arrays_eq!(elements, list_view.elements().clone());
324
325 let expected_offsets = buffer![0u32, 3, 5, 7].into_array();
327 assert_arrays_eq!(expected_offsets, list_view.offsets().clone());
328
329 let expected_sizes = buffer![3u32, 2, 2, 3].into_array();
331 assert_arrays_eq!(expected_sizes, list_view.sizes().clone());
332
333 assert_arrays_eq!(list_array, list_view);
335 }
336
337 #[test]
338 fn test_listview_to_list_zero_copy() {
339 let list_view = create_basic_listview();
340 let list_array = list_from_list_view(list_view.clone());
341
342 assert_arrays_eq!(list_view.elements().clone(), list_array.elements().clone());
344
345 let list_array_offsets_without_last = list_array.offsets().slice(0..list_view.len());
348 assert_arrays_eq!(list_view.offsets().clone(), list_array_offsets_without_last);
349
350 assert_arrays_eq!(list_view, list_array);
352 }
353
354 #[test]
355 fn test_empty_array_conversions() {
356 let empty_elements = PrimitiveArray::from_iter::<[i32; 0]>([]).into_array();
358 let empty_offsets = buffer![0u32].into_array();
359 let empty_list =
360 ListArray::try_new(empty_elements.clone(), empty_offsets, Validity::NonNullable)
361 .unwrap();
362
363 let empty_list_view = list_view_from_list(empty_list.clone());
366 assert_eq!(empty_list_view.len(), 0);
367
368 let converted_back = list_from_list_view(empty_list_view);
370 assert_eq!(converted_back.len(), 0);
371 assert_eq!(empty_list.len(), converted_back.len());
374 }
375
376 #[test]
377 fn test_nullable_conversions() {
378 let elements = buffer![10i32, 20, 30, 40, 50].into_array();
380 let offsets = buffer![0u32, 2, 4, 5].into_array();
381 let validity = Validity::Array(BoolArray::from_iter(vec![true, false, true]).into_array());
382 let nullable_list =
383 ListArray::try_new(elements.clone(), offsets.clone(), validity.clone()).unwrap();
384
385 let nullable_list_view = list_view_from_list(nullable_list.clone());
386
387 assert_eq!(nullable_list_view.validity(), &validity);
389 assert_eq!(nullable_list_view.len(), 3);
390
391 let converted_back = list_from_list_view(nullable_list_view);
393 assert_arrays_eq!(nullable_list, converted_back);
394 }
395
396 #[test]
397 fn test_non_zero_copy_listview_to_list() {
398 let list_view = create_overlapping_listview();
400 let list_array = list_from_list_view(list_view.clone());
401
402 for i in 0..list_array.len() {
404 let start = list_array.offset_at(i);
405 let end = list_array.offset_at(i + 1);
406 assert!(end >= start, "Offsets should be monotonic after conversion");
407 }
408
409 assert_arrays_eq!(list_view, list_array);
411 }
412
413 #[test]
414 fn test_empty_sublists() {
415 let empty_lists_view = create_empty_lists_listview();
416
417 let list_array = list_from_list_view(empty_lists_view.clone());
419 assert_eq!(list_array.len(), 4);
420
421 for i in 0..list_array.len() {
423 assert_eq!(list_array.list_elements_at(i).len(), 0);
424 }
425
426 let converted_back = list_view_from_list(list_array);
428 assert_arrays_eq!(empty_lists_view, converted_back);
429 }
430
431 #[test]
432 fn test_different_offset_types() {
433 let elements = buffer![1i32, 2, 3, 4, 5].into_array();
435 let i32_offsets = buffer![0i32, 2, 5].into_array();
436 let list_i32 =
437 ListArray::try_new(elements.clone(), i32_offsets.clone(), Validity::NonNullable)
438 .unwrap();
439
440 let list_view_i32 = list_view_from_list(list_i32.clone());
441 assert_eq!(list_view_i32.offsets().dtype(), i32_offsets.dtype());
442 assert_eq!(list_view_i32.sizes().dtype(), i32_offsets.dtype());
443
444 let i64_offsets = buffer![0i64, 2, 5].into_array();
446 let list_i64 =
447 ListArray::try_new(elements.clone(), i64_offsets.clone(), Validity::NonNullable)
448 .unwrap();
449
450 let list_view_i64 = list_view_from_list(list_i64.clone());
451 assert_eq!(list_view_i64.offsets().dtype(), i64_offsets.dtype());
452 assert_eq!(list_view_i64.sizes().dtype(), i64_offsets.dtype());
453
454 assert_arrays_eq!(list_i32, list_view_i32);
456 assert_arrays_eq!(list_i64, list_view_i64);
457 }
458
459 #[test]
460 fn test_round_trip_conversions() {
461 let original = create_basic_listview();
463 let to_list = list_from_list_view(original.clone());
464 let back_to_view = list_view_from_list(to_list);
465 assert_arrays_eq!(original, back_to_view);
466
467 let nullable = create_nullable_listview();
469 let nullable_to_list = list_from_list_view(nullable.clone());
470 let nullable_back = list_view_from_list(nullable_to_list);
471 assert_arrays_eq!(nullable, nullable_back);
472
473 let overlapping = create_overlapping_listview();
475
476 let overlapping_to_list = list_from_list_view(overlapping.clone());
477 let overlapping_back = list_view_from_list(overlapping_to_list);
478 assert_arrays_eq!(overlapping, overlapping_back);
479 }
480
481 #[test]
482 fn test_single_element_lists() {
483 let elements = buffer![100i32, 200, 300].into_array();
485 let offsets = buffer![0u32, 1, 2, 3].into_array();
486 let single_elem_list =
487 ListArray::try_new(elements.clone(), offsets, Validity::NonNullable).unwrap();
488
489 let list_view = list_view_from_list(single_elem_list.clone());
490 assert_eq!(list_view.len(), 3);
491
492 let expected_sizes = buffer![1u32, 1, 1].into_array();
494 assert_arrays_eq!(expected_sizes, list_view.sizes().clone());
495
496 let converted_back = list_from_list_view(list_view);
498 assert_arrays_eq!(single_elem_list, converted_back);
499 }
500
501 #[test]
502 fn test_mixed_empty_and_non_empty_lists() {
503 let elements = buffer![1i32, 2, 3, 4, 5, 6].into_array();
505 let offsets = buffer![0u32, 2, 2, 3, 3, 6].into_array();
506 let mixed_list =
507 ListArray::try_new(elements.clone(), offsets.clone(), Validity::NonNullable).unwrap();
508
509 let list_view = list_view_from_list(mixed_list.clone());
510 assert_eq!(list_view.len(), 5);
511
512 let expected_sizes = buffer![2u32, 0, 1, 0, 3].into_array();
514 assert_arrays_eq!(expected_sizes, list_view.sizes().clone());
515
516 let converted_back = list_from_list_view(list_view);
518 assert_arrays_eq!(mixed_list, converted_back);
519 }
520
521 #[test]
522 fn test_recursive_simple_listview() {
523 let list_view = create_basic_listview();
524 let result = recursive_list_from_list_view(list_view.clone().into_array());
525
526 assert_eq!(result.len(), list_view.len());
527 assert_arrays_eq!(list_view.into_array(), result);
528 }
529
530 #[test]
531 fn test_recursive_nested_listview() {
532 let inner_elements = buffer![1i32, 2, 3].into_array();
533 let inner_offsets = buffer![0u32, 2].into_array();
534 let inner_sizes = buffer![2u32, 1].into_array();
535 let inner_listview = unsafe {
536 ListViewArray::new_unchecked(
537 inner_elements,
538 inner_offsets,
539 inner_sizes,
540 Validity::NonNullable,
541 )
542 .with_zero_copy_to_list(true)
543 };
544
545 let outer_offsets = buffer![0u32, 1].into_array();
546 let outer_sizes = buffer![1u32, 1].into_array();
547 let outer_listview = unsafe {
548 ListViewArray::new_unchecked(
549 inner_listview.into_array(),
550 outer_offsets,
551 outer_sizes,
552 Validity::NonNullable,
553 )
554 .with_zero_copy_to_list(true)
555 };
556
557 let result = recursive_list_from_list_view(outer_listview.clone().into_array());
558
559 assert_eq!(result.len(), 2);
560 assert_arrays_eq!(outer_listview.into_array(), result);
561 }
562
563 #[test]
564 fn test_recursive_struct_with_listview_fields() {
565 let listview_field = create_basic_listview().into_array();
566 let primitive_field = buffer![10i32, 20, 30, 40].into_array();
567
568 let struct_array = StructArray::try_new(
569 FieldNames::from(["lists", "values"]),
570 vec![listview_field, primitive_field],
571 4,
572 Validity::NonNullable,
573 )
574 .unwrap();
575
576 let result = recursive_list_from_list_view(struct_array.clone().into_array());
577
578 assert_eq!(result.len(), 4);
579 assert_arrays_eq!(struct_array.into_array(), result);
580 }
581
582 #[test]
583 fn test_recursive_fixed_size_list_with_listview_elements() {
584 let lv1_elements = buffer![1i32, 2].into_array();
585 let lv1_offsets = buffer![0u32].into_array();
586 let lv1_sizes = buffer![2u32].into_array();
587 let lv1 = unsafe {
588 ListViewArray::new_unchecked(
589 lv1_elements,
590 lv1_offsets,
591 lv1_sizes,
592 Validity::NonNullable,
593 )
594 .with_zero_copy_to_list(true)
595 };
596
597 let lv2_elements = buffer![3i32, 4].into_array();
598 let lv2_offsets = buffer![0u32].into_array();
599 let lv2_sizes = buffer![2u32].into_array();
600 let lv2 = unsafe {
601 ListViewArray::new_unchecked(
602 lv2_elements,
603 lv2_offsets,
604 lv2_sizes,
605 Validity::NonNullable,
606 )
607 .with_zero_copy_to_list(true)
608 };
609
610 let dtype = lv1.dtype().clone();
611 let chunked_listviews =
612 crate::arrays::ChunkedArray::try_new(vec![lv1.into_array(), lv2.into_array()], dtype)
613 .unwrap();
614
615 let fixed_list =
616 FixedSizeListArray::new(chunked_listviews.into_array(), 1, Validity::NonNullable, 2);
617
618 let result = recursive_list_from_list_view(fixed_list.clone().into_array());
619
620 assert_eq!(result.len(), 2);
621 assert_arrays_eq!(fixed_list.into_array(), result);
622 }
623
624 #[test]
625 fn test_recursive_deep_nesting() {
626 let innermost_elements = buffer![1i32, 2, 3].into_array();
627 let innermost_offsets = buffer![0u32, 2].into_array();
628 let innermost_sizes = buffer![2u32, 1].into_array();
629 let innermost_listview = unsafe {
630 ListViewArray::new_unchecked(
631 innermost_elements,
632 innermost_offsets,
633 innermost_sizes,
634 Validity::NonNullable,
635 )
636 .with_zero_copy_to_list(true)
637 };
638
639 let struct_array = StructArray::try_new(
640 FieldNames::from(["inner_lists"]),
641 vec![innermost_listview.into_array()],
642 2,
643 Validity::NonNullable,
644 )
645 .unwrap();
646
647 let outer_offsets = buffer![0u32, 1].into_array();
648 let outer_sizes = buffer![1u32, 1].into_array();
649 let outer_listview = unsafe {
650 ListViewArray::new_unchecked(
651 struct_array.into_array(),
652 outer_offsets,
653 outer_sizes,
654 Validity::NonNullable,
655 )
656 .with_zero_copy_to_list(true)
657 };
658
659 let result = recursive_list_from_list_view(outer_listview.clone().into_array());
660
661 assert_eq!(result.len(), 2);
662 assert_arrays_eq!(outer_listview.into_array(), result);
663 }
664
665 #[test]
666 fn test_recursive_primitive_unchanged() {
667 let prim = buffer![1i32, 2, 3].into_array();
668 let prim_clone = prim.clone();
669 let result = recursive_list_from_list_view(prim);
670
671 assert!(Arc::ptr_eq(&result, &prim_clone));
672 }
673
674 #[test]
675 fn test_recursive_mixed_listview_and_list() {
676 let listview = create_basic_listview();
677 let list = list_from_list_view(listview.clone());
678
679 let struct_array = StructArray::try_new(
680 FieldNames::from(["listview_field", "list_field"]),
681 vec![listview.into_array(), list.into_array()],
682 4,
683 Validity::NonNullable,
684 )
685 .unwrap();
686
687 let result = recursive_list_from_list_view(struct_array.clone().into_array());
688
689 assert_eq!(result.len(), 4);
690 assert_arrays_eq!(struct_array.into_array(), result);
691 }
692}