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