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