1use std::fmt::Debug;
5use std::hash::Hash;
6use std::ops::Range;
7
8use num_traits::NumCast;
9use vortex_buffer::BitBuffer;
10use vortex_buffer::BitBufferMut;
11use vortex_buffer::BufferAllocatorRef;
12use vortex_buffer::BufferMut;
13use vortex_error::VortexError;
14use vortex_error::VortexExpect as _;
15use vortex_error::VortexResult;
16use vortex_error::vortex_bail;
17use vortex_error::vortex_ensure;
18use vortex_error::vortex_ensure_eq;
19use vortex_error::vortex_err;
20use vortex_mask::AllOr;
21use vortex_mask::Mask;
22use vortex_mask::MaskValues;
23use vortex_utils::aliases::hash_map::HashMap;
24
25use crate::ArrayRef;
26use crate::ArraySlots;
27use crate::ExecutionCtx;
28use crate::IntoArray;
29use crate::arrays::Primitive;
30use crate::arrays::PrimitiveArray;
31use crate::arrays::primitive::PrimitiveArrayExt;
32use crate::builtins::ArrayBuiltins;
33use crate::dtype::DType;
34use crate::dtype::IntegerPType;
35use crate::dtype::NativePType;
36use crate::dtype::Nullability;
37use crate::dtype::Nullability::NonNullable;
38use crate::dtype::PType;
39use crate::dtype::UnsignedPType;
40use crate::match_each_unsigned_integer_ptype;
41use crate::scalar::Scalar;
42use crate::search_sorted::SearchResult;
43use crate::search_sorted::SearchSorted;
44use crate::search_sorted::SearchSortedPrimitiveArray;
45use crate::search_sorted::SearchSortedSide;
46use crate::validity::Validity;
47
48pub const PATCH_CHUNK_SIZE: usize = 1024;
51
52#[derive(Copy, Clone, prost::Message)]
53pub struct PatchesMetadata {
54 #[prost(uint64, tag = "1")]
55 len: u64,
56 #[prost(uint64, tag = "2")]
57 offset: u64,
58 #[prost(enumeration = "PType", tag = "3")]
59 indices_ptype: i32,
60 #[prost(uint64, optional, tag = "4")]
61 chunk_offsets_len: Option<u64>,
62 #[prost(enumeration = "PType", optional, tag = "5")]
63 chunk_offsets_ptype: Option<i32>,
64 #[prost(uint64, optional, tag = "6")]
65 offset_within_chunk: Option<u64>,
66}
67
68impl PatchesMetadata {
69 #[inline]
70 pub fn new(
71 len: usize,
72 offset: usize,
73 indices_ptype: PType,
74 chunk_offsets_len: Option<usize>,
75 chunk_offsets_ptype: Option<PType>,
76 offset_within_chunk: Option<usize>,
77 ) -> Self {
78 Self {
79 len: len as u64,
80 offset: offset as u64,
81 indices_ptype: indices_ptype as i32,
82 chunk_offsets_len: chunk_offsets_len.map(|len| len as u64),
83 chunk_offsets_ptype: chunk_offsets_ptype.map(|pt| pt as i32),
84 offset_within_chunk: offset_within_chunk.map(|len| len as u64),
85 }
86 }
87
88 #[inline]
89 pub fn len(&self) -> VortexResult<usize> {
90 usize::try_from(self.len).map_err(|_| vortex_err!("len does not fit in usize"))
91 }
92
93 #[inline]
94 pub fn is_empty(&self) -> bool {
95 self.len == 0
96 }
97
98 #[inline]
99 pub fn offset(&self) -> VortexResult<usize> {
100 usize::try_from(self.offset).map_err(|_| vortex_err!("offset does not fit in usize"))
101 }
102
103 #[inline]
104 pub fn chunk_offsets_dtype(&self) -> VortexResult<Option<DType>> {
105 self.chunk_offsets_ptype
106 .map(|t| {
107 PType::try_from(t)
108 .map_err(|e| vortex_err!("invalid i32 value {t} for PType: {}", e))
109 .map(|ptype| DType::Primitive(ptype, NonNullable))
110 })
111 .transpose()
112 }
113
114 #[inline]
115 pub fn indices_dtype(&self) -> VortexResult<DType> {
116 let ptype = PType::try_from(self.indices_ptype).map_err(|e| {
117 vortex_err!("invalid i32 value {} for PType: {}", self.indices_ptype, e)
118 })?;
119 vortex_ensure!(
120 ptype.is_unsigned_int(),
121 "Patch indices must be unsigned integers"
122 );
123 Ok(DType::Primitive(ptype, NonNullable))
124 }
125}
126
127#[derive(Clone, Debug, Hash, PartialEq, Eq)]
132pub struct PatchesData {
133 offset: usize,
134 offset_within_chunk: Option<usize>,
135}
136
137#[derive(Copy, Clone, Debug)]
139pub struct PatchSlotIndices {
140 pub indices: usize,
141 pub values: usize,
142 pub chunk_offsets: usize,
143}
144
145impl PatchesData {
146 pub fn from_patches(patches: &Patches) -> Self {
148 Self {
149 offset: patches.offset(),
150 offset_within_chunk: patches.offset_within_chunk(),
151 }
152 }
153
154 pub fn patches_from_slots(
159 patches_data: Option<&Self>,
160 len: usize,
161 slots: &[Option<ArrayRef>],
162 slot_idx: PatchSlotIndices,
163 ) -> Option<Patches> {
164 let data = patches_data?;
165 let indices = slots[slot_idx.indices]
166 .as_ref()
167 .vortex_expect("patches_data is set but patch_indices slot is missing");
168 let values = slots[slot_idx.values]
169 .as_ref()
170 .vortex_expect("patches_data is set but patch_values slot is missing");
171 Some(unsafe {
172 Patches::new_unchecked(
173 len,
174 data.offset,
175 indices.clone(),
176 values.clone(),
177 slots[slot_idx.chunk_offsets].clone(),
178 data.offset_within_chunk,
179 )
180 })
181 }
182
183 pub fn push_slots(slots: &mut ArraySlots, patches: Option<&Patches>) {
187 match patches {
188 Some(p) => {
189 slots.push(Some(p.indices().clone()));
190 slots.push(Some(p.values().clone()));
191 slots.push(p.chunk_offsets().clone());
192 }
193 None => {
194 slots.push(None);
195 slots.push(None);
196 slots.push(None);
197 }
198 }
199 }
200
201 #[inline]
203 pub fn offset(&self) -> usize {
204 self.offset
205 }
206
207 #[inline]
209 pub fn offset_within_chunk(&self) -> Option<usize> {
210 self.offset_within_chunk
211 }
212}
213
214#[derive(Debug, Clone)]
216pub struct Patches {
217 array_len: usize,
218 offset: usize,
219 indices: ArrayRef,
220 values: ArrayRef,
221 chunk_offsets: Option<ArrayRef>,
228 offset_within_chunk: Option<usize>,
238}
239
240impl Patches {
241 pub fn new(
242 array_len: usize,
243 offset: usize,
244 indices: ArrayRef,
245 values: ArrayRef,
246 chunk_offsets: Option<ArrayRef>,
247 ctx: &mut ExecutionCtx,
248 ) -> VortexResult<Self> {
249 vortex_ensure_eq!(indices.len(), values.len());
250 vortex_ensure!(
251 indices.dtype().is_unsigned_int() && !indices.dtype().is_nullable(),
252 "Patch indices must be non-nullable unsigned integers, got {:?}",
253 indices.dtype()
254 );
255
256 vortex_ensure!(
257 indices.len() <= array_len,
258 "Patch indices must be shorter than the array length"
259 );
260 vortex_ensure!(!indices.is_empty(), "Patch indices must not be empty");
261
262 if indices.is_host() && values.is_host() {
265 let max = usize::try_from(&indices.execute_scalar(indices.len() - 1, ctx)?)
266 .map_err(|_| vortex_err!("indices must be a number"))?;
267 vortex_ensure!(
268 max - offset < array_len,
269 "Patch indices {max:?}, offset {offset} are longer than the array length {array_len}"
270 );
271
272 #[cfg(debug_assertions)]
273 {
274 use crate::aggregate_fn::fns::is_sorted::is_sorted;
275 assert!(
276 is_sorted(&indices, ctx).unwrap_or(false),
277 "Patch indices must be sorted"
278 );
279 }
280 }
281
282 Ok(Self {
283 array_len,
284 offset,
285 indices,
286 values,
287 chunk_offsets: chunk_offsets.clone(),
288 offset_within_chunk: chunk_offsets.map(|_| 0),
290 })
291 }
292
293 pub unsafe fn new_unchecked(
303 array_len: usize,
304 offset: usize,
305 indices: ArrayRef,
306 values: ArrayRef,
307 chunk_offsets: Option<ArrayRef>,
308 offset_within_chunk: Option<usize>,
309 ) -> Self {
310 Self {
311 array_len,
312 offset,
313 indices,
314 values,
315 chunk_offsets,
316 offset_within_chunk,
317 }
318 }
319
320 #[inline]
321 pub fn array_len(&self) -> usize {
322 self.array_len
323 }
324
325 #[inline]
326 pub fn num_patches(&self) -> usize {
327 self.indices.len()
328 }
329
330 #[inline]
331 pub fn dtype(&self) -> &DType {
332 self.values.dtype()
333 }
334
335 #[inline]
336 pub fn indices(&self) -> &ArrayRef {
337 &self.indices
338 }
339
340 #[inline]
341 pub fn into_indices(self) -> ArrayRef {
342 self.indices
343 }
344
345 #[inline]
346 pub fn indices_mut(&mut self) -> &mut ArrayRef {
347 &mut self.indices
348 }
349
350 #[inline]
351 pub fn values(&self) -> &ArrayRef {
352 &self.values
353 }
354
355 #[inline]
356 pub fn into_values(self) -> ArrayRef {
357 self.values
358 }
359
360 #[inline]
361 pub fn values_mut(&mut self) -> &mut ArrayRef {
362 &mut self.values
363 }
364
365 #[inline]
366 pub fn offset(&self) -> usize {
368 self.offset
369 }
370
371 #[inline]
372 pub fn chunk_offsets(&self) -> &Option<ArrayRef> {
373 &self.chunk_offsets
374 }
375
376 #[inline]
377 pub fn chunk_offset_at(&self, idx: usize, ctx: &mut ExecutionCtx) -> VortexResult<usize> {
378 let Some(chunk_offsets) = &self.chunk_offsets else {
379 vortex_bail!("chunk_offsets must be set to retrieve offset at index")
380 };
381
382 chunk_offsets
383 .execute_scalar(idx, ctx)?
384 .as_primitive()
385 .as_::<usize>()
386 .ok_or_else(|| vortex_err!("chunk offset does not fit in usize"))
387 }
388
389 #[inline]
398 pub fn offset_within_chunk(&self) -> Option<usize> {
399 self.offset_within_chunk
400 }
401
402 #[inline]
403 pub fn indices_ptype(&self) -> VortexResult<PType> {
404 PType::try_from(self.indices.dtype())
405 .map_err(|_| vortex_err!("indices dtype is not primitive"))
406 }
407
408 pub fn to_metadata(&self, len: usize, dtype: &DType) -> VortexResult<PatchesMetadata> {
409 if self.indices.len() > len {
410 vortex_bail!(
411 "Patch indices {} are longer than the array length {}",
412 self.indices.len(),
413 len
414 );
415 }
416 if self.values.dtype() != dtype {
417 vortex_bail!(
418 "Patch values dtype {} does not match array dtype {}",
419 self.values.dtype(),
420 dtype
421 );
422 }
423 let chunk_offsets_len = self.chunk_offsets.as_ref().map(|co| co.len());
424 let chunk_offsets_ptype = self.chunk_offsets.as_ref().map(|co| co.dtype().as_ptype());
425
426 Ok(PatchesMetadata::new(
427 self.indices.len(),
428 self.offset,
429 self.indices.dtype().as_ptype(),
430 chunk_offsets_len,
431 chunk_offsets_ptype,
432 self.offset_within_chunk,
433 ))
434 }
435
436 pub fn get_patched(
438 &self,
439 index: usize,
440 ctx: &mut ExecutionCtx,
441 ) -> VortexResult<Option<Scalar>> {
442 self.search_index(index, ctx)?
443 .to_found()
444 .map(|patch_idx| self.values().execute_scalar(patch_idx, ctx))
445 .transpose()
446 }
447
448 pub fn search_index(&self, index: usize, ctx: &mut ExecutionCtx) -> VortexResult<SearchResult> {
466 if self.chunk_offsets.is_some() {
467 return self.search_index_chunked(index, ctx);
468 }
469
470 search_index_binary_search(&self.indices, index + self.offset, ctx)
471 }
472
473 fn search_index_chunked(
483 &self,
484 index: usize,
485 ctx: &mut ExecutionCtx,
486 ) -> VortexResult<SearchResult> {
487 let Some(chunk_offsets) = &self.chunk_offsets else {
488 vortex_bail!("chunk_offsets is required to be set")
489 };
490
491 let Some(offset_within_chunk) = self.offset_within_chunk else {
492 vortex_bail!("offset_within_chunk is required to be set")
493 };
494
495 if index >= self.array_len() {
496 return Ok(SearchResult::NotFound(self.indices().len()));
497 }
498
499 let chunk_idx = (index + self.offset % PATCH_CHUNK_SIZE) / PATCH_CHUNK_SIZE;
500
501 let mut probe = chunk_offsets.repeated_probe();
504 let mut chunk_offset_at = |idx: usize| -> VortexResult<usize> {
505 probe
506 .execute_scalar(idx, ctx)?
507 .as_primitive()
508 .as_::<usize>()
509 .ok_or_else(|| vortex_err!("chunk offset does not fit in usize"))
510 };
511
512 let base_offset = chunk_offset_at(0)?;
514
515 let patches_start_idx = (chunk_offset_at(chunk_idx)? - base_offset)
516 .saturating_sub(offset_within_chunk);
523
524 let patches_end_idx = if chunk_idx < chunk_offsets.len() - 1 {
525 (chunk_offset_at(chunk_idx + 1)? - base_offset)
526 .saturating_sub(offset_within_chunk)
527 .min(self.indices.len())
528 } else {
529 self.indices.len()
530 };
531
532 let chunk_indices = self.indices.slice(patches_start_idx..patches_end_idx)?;
533 let result = search_index_binary_search(&chunk_indices, index + self.offset, ctx)?;
534
535 Ok(match result {
536 SearchResult::Found(idx) => SearchResult::Found(patches_start_idx + idx),
537 SearchResult::NotFound(idx) => SearchResult::NotFound(patches_start_idx + idx),
538 })
539 }
540
541 fn search_index_chunked_batch<T, O>(
547 &self,
548 indices: &[T],
549 chunk_offsets: &[O],
550 index: T,
551 ) -> VortexResult<SearchResult>
552 where
553 T: UnsignedPType,
554 O: UnsignedPType,
555 usize: TryFrom<T>,
556 usize: TryFrom<O>,
557 {
558 let Some(offset_within_chunk) = self.offset_within_chunk else {
559 vortex_bail!("offset_within_chunk is required to be set")
560 };
561
562 let chunk_idx = {
563 let Ok(index) = usize::try_from(index) else {
564 return Ok(SearchResult::NotFound(indices.len()));
566 };
567
568 if index >= self.array_len() {
569 return Ok(SearchResult::NotFound(self.indices().len()));
570 }
571
572 (index + self.offset % PATCH_CHUNK_SIZE) / PATCH_CHUNK_SIZE
573 };
574
575 let chunk_offset = usize::try_from(chunk_offsets[chunk_idx] - chunk_offsets[0])
577 .map_err(|_| vortex_err!("chunk_offset failed to convert to usize"))?;
578
579 let patches_start_idx = chunk_offset
580 .saturating_sub(offset_within_chunk);
587
588 let patches_end_idx = if chunk_idx < chunk_offsets.len() - 1 {
589 usize::try_from(chunk_offsets[chunk_idx + 1] - chunk_offsets[0])
590 .map_err(|_| vortex_err!("patches_end_idx failed to convert to usize"))?
591 .saturating_sub(offset_within_chunk)
592 .min(indices.len())
593 } else {
594 self.indices.len()
595 };
596
597 let Some(offset) = T::from(self.offset) else {
598 return Ok(SearchResult::NotFound(indices.len()));
600 };
601
602 let chunk_indices = &indices[patches_start_idx..patches_end_idx];
603 let result = chunk_indices.search_sorted(&(index + offset), SearchSortedSide::Left)?;
604
605 Ok(match result {
606 SearchResult::Found(idx) => SearchResult::Found(patches_start_idx + idx),
607 SearchResult::NotFound(idx) => SearchResult::NotFound(patches_start_idx + idx),
608 })
609 }
610
611 pub fn min_index(&self, ctx: &mut ExecutionCtx) -> VortexResult<usize> {
613 let first = self
614 .indices
615 .execute_scalar(0, ctx)?
616 .as_primitive()
617 .as_::<usize>()
618 .ok_or_else(|| vortex_err!("index does not fit in usize"))?;
619 Ok(first - self.offset)
620 }
621
622 pub fn max_index(&self, ctx: &mut ExecutionCtx) -> VortexResult<usize> {
624 let last = self
625 .indices
626 .execute_scalar(self.indices.len() - 1, ctx)?
627 .as_primitive()
628 .as_::<usize>()
629 .ok_or_else(|| vortex_err!("index does not fit in usize"))?;
630 Ok(last - self.offset)
631 }
632
633 pub fn filter(&self, mask: &Mask, ctx: &mut ExecutionCtx) -> VortexResult<Option<Self>> {
635 if mask.len() != self.array_len {
636 vortex_bail!(
637 "Filter mask length {} does not match array length {}",
638 mask.len(),
639 self.array_len
640 );
641 }
642
643 match mask {
644 Mask::AllTrue(_) => Ok(Some(self.clone())),
645 Mask::AllFalse(_) => Ok(None),
646 Mask::Values(mask_values) => {
647 let flat_indices = self.indices().clone().execute::<PrimitiveArray>(ctx)?;
648 match_each_unsigned_integer_ptype!(flat_indices.ptype(), |I| {
649 filter_patches_with_mask(
650 flat_indices.as_slice::<I>(),
651 self.offset(),
652 self.values(),
653 mask_values,
654 ctx.allocator(),
655 )
656 })
657 }
658 }
659 }
660
661 pub fn mask(&self, mask: &Mask, ctx: &mut ExecutionCtx) -> VortexResult<Option<Self>> {
670 if mask.len() != self.array_len {
671 vortex_bail!(
672 "Filter mask length {} does not match array length {}",
673 mask.len(),
674 self.array_len
675 );
676 }
677
678 let filter_mask = match mask.bit_buffer() {
679 AllOr::All => return Ok(None),
680 AllOr::None => return self.clone().into_nullable_values().map(Some),
681 AllOr::Some(masked) => {
682 let patch_indices = self.indices().clone().execute::<PrimitiveArray>(ctx)?;
683 match_each_unsigned_integer_ptype!(patch_indices.ptype(), |P| {
684 let patch_indices = patch_indices.as_slice::<P>();
685 Mask::from_buffer(BitBuffer::collect_bool_in(
686 patch_indices.len(),
687 |i| {
688 #[allow(clippy::cast_possible_truncation)]
689 let idx = (patch_indices[i] as usize) - self.offset;
690 !masked.value(idx)
691 },
692 ctx.allocator().clone(),
693 ))
694 })
695 }
696 };
697
698 if filter_mask.all_false() {
699 return Ok(None);
700 }
701
702 let filtered_indices = self.indices.filter(filter_mask.clone())?;
704 let filtered_values = self.values.filter(filter_mask)?;
705
706 Self {
707 array_len: self.array_len,
708 offset: self.offset,
709 indices: filtered_indices,
710 values: filtered_values,
711 chunk_offsets: None,
713 offset_within_chunk: self.offset_within_chunk,
714 }
715 .into_nullable_values()
716 .map(Some)
717 }
718
719 fn into_nullable_values(self) -> VortexResult<Self> {
724 if self.values.dtype().is_nullable() {
725 return Ok(self);
726 }
727 let nullable = self.values.dtype().as_nullable();
728 self.map_values(|values| values.cast(nullable))
729 }
730
731 pub fn slice(&self, range: Range<usize>, ctx: &mut ExecutionCtx) -> VortexResult<Option<Self>> {
733 let slice_start_idx = self.search_index(range.start, ctx)?.to_index();
734 let slice_end_idx = self.search_index(range.end, ctx)?.to_index();
735
736 if slice_start_idx == slice_end_idx {
737 return Ok(None);
738 }
739
740 let values = self.values().slice(slice_start_idx..slice_end_idx)?;
741 let indices = self.indices().slice(slice_start_idx..slice_end_idx)?;
742
743 let new_chunk_offsets = self
744 .chunk_offsets
745 .as_ref()
746 .map(|chunk_offsets| -> VortexResult<ArrayRef> {
747 let chunk_relative_offset = self.offset % PATCH_CHUNK_SIZE;
748 let chunk_start_idx = (chunk_relative_offset + range.start) / PATCH_CHUNK_SIZE;
749 let chunk_end_idx = (chunk_relative_offset + range.end).div_ceil(PATCH_CHUNK_SIZE);
750 chunk_offsets.slice(chunk_start_idx..chunk_end_idx)
751 })
752 .transpose()?;
753
754 let offset_within_chunk = new_chunk_offsets
755 .as_ref()
756 .map(|new_chunk_offsets| -> VortexResult<usize> {
757 let new_chunk_base = new_chunk_offsets
758 .execute_scalar(0, ctx)?
759 .as_primitive()
760 .as_::<usize>()
761 .ok_or_else(|| vortex_err!("chunk offset does not fit in usize"))?;
762 let parent_chunk_base = self.chunk_offset_at(0, ctx)?;
763 let parent_within = self.offset_within_chunk.unwrap_or(0);
764 Ok(parent_chunk_base + parent_within + slice_start_idx - new_chunk_base)
765 })
766 .transpose()?;
767
768 Ok(Some(Self {
769 array_len: range.len(),
770 offset: range.start + self.offset(),
771 indices,
772 values,
773 chunk_offsets: new_chunk_offsets,
774 offset_within_chunk,
775 }))
776 }
777
778 const PREFER_MAP_WHEN_PATCHES_OVER_INDICES_LESS_THAN: f64 = 5.0;
780
781 fn is_map_faster_than_search(&self, take_indices: &PrimitiveArray) -> bool {
782 (self.num_patches() as f64 / take_indices.len() as f64)
783 < Self::PREFER_MAP_WHEN_PATCHES_OVER_INDICES_LESS_THAN
784 }
785
786 pub fn take_with_nulls(
790 &self,
791 take_indices: &ArrayRef,
792 ctx: &mut ExecutionCtx,
793 ) -> VortexResult<Option<Self>> {
794 if take_indices.is_empty() {
795 return Ok(None);
796 }
797
798 let take_indices = take_indices.clone().execute::<PrimitiveArray>(ctx)?;
799 if self.is_map_faster_than_search(&take_indices) {
800 self.take_map(take_indices, true, ctx)
801 } else {
802 self.take_search(take_indices, true, ctx)
803 }
804 }
805
806 pub fn take(
810 &self,
811 take_indices: &ArrayRef,
812 ctx: &mut ExecutionCtx,
813 ) -> VortexResult<Option<Self>> {
814 if take_indices.is_empty() {
815 return Ok(None);
816 }
817
818 let take_indices = take_indices.clone().execute::<PrimitiveArray>(ctx)?;
819 if self.is_map_faster_than_search(&take_indices) {
820 self.take_map(take_indices, false, ctx)
821 } else {
822 self.take_search(take_indices, false, ctx)
823 }
824 }
825
826 #[expect(
827 clippy::cognitive_complexity,
828 reason = "complexity is from nested match_each_* macros"
829 )]
830 pub fn take_search(
831 &self,
832 take_indices: PrimitiveArray,
833 include_nulls: bool,
834 ctx: &mut ExecutionCtx,
835 ) -> VortexResult<Option<Self>> {
836 let take_indices_validity = take_indices.validity()?;
837 let take_indices_unsigned =
840 take_indices.reinterpret_cast(take_indices.ptype().to_unsigned());
841 let patch_indices = self.indices.clone().execute::<PrimitiveArray>(ctx)?;
842 let chunk_offsets = self
843 .chunk_offsets()
844 .as_ref()
845 .map(|co| co.clone().execute::<PrimitiveArray>(ctx))
846 .transpose()?;
847
848 let (values_indices, new_indices): (BufferMut<u64>, BufferMut<u64>) =
849 match_each_unsigned_integer_ptype!(patch_indices.ptype(), |PatchT| {
850 let patch_indices_slice = patch_indices.as_slice::<PatchT>();
851 match_each_unsigned_integer_ptype!(take_indices_unsigned.ptype(), |TakeT| {
852 let take_slice = take_indices_unsigned.as_slice::<TakeT>();
853
854 if let Some(chunk_offsets) = chunk_offsets {
855 match_each_unsigned_integer_ptype!(chunk_offsets.ptype(), |OffsetT| {
856 let chunk_offsets = chunk_offsets.as_slice::<OffsetT>();
857 take_indices_with_search_fn(
858 patch_indices_slice,
859 take_slice,
860 take_indices
861 .as_ref()
862 .validity()?
863 .execute_mask(take_indices.as_ref().len(), ctx)?,
864 include_nulls,
865 ctx.allocator(),
866 |take_idx| {
867 self.search_index_chunked_batch(
868 patch_indices_slice,
869 chunk_offsets,
870 take_idx,
871 )
872 },
873 )?
874 })
875 } else {
876 take_indices_with_search_fn(
877 patch_indices_slice,
878 take_slice,
879 take_indices
880 .as_ref()
881 .validity()?
882 .execute_mask(take_indices.as_ref().len(), ctx)?,
883 include_nulls,
884 ctx.allocator(),
885 |take_idx| {
886 let Some(offset) = <PatchT as NumCast>::from(self.offset) else {
887 return Ok(SearchResult::NotFound(patch_indices_slice.len()));
889 };
890
891 patch_indices_slice
892 .search_sorted(&(take_idx + offset), SearchSortedSide::Left)
893 },
894 )?
895 }
896 })
897 });
898
899 if new_indices.is_empty() {
900 return Ok(None);
901 }
902
903 let new_indices = new_indices.into_array();
904 let new_array_len = take_indices.len();
905 let values_validity = take_indices_validity.take(&new_indices)?;
906
907 Ok(Some(Self {
908 array_len: new_array_len,
909 offset: 0,
910 indices: new_indices,
911 values: self
912 .values()
913 .take(PrimitiveArray::new(values_indices, values_validity).into_array())?,
914 chunk_offsets: None,
915 offset_within_chunk: Some(0), }))
917 }
918
919 pub fn take_map(
920 &self,
921 take_indices: PrimitiveArray,
922 include_nulls: bool,
923 ctx: &mut ExecutionCtx,
924 ) -> VortexResult<Option<Self>> {
925 let indices = self.indices.clone().execute::<PrimitiveArray>(ctx)?;
926 let new_length = take_indices.len();
927 let take_indices_unsigned =
929 take_indices.reinterpret_cast(take_indices.ptype().to_unsigned());
930
931 let min_index = self.min_index(ctx)?;
932 let max_index = self.max_index(ctx)?;
933
934 let Some((new_sparse_indices, value_indices)) =
935 match_each_unsigned_integer_ptype!(indices.ptype(), |Indices| {
936 match_each_unsigned_integer_ptype!(take_indices_unsigned.ptype(), |TakeIndices| {
937 let take_validity = take_indices
938 .validity()?
939 .execute_mask(take_indices.len(), ctx)?;
940 let take_nullability = take_indices.validity()?.nullability();
941 let take_slice = take_indices_unsigned.as_slice::<TakeIndices>();
942 take_map::<_, TakeIndices>(
943 indices.as_slice::<Indices>(),
944 take_slice,
945 take_validity,
946 take_nullability,
947 self.offset(),
948 min_index,
949 max_index,
950 include_nulls,
951 ctx.allocator(),
952 )?
953 })
954 })
955 else {
956 return Ok(None);
957 };
958
959 let taken_values = self.values().take(value_indices)?;
960
961 Ok(Some(Patches {
962 array_len: new_length,
963 offset: 0,
964 indices: new_sparse_indices,
965 values: taken_values,
966 chunk_offsets: None,
968 offset_within_chunk: self.offset_within_chunk,
969 }))
970 }
971
972 pub fn map_values<F>(self, f: F) -> VortexResult<Self>
973 where
974 F: FnOnce(ArrayRef) -> VortexResult<ArrayRef>,
975 {
976 let values = f(self.values)?;
977 if self.indices.len() != values.len() {
978 vortex_bail!(
979 "map_values must preserve length: expected {} received {}",
980 self.indices.len(),
981 values.len()
982 )
983 }
984
985 Ok(Self {
986 array_len: self.array_len,
987 offset: self.offset,
988 indices: self.indices,
989 values,
990 chunk_offsets: self.chunk_offsets,
991 offset_within_chunk: self.offset_within_chunk,
992 })
993 }
994}
995
996fn search_index_binary_search(
1002 indices: &ArrayRef,
1003 needle: usize,
1004 ctx: &mut ExecutionCtx,
1005) -> VortexResult<SearchResult> {
1006 if let Some(primitive) = indices.as_opt::<Primitive>() {
1007 match_each_unsigned_integer_ptype!(primitive.ptype(), |T| {
1008 let Ok(needle) = T::try_from(needle) else {
1009 return Ok(SearchResult::NotFound(primitive.len()));
1014 };
1015 return primitive
1016 .as_slice::<T>()
1017 .search_sorted(&needle, SearchSortedSide::Left);
1018 });
1019 }
1020
1021 search_index_binary_search_scalar(indices, needle, ctx)
1022}
1023
1024fn search_index_binary_search_scalar(
1025 indices: &ArrayRef,
1026 needle: usize,
1027 ctx: &mut ExecutionCtx,
1028) -> VortexResult<SearchResult> {
1029 match_each_unsigned_integer_ptype!(indices.dtype().as_ptype(), |T| {
1030 SearchSortedPrimitiveArray::<T>::new(indices, ctx)
1031 .search_sorted(&needle, SearchSortedSide::Left)
1032 })
1033}
1034
1035#[expect(clippy::too_many_arguments)] fn take_map<I: NativePType + Hash + Eq + TryFrom<usize>, T: NativePType>(
1037 indices: &[I],
1038 take_indices: &[T],
1039 take_validity: Mask,
1040 take_nullability: Nullability,
1041 indices_offset: usize,
1042 min_index: usize,
1043 max_index: usize,
1044 include_nulls: bool,
1045 allocator: &BufferAllocatorRef,
1046) -> VortexResult<Option<(ArrayRef, ArrayRef)>>
1047where
1048 usize: TryFrom<T>,
1049 VortexError: From<<I as TryFrom<usize>>::Error>,
1050{
1051 let offset_i = I::try_from(indices_offset)?;
1052
1053 let sparse_index_to_value_index: HashMap<I, usize> = indices
1054 .iter()
1055 .copied()
1056 .map(|idx| idx - offset_i)
1057 .enumerate()
1058 .map(|(value_index, sparse_index)| (sparse_index, value_index))
1059 .collect();
1060
1061 let mut new_sparse_indices =
1062 BufferMut::<u64>::with_capacity_in(take_indices.len(), allocator.clone());
1063 let mut value_indices =
1064 BufferMut::<u64>::with_capacity_in(take_indices.len(), allocator.clone());
1065
1066 for (idx_in_take, &take_idx) in take_indices.iter().enumerate() {
1067 let ti = usize::try_from(take_idx)
1068 .map_err(|_| vortex_err!("Failed to convert index to usize"))?;
1069
1070 let is_null = match take_validity.bit_buffer() {
1072 AllOr::All => false,
1073 AllOr::None => true,
1074 AllOr::Some(buf) => !buf.value(idx_in_take),
1075 };
1076 if is_null {
1077 if include_nulls {
1078 new_sparse_indices.push(idx_in_take as u64);
1079 value_indices.push(0);
1080 }
1081 } else if ti >= min_index && ti <= max_index {
1082 let ti_as_i = I::try_from(ti)
1083 .map_err(|_| vortex_err!("take index does not fit in index type"))?;
1084 if let Some(&value_index) = sparse_index_to_value_index.get(&ti_as_i) {
1085 new_sparse_indices.push(idx_in_take as u64);
1086 value_indices.push(value_index as u64);
1087 }
1088 }
1089 }
1090
1091 if new_sparse_indices.is_empty() {
1092 return Ok(None);
1093 }
1094
1095 let new_sparse_indices = new_sparse_indices.into_array();
1096 let values_validity =
1097 Validity::from_mask(take_validity, take_nullability).take(&new_sparse_indices)?;
1098 Ok(Some((
1099 new_sparse_indices,
1100 PrimitiveArray::new(value_indices, values_validity).into_array(),
1101 )))
1102}
1103
1104fn filter_patches_with_mask<T: IntegerPType>(
1117 patch_indices: &[T],
1118 offset: usize,
1119 patch_values: &ArrayRef,
1120 mask: &MaskValues,
1121 allocator: &BufferAllocatorRef,
1122) -> VortexResult<Option<Patches>> {
1123 let true_count = mask.true_count();
1124 let patch_count = patch_indices.len();
1125 let expected_matches = true_count.min(patch_count);
1126
1127 let mut new_patch_indices =
1129 BufferMut::<u64>::with_capacity_in(expected_matches, allocator.clone());
1130 let mut kept_patches = BitBufferMut::from_buffer(
1131 BufferMut::zeroed_in(patch_count.div_ceil(8), allocator.clone()),
1132 0,
1133 patch_count,
1134 );
1135
1136 if patch_count > true_count {
1137 gallop_patches_for_mask_indices(
1138 patch_indices,
1139 offset,
1140 mask,
1141 &mut new_patch_indices,
1142 &mut kept_patches,
1143 )?;
1144 } else {
1145 match mask.cached_indices() {
1146 Some(mask_indices) => gallop_mask_indices_for_patches(
1147 patch_indices,
1148 offset,
1149 mask_indices,
1150 &mut new_patch_indices,
1151 &mut kept_patches,
1152 )?,
1153 None => rank_patches_in_mask_bitmap(
1154 patch_indices,
1155 offset,
1156 mask.bit_buffer(),
1157 &mut new_patch_indices,
1158 &mut kept_patches,
1159 )?,
1160 }
1161 }
1162
1163 let new_patch_values = match new_patch_indices.len() {
1164 0 => return Ok(None),
1165 kept if kept == patch_count => patch_values.clone(),
1166 _ => patch_values.filter(Mask::from_buffer(kept_patches.freeze()))?,
1167 };
1168 let new_patch_indices = new_patch_indices.into_array();
1169
1170 Ok(Some(unsafe {
1173 Patches::new_unchecked(
1174 true_count,
1175 0,
1176 new_patch_indices,
1177 new_patch_values,
1178 None,
1180 None,
1181 )
1182 }))
1183}
1184
1185fn gallop_lower_bound<T: Ord>(values: &[T], start: usize, needle: &T) -> usize {
1189 let mut low = start;
1190 let mut high = start;
1191 let mut step = 1;
1192
1193 while high < values.len() && values[high] < *needle {
1194 low = high + 1;
1195 high += step;
1196 step *= 2;
1197 }
1198
1199 let high = high.min(values.len());
1200 low + values[low..high].partition_point(|value| value < needle)
1201}
1202
1203fn gallop_patches_for_mask_indices<T: IntegerPType>(
1207 patch_indices: &[T],
1208 offset: usize,
1209 mask: &MaskValues,
1210 new_patch_indices: &mut BufferMut<u64>,
1211 kept_patches: &mut BitBufferMut,
1212) -> VortexResult<()> {
1213 let Some(&last_patch_index) = patch_indices.last() else {
1214 return Ok(());
1215 };
1216 let last_mask_index = patch_index_to_usize(last_patch_index, offset)?;
1217
1218 let mut patch_position = 0;
1219 let mut mask_position = 0;
1220
1221 let mut visit = |mask_index: usize| {
1222 let needle = <T as NumCast>::from(mask_index + offset)
1224 .vortex_expect("mask index is at most the last patch index");
1225
1226 patch_position = gallop_lower_bound(patch_indices, patch_position, &needle);
1227 if patch_indices[patch_position] == needle {
1228 new_patch_indices.push(mask_position as u64);
1229 kept_patches.set(patch_position);
1230 patch_position += 1;
1231 }
1232
1233 mask_position += 1;
1234 };
1235
1236 match mask.cached_indices() {
1237 Some(mask_indices) => {
1238 let end = mask_indices.partition_point(|&mask_index| mask_index <= last_mask_index);
1239 mask_indices[..end]
1240 .iter()
1241 .for_each(|&mask_index| visit(mask_index));
1242 }
1243 None => mask
1244 .bit_buffer()
1245 .slice(..=last_mask_index)
1246 .for_each_set_index(visit),
1247 }
1248
1249 Ok(())
1250}
1251
1252fn gallop_mask_indices_for_patches<T: IntegerPType>(
1254 patch_indices: &[T],
1255 offset: usize,
1256 mask_indices: &[usize],
1257 new_patch_indices: &mut BufferMut<u64>,
1258 kept_patches: &mut BitBufferMut,
1259) -> VortexResult<()> {
1260 let mut mask_position = 0;
1261
1262 for (patch_position, patch_index) in patch_indices.iter().enumerate() {
1263 let index = patch_index_to_usize(*patch_index, offset)?;
1264
1265 mask_position = gallop_lower_bound(mask_indices, mask_position, &index);
1266 if mask_position == mask_indices.len() {
1267 break;
1268 }
1269
1270 if mask_indices[mask_position] == index {
1271 new_patch_indices.push(mask_position as u64);
1272 kept_patches.set(patch_position);
1273 mask_position += 1;
1274 }
1275 }
1276
1277 Ok(())
1278}
1279
1280fn rank_patches_in_mask_bitmap<T: IntegerPType>(
1284 patch_indices: &[T],
1285 offset: usize,
1286 mask: &BitBuffer,
1287 new_patch_indices: &mut BufferMut<u64>,
1288 kept_patches: &mut BitBufferMut,
1289) -> VortexResult<()> {
1290 let mut rank = 0;
1291 let mut previous_index = 0;
1292
1293 for (patch_position, patch_index) in patch_indices.iter().enumerate() {
1294 let index = patch_index_to_usize(*patch_index, offset)?;
1295
1296 rank += mask.count_range(previous_index, index);
1297 previous_index = index;
1298
1299 if mask.value(index) {
1300 new_patch_indices.push(rank as u64);
1301 kept_patches.set(patch_position);
1302 }
1303 }
1304
1305 Ok(())
1306}
1307
1308#[inline]
1309fn patch_index_to_usize<T: IntegerPType>(patch_index: T, offset: usize) -> VortexResult<usize> {
1310 Ok(patch_index
1311 .to_usize()
1312 .ok_or_else(|| vortex_err!("patch index does not fit in usize"))?
1313 - offset)
1314}
1315
1316fn take_indices_with_search_fn<
1317 I: UnsignedPType,
1318 T: IntegerPType,
1319 F: Fn(I) -> VortexResult<SearchResult>,
1320>(
1321 indices: &[I],
1322 take_indices: &[T],
1323 take_validity: Mask,
1324 include_nulls: bool,
1325 allocator: &BufferAllocatorRef,
1326 search_fn: F,
1327) -> VortexResult<(BufferMut<u64>, BufferMut<u64>)> {
1328 let mut values_indices = BufferMut::with_capacity_in(take_indices.len(), allocator.clone());
1329 let mut new_indices = BufferMut::with_capacity_in(take_indices.len(), allocator.clone());
1330
1331 for (new_patch_idx, &take_idx) in take_indices.iter().enumerate() {
1332 if !take_validity.value(new_patch_idx) {
1333 if include_nulls {
1334 values_indices.push(0u64);
1336 new_indices.push(new_patch_idx as u64);
1337 }
1338 continue;
1339 } else {
1340 let search_result = match I::from(take_idx) {
1341 Some(idx) => search_fn(idx)?,
1342 None => SearchResult::NotFound(indices.len()),
1343 };
1344
1345 if let Some(patch_idx) = search_result.to_found() {
1346 values_indices.push(patch_idx as u64);
1347 new_indices.push(new_patch_idx as u64);
1348 }
1349 }
1350 }
1351
1352 Ok((values_indices, new_indices))
1353}
1354
1355#[cfg(test)]
1356mod test {
1357 use rstest::rstest;
1358 use vortex_buffer::BitBuffer;
1359 use vortex_buffer::Buffer;
1360 use vortex_buffer::BufferMut;
1361 use vortex_buffer::buffer;
1362 use vortex_error::VortexExpect;
1363 use vortex_error::VortexResult;
1364 use vortex_mask::Mask;
1365
1366 use crate::IntoArray;
1367 use crate::VortexSessionExecute;
1368 use crate::array_session;
1369 use crate::assert_arrays_eq;
1370 use crate::patches::Patches;
1371 use crate::patches::PrimitiveArray;
1372 use crate::search_sorted::SearchResult;
1373 use crate::validity::Validity;
1374
1375 #[test]
1376 fn test_filter() {
1377 let mut ctx = array_session().create_execution_ctx();
1378 let patches = Patches::new(
1379 100,
1380 0,
1381 buffer![10u32, 11, 20].into_array(),
1382 buffer![100, 110, 200].into_array(),
1383 None,
1384 &mut ctx,
1385 )
1386 .unwrap();
1387
1388 let filtered = patches
1389 .filter(&Mask::from_indices(100, vec![10, 20, 30]), &mut ctx)
1390 .unwrap()
1391 .unwrap();
1392
1393 assert_arrays_eq!(
1394 filtered.indices(),
1395 PrimitiveArray::from_iter([0u64, 1]),
1396 &mut ctx
1397 );
1398 assert_arrays_eq!(
1399 filtered.values(),
1400 PrimitiveArray::from_iter([100i32, 200]),
1401 &mut ctx
1402 );
1403 }
1404
1405 #[test]
1406 fn take_with_nulls() {
1407 let mut ctx = array_session().create_execution_ctx();
1408 let patches = Patches::new(
1409 20,
1410 0,
1411 buffer![2u64, 9, 15].into_array(),
1412 PrimitiveArray::new(buffer![33_i32, 44, 55], Validity::AllValid).into_array(),
1413 None,
1414 &mut ctx,
1415 )
1416 .unwrap();
1417
1418 let taken = patches
1419 .take(
1420 &PrimitiveArray::new(buffer![9, 0], Validity::from_iter(vec![true, false]))
1421 .into_array(),
1422 &mut ctx,
1423 )
1424 .unwrap()
1425 .unwrap();
1426 let values = taken.values();
1427 let primitive_indices = taken.indices();
1428 assert_eq!(taken.array_len(), 2);
1429 assert_arrays_eq!(
1430 values,
1431 PrimitiveArray::from_option_iter([Some(44i32)]),
1432 &mut ctx
1433 );
1434 assert_arrays_eq!(
1435 primitive_indices,
1436 PrimitiveArray::from_iter([0u64]),
1437 &mut ctx
1438 );
1439 assert_eq!(
1440 values
1441 .validity()
1442 .unwrap()
1443 .execute_mask(values.len(), &mut ctx)
1444 .unwrap(),
1445 Mask::from_iter(vec![true])
1446 );
1447 }
1448
1449 #[test]
1450 fn take_search_with_nulls_chunked() {
1451 let mut ctx = array_session().create_execution_ctx();
1452 let patches = Patches::new(
1453 20,
1454 0,
1455 buffer![2u64, 9, 15].into_array(),
1456 buffer![33_i32, 44, 55].into_array(),
1457 Some(buffer![0u64].into_array()),
1458 &mut ctx,
1459 )
1460 .unwrap();
1461
1462 let taken = patches
1463 .take_search(
1464 PrimitiveArray::new(buffer![9, 0], Validity::from_iter([true, false])),
1465 true,
1466 &mut ctx,
1467 )
1468 .unwrap()
1469 .unwrap();
1470
1471 let values = taken.values();
1472 assert_eq!(taken.array_len(), 2);
1473 assert_arrays_eq!(
1474 values,
1475 PrimitiveArray::from_option_iter([Some(44i32), None]),
1476 &mut ctx
1477 );
1478 assert_arrays_eq!(
1479 taken.indices(),
1480 PrimitiveArray::from_iter([0u64, 1]),
1481 &mut ctx
1482 );
1483
1484 assert_eq!(
1485 values
1486 .validity()
1487 .unwrap()
1488 .execute_mask(values.len(), &mut ctx)
1489 .unwrap(),
1490 Mask::from_iter([true, false])
1491 );
1492 }
1493
1494 #[test]
1495 fn take_search_chunked_multiple_chunks() {
1496 let mut ctx = array_session().create_execution_ctx();
1497 let patches = Patches::new(
1498 2048,
1499 0,
1500 buffer![100u64, 500, 1200, 1800].into_array(),
1501 buffer![10_i32, 20, 30, 40].into_array(),
1502 Some(buffer![0u64, 2].into_array()),
1503 &mut ctx,
1504 )
1505 .unwrap();
1506
1507 let taken = patches
1508 .take_search(
1509 PrimitiveArray::new(buffer![500, 1200, 999], Validity::AllValid),
1510 true,
1511 &mut ctx,
1512 )
1513 .unwrap()
1514 .unwrap();
1515
1516 assert_eq!(taken.array_len(), 3);
1517 assert_arrays_eq!(
1518 taken.values(),
1519 PrimitiveArray::from_option_iter([Some(20i32), Some(30)]),
1520 &mut ctx
1521 );
1522 assert_arrays_eq!(
1523 taken.indices(),
1524 PrimitiveArray::from_iter([0u64, 1]),
1525 &mut ctx
1526 );
1527 }
1528
1529 #[test]
1530 fn take_search_chunked_indices_with_no_patches() {
1531 let mut ctx = array_session().create_execution_ctx();
1532 let patches = Patches::new(
1533 20,
1534 0,
1535 buffer![2u64, 9, 15].into_array(),
1536 buffer![33_i32, 44, 55].into_array(),
1537 Some(buffer![0u64].into_array()),
1538 &mut ctx,
1539 )
1540 .unwrap();
1541
1542 let taken = patches
1543 .take_search(
1544 PrimitiveArray::new(buffer![3, 4, 5], Validity::AllValid),
1545 true,
1546 &mut ctx,
1547 )
1548 .unwrap();
1549
1550 assert!(taken.is_none());
1551 }
1552
1553 #[test]
1554 fn take_search_chunked_interleaved() {
1555 let mut ctx = array_session().create_execution_ctx();
1556 let patches = Patches::new(
1557 30,
1558 0,
1559 buffer![5u64, 10, 20, 25].into_array(),
1560 buffer![100_i32, 200, 300, 400].into_array(),
1561 Some(buffer![0u64].into_array()),
1562 &mut ctx,
1563 )
1564 .unwrap();
1565
1566 let taken = patches
1567 .take_search(
1568 PrimitiveArray::new(buffer![10, 15, 20, 99], Validity::AllValid),
1569 true,
1570 &mut ctx,
1571 )
1572 .unwrap()
1573 .unwrap();
1574
1575 assert_eq!(taken.array_len(), 4);
1576 assert_arrays_eq!(
1577 taken.values(),
1578 PrimitiveArray::from_option_iter([Some(200i32), Some(300)]),
1579 &mut ctx
1580 );
1581 assert_arrays_eq!(
1582 taken.indices(),
1583 PrimitiveArray::from_iter([0u64, 2]),
1584 &mut ctx
1585 );
1586 }
1587
1588 #[test]
1589 fn test_take_search_multiple_chunk_offsets() {
1590 let mut ctx = array_session().create_execution_ctx();
1591 let patches = Patches::new(
1592 1500,
1593 0,
1594 BufferMut::from_iter(0..1500u64).into_array(),
1595 BufferMut::from_iter(0..1500i32).into_array(),
1596 Some(buffer![0u64, 1024u64].into_array()),
1597 &mut ctx,
1598 )
1599 .unwrap();
1600
1601 let taken = patches
1602 .take_search(
1603 PrimitiveArray::new(BufferMut::from_iter(0..1500u64), Validity::AllValid),
1604 false,
1605 &mut ctx,
1606 )
1607 .unwrap()
1608 .unwrap();
1609
1610 assert_eq!(taken.array_len(), 1500);
1611 assert_arrays_eq!(
1612 taken.indices(),
1613 PrimitiveArray::from_iter(0..1500u64),
1614 &mut ctx
1615 );
1616 assert_arrays_eq!(
1617 taken.values(),
1618 PrimitiveArray::from_option_iter((0..1500i32).map(Some)),
1619 &mut ctx
1620 );
1621 }
1622
1623 #[test]
1624 fn doubly_sliced() {
1625 let mut ctx = array_session().create_execution_ctx();
1626 let values = buffer![15_u32, 135, 13531, 42].into_array();
1627 let indices = buffer![10_u64, 11, 50, 100].into_array();
1628
1629 let patches = Patches::new(101, 0, indices, values, None, &mut ctx).unwrap();
1630
1631 let sliced = patches.slice(15..100, &mut ctx).unwrap().unwrap();
1632 assert_eq!(sliced.array_len(), 100 - 15);
1633 assert_arrays_eq!(
1634 sliced.values(),
1635 PrimitiveArray::from_iter([13531u32]),
1636 &mut ctx
1637 );
1638
1639 assert_eq!(sliced.offset(), 15);
1640 assert_arrays_eq!(
1641 sliced.indices(),
1642 PrimitiveArray::from_iter([50u64]),
1643 &mut ctx
1644 );
1645 let doubly_sliced = sliced.slice(35..36, &mut ctx).unwrap().unwrap();
1646 assert_eq!(doubly_sliced.array_len(), 1);
1647 assert_eq!(doubly_sliced.offset(), 50);
1648 assert_eq!(
1649 doubly_sliced.search_index(0, &mut ctx).unwrap(),
1650 SearchResult::Found(0)
1651 );
1652 assert_arrays_eq!(
1653 doubly_sliced.values(),
1654 PrimitiveArray::from_iter([13531u32]),
1655 &mut ctx
1656 );
1657 }
1658
1659 #[test]
1660 fn test_mask_all_true() {
1661 let mut ctx = array_session().create_execution_ctx();
1662 let patches = Patches::new(
1663 10,
1664 0,
1665 buffer![2u64, 5, 8].into_array(),
1666 buffer![100i32, 200, 300].into_array(),
1667 None,
1668 &mut ctx,
1669 )
1670 .unwrap();
1671
1672 let mask = Mask::new_true(10);
1673 let masked = patches.mask(&mask, &mut ctx).unwrap();
1674 assert!(masked.is_none());
1675 }
1676
1677 #[test]
1678 fn test_mask_all_false() {
1679 let mut ctx = array_session().create_execution_ctx();
1680 let patches = Patches::new(
1681 10,
1682 0,
1683 buffer![2u64, 5, 8].into_array(),
1684 buffer![100i32, 200, 300].into_array(),
1685 None,
1686 &mut ctx,
1687 )
1688 .unwrap();
1689
1690 let mask = Mask::new_false(10);
1691 let masked = patches.mask(&mask, &mut ctx).unwrap().unwrap();
1692
1693 assert!(masked.values().dtype().is_nullable());
1695 assert_arrays_eq!(
1696 masked.values(),
1697 PrimitiveArray::from_option_iter([Some(100i32), Some(200), Some(300)]),
1698 &mut ctx
1699 );
1700 assert!(masked.values().is_valid(0, &mut ctx).unwrap());
1701 assert!(masked.values().is_valid(1, &mut ctx).unwrap());
1702 assert!(masked.values().is_valid(2, &mut ctx).unwrap());
1703
1704 assert_arrays_eq!(
1706 masked.indices(),
1707 PrimitiveArray::from_iter([2u64, 5, 8]),
1708 &mut ctx
1709 );
1710 }
1711
1712 #[test]
1713 fn test_mask_partial() {
1714 let mut ctx = array_session().create_execution_ctx();
1715 let patches = Patches::new(
1716 10,
1717 0,
1718 buffer![2u64, 5, 8].into_array(),
1719 buffer![100i32, 200, 300].into_array(),
1720 None,
1721 &mut ctx,
1722 )
1723 .unwrap();
1724
1725 let mask = Mask::from_iter([
1727 false, false, true, false, false, false, false, false, true, false,
1728 ]);
1729 let masked = patches.mask(&mask, &mut ctx).unwrap().unwrap();
1730
1731 assert_eq!(masked.values().len(), 1);
1733 assert_arrays_eq!(
1734 masked.values(),
1735 PrimitiveArray::from_option_iter([Some(200i32)]),
1736 &mut ctx
1737 );
1738
1739 assert_arrays_eq!(
1741 masked.indices(),
1742 PrimitiveArray::from_iter([5u64]),
1743 &mut ctx
1744 );
1745 }
1746
1747 #[test]
1748 fn test_mask_with_offset() {
1749 let mut ctx = array_session().create_execution_ctx();
1750 let patches = Patches::new(
1751 10,
1752 5, buffer![7u64, 10, 13].into_array(), buffer![100i32, 200, 300].into_array(),
1755 None,
1756 &mut ctx,
1757 )
1758 .unwrap();
1759
1760 let mask = Mask::from_iter([
1762 false, false, true, false, false, false, false, false, false, false,
1763 ]);
1764
1765 let masked = patches.mask(&mask, &mut ctx).unwrap().unwrap();
1766 assert_eq!(masked.array_len(), 10);
1767 assert_eq!(masked.offset(), 5);
1768 assert_arrays_eq!(
1769 masked.indices(),
1770 PrimitiveArray::from_iter([10u64, 13]),
1771 &mut ctx
1772 );
1773 assert_arrays_eq!(
1774 masked.values(),
1775 PrimitiveArray::from_option_iter([Some(200i32), Some(300)]),
1776 &mut ctx
1777 );
1778 }
1779
1780 #[test]
1781 fn test_mask_nullable_values() {
1782 let mut ctx = array_session().create_execution_ctx();
1783 let patches = Patches::new(
1784 10,
1785 0,
1786 buffer![2u64, 5, 8].into_array(),
1787 PrimitiveArray::from_option_iter([Some(100i32), None, Some(300)]).into_array(),
1788 None,
1789 &mut ctx,
1790 )
1791 .unwrap();
1792
1793 let mask = Mask::from_iter([
1795 false, false, true, false, false, false, false, false, false, false,
1796 ]);
1797 let masked = patches.mask(&mask, &mut ctx).unwrap().unwrap();
1798
1799 assert_arrays_eq!(
1801 masked.indices(),
1802 PrimitiveArray::from_iter([5u64, 8]),
1803 &mut ctx
1804 );
1805
1806 let masked_values = masked
1808 .values()
1809 .clone()
1810 .execute::<PrimitiveArray>(&mut ctx)
1811 .unwrap();
1812 assert_eq!(masked_values.len(), 2);
1813 assert!(!masked_values.is_valid(0, &mut ctx).unwrap()); assert!(masked_values.is_valid(1, &mut ctx).unwrap()); assert_eq!(
1816 i32::try_from(&masked_values.execute_scalar(1, &mut ctx).unwrap()).unwrap(),
1817 300i32
1818 );
1819 }
1820
1821 #[rstest]
1823 #[case::sparse_mask((0..1000).step_by(2).collect(), (0..1000).step_by(97).collect())]
1824 #[case::sparse_patches((0..1000).step_by(97).collect(), (0..1000).step_by(2).collect())]
1825 #[case::similar_sizes((0..1000).step_by(3).collect(), (0..1000).step_by(2).collect())]
1826 #[case::no_overlap((0..500).collect(), (500..1000).step_by(50).collect())]
1827 #[case::no_overlap_sparse_patches((500..1000).step_by(100).collect(), (0..500).collect())]
1828 #[case::edges(vec![0, 1, 998, 999], vec![0, 999])]
1829 #[case::all_patches_kept((0..1000).step_by(100).collect(), (0..1000).step_by(2).collect())]
1830 fn test_filter_matches_naive(
1831 #[case] patch_indices: Vec<usize>,
1832 #[case] mask_indices: Vec<usize>,
1833 #[values(0, 5)] offset: usize,
1834 #[values(true, false)] cache_mask_indices: bool,
1835 ) -> VortexResult<()> {
1836 const LEN: usize = 1000;
1837 let mut ctx = array_session().create_execution_ctx();
1838
1839 let patches = Patches::new(
1840 LEN,
1841 offset,
1842 patch_indices
1843 .iter()
1844 .map(|&index| u32::try_from(index + offset).vortex_expect("index fits in u32"))
1845 .collect::<Buffer<u32>>()
1846 .into_array(),
1847 (0i32..)
1848 .take(patch_indices.len())
1849 .collect::<Buffer<i32>>()
1850 .into_array(),
1851 None,
1852 &mut ctx,
1853 )?;
1854
1855 let mask = if cache_mask_indices {
1856 Mask::from_indices(LEN, mask_indices.iter().copied())
1857 } else {
1858 Mask::from_buffer(BitBuffer::from_iter(
1859 (0..LEN).map(|index| mask_indices.binary_search(&index).is_ok()),
1860 ))
1861 };
1862
1863 let (expected_indices, expected_values): (Vec<u64>, Vec<i32>) = mask_indices
1864 .iter()
1865 .enumerate()
1866 .filter_map(|(mask_position, index)| {
1867 patch_indices
1868 .binary_search(index)
1869 .ok()
1870 .map(|patch_position| {
1871 (
1872 mask_position as u64,
1873 i32::try_from(patch_position).vortex_expect("position fits in i32"),
1874 )
1875 })
1876 })
1877 .unzip();
1878
1879 let filtered = patches.filter(&mask, &mut ctx)?;
1880
1881 let Some(filtered) = filtered else {
1882 assert!(expected_indices.is_empty());
1883 return Ok(());
1884 };
1885
1886 assert_eq!(filtered.array_len(), mask_indices.len());
1887 assert_arrays_eq!(
1888 filtered.indices(),
1889 PrimitiveArray::from_iter(expected_indices),
1890 &mut ctx
1891 );
1892 assert_arrays_eq!(
1893 filtered.values(),
1894 PrimitiveArray::from_iter(expected_values),
1895 &mut ctx
1896 );
1897
1898 Ok(())
1899 }
1900
1901 #[test]
1903 fn test_filter_sparse_mask_past_index_type_max() -> VortexResult<()> {
1904 let mut ctx = array_session().create_execution_ctx();
1905 let patches = Patches::new(
1906 1000,
1907 0,
1908 (0..=u8::MAX).collect::<Buffer<u8>>().into_array(),
1909 (0..256i32).collect::<Buffer<i32>>().into_array(),
1910 None,
1911 &mut ctx,
1912 )?;
1913
1914 let mask = Mask::from_indices(1000, [3, 255, 256, 900]);
1915 let filtered = patches
1916 .filter(&mask, &mut ctx)?
1917 .expect("patches at 3 and 255 are kept");
1918
1919 assert_arrays_eq!(
1920 filtered.indices(),
1921 PrimitiveArray::from_iter([0u64, 1]),
1922 &mut ctx
1923 );
1924 assert_arrays_eq!(
1925 filtered.values(),
1926 PrimitiveArray::from_iter([3i32, 255]),
1927 &mut ctx
1928 );
1929
1930 Ok(())
1931 }
1932
1933 #[test]
1934 fn test_filter_keep_all() {
1935 let mut ctx = array_session().create_execution_ctx();
1936 let patches = Patches::new(
1937 10,
1938 0,
1939 buffer![2u64, 5, 8].into_array(),
1940 buffer![100i32, 200, 300].into_array(),
1941 None,
1942 &mut ctx,
1943 )
1944 .unwrap();
1945
1946 let mask = Mask::from_indices(10, 0..10);
1948 let filtered = patches.filter(&mask, &mut ctx).unwrap().unwrap();
1949
1950 assert_arrays_eq!(
1951 filtered.indices(),
1952 PrimitiveArray::from_iter([2u64, 5, 8]),
1953 &mut ctx
1954 );
1955 assert_arrays_eq!(
1956 filtered.values(),
1957 PrimitiveArray::from_iter([100i32, 200, 300]),
1958 &mut ctx
1959 );
1960 }
1961
1962 #[test]
1963 fn test_filter_none() {
1964 let mut ctx = array_session().create_execution_ctx();
1965 let patches = Patches::new(
1966 10,
1967 0,
1968 buffer![2u64, 5, 8].into_array(),
1969 buffer![100i32, 200, 300].into_array(),
1970 None,
1971 &mut ctx,
1972 )
1973 .unwrap();
1974
1975 let mask = Mask::from_indices(10, vec![]);
1977 let filtered = patches.filter(&mask, &mut ctx).unwrap();
1978 assert!(filtered.is_none());
1979 }
1980
1981 #[test]
1982 fn test_filter_with_indices() {
1983 let mut ctx = array_session().create_execution_ctx();
1984 let patches = Patches::new(
1985 10,
1986 0,
1987 buffer![2u64, 5, 8].into_array(),
1988 buffer![100i32, 200, 300].into_array(),
1989 None,
1990 &mut ctx,
1991 )
1992 .unwrap();
1993
1994 let mask = Mask::from_indices(10, vec![2, 5, 9]);
1996 let filtered = patches.filter(&mask, &mut ctx).unwrap().unwrap();
1997
1998 assert_arrays_eq!(
1999 filtered.indices(),
2000 PrimitiveArray::from_iter([0u64, 1]),
2001 &mut ctx
2002 ); assert_arrays_eq!(
2004 filtered.values(),
2005 PrimitiveArray::from_iter([100i32, 200]),
2006 &mut ctx
2007 );
2008 }
2009
2010 #[test]
2011 fn test_slice_full_range() {
2012 let mut ctx = array_session().create_execution_ctx();
2013 let patches = Patches::new(
2014 10,
2015 0,
2016 buffer![2u64, 5, 8].into_array(),
2017 buffer![100i32, 200, 300].into_array(),
2018 None,
2019 &mut ctx,
2020 )
2021 .unwrap();
2022
2023 let sliced = patches.slice(0..10, &mut ctx).unwrap().unwrap();
2024
2025 assert_arrays_eq!(
2026 sliced.indices(),
2027 PrimitiveArray::from_iter([2u64, 5, 8]),
2028 &mut ctx
2029 );
2030 assert_arrays_eq!(
2031 sliced.values(),
2032 PrimitiveArray::from_iter([100i32, 200, 300]),
2033 &mut ctx
2034 );
2035 }
2036
2037 #[test]
2038 fn test_slice_partial() {
2039 let mut ctx = array_session().create_execution_ctx();
2040 let patches = Patches::new(
2041 10,
2042 0,
2043 buffer![2u64, 5, 8].into_array(),
2044 buffer![100i32, 200, 300].into_array(),
2045 None,
2046 &mut ctx,
2047 )
2048 .unwrap();
2049
2050 let sliced = patches.slice(3..8, &mut ctx).unwrap().unwrap();
2052
2053 assert_arrays_eq!(
2054 sliced.indices(),
2055 PrimitiveArray::from_iter([5u64]),
2056 &mut ctx
2057 ); assert_arrays_eq!(
2059 sliced.values(),
2060 PrimitiveArray::from_iter([200i32]),
2061 &mut ctx
2062 );
2063 assert_eq!(sliced.array_len(), 5); assert_eq!(sliced.offset(), 3); }
2066
2067 #[test]
2068 fn test_slice_no_patches() {
2069 let mut ctx = array_session().create_execution_ctx();
2070 let patches = Patches::new(
2071 10,
2072 0,
2073 buffer![2u64, 5, 8].into_array(),
2074 buffer![100i32, 200, 300].into_array(),
2075 None,
2076 &mut ctx,
2077 )
2078 .unwrap();
2079
2080 let sliced = patches.slice(6..7, &mut ctx).unwrap();
2082 assert!(sliced.is_none());
2083 }
2084
2085 #[test]
2086 fn test_slice_with_offset() {
2087 let mut ctx = array_session().create_execution_ctx();
2088 let patches = Patches::new(
2089 10,
2090 5, buffer![7u64, 10, 13].into_array(), buffer![100i32, 200, 300].into_array(),
2093 None,
2094 &mut ctx,
2095 )
2096 .unwrap();
2097
2098 let sliced = patches.slice(3..8, &mut ctx).unwrap().unwrap();
2100
2101 assert_arrays_eq!(
2102 sliced.indices(),
2103 PrimitiveArray::from_iter([10u64]),
2104 &mut ctx
2105 ); assert_arrays_eq!(
2107 sliced.values(),
2108 PrimitiveArray::from_iter([200i32]),
2109 &mut ctx
2110 );
2111 assert_eq!(sliced.offset(), 8); }
2113
2114 #[test]
2115 fn test_indices_range() {
2116 let mut ctx = array_session().create_execution_ctx();
2117 let patches = Patches::new(
2118 10,
2119 0,
2120 buffer![2u64, 5, 8].into_array(),
2121 buffer![100i32, 200, 300].into_array(),
2122 None,
2123 &mut ctx,
2124 )
2125 .unwrap();
2126
2127 assert_eq!(patches.min_index(&mut ctx).unwrap(), 2);
2128 assert_eq!(patches.max_index(&mut ctx).unwrap(), 8);
2129 }
2130
2131 #[test]
2132 fn test_search_index() {
2133 let mut ctx = array_session().create_execution_ctx();
2134 let patches = Patches::new(
2135 10,
2136 0,
2137 buffer![2u64, 5, 8].into_array(),
2138 buffer![100i32, 200, 300].into_array(),
2139 None,
2140 &mut ctx,
2141 )
2142 .unwrap();
2143
2144 assert_eq!(
2146 patches.search_index(2, &mut ctx).unwrap(),
2147 SearchResult::Found(0)
2148 );
2149 assert_eq!(
2150 patches.search_index(5, &mut ctx).unwrap(),
2151 SearchResult::Found(1)
2152 );
2153 assert_eq!(
2154 patches.search_index(8, &mut ctx).unwrap(),
2155 SearchResult::Found(2)
2156 );
2157
2158 assert_eq!(
2160 patches.search_index(0, &mut ctx).unwrap(),
2161 SearchResult::NotFound(0)
2162 );
2163 assert_eq!(
2164 patches.search_index(3, &mut ctx).unwrap(),
2165 SearchResult::NotFound(1)
2166 );
2167 assert_eq!(
2168 patches.search_index(6, &mut ctx).unwrap(),
2169 SearchResult::NotFound(2)
2170 );
2171 assert_eq!(
2172 patches.search_index(9, &mut ctx).unwrap(),
2173 SearchResult::NotFound(3)
2174 );
2175 }
2176
2177 #[test]
2178 fn test_mask_boundary_patches() {
2179 let mut ctx = array_session().create_execution_ctx();
2180 let patches = Patches::new(
2182 10,
2183 0,
2184 buffer![0u64, 9].into_array(),
2185 buffer![100i32, 200].into_array(),
2186 None,
2187 &mut ctx,
2188 )
2189 .unwrap();
2190
2191 let mask = Mask::from_iter([
2192 true, false, false, false, false, false, false, false, false, false,
2193 ]);
2194 let masked = patches.mask(&mask, &mut ctx).unwrap();
2195 assert!(masked.is_some());
2196 let masked = masked.unwrap();
2197 assert_arrays_eq!(
2198 masked.indices(),
2199 PrimitiveArray::from_iter([9u64]),
2200 &mut ctx
2201 );
2202 assert_arrays_eq!(
2203 masked.values(),
2204 PrimitiveArray::from_option_iter([Some(200i32)]),
2205 &mut ctx
2206 );
2207 }
2208
2209 #[test]
2210 fn test_mask_all_patches_removed() {
2211 let mut ctx = array_session().create_execution_ctx();
2212 let patches = Patches::new(
2214 10,
2215 0,
2216 buffer![2u64, 5, 8].into_array(),
2217 buffer![100i32, 200, 300].into_array(),
2218 None,
2219 &mut ctx,
2220 )
2221 .unwrap();
2222
2223 let mask = Mask::from_iter([
2225 false, false, true, false, false, true, false, false, true, false,
2226 ]);
2227 let masked = patches.mask(&mask, &mut ctx).unwrap();
2228 assert!(masked.is_none());
2229 }
2230
2231 #[test]
2232 fn test_mask_no_patches_removed() {
2233 let mut ctx = array_session().create_execution_ctx();
2234 let patches = Patches::new(
2236 10,
2237 0,
2238 buffer![2u64, 5, 8].into_array(),
2239 buffer![100i32, 200, 300].into_array(),
2240 None,
2241 &mut ctx,
2242 )
2243 .unwrap();
2244
2245 let mask = Mask::from_iter([
2247 true, false, false, true, false, false, true, false, false, true,
2248 ]);
2249 let masked = patches.mask(&mask, &mut ctx).unwrap().unwrap();
2250
2251 assert_arrays_eq!(
2252 masked.indices(),
2253 PrimitiveArray::from_iter([2u64, 5, 8]),
2254 &mut ctx
2255 );
2256 assert_arrays_eq!(
2257 masked.values(),
2258 PrimitiveArray::from_option_iter([Some(100i32), Some(200), Some(300)]),
2259 &mut ctx
2260 );
2261 }
2262
2263 #[test]
2264 fn test_mask_single_patch() {
2265 let mut ctx = array_session().create_execution_ctx();
2266 let patches = Patches::new(
2268 5,
2269 0,
2270 buffer![2u64].into_array(),
2271 buffer![42i32].into_array(),
2272 None,
2273 &mut ctx,
2274 )
2275 .unwrap();
2276
2277 let mask = Mask::from_iter([false, false, true, false, false]);
2279 let masked = patches.mask(&mask, &mut ctx).unwrap();
2280 assert!(masked.is_none());
2281
2282 let mask = Mask::from_iter([true, false, false, true, false]);
2284 let masked = patches.mask(&mask, &mut ctx).unwrap().unwrap();
2285 assert_arrays_eq!(
2286 masked.indices(),
2287 PrimitiveArray::from_iter([2u64]),
2288 &mut ctx
2289 );
2290 assert_arrays_eq!(
2291 masked.values(),
2292 PrimitiveArray::from_option_iter([Some(42i32)]),
2293 &mut ctx
2294 );
2295 }
2296
2297 #[test]
2298 fn test_mask_contiguous_patches() {
2299 let mut ctx = array_session().create_execution_ctx();
2300 let patches = Patches::new(
2302 10,
2303 0,
2304 buffer![3u64, 4, 5, 6].into_array(),
2305 buffer![100i32, 200, 300, 400].into_array(),
2306 None,
2307 &mut ctx,
2308 )
2309 .unwrap();
2310
2311 let mask = Mask::from_iter([
2313 false, false, false, false, true, true, false, false, false, false,
2314 ]);
2315 let masked = patches.mask(&mask, &mut ctx).unwrap().unwrap();
2316
2317 assert_arrays_eq!(
2318 masked.indices(),
2319 PrimitiveArray::from_iter([3u64, 6]),
2320 &mut ctx
2321 );
2322 assert_arrays_eq!(
2323 masked.values(),
2324 PrimitiveArray::from_option_iter([Some(100i32), Some(400)]),
2325 &mut ctx
2326 );
2327 }
2328
2329 #[test]
2330 fn test_mask_with_large_offset() {
2331 let mut ctx = array_session().create_execution_ctx();
2332 let patches = Patches::new(
2334 20,
2335 15,
2336 buffer![16u64, 17, 19].into_array(), buffer![100i32, 200, 300].into_array(),
2338 None,
2339 &mut ctx,
2340 )
2341 .unwrap();
2342
2343 let mask = Mask::from_iter([
2345 false, false, true, false, false, false, false, false, false, false, false, false,
2346 false, false, false, false, false, false, false, false,
2347 ]);
2348 let masked = patches.mask(&mask, &mut ctx).unwrap().unwrap();
2349
2350 assert_arrays_eq!(
2351 masked.indices(),
2352 PrimitiveArray::from_iter([16u64, 19]),
2353 &mut ctx
2354 );
2355 assert_arrays_eq!(
2356 masked.values(),
2357 PrimitiveArray::from_option_iter([Some(100i32), Some(300)]),
2358 &mut ctx
2359 );
2360 }
2361
2362 #[test]
2363 #[should_panic(expected = "Filter mask length 5 does not match array length 10")]
2364 fn test_mask_wrong_length() {
2365 let mut ctx = array_session().create_execution_ctx();
2366 let patches = Patches::new(
2367 10,
2368 0,
2369 buffer![2u64, 5, 8].into_array(),
2370 buffer![100i32, 200, 300].into_array(),
2371 None,
2372 &mut ctx,
2373 )
2374 .unwrap();
2375
2376 let mask = Mask::from_iter([false, false, true, false, false]);
2378 patches.mask(&mask, &mut ctx).unwrap();
2379 }
2380
2381 #[test]
2382 fn test_chunk_offsets_search() {
2383 let mut ctx = array_session().create_execution_ctx();
2384 let indices = buffer![100u64, 200, 3000, 3100].into_array();
2385 let values = buffer![10i32, 20, 30, 40].into_array();
2386 let chunk_offsets = buffer![0u64, 2, 2, 3].into_array();
2387 let patches =
2388 Patches::new(4096, 0, indices, values, Some(chunk_offsets), &mut ctx).unwrap();
2389
2390 assert!(patches.chunk_offsets.is_some());
2391
2392 assert_eq!(
2394 patches.search_index(100, &mut ctx).unwrap(),
2395 SearchResult::Found(0)
2396 );
2397 assert_eq!(
2398 patches.search_index(200, &mut ctx).unwrap(),
2399 SearchResult::Found(1)
2400 );
2401
2402 assert_eq!(
2404 patches.search_index(1500, &mut ctx).unwrap(),
2405 SearchResult::NotFound(2)
2406 );
2407 assert_eq!(
2408 patches.search_index(2000, &mut ctx).unwrap(),
2409 SearchResult::NotFound(2)
2410 );
2411
2412 assert_eq!(
2414 patches.search_index(3000, &mut ctx).unwrap(),
2415 SearchResult::Found(2)
2416 );
2417 assert_eq!(
2418 patches.search_index(3100, &mut ctx).unwrap(),
2419 SearchResult::Found(3)
2420 );
2421
2422 assert_eq!(
2423 patches.search_index(1024, &mut ctx).unwrap(),
2424 SearchResult::NotFound(2)
2425 );
2426 }
2427
2428 #[test]
2429 fn test_chunk_offsets_with_slice() {
2430 let mut ctx = array_session().create_execution_ctx();
2431 let indices = buffer![100u64, 500, 1200, 1300, 1500, 1800, 2100, 2500].into_array();
2432 let values = buffer![10i32, 20, 30, 35, 40, 45, 50, 60].into_array();
2433 let chunk_offsets = buffer![0u64, 2, 6].into_array();
2434 let patches =
2435 Patches::new(3000, 0, indices, values, Some(chunk_offsets), &mut ctx).unwrap();
2436
2437 let sliced = patches.slice(1000..2200, &mut ctx).unwrap().unwrap();
2438
2439 assert!(sliced.chunk_offsets.is_some());
2440 assert_eq!(sliced.offset(), 1000);
2441
2442 assert_eq!(
2443 sliced.search_index(200, &mut ctx).unwrap(),
2444 SearchResult::Found(0)
2445 );
2446 assert_eq!(
2447 sliced.search_index(500, &mut ctx).unwrap(),
2448 SearchResult::Found(2)
2449 );
2450 assert_eq!(
2451 sliced.search_index(1100, &mut ctx).unwrap(),
2452 SearchResult::Found(4)
2453 );
2454
2455 assert_eq!(
2456 sliced.search_index(250, &mut ctx).unwrap(),
2457 SearchResult::NotFound(1)
2458 );
2459 assert_arrays_eq!(
2460 sliced.values(),
2461 PrimitiveArray::from_iter([30i32, 35, 40, 45, 50]),
2462 &mut ctx
2463 );
2464 }
2465
2466 #[test]
2467 fn test_chunk_offsets_with_slice_after_first_chunk() {
2468 let mut ctx = array_session().create_execution_ctx();
2469 let indices = buffer![100u64, 500, 1200, 1300, 1500, 1800, 2100, 2500].into_array();
2470 let values = buffer![10i32, 20, 30, 35, 40, 45, 50, 60].into_array();
2471 let chunk_offsets = buffer![0u64, 2, 6].into_array();
2472 let patches =
2473 Patches::new(3000, 0, indices, values, Some(chunk_offsets), &mut ctx).unwrap();
2474
2475 let sliced = patches.slice(1300..2200, &mut ctx).unwrap().unwrap();
2476
2477 assert!(sliced.chunk_offsets.is_some());
2478 assert_eq!(sliced.offset(), 1300);
2479
2480 assert_eq!(
2481 sliced.search_index(0, &mut ctx).unwrap(),
2482 SearchResult::Found(0)
2483 );
2484 assert_eq!(
2485 sliced.search_index(200, &mut ctx).unwrap(),
2486 SearchResult::Found(1)
2487 );
2488 assert_eq!(
2489 sliced.search_index(500, &mut ctx).unwrap(),
2490 SearchResult::Found(2)
2491 );
2492 assert_eq!(
2493 sliced.search_index(250, &mut ctx).unwrap(),
2494 SearchResult::NotFound(2)
2495 );
2496 assert_eq!(
2497 sliced.search_index(900, &mut ctx).unwrap(),
2498 SearchResult::NotFound(4)
2499 );
2500 assert_arrays_eq!(
2501 sliced.values(),
2502 PrimitiveArray::from_iter([35i32, 40, 45, 50]),
2503 &mut ctx
2504 );
2505 }
2506
2507 #[test]
2508 fn test_chunk_offsets_slice_empty_result() {
2509 let mut ctx = array_session().create_execution_ctx();
2510 let indices = buffer![100u64, 200, 3000, 3100].into_array();
2511 let values = buffer![10i32, 20, 30, 40].into_array();
2512 let chunk_offsets = buffer![0u64, 2, 2, 3].into_array();
2513 let patches =
2514 Patches::new(4000, 0, indices, values, Some(chunk_offsets), &mut ctx).unwrap();
2515
2516 let sliced = patches.slice(1000..2000, &mut ctx).unwrap();
2517 assert!(sliced.is_none());
2518 }
2519
2520 #[test]
2521 fn test_chunk_offsets_slice_single_patch() {
2522 let mut ctx = array_session().create_execution_ctx();
2523 let indices = buffer![100u64, 1200, 1300, 2500].into_array();
2524 let values = buffer![10i32, 20, 30, 40].into_array();
2525 let chunk_offsets = buffer![0u64, 1, 3].into_array();
2526 let patches =
2527 Patches::new(3000, 0, indices, values, Some(chunk_offsets), &mut ctx).unwrap();
2528
2529 let sliced = patches.slice(1100..1250, &mut ctx).unwrap().unwrap();
2530
2531 assert_eq!(sliced.num_patches(), 1);
2532 assert_eq!(sliced.offset(), 1100);
2533 assert_eq!(
2534 sliced.search_index(100, &mut ctx).unwrap(),
2535 SearchResult::Found(0)
2536 ); assert_eq!(
2538 sliced.search_index(50, &mut ctx).unwrap(),
2539 SearchResult::NotFound(0)
2540 );
2541 assert_eq!(
2542 sliced.search_index(150, &mut ctx).unwrap(),
2543 SearchResult::NotFound(1)
2544 );
2545 assert_arrays_eq!(
2546 sliced.values(),
2547 PrimitiveArray::from_iter([20i32]),
2548 &mut ctx
2549 );
2550 }
2551
2552 #[test]
2553 fn test_chunk_offsets_slice_across_chunks() {
2554 let mut ctx = array_session().create_execution_ctx();
2555 let indices = buffer![100u64, 200, 1100, 1200, 2100, 2200].into_array();
2556 let values = buffer![10i32, 20, 30, 40, 50, 60].into_array();
2557 let chunk_offsets = buffer![0u64, 2, 4].into_array();
2558 let patches =
2559 Patches::new(3000, 0, indices, values, Some(chunk_offsets), &mut ctx).unwrap();
2560
2561 let sliced = patches.slice(150..2150, &mut ctx).unwrap().unwrap();
2562
2563 assert_eq!(sliced.num_patches(), 4);
2564 assert_eq!(sliced.offset(), 150);
2565
2566 assert_eq!(
2567 sliced.search_index(50, &mut ctx).unwrap(),
2568 SearchResult::Found(0)
2569 ); assert_eq!(
2571 sliced.search_index(950, &mut ctx).unwrap(),
2572 SearchResult::Found(1)
2573 ); assert_eq!(
2575 sliced.search_index(1050, &mut ctx).unwrap(),
2576 SearchResult::Found(2)
2577 ); assert_eq!(
2579 sliced.search_index(1950, &mut ctx).unwrap(),
2580 SearchResult::Found(3)
2581 ); assert_arrays_eq!(
2583 sliced.values(),
2584 PrimitiveArray::from_iter([20i32, 30, 40, 50]),
2585 &mut ctx
2586 );
2587 }
2588
2589 #[test]
2590 fn test_chunk_offsets_boundary_searches() {
2591 let mut ctx = array_session().create_execution_ctx();
2592 let indices = buffer![1023u64, 1024, 1025, 2047, 2048].into_array();
2593 let values = buffer![10i32, 20, 30, 40, 50].into_array();
2594 let chunk_offsets = buffer![0u64, 1, 4].into_array();
2595 let patches =
2596 Patches::new(3000, 0, indices, values, Some(chunk_offsets), &mut ctx).unwrap();
2597
2598 assert_eq!(
2599 patches.search_index(1023, &mut ctx).unwrap(),
2600 SearchResult::Found(0)
2601 );
2602 assert_eq!(
2603 patches.search_index(1024, &mut ctx).unwrap(),
2604 SearchResult::Found(1)
2605 );
2606 assert_eq!(
2607 patches.search_index(1025, &mut ctx).unwrap(),
2608 SearchResult::Found(2)
2609 );
2610 assert_eq!(
2611 patches.search_index(2047, &mut ctx).unwrap(),
2612 SearchResult::Found(3)
2613 );
2614 assert_eq!(
2615 patches.search_index(2048, &mut ctx).unwrap(),
2616 SearchResult::Found(4)
2617 );
2618
2619 assert_eq!(
2620 patches.search_index(1022, &mut ctx).unwrap(),
2621 SearchResult::NotFound(0)
2622 );
2623 assert_eq!(
2624 patches.search_index(2046, &mut ctx).unwrap(),
2625 SearchResult::NotFound(3)
2626 );
2627 }
2628
2629 #[test]
2630 fn test_chunk_offsets_slice_edge_cases() {
2631 let mut ctx = array_session().create_execution_ctx();
2632 let indices = buffer![0u64, 1, 1023, 1024, 2047, 2048].into_array();
2633 let values = buffer![10i32, 20, 30, 40, 50, 60].into_array();
2634 let chunk_offsets = buffer![0u64, 3, 5].into_array();
2635 let patches =
2636 Patches::new(3000, 0, indices, values, Some(chunk_offsets), &mut ctx).unwrap();
2637
2638 let sliced = patches.slice(0..10, &mut ctx).unwrap().unwrap();
2640 assert_eq!(sliced.num_patches(), 2);
2641 assert_eq!(
2642 sliced.search_index(0, &mut ctx).unwrap(),
2643 SearchResult::Found(0)
2644 );
2645 assert_eq!(
2646 sliced.search_index(1, &mut ctx).unwrap(),
2647 SearchResult::Found(1)
2648 );
2649
2650 assert_arrays_eq!(
2651 sliced.values(),
2652 PrimitiveArray::from_iter([10i32, 20]),
2653 &mut ctx
2654 );
2655
2656 let sliced = patches.slice(2040..3000, &mut ctx).unwrap().unwrap();
2658 assert_eq!(sliced.num_patches(), 2); assert_eq!(
2660 sliced.search_index(7, &mut ctx).unwrap(),
2661 SearchResult::Found(0)
2662 ); assert_eq!(
2664 sliced.search_index(8, &mut ctx).unwrap(),
2665 SearchResult::Found(1)
2666 ); assert_arrays_eq!(
2668 sliced.values(),
2669 PrimitiveArray::from_iter([50i32, 60]),
2670 &mut ctx
2671 );
2672 }
2673
2674 #[test]
2675 fn test_chunk_offsets_slice_nested() {
2676 let mut ctx = array_session().create_execution_ctx();
2677 let indices = buffer![100u64, 200, 300, 400, 500, 600].into_array();
2678 let values = buffer![10i32, 20, 30, 40, 50, 60].into_array();
2679 let chunk_offsets = buffer![0u64].into_array();
2680 let patches =
2681 Patches::new(1000, 0, indices, values, Some(chunk_offsets), &mut ctx).unwrap();
2682
2683 let sliced1 = patches.slice(150..550, &mut ctx).unwrap().unwrap();
2684 assert_eq!(sliced1.num_patches(), 4); let sliced2 = sliced1.slice(100..250, &mut ctx).unwrap().unwrap();
2687 assert_eq!(sliced2.num_patches(), 1); assert_eq!(sliced2.offset(), 250);
2689
2690 assert_eq!(
2691 sliced2.search_index(50, &mut ctx).unwrap(),
2692 SearchResult::Found(0)
2693 ); assert_eq!(
2695 sliced2.search_index(150, &mut ctx).unwrap(),
2696 SearchResult::NotFound(1)
2697 );
2698 assert_arrays_eq!(
2699 sliced1.values(),
2700 PrimitiveArray::from_iter([20i32, 30, 40, 50]),
2701 &mut ctx
2702 );
2703 assert_arrays_eq!(
2704 sliced2.values(),
2705 PrimitiveArray::from_iter([30i32]),
2706 &mut ctx
2707 );
2708 }
2709
2710 #[test]
2711 fn test_nested_slice_with_dropped_first_chunk() {
2712 let mut ctx = array_session().create_execution_ctx();
2713 let indices = buffer![0u64, 1024].into_array();
2715 let values = buffer![1i32, 2].into_array();
2716 let chunk_offsets = buffer![0u64, 1].into_array();
2717 let patches =
2718 Patches::new(2048, 0, indices, values, Some(chunk_offsets), &mut ctx).unwrap();
2719
2720 let dropped_first = patches.slice(1024..2048, &mut ctx).unwrap().unwrap();
2722 let resliced = dropped_first.slice(0..1024, &mut ctx).unwrap().unwrap();
2723 assert_eq!(resliced.num_patches(), 1);
2724 assert_eq!(resliced.array_len(), 1024);
2725 assert_eq!(resliced.offset(), 1024);
2726 assert_eq!(
2727 resliced.search_index(0, &mut ctx).unwrap(),
2728 SearchResult::Found(0)
2729 );
2730 assert_arrays_eq!(
2731 resliced.values(),
2732 PrimitiveArray::from_iter([2i32]),
2733 &mut ctx
2734 );
2735 }
2736
2737 #[test]
2738 fn test_index_larger_than_length() {
2739 let mut ctx = array_session().create_execution_ctx();
2740 let chunk_offsets = buffer![0u64].into_array();
2741 let indices = buffer![1023u64].into_array();
2742 let values = buffer![42i32].into_array();
2743 let patches =
2744 Patches::new(1024, 0, indices, values, Some(chunk_offsets), &mut ctx).unwrap();
2745 assert_eq!(
2746 patches.search_index(2048, &mut ctx).unwrap(),
2747 SearchResult::NotFound(1)
2748 );
2749 }
2750}