1use vortex_error::VortexExpect;
5use vortex_error::VortexResult;
6
7use crate::ArrayRef;
8use crate::Canonical;
9use crate::ExecutionCtx;
10use crate::IntoArray;
11#[expect(deprecated)]
12use crate::ToCanonical as _;
13use crate::arrays::ExtensionArray;
14use crate::arrays::FixedSizeListArray;
15use crate::arrays::ListArray;
16use crate::arrays::ListViewArray;
17use crate::arrays::PrimitiveArray;
18use crate::arrays::StructArray;
19use crate::arrays::extension::ExtensionArrayExt;
20use crate::arrays::fixed_size_list::FixedSizeListArrayExt;
21use crate::arrays::list::ListArrayExt;
22use crate::arrays::listview::ListViewArrayExt;
23use crate::arrays::listview::ListViewRebuildMode;
24use crate::arrays::struct_::StructArrayExt;
25use crate::builders::PrimitiveBuilder;
26use crate::dtype::IntegerPType;
27use crate::dtype::Nullability;
28use crate::match_each_integer_ptype;
29
30pub fn list_view_from_list(list: ListArray, ctx: &mut ExecutionCtx) -> VortexResult<ListViewArray> {
35 if list.is_empty() {
38 return Ok(Canonical::empty(list.dtype()).into_listview());
39 }
40
41 let list = list.reset_offsets(false).vortex_expect("This can't fail");
45
46 let list_offsets = list.offsets().clone();
47
48 let sizes = match_each_integer_ptype!(list_offsets.dtype().as_ptype(), |O| {
51 build_sizes_from_offsets::<O>(&list, ctx)?
52 });
53
54 debug_assert_eq!(list_offsets.len(), list.len() + 1);
56 let adjusted_offsets = list_offsets.slice(0..list.len())?;
57
58 Ok(unsafe {
62 ListViewArray::new_unchecked(
63 list.elements().clone(),
64 adjusted_offsets,
65 sizes,
66 list.validity()?,
67 )
68 .with_zero_copy_to_list(true)
69 })
70}
71
72fn build_sizes_from_offsets<O: IntegerPType>(
74 list: &ListArray,
75 ctx: &mut ExecutionCtx,
76) -> VortexResult<ArrayRef> {
77 let len = list.len();
78 let mut sizes_builder = PrimitiveBuilder::<O>::with_capacity(Nullability::NonNullable, len);
79
80 let mut sizes_range = sizes_builder.uninit_range(len);
82
83 let offsets = list.offsets().clone().execute::<PrimitiveArray>(ctx)?;
84 let offsets_slice = offsets.as_slice::<O>();
85 debug_assert_eq!(len + 1, offsets_slice.len());
86 debug_assert!(offsets_slice.is_sorted());
87
88 for i in 0..len {
90 let size = offsets_slice[i + 1] - offsets_slice[i];
91 sizes_range.set_value(i, size);
92 }
93
94 unsafe {
96 sizes_range.finish();
97 }
98
99 Ok(sizes_builder.finish_into_primitive().into_array())
100}
101
102pub fn list_from_list_view(list_view: ListViewArray) -> VortexResult<ListArray> {
112 let zctl_array = list_view.rebuild(ListViewRebuildMode::MakeExact)?;
114 debug_assert!(zctl_array.is_zero_copy_to_list());
115
116 let list_offsets = match_each_integer_ptype!(zctl_array.offsets().dtype().as_ptype(), |O| {
117 unsafe { build_list_offsets_from_list_view::<O>(&zctl_array) }
120 });
121
122 Ok(unsafe {
126 ListArray::new_unchecked(
127 zctl_array.elements().clone(),
128 list_offsets,
129 zctl_array.validity()?,
130 )
131 })
132}
133
134unsafe fn build_list_offsets_from_list_view<O: IntegerPType>(
144 list_view: &ListViewArray,
145) -> ArrayRef {
146 let len = list_view.len();
147 let mut offsets_builder =
148 PrimitiveBuilder::<O>::with_capacity(Nullability::NonNullable, len + 1);
149
150 let mut offsets_range = offsets_builder.uninit_range(len + 1);
152
153 #[expect(deprecated)]
154 let offsets = list_view.offsets().to_primitive();
155 let offsets_slice = offsets.as_slice::<O>();
156 debug_assert!(offsets_slice.is_sorted());
157
158 offsets_range.copy_from_slice(0, offsets_slice);
160
161 let final_offset = if len != 0 {
163 let last_offset = offsets_slice[len - 1];
164
165 let last_size = list_view.size_at(len - 1);
166 let last_size =
167 O::from_usize(last_size).vortex_expect("size somehow did not fit into offsets");
168
169 last_offset + last_size
170 } else {
171 O::zero()
172 };
173
174 offsets_range.set_value(len, final_offset);
175
176 unsafe {
178 offsets_range.finish();
179 }
180
181 offsets_builder.finish_into_primitive().into_array()
182}
183
184pub fn recursive_list_from_list_view(array: ArrayRef) -> VortexResult<ArrayRef> {
188 if !array.dtype().is_nested() {
189 return Ok(array);
190 }
191
192 #[expect(deprecated)]
193 let canonical = array.to_canonical()?;
194
195 Ok(match canonical {
196 Canonical::List(listview) => {
197 let converted_elements = recursive_list_from_list_view(listview.elements().clone())?;
198 debug_assert_eq!(converted_elements.len(), listview.elements().len());
199
200 let listview_with_converted_elements =
202 if !ArrayRef::ptr_eq(&converted_elements, listview.elements()) {
203 unsafe {
206 ListViewArray::new_unchecked(
207 converted_elements,
208 listview.offsets().clone(),
209 listview.sizes().clone(),
210 listview.validity()?,
211 )
212 .with_zero_copy_to_list(listview.is_zero_copy_to_list())
213 }
214 } else {
215 listview
216 };
217
218 let list_array = list_from_list_view(listview_with_converted_elements)?;
220 list_array.into_array()
221 }
222 Canonical::FixedSizeList(fixed_size_list) => {
223 let converted_elements =
224 recursive_list_from_list_view(fixed_size_list.elements().clone())?;
225
226 if !ArrayRef::ptr_eq(&converted_elements, fixed_size_list.elements()) {
228 FixedSizeListArray::try_new(
229 converted_elements,
230 fixed_size_list.list_size(),
231 fixed_size_list.validity()?,
232 fixed_size_list.len(),
233 )
234 .vortex_expect(
235 "FixedSizeListArray reconstruction should not fail with valid components",
236 )
237 .into_array()
238 } else {
239 fixed_size_list.into_array()
240 }
241 }
242 Canonical::Struct(struct_array) => {
243 let fields = struct_array.unmasked_fields();
244 let mut converted_fields = Vec::with_capacity(fields.len());
245 let mut any_changed = false;
246
247 for field in fields.iter() {
248 let converted_field = recursive_list_from_list_view(field.clone())?;
249 any_changed |= !ArrayRef::ptr_eq(&converted_field, field);
251 converted_fields.push(converted_field);
252 }
253
254 if any_changed {
255 StructArray::try_new(
256 struct_array.names().clone(),
257 converted_fields,
258 struct_array.len(),
259 struct_array.validity()?,
260 )
261 .vortex_expect("StructArray reconstruction should not fail with valid components")
262 .into_array()
263 } else {
264 struct_array.into_array()
265 }
266 }
267 Canonical::Extension(ext_array) => {
268 let converted_storage =
269 recursive_list_from_list_view(ext_array.storage_array().clone())?;
270
271 if !ArrayRef::ptr_eq(&converted_storage, ext_array.storage_array()) {
273 ExtensionArray::new(ext_array.ext_dtype().clone(), converted_storage).into_array()
274 } else {
275 ext_array.into_array()
276 }
277 }
278 _ => unreachable!(),
279 })
280}
281
282#[cfg(test)]
283mod tests {
284
285 use vortex_buffer::buffer;
286 use vortex_error::VortexExpect;
287 use vortex_error::VortexResult;
288
289 use super::super::tests::common::create_basic_listview;
290 use super::super::tests::common::create_empty_lists_listview;
291 use super::super::tests::common::create_nullable_listview;
292 use super::super::tests::common::create_overlapping_listview;
293 use super::recursive_list_from_list_view;
294 use crate::ArrayEq;
295 use crate::ArrayRef;
296 use crate::IntoArray;
297 use crate::LEGACY_SESSION;
298 use crate::Precision;
299 use crate::VortexSessionExecute;
300 use crate::arrays::BoolArray;
301 use crate::arrays::FixedSizeListArray;
302 use crate::arrays::ListArray;
303 use crate::arrays::ListViewArray;
304 use crate::arrays::PrimitiveArray;
305 use crate::arrays::StructArray;
306 use crate::arrays::VarBinViewArray;
307 use crate::arrays::list::ListArrayExt;
308 use crate::arrays::listview::ListViewArrayExt;
309 use crate::arrays::listview::list_from_list_view;
310 use crate::arrays::listview::list_view_from_list;
311 use crate::assert_arrays_eq;
312 use crate::dtype::FieldNames;
313 use crate::validity::Validity;
314
315 #[test]
316 fn test_list_to_listview_basic() -> VortexResult<()> {
317 let elements = buffer![0i32, 1, 2, 3, 4, 5, 6, 7, 8, 9].into_array();
319 let offsets = buffer![0u32, 3, 5, 7, 10].into_array();
320 let list_array = ListArray::try_new(elements.clone(), offsets, Validity::NonNullable)?;
321
322 let mut ctx = LEGACY_SESSION.create_execution_ctx();
323 let list_view = list_view_from_list(list_array.clone(), &mut ctx)?;
324
325 assert_eq!(list_view.len(), 4);
327 assert_arrays_eq!(elements, list_view.elements().clone());
328
329 let expected_offsets = buffer![0u32, 3, 5, 7].into_array();
331 assert_arrays_eq!(expected_offsets, list_view.offsets().clone());
332
333 let expected_sizes = buffer![3u32, 2, 2, 3].into_array();
335 assert_arrays_eq!(expected_sizes, list_view.sizes().clone());
336
337 assert_arrays_eq!(list_array, list_view);
339 Ok(())
340 }
341
342 #[test]
343 fn test_listview_to_list_zero_copy() -> VortexResult<()> {
344 let list_view = create_basic_listview();
345 let list_array = list_from_list_view(list_view.clone())?;
346
347 assert_arrays_eq!(list_view.elements().clone(), list_array.elements().clone());
349
350 let list_array_offsets_without_last = list_array.offsets().slice(0..list_view.len())?;
353 assert_arrays_eq!(list_view.offsets().clone(), list_array_offsets_without_last);
354
355 assert_arrays_eq!(list_view, list_array);
357 Ok(())
358 }
359
360 #[test]
361 fn test_empty_array_conversions() -> VortexResult<()> {
362 let empty_elements = PrimitiveArray::from_iter::<[i32; 0]>([]).into_array();
364 let empty_offsets = buffer![0u32].into_array();
365 let empty_list = ListArray::try_new(empty_elements, empty_offsets, Validity::NonNullable)?;
366
367 let mut ctx = LEGACY_SESSION.create_execution_ctx();
370 let empty_list_view = list_view_from_list(empty_list.clone(), &mut ctx)?;
371 assert_eq!(empty_list_view.len(), 0);
372
373 let converted_back = list_from_list_view(empty_list_view)?;
375 assert_eq!(converted_back.len(), 0);
376 assert_eq!(empty_list.len(), converted_back.len());
379 Ok(())
380 }
381
382 #[test]
383 fn test_nullable_conversions() -> VortexResult<()> {
384 let elements = buffer![10i32, 20, 30, 40, 50].into_array();
386 let offsets = buffer![0u32, 2, 4, 5].into_array();
387 let validity = Validity::Array(BoolArray::from_iter(vec![true, false, true]).into_array());
388 let nullable_list = ListArray::try_new(elements, offsets, validity.clone())?;
389
390 let mut ctx = LEGACY_SESSION.create_execution_ctx();
391 let nullable_list_view = list_view_from_list(nullable_list.clone(), &mut ctx)?;
392
393 assert!(
395 nullable_list_view
396 .validity()
397 .vortex_expect("listview validity should be derivable")
398 .array_eq(&validity, Precision::Ptr)
399 );
400 assert_eq!(nullable_list_view.len(), 3);
401
402 let converted_back = list_from_list_view(nullable_list_view)?;
404 assert_arrays_eq!(nullable_list, converted_back);
405 Ok(())
406 }
407
408 #[test]
409 fn test_non_zero_copy_listview_to_list() -> VortexResult<()> {
410 let list_view = create_overlapping_listview();
412 let list_array = list_from_list_view(list_view.clone())?;
413
414 for i in 0..list_array.len() {
416 let start = list_array.offset_at(i)?;
417 let end = list_array.offset_at(i + 1)?;
418 assert!(end >= start, "Offsets should be monotonic after conversion");
419 }
420
421 assert_arrays_eq!(list_view, list_array);
423 Ok(())
424 }
425
426 #[test]
427 fn test_empty_sublists() -> VortexResult<()> {
428 let empty_lists_view = create_empty_lists_listview();
429
430 let list_array = list_from_list_view(empty_lists_view.clone())?;
432 assert_eq!(list_array.len(), 4);
433
434 for i in 0..list_array.len() {
436 assert_eq!(list_array.list_elements_at(i)?.len(), 0);
437 }
438
439 let mut ctx = LEGACY_SESSION.create_execution_ctx();
441 let converted_back = list_view_from_list(list_array, &mut ctx)?;
442 assert_arrays_eq!(empty_lists_view, converted_back);
443 Ok(())
444 }
445
446 #[test]
447 fn test_different_offset_types() -> VortexResult<()> {
448 let elements = buffer![1i32, 2, 3, 4, 5].into_array();
450 let i32_offsets = buffer![0i32, 2, 5].into_array();
451 let list_i32 =
452 ListArray::try_new(elements.clone(), i32_offsets.clone(), Validity::NonNullable)?;
453
454 let mut ctx = LEGACY_SESSION.create_execution_ctx();
455 let list_view_i32 = list_view_from_list(list_i32.clone(), &mut ctx)?;
456 assert_eq!(list_view_i32.offsets().dtype(), i32_offsets.dtype());
457 assert_eq!(list_view_i32.sizes().dtype(), i32_offsets.dtype());
458
459 let i64_offsets = buffer![0i64, 2, 5].into_array();
461 let list_i64 = ListArray::try_new(elements, i64_offsets.clone(), Validity::NonNullable)?;
462
463 let list_view_i64 = list_view_from_list(list_i64.clone(), &mut ctx)?;
464 assert_eq!(list_view_i64.offsets().dtype(), i64_offsets.dtype());
465 assert_eq!(list_view_i64.sizes().dtype(), i64_offsets.dtype());
466
467 assert_arrays_eq!(list_i32, list_view_i32);
469 assert_arrays_eq!(list_i64, list_view_i64);
470 Ok(())
471 }
472
473 #[test]
474 fn test_round_trip_conversions() -> VortexResult<()> {
475 let mut ctx = LEGACY_SESSION.create_execution_ctx();
476
477 let original = create_basic_listview();
479 let to_list = list_from_list_view(original.clone())?;
480 let back_to_view = list_view_from_list(to_list, &mut ctx)?;
481 assert_arrays_eq!(original, back_to_view);
482
483 let nullable = create_nullable_listview();
485 let nullable_to_list = list_from_list_view(nullable.clone())?;
486 let nullable_back = list_view_from_list(nullable_to_list, &mut ctx)?;
487 assert_arrays_eq!(nullable, nullable_back);
488
489 let overlapping = create_overlapping_listview();
491
492 let overlapping_to_list = list_from_list_view(overlapping.clone())?;
493 let overlapping_back = list_view_from_list(overlapping_to_list, &mut ctx)?;
494 assert_arrays_eq!(overlapping, overlapping_back);
495 Ok(())
496 }
497
498 #[test]
499 fn test_single_element_lists() -> VortexResult<()> {
500 let elements = buffer![100i32, 200, 300].into_array();
502 let offsets = buffer![0u32, 1, 2, 3].into_array();
503 let single_elem_list = ListArray::try_new(elements, offsets, Validity::NonNullable)?;
504
505 let mut ctx = LEGACY_SESSION.create_execution_ctx();
506 let list_view = list_view_from_list(single_elem_list.clone(), &mut ctx)?;
507 assert_eq!(list_view.len(), 3);
508
509 let expected_sizes = buffer![1u32, 1, 1].into_array();
511 assert_arrays_eq!(expected_sizes, list_view.sizes().clone());
512
513 let converted_back = list_from_list_view(list_view)?;
515 assert_arrays_eq!(single_elem_list, converted_back);
516 Ok(())
517 }
518
519 #[test]
520 fn test_mixed_empty_and_non_empty_lists() -> VortexResult<()> {
521 let elements = buffer![1i32, 2, 3, 4, 5, 6].into_array();
523 let offsets = buffer![0u32, 2, 2, 3, 3, 6].into_array();
524 let mixed_list = ListArray::try_new(elements, offsets, Validity::NonNullable)?;
525
526 let mut ctx = LEGACY_SESSION.create_execution_ctx();
527 let list_view = list_view_from_list(mixed_list.clone(), &mut ctx)?;
528 assert_eq!(list_view.len(), 5);
529
530 let expected_sizes = buffer![2u32, 0, 1, 0, 3].into_array();
532 assert_arrays_eq!(expected_sizes, list_view.sizes().clone());
533
534 let converted_back = list_from_list_view(list_view)?;
536 assert_arrays_eq!(mixed_list, converted_back);
537 Ok(())
538 }
539
540 #[test]
541 fn test_recursive_simple_listview() -> VortexResult<()> {
542 let list_view = create_basic_listview();
543 let result = recursive_list_from_list_view(list_view.clone().into_array())?;
544
545 assert_eq!(result.len(), list_view.len());
546 assert_arrays_eq!(list_view.into_array(), result);
547 Ok(())
548 }
549
550 #[test]
551 fn test_recursive_nested_listview() -> VortexResult<()> {
552 let inner_elements = buffer![1i32, 2, 3].into_array();
553 let inner_offsets = buffer![0u32, 2].into_array();
554 let inner_sizes = buffer![2u32, 1].into_array();
555 let inner_listview = unsafe {
556 ListViewArray::new_unchecked(
557 inner_elements,
558 inner_offsets,
559 inner_sizes,
560 Validity::NonNullable,
561 )
562 .with_zero_copy_to_list(true)
563 };
564
565 let outer_offsets = buffer![0u32, 1].into_array();
566 let outer_sizes = buffer![1u32, 1].into_array();
567 let outer_listview = unsafe {
568 ListViewArray::new_unchecked(
569 inner_listview.into_array(),
570 outer_offsets,
571 outer_sizes,
572 Validity::NonNullable,
573 )
574 .with_zero_copy_to_list(true)
575 };
576
577 let result = recursive_list_from_list_view(outer_listview.clone().into_array())?;
578
579 assert_eq!(result.len(), 2);
580 assert_arrays_eq!(outer_listview.into_array(), result);
581 Ok(())
582 }
583
584 #[test]
585 fn test_recursive_struct_with_listview_fields() -> VortexResult<()> {
586 let listview_field = create_basic_listview().into_array();
587 let primitive_field = buffer![10i32, 20, 30, 40].into_array();
588
589 let struct_array = StructArray::try_new(
590 FieldNames::from(["lists", "values"]),
591 vec![listview_field, primitive_field],
592 4,
593 Validity::NonNullable,
594 )?;
595
596 let result = recursive_list_from_list_view(struct_array.clone().into_array())?;
597
598 assert_eq!(result.len(), 4);
599 assert_arrays_eq!(struct_array.into_array(), result);
600 Ok(())
601 }
602
603 #[test]
604 fn test_recursive_fixed_size_list_with_listview_elements() -> VortexResult<()> {
605 let lv1_elements = buffer![1i32, 2].into_array();
606 let lv1_offsets = buffer![0u32].into_array();
607 let lv1_sizes = buffer![2u32].into_array();
608 let lv1 = unsafe {
609 ListViewArray::new_unchecked(
610 lv1_elements,
611 lv1_offsets,
612 lv1_sizes,
613 Validity::NonNullable,
614 )
615 .with_zero_copy_to_list(true)
616 };
617
618 let lv2_elements = buffer![3i32, 4].into_array();
619 let lv2_offsets = buffer![0u32].into_array();
620 let lv2_sizes = buffer![2u32].into_array();
621 let lv2 = unsafe {
622 ListViewArray::new_unchecked(
623 lv2_elements,
624 lv2_offsets,
625 lv2_sizes,
626 Validity::NonNullable,
627 )
628 .with_zero_copy_to_list(true)
629 };
630
631 let dtype = lv1.dtype().clone();
632 let chunked_listviews =
633 crate::arrays::ChunkedArray::try_new(vec![lv1.into_array(), lv2.into_array()], dtype)?;
634
635 let fixed_list =
636 FixedSizeListArray::new(chunked_listviews.into_array(), 1, Validity::NonNullable, 2);
637
638 let result = recursive_list_from_list_view(fixed_list.clone().into_array())?;
639
640 assert_eq!(result.len(), 2);
641 assert_arrays_eq!(fixed_list.into_array(), result);
642 Ok(())
643 }
644
645 #[test]
646 fn test_recursive_deep_nesting() -> VortexResult<()> {
647 let innermost_elements = buffer![1i32, 2, 3].into_array();
648 let innermost_offsets = buffer![0u32, 2].into_array();
649 let innermost_sizes = buffer![2u32, 1].into_array();
650 let innermost_listview = unsafe {
651 ListViewArray::new_unchecked(
652 innermost_elements,
653 innermost_offsets,
654 innermost_sizes,
655 Validity::NonNullable,
656 )
657 .with_zero_copy_to_list(true)
658 };
659
660 let struct_array = StructArray::try_new(
661 FieldNames::from(["inner_lists"]),
662 vec![innermost_listview.into_array()],
663 2,
664 Validity::NonNullable,
665 )?;
666
667 let outer_offsets = buffer![0u32, 1].into_array();
668 let outer_sizes = buffer![1u32, 1].into_array();
669 let outer_listview = unsafe {
670 ListViewArray::new_unchecked(
671 struct_array.into_array(),
672 outer_offsets,
673 outer_sizes,
674 Validity::NonNullable,
675 )
676 .with_zero_copy_to_list(true)
677 };
678
679 let result = recursive_list_from_list_view(outer_listview.clone().into_array())?;
680
681 assert_eq!(result.len(), 2);
682 assert_arrays_eq!(outer_listview.into_array(), result);
683 Ok(())
684 }
685
686 #[test]
687 fn test_recursive_primitive_unchanged() -> VortexResult<()> {
688 let prim = buffer![1i32, 2, 3].into_array();
689 let prim_clone = prim.clone();
690 let result = recursive_list_from_list_view(prim)?;
691
692 assert!(ArrayRef::ptr_eq(&result, &prim_clone));
693 Ok(())
694 }
695
696 #[test]
697 fn test_recursive_mixed_listview_and_list() -> VortexResult<()> {
698 let listview = create_basic_listview();
699 let list = list_from_list_view(listview.clone())?;
700
701 let struct_array = StructArray::try_new(
702 FieldNames::from(["listview_field", "list_field"]),
703 vec![listview.into_array(), list.into_array()],
704 4,
705 Validity::NonNullable,
706 )?;
707
708 let result = recursive_list_from_list_view(struct_array.clone().into_array())?;
709
710 assert_eq!(result.len(), 4);
711 assert_arrays_eq!(struct_array.into_array(), result);
712 Ok(())
713 }
714
715 #[test]
721 fn test_empty_listview_to_list_without_zctl_flag() -> VortexResult<()> {
722 let elements = VarBinViewArray::from_iter_str(Vec::<&str>::new()).into_array();
723 let offsets = PrimitiveArray::from_iter(Vec::<i16>::new()).into_array();
724 let sizes = PrimitiveArray::from_iter(Vec::<i16>::new()).into_array();
725 let list_view = ListViewArray::try_new(elements, offsets, sizes, Validity::AllValid)?;
726
727 assert!(!list_view.is_zero_copy_to_list());
729
730 let list_array = list_from_list_view(list_view)?;
731 assert_eq!(list_array.len(), 0);
732 Ok(())
733 }
734}