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