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