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