1use num_traits::FromPrimitive;
5use vortex_buffer::BufferMut;
6use vortex_error::VortexExpect;
7use vortex_error::VortexResult;
8use vortex_error::vortex_err;
9use vortex_mask::Mask;
10
11use crate::ExecutionCtx;
12use crate::IntoArray;
13use crate::arrays::ConstantArray;
14use crate::arrays::ListViewArray;
15use crate::arrays::PiecewiseSequenceArray;
16use crate::arrays::PrimitiveArray;
17use crate::arrays::listview::ListViewArrayExt;
18use crate::arrays::primitive::PrimitiveArrayExt;
19use crate::builtins::ArrayBuiltins;
20use crate::dtype::IntegerPType;
21use crate::dtype::Nullability;
22use crate::dtype::PType;
23use crate::match_each_integer_ptype;
24use crate::match_each_unsigned_integer_ptype;
25use crate::scalar::Scalar;
26use crate::scalar_fn::fns::operators::Operator;
27use crate::validity::Validity;
28
29fn rebuilt_offset_ptype(offsets_ptype: PType) -> PType {
35 match offsets_ptype {
36 PType::U8 | PType::U16 | PType::U32 => PType::U32,
37 PType::U64 => PType::U64,
38 PType::I8 | PType::I16 | PType::I32 => PType::I32,
39 PType::I64 => PType::I64,
40 _ => unreachable!("invalid offsets PType"),
41 }
42}
43
44pub const DEFAULT_REBUILD_DENSITY_THRESHOLD: f32 = 0.1;
53
54pub const DEFAULT_TRIM_ELEMENTS_THRESHOLD: f32 = 0.05;
63
64pub enum ListViewRebuildMode {
66 MakeZeroCopyToList,
74
75 TrimElements,
81
82 MakeExact,
89
90 OverlapCompression,
97}
98
99impl ListViewArray {
100 pub fn rebuild(
102 &self,
103 mode: ListViewRebuildMode,
104 ctx: &mut ExecutionCtx,
105 ) -> VortexResult<ListViewArray> {
106 if self.is_empty() {
107 return Ok(unsafe { self.clone().with_zero_copy_to_list(true) });
109 }
110
111 match mode {
112 ListViewRebuildMode::MakeZeroCopyToList => self.rebuild_zero_copy_to_list(ctx),
113 ListViewRebuildMode::TrimElements => self.rebuild_trim_elements(ctx),
114 ListViewRebuildMode::MakeExact => self.rebuild_make_exact(ctx),
115 ListViewRebuildMode::OverlapCompression => unimplemented!("Does P=NP?"),
116 }
117 }
118
119 fn rebuild_zero_copy_to_list(&self, ctx: &mut ExecutionCtx) -> VortexResult<ListViewArray> {
127 if self.is_zero_copy_to_list() {
128 return Ok(self.clone());
130 }
131
132 let offsets_ptype = self.offsets().dtype().as_ptype();
133 let sizes_ptype = self.sizes().dtype().as_ptype();
134
135 match_each_unsigned_integer_ptype!(sizes_ptype.to_unsigned(), |S| {
143 match offsets_ptype.to_unsigned() {
144 PType::U8 => self.naive_rebuild::<u8, u32, S>(ctx),
145 PType::U16 => self.naive_rebuild::<u16, u32, S>(ctx),
146 PType::U32 => self.naive_rebuild::<u32, u32, S>(ctx),
147 PType::U64 => self.naive_rebuild::<u64, u64, S>(ctx),
148 _ => unreachable!("invalid offsets PType"),
149 }
150 })
151 }
152
153 fn naive_rebuild<O: IntegerPType, NewOffset: IntegerPType, S: IntegerPType>(
156 &self,
157 ctx: &mut ExecutionCtx,
158 ) -> VortexResult<ListViewArray> {
159 let sizes_canonical = self.sizes().clone().execute::<PrimitiveArray>(ctx)?;
160 let sizes_canonical =
161 sizes_canonical.reinterpret_cast(sizes_canonical.ptype().to_unsigned());
162 let total: u64 = sizes_canonical
163 .as_slice::<S>()
164 .iter()
165 .map(|s| (*s).as_() as u64)
166 .sum();
167 if Self::should_use_take(total, self.len()) {
168 self.rebuild_with_take::<O, NewOffset, S>(ctx)
169 } else {
170 self.rebuild_with_piecewise::<NewOffset, S>(ctx)
171 }
172 }
173
174 fn should_use_take(total_output_elements: u64, num_lists: usize) -> bool {
182 if num_lists == 0 {
183 return true;
184 }
185 let avg = total_output_elements / num_lists as u64;
186 avg < 128
187 }
188
189 fn rebuild_with_take<O: IntegerPType, NewOffset: IntegerPType, S: IntegerPType>(
192 &self,
193 ctx: &mut ExecutionCtx,
194 ) -> VortexResult<ListViewArray> {
195 let new_offset_ptype = rebuilt_offset_ptype(self.offsets().dtype().as_ptype());
196 let size_ptype = self.sizes().dtype().as_ptype();
197
198 let offsets_canonical = self.offsets().clone().execute::<PrimitiveArray>(ctx)?;
199 let offsets_canonical =
200 offsets_canonical.reinterpret_cast(offsets_canonical.ptype().to_unsigned());
201 let offsets_slice = offsets_canonical.as_slice::<O>();
202 let sizes_canonical = self.sizes().clone().execute::<PrimitiveArray>(ctx)?;
203 let sizes_canonical =
204 sizes_canonical.reinterpret_cast(sizes_canonical.ptype().to_unsigned());
205 let sizes_slice = sizes_canonical.as_slice::<S>();
206
207 let len = offsets_slice.len();
208
209 let mut new_offsets = BufferMut::<NewOffset>::with_capacity(len);
210 let mut new_sizes = BufferMut::<S>::with_capacity(len);
211 let mut take_indices = BufferMut::<u64>::with_capacity(self.elements().len());
212
213 let validity = self.validity()?.execute_mask(len, ctx)?;
217
218 let mut n_elements = NewOffset::zero();
219 for index in 0..len {
220 if !validity.value(index) {
221 new_offsets.push(n_elements);
222 new_sizes.push(S::zero());
223 continue;
224 }
225
226 let offset = offsets_slice[index];
227 let size = sizes_slice[index];
228 let start = offset.as_();
229 let stop = start + size.as_();
230
231 new_offsets.push(n_elements);
232 new_sizes.push(size);
233 take_indices.extend(start as u64..stop as u64);
234 n_elements += num_traits::cast(size).vortex_expect("Cast failed");
235 }
236
237 let elements = self.elements().take(take_indices.into_array())?;
238 let offsets = PrimitiveArray::new(new_offsets.freeze(), Validity::NonNullable)
240 .reinterpret_cast(new_offset_ptype)
241 .into_array();
242 let sizes = PrimitiveArray::new(new_sizes.freeze(), Validity::NonNullable)
243 .reinterpret_cast(size_ptype)
244 .into_array();
245
246 Ok(unsafe {
249 ListViewArray::new_unchecked(elements, offsets, sizes, self.validity()?)
250 .with_zero_copy_to_list(true)
251 })
252 }
253
254 fn rebuild_with_piecewise<NewOffset: IntegerPType, S: IntegerPType>(
256 &self,
257 ctx: &mut ExecutionCtx,
258 ) -> VortexResult<ListViewArray> {
259 let new_offset_ptype = rebuilt_offset_ptype(self.offsets().dtype().as_ptype());
260 let size_ptype = self.sizes().dtype().as_ptype();
261
262 let offsets_canonical = self.offsets().clone().execute::<PrimitiveArray>(ctx)?;
263 let offsets_canonical =
264 offsets_canonical.reinterpret_cast(offsets_canonical.ptype().to_unsigned());
265 let sizes_canonical = self.sizes().clone().execute::<PrimitiveArray>(ctx)?;
266 let sizes_canonical =
267 sizes_canonical.reinterpret_cast(sizes_canonical.ptype().to_unsigned());
268
269 let len = offsets_canonical.len();
270 let validity = self.validity()?;
271 let validity_mask = validity.execute_mask(len, ctx)?;
272
273 let ranges = match_each_unsigned_integer_ptype!(offsets_canonical.ptype(), |O| {
274 rebuild_ranges::<NewOffset, O, S>(
275 offsets_canonical.as_slice::<O>(),
276 sizes_canonical.as_slice::<S>(),
277 &validity_mask,
278 )
279 })?;
280
281 let RebuildRanges {
282 new_offsets,
283 new_sizes,
284 starts,
285 lengths,
286 elements_len,
287 } = ranges;
288 let constant_length = lengths
289 .first()
290 .copied()
291 .filter(|first| lengths.iter().all(|length| *length == *first));
292 let lengths = match constant_length {
293 Some(length) => ConstantArray::new(length, starts.len()).into_array(),
294 None => lengths.into_array(),
295 };
296
297 let multipliers = ConstantArray::new(1u64, starts.len()).into_array();
300 let element_indices = unsafe {
301 PiecewiseSequenceArray::new_unchecked(
302 starts.into_array(),
303 lengths,
304 multipliers,
305 elements_len,
306 )
307 };
308 let elements = self.elements().take(element_indices.into_array())?;
309
310 let offsets = PrimitiveArray::new(new_offsets.freeze(), Validity::NonNullable)
312 .reinterpret_cast(new_offset_ptype)
313 .into_array();
314 let sizes = PrimitiveArray::new(new_sizes.freeze(), Validity::NonNullable)
315 .reinterpret_cast(size_ptype)
316 .into_array();
317
318 Ok(unsafe {
321 ListViewArray::new_unchecked(elements, offsets, sizes, validity)
322 .with_zero_copy_to_list(true)
323 })
324 }
325
326 fn rebuild_trim_elements(&self, ctx: &mut ExecutionCtx) -> VortexResult<ListViewArray> {
330 let (start, end) = self.referenced_element_bounds(ctx)?;
331
332 unsafe { self.trim_elements(start, end) }
334 }
335
336 pub unsafe fn trim_elements(&self, start: usize, end: usize) -> VortexResult<ListViewArray> {
345 let adjusted_offsets = match_each_integer_ptype!(self.offsets().dtype().as_ptype(), |O| {
346 let offset = <O as FromPrimitive>::from_usize(start)
347 .vortex_expect("unable to convert the min offset `start` into a `usize`");
348 let scalar = Scalar::primitive(offset, Nullability::NonNullable);
349
350 self.offsets()
351 .clone()
352 .binary(
353 ConstantArray::new(scalar, self.offsets().len()).into_array(),
354 Operator::Sub,
355 )
356 .vortex_expect("was somehow unable to adjust offsets down by their minimum")
357 });
358
359 let sliced_elements = self.elements().slice(start..end)?;
360
361 Ok(unsafe {
366 ListViewArray::new_unchecked(
367 sliced_elements,
368 adjusted_offsets,
369 self.sizes().clone(),
370 self.validity()?,
371 )
372 .with_zero_copy_to_list(self.is_zero_copy_to_list())
373 })
374 }
375
376 fn rebuild_make_exact(&self, ctx: &mut ExecutionCtx) -> VortexResult<ListViewArray> {
377 if self.is_zero_copy_to_list() {
378 self.rebuild_trim_elements(ctx)
379 } else {
380 self.rebuild_zero_copy_to_list(ctx)
383 }
384 }
385}
386
387struct RebuildRanges<NewOffset, S> {
388 new_offsets: BufferMut<NewOffset>,
389 new_sizes: BufferMut<S>,
390 starts: BufferMut<u64>,
391 lengths: BufferMut<u64>,
392 elements_len: usize,
393}
394
395fn rebuild_ranges<NewOffset, O, S>(
396 offsets: &[O],
397 sizes: &[S],
398 validity_mask: &Mask,
399) -> VortexResult<RebuildRanges<NewOffset, S>>
400where
401 NewOffset: IntegerPType,
402 O: IntegerPType,
403 S: IntegerPType,
404{
405 let len = offsets.len();
406 let mut new_offsets = BufferMut::<NewOffset>::with_capacity(len);
407 let mut new_sizes = BufferMut::<S>::with_capacity(len);
408 let mut starts = BufferMut::<u64>::with_capacity(len);
409 let mut lengths = BufferMut::<u64>::with_capacity(len);
410 let mut n_elements = NewOffset::zero();
411 let mut elements_len = 0usize;
412
413 for (index, is_valid) in validity_mask.iter().enumerate() {
414 if !is_valid {
415 new_offsets.push(n_elements);
416 new_sizes.push(S::zero());
417 starts.push(0);
418 lengths.push(0);
419 continue;
420 }
421
422 let size = sizes[index];
423 let start: usize = offsets[index].as_();
424 let length: usize = size.as_();
425 start.checked_add(length).ok_or_else(|| {
426 vortex_err!(
427 "ListView rebuild element range overflow for start {start} and length {length}"
428 )
429 })?;
430 elements_len = elements_len
431 .checked_add(length)
432 .ok_or_else(|| vortex_err!("ListView rebuild elements length overflow"))?;
433
434 new_offsets.push(n_elements);
435 new_sizes.push(size);
436 starts.push(start as u64);
437 lengths.push(length as u64);
438 n_elements += num_traits::cast(size).vortex_expect("Cast failed");
439 }
440
441 Ok(RebuildRanges {
442 new_offsets,
443 new_sizes,
444 starts,
445 lengths,
446 elements_len,
447 })
448}
449
450#[cfg(test)]
451mod tests {
452 use vortex_buffer::BitBuffer;
453 use vortex_error::VortexResult;
454
455 use super::super::tests::common::SESSION;
456 use super::ListViewRebuildMode;
457 use crate::IntoArray;
458 use crate::VortexSessionExecute;
459 use crate::arrays::ListViewArray;
460 use crate::arrays::PrimitiveArray;
461 use crate::arrays::listview::ListViewArrayExt;
462 use crate::assert_arrays_eq;
463 use crate::dtype::Nullability;
464 use crate::validity::Validity;
465
466 #[test]
467 fn test_rebuild_flatten_removes_overlaps() -> VortexResult<()> {
468 let elements = PrimitiveArray::from_iter(vec![1i32, 2, 3]).into_array();
472 let offsets = PrimitiveArray::from_iter(vec![0u32, 1]).into_array();
473 let sizes = PrimitiveArray::from_iter(vec![3u32, 2]).into_array();
474
475 let listview = ListViewArray::new(elements, offsets, sizes, Validity::NonNullable);
476
477 let mut ctx = SESSION.create_execution_ctx();
478 let flattened = listview.rebuild(ListViewRebuildMode::MakeZeroCopyToList, &mut ctx)?;
479
480 assert_eq!(flattened.elements().len(), 5);
483
484 assert_eq!(flattened.offset_at(0), 0);
486 assert_eq!(flattened.size_at(0), 3);
487 assert_eq!(flattened.offset_at(1), 3);
488 assert_eq!(flattened.size_at(1), 2);
489
490 assert_arrays_eq!(
492 flattened.list_elements_at(0)?,
493 PrimitiveArray::from_iter([1i32, 2, 3]),
494 &mut ctx
495 );
496
497 assert_arrays_eq!(
498 flattened.list_elements_at(1)?,
499 PrimitiveArray::from_iter([2i32, 3]),
500 &mut ctx
501 );
502 Ok(())
503 }
504
505 #[test]
506 fn test_rebuild_flatten_with_nullable() -> VortexResult<()> {
507 use crate::arrays::BoolArray;
508
509 let elements = PrimitiveArray::from_iter(vec![1i32, 2, 3]).into_array();
511 let offsets = PrimitiveArray::from_iter(vec![0u32, 1, 2]).into_array();
512 let sizes = PrimitiveArray::from_iter(vec![2u32, 1, 1]).into_array();
513 let validity = Validity::Array(
514 BoolArray::new(
515 BitBuffer::from(vec![true, false, true]),
516 Validity::NonNullable,
517 )
518 .into_array(),
519 );
520
521 let listview = ListViewArray::new(elements, offsets, sizes, validity);
522
523 let mut ctx = SESSION.create_execution_ctx();
524 let flattened = listview.rebuild(ListViewRebuildMode::MakeZeroCopyToList, &mut ctx)?;
525
526 assert_eq!(flattened.dtype().nullability(), Nullability::Nullable);
528 assert!(flattened.validity()?.execute_is_valid(0, &mut ctx)?);
529 assert!(!flattened.validity()?.execute_is_valid(1, &mut ctx)?);
530 assert!(flattened.validity()?.execute_is_valid(2, &mut ctx)?);
531
532 assert_arrays_eq!(
534 flattened.list_elements_at(0)?,
535 PrimitiveArray::from_iter([1i32, 2]),
536 &mut ctx
537 );
538
539 assert_arrays_eq!(
540 flattened.list_elements_at(2)?,
541 PrimitiveArray::from_iter([3i32]),
542 &mut ctx
543 );
544 Ok(())
545 }
546
547 #[test]
548 fn test_rebuild_flatten_null_row_uses_valid_empty_range() -> VortexResult<()> {
549 let elements = PrimitiveArray::from_iter(vec![1i32, 2, 3, 4]).into_array();
550 let offsets = PrimitiveArray::from_iter(vec![0u32, 1, 2]).into_array();
551 let sizes = PrimitiveArray::from_iter(vec![1u32, 2, 2]).into_array();
552 let validity = Validity::from_iter([true, false, true]);
553
554 let listview = unsafe { ListViewArray::new_unchecked(elements, offsets, sizes, validity) };
556
557 let mut ctx = SESSION.create_execution_ctx();
558 let flattened = listview.rebuild(ListViewRebuildMode::MakeZeroCopyToList, &mut ctx)?;
559
560 assert_eq!(flattened.offset_at(0), 0);
561 assert_eq!(flattened.size_at(0), 1);
562 assert_eq!(flattened.offset_at(1), 1);
563 assert_eq!(flattened.size_at(1), 0);
564 assert_eq!(flattened.offset_at(2), 1);
565 assert_eq!(flattened.size_at(2), 2);
566 assert!(flattened.validity()?.execute_is_valid(0, &mut ctx)?);
567 assert!(!flattened.validity()?.execute_is_valid(1, &mut ctx)?);
568 assert!(flattened.validity()?.execute_is_valid(2, &mut ctx)?);
569
570 assert_arrays_eq!(
571 flattened.list_elements_at(0)?,
572 PrimitiveArray::from_iter([1i32]),
573 &mut ctx
574 );
575 assert_arrays_eq!(
576 flattened.list_elements_at(2)?,
577 PrimitiveArray::from_iter([3i32, 4]),
578 &mut ctx
579 );
580 Ok(())
581 }
582
583 #[test]
584 fn test_rebuild_trim_elements_basic() -> VortexResult<()> {
585 let elements =
593 PrimitiveArray::from_iter(vec![99i32, 98, 1, 2, 97, 3, 4, 96, 95]).into_array();
594 let offsets = PrimitiveArray::from_iter(vec![2u32, 5]).into_array();
595 let sizes = PrimitiveArray::from_iter(vec![2u32, 2]).into_array();
596
597 let listview = ListViewArray::new(elements, offsets, sizes, Validity::NonNullable);
598
599 let mut ctx = SESSION.create_execution_ctx();
600 let trimmed = listview.rebuild(ListViewRebuildMode::TrimElements, &mut ctx)?;
601
602 assert_eq!(trimmed.elements().len(), 5);
604
605 assert_eq!(trimmed.offset_at(0), 0);
607 assert_eq!(trimmed.size_at(0), 2);
608 assert_eq!(trimmed.offset_at(1), 3);
609 assert_eq!(trimmed.size_at(1), 2);
610
611 assert_arrays_eq!(
613 trimmed.list_elements_at(0)?,
614 PrimitiveArray::from_iter([1i32, 2]),
615 &mut ctx
616 );
617
618 assert_arrays_eq!(
619 trimmed.list_elements_at(1)?,
620 PrimitiveArray::from_iter([3i32, 4]),
621 &mut ctx
622 );
623
624 let all_elements = trimmed
626 .elements()
627 .clone()
628 .execute::<PrimitiveArray>(&mut ctx)?;
629 assert_eq!(all_elements.execute_scalar(2, &mut ctx)?, 97i32.into());
630 Ok(())
631 }
632
633 #[test]
634 fn test_rebuild_flatten_large_lists_with_piecewise_indices() -> VortexResult<()> {
635 let elements = PrimitiveArray::from_iter(0i32..300).into_array();
636 let offsets = PrimitiveArray::from_iter(vec![10u32, 0]).into_array();
637 let sizes = PrimitiveArray::from_iter(vec![150u32, 130]).into_array();
638 let listview = ListViewArray::new(elements, offsets, sizes, Validity::NonNullable);
639
640 let mut ctx = SESSION.create_execution_ctx();
641 let flattened = listview.rebuild(ListViewRebuildMode::MakeZeroCopyToList, &mut ctx)?;
642
643 assert_eq!(flattened.elements().len(), 280);
644 assert_eq!(flattened.offset_at(0), 0);
645 assert_eq!(flattened.size_at(0), 150);
646 assert_eq!(flattened.offset_at(1), 150);
647 assert_eq!(flattened.size_at(1), 130);
648
649 assert_arrays_eq!(
650 flattened.list_elements_at(0)?,
651 PrimitiveArray::from_iter(10i32..160),
652 &mut ctx
653 );
654 assert_arrays_eq!(
655 flattened.list_elements_at(1)?,
656 PrimitiveArray::from_iter(0i32..130),
657 &mut ctx
658 );
659 Ok(())
660 }
661
662 #[test]
663 fn test_rebuild_with_trailing_nulls_regression() -> VortexResult<()> {
664 let elements = PrimitiveArray::from_iter(vec![1i32, 2, 3, 4]).into_array();
670 let offsets = PrimitiveArray::from_iter(vec![0u32, 2, 0, 0]).into_array();
671 let sizes = PrimitiveArray::from_iter(vec![2u32, 2, 0, 0]).into_array();
672 let validity = Validity::from_iter(vec![true, true, false, false]);
673
674 let listview = ListViewArray::new(elements, offsets, sizes, validity);
675
676 let mut ctx = SESSION.create_execution_ctx();
678 let rebuilt = listview.rebuild(ListViewRebuildMode::MakeZeroCopyToList, &mut ctx)?;
679 assert!(rebuilt.is_zero_copy_to_list());
680
681 assert_eq!(rebuilt.offset_at(0), 0);
684 assert_eq!(rebuilt.offset_at(1), 2);
685 assert_eq!(rebuilt.offset_at(2), 4); assert_eq!(rebuilt.offset_at(3), 4); assert_eq!(rebuilt.size_at(0), 2);
690 assert_eq!(rebuilt.size_at(1), 2);
691 assert_eq!(rebuilt.size_at(2), 0); assert_eq!(rebuilt.size_at(3), 0); let exact = rebuilt.rebuild(ListViewRebuildMode::MakeExact, &mut ctx)?;
697
698 assert!(exact.is_valid(0, &mut ctx)?);
700 assert!(exact.is_valid(1, &mut ctx)?);
701 assert!(!exact.is_valid(2, &mut ctx)?);
702 assert!(!exact.is_valid(3, &mut ctx)?);
703
704 assert_arrays_eq!(
706 exact.list_elements_at(0)?,
707 PrimitiveArray::from_iter([1i32, 2]),
708 &mut ctx
709 );
710
711 assert_arrays_eq!(
712 exact.list_elements_at(1)?,
713 PrimitiveArray::from_iter([3i32, 4]),
714 &mut ctx
715 );
716 Ok(())
717 }
718
719 #[test]
722 fn test_rebuild_trim_elements_offsets_wider_than_sizes() -> VortexResult<()> {
723 let mut elems = vec![0i32; 70_005];
724 elems[70_000] = 10;
725 elems[70_001] = 20;
726 elems[70_002] = 30;
727 elems[70_003] = 40;
728 let elements = PrimitiveArray::from_iter(elems).into_array();
729 let offsets = PrimitiveArray::from_iter(vec![70_000u32, 70_002]).into_array();
730 let sizes = PrimitiveArray::from_iter(vec![2u16, 2]).into_array();
731
732 let listview = ListViewArray::new(elements, offsets, sizes, Validity::NonNullable);
733 let mut ctx = SESSION.create_execution_ctx();
734 let trimmed = listview.rebuild(ListViewRebuildMode::TrimElements, &mut ctx)?;
735 assert_arrays_eq!(
736 trimmed.list_elements_at(1)?,
737 PrimitiveArray::from_iter([30i32, 40]),
738 &mut ctx
739 );
740 Ok(())
741 }
742
743 #[test]
746 fn test_rebuild_trim_elements_sizes_wider_than_offsets() -> VortexResult<()> {
747 let mut elems = vec![0i32; 70_001];
748 elems[3] = 30;
749 elems[4] = 40;
750 let elements = PrimitiveArray::from_iter(elems).into_array();
751 let offsets = PrimitiveArray::from_iter(vec![1u16, 3]).into_array();
752 let sizes = PrimitiveArray::from_iter(vec![70_000u32, 2]).into_array();
753
754 let listview = ListViewArray::new(elements, offsets, sizes, Validity::NonNullable);
755 let mut ctx = SESSION.create_execution_ctx();
756 let trimmed = listview.rebuild(ListViewRebuildMode::TrimElements, &mut ctx)?;
757 assert_arrays_eq!(
758 trimmed.list_elements_at(1)?,
759 PrimitiveArray::from_iter([30i32, 40]),
760 &mut ctx
761 );
762 Ok(())
763 }
764
765 #[test]
768 fn test_rebuild_preserves_signed_offset_and_size_types() -> VortexResult<()> {
769 use crate::dtype::PType;
770
771 let elements = PrimitiveArray::from_iter(vec![1i32, 2, 3]).into_array();
773 let offsets = PrimitiveArray::from_iter(vec![0i32, 1]).into_array();
774 let sizes = PrimitiveArray::from_iter(vec![3i16, 2]).into_array();
775
776 let listview = ListViewArray::new(elements, offsets, sizes, Validity::NonNullable);
777 let mut ctx = SESSION.create_execution_ctx();
778 let rebuilt = listview.rebuild(ListViewRebuildMode::MakeZeroCopyToList, &mut ctx)?;
779
780 assert_arrays_eq!(
782 rebuilt.list_elements_at(0)?,
783 PrimitiveArray::from_iter([1i32, 2, 3]),
784 &mut ctx
785 );
786 assert_arrays_eq!(
787 rebuilt.list_elements_at(1)?,
788 PrimitiveArray::from_iter([2i32, 3]),
789 &mut ctx
790 );
791
792 assert_eq!(rebuilt.offsets().dtype().as_ptype(), PType::I32);
794 assert_eq!(rebuilt.sizes().dtype().as_ptype(), PType::I16);
795 Ok(())
796 }
797
798 #[test]
801 fn heuristic_zero_lists_uses_take() {
802 assert!(ListViewArray::should_use_take(0, 0));
803 }
804
805 #[test]
806 fn heuristic_small_lists_use_take() {
807 assert!(ListViewArray::should_use_take(127_000, 1_000));
809 assert!(!ListViewArray::should_use_take(128_000, 1_000));
811 }
812
813 #[test]
816 fn test_rebuild_trim_elements_sum_overflows_type() -> VortexResult<()> {
817 let elements = PrimitiveArray::from_iter(vec![0i32; 261]).into_array();
818 let offsets = PrimitiveArray::from_iter(vec![215u8, 0]).into_array();
819 let sizes = PrimitiveArray::from_iter(vec![46u8, 10]).into_array();
820
821 let listview = ListViewArray::new(elements, offsets, sizes, Validity::NonNullable);
822 let mut ctx = SESSION.create_execution_ctx();
823 let trimmed = listview.rebuild(ListViewRebuildMode::TrimElements, &mut ctx)?;
824
825 assert_arrays_eq!(trimmed, listview, &mut ctx);
827 Ok(())
828 }
829}