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