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