1use std::cmp::Ordering;
5use std::fmt::Debug;
6use std::hash::Hash;
7use std::ops::Range;
8
9use num_traits::NumCast;
10use vortex_buffer::BitBuffer;
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_err;
18use vortex_mask::AllOr;
19use vortex_mask::Mask;
20use vortex_utils::aliases::hash_map::HashMap;
21
22use crate::ArrayRef;
23use crate::ArraySlots;
24use crate::ExecutionCtx;
25use crate::IntoArray;
26use crate::VortexSessionExecute;
27use crate::arrays::Primitive;
28use crate::arrays::PrimitiveArray;
29use crate::arrays::primitive::PrimitiveArrayExt;
30use crate::builtins::ArrayBuiltins;
31use crate::dtype::DType;
32use crate::dtype::IntegerPType;
33use crate::dtype::NativePType;
34use crate::dtype::Nullability;
35use crate::dtype::Nullability::NonNullable;
36use crate::dtype::PType;
37use crate::dtype::UnsignedPType;
38use crate::legacy_session;
39use crate::match_each_unsigned_integer_ptype;
40use crate::scalar::Scalar;
41use crate::search_sorted::SearchResult;
42use crate::search_sorted::SearchSorted;
43use crate::search_sorted::SearchSortedPrimitiveArray;
44use crate::search_sorted::SearchSortedSide;
45use crate::validity::Validity;
46
47pub const PATCH_CHUNK_SIZE: usize = 1024;
50
51#[derive(Copy, Clone, prost::Message)]
52pub struct PatchesMetadata {
53 #[prost(uint64, tag = "1")]
54 len: u64,
55 #[prost(uint64, tag = "2")]
56 offset: u64,
57 #[prost(enumeration = "PType", tag = "3")]
58 indices_ptype: i32,
59 #[prost(uint64, optional, tag = "4")]
60 chunk_offsets_len: Option<u64>,
61 #[prost(enumeration = "PType", optional, tag = "5")]
62 chunk_offsets_ptype: Option<i32>,
63 #[prost(uint64, optional, tag = "6")]
64 offset_within_chunk: Option<u64>,
65}
66
67impl PatchesMetadata {
68 #[inline]
69 pub fn new(
70 len: usize,
71 offset: usize,
72 indices_ptype: PType,
73 chunk_offsets_len: Option<usize>,
74 chunk_offsets_ptype: Option<PType>,
75 offset_within_chunk: Option<usize>,
76 ) -> Self {
77 Self {
78 len: len as u64,
79 offset: offset as u64,
80 indices_ptype: indices_ptype as i32,
81 chunk_offsets_len: chunk_offsets_len.map(|len| len as u64),
82 chunk_offsets_ptype: chunk_offsets_ptype.map(|pt| pt as i32),
83 offset_within_chunk: offset_within_chunk.map(|len| len as u64),
84 }
85 }
86
87 #[inline]
88 pub fn len(&self) -> VortexResult<usize> {
89 usize::try_from(self.len).map_err(|_| vortex_err!("len does not fit in usize"))
90 }
91
92 #[inline]
93 pub fn is_empty(&self) -> bool {
94 self.len == 0
95 }
96
97 #[inline]
98 pub fn offset(&self) -> VortexResult<usize> {
99 usize::try_from(self.offset).map_err(|_| vortex_err!("offset does not fit in usize"))
100 }
101
102 #[inline]
103 pub fn chunk_offsets_dtype(&self) -> VortexResult<Option<DType>> {
104 self.chunk_offsets_ptype
105 .map(|t| {
106 PType::try_from(t)
107 .map_err(|e| vortex_err!("invalid i32 value {t} for PType: {}", e))
108 .map(|ptype| DType::Primitive(ptype, NonNullable))
109 })
110 .transpose()
111 }
112
113 #[inline]
114 pub fn indices_dtype(&self) -> VortexResult<DType> {
115 let ptype = PType::try_from(self.indices_ptype).map_err(|e| {
116 vortex_err!("invalid i32 value {} for PType: {}", self.indices_ptype, e)
117 })?;
118 vortex_ensure!(
119 ptype.is_unsigned_int(),
120 "Patch indices must be unsigned integers"
121 );
122 Ok(DType::Primitive(ptype, NonNullable))
123 }
124}
125
126#[derive(Clone, Debug, Hash, PartialEq, Eq)]
131pub struct PatchesData {
132 offset: usize,
133 offset_within_chunk: Option<usize>,
134}
135
136#[derive(Copy, Clone, Debug)]
138pub struct PatchSlotIndices {
139 pub indices: usize,
140 pub values: usize,
141 pub chunk_offsets: usize,
142}
143
144impl PatchesData {
145 pub fn from_patches(patches: &Patches) -> Self {
147 Self {
148 offset: patches.offset(),
149 offset_within_chunk: patches.offset_within_chunk(),
150 }
151 }
152
153 pub fn patches_from_slots(
158 patches_data: Option<&Self>,
159 len: usize,
160 slots: &[Option<ArrayRef>],
161 slot_idx: PatchSlotIndices,
162 ) -> Option<Patches> {
163 let data = patches_data?;
164 let indices = slots[slot_idx.indices]
165 .as_ref()
166 .vortex_expect("patches_data is set but patch_indices slot is missing");
167 let values = slots[slot_idx.values]
168 .as_ref()
169 .vortex_expect("patches_data is set but patch_values slot is missing");
170 Some(unsafe {
171 Patches::new_unchecked(
172 len,
173 data.offset,
174 indices.clone(),
175 values.clone(),
176 slots[slot_idx.chunk_offsets].clone(),
177 data.offset_within_chunk,
178 )
179 })
180 }
181
182 pub fn push_slots(slots: &mut ArraySlots, patches: Option<&Patches>) {
186 match patches {
187 Some(p) => {
188 slots.push(Some(p.indices().clone()));
189 slots.push(Some(p.values().clone()));
190 slots.push(p.chunk_offsets().clone());
191 }
192 None => {
193 slots.push(None);
194 slots.push(None);
195 slots.push(None);
196 }
197 }
198 }
199
200 #[inline]
202 pub fn offset(&self) -> usize {
203 self.offset
204 }
205
206 #[inline]
208 pub fn offset_within_chunk(&self) -> Option<usize> {
209 self.offset_within_chunk
210 }
211}
212
213#[derive(Debug, Clone)]
215pub struct Patches {
216 array_len: usize,
217 offset: usize,
218 indices: ArrayRef,
219 values: ArrayRef,
220 chunk_offsets: Option<ArrayRef>,
227 offset_within_chunk: Option<usize>,
237}
238
239impl Patches {
240 #[allow(clippy::disallowed_methods)]
241 pub fn new(
242 array_len: usize,
243 offset: usize,
244 indices: ArrayRef,
245 values: ArrayRef,
246 chunk_offsets: Option<ArrayRef>,
247 ) -> VortexResult<Self> {
248 vortex_ensure!(
249 indices.len() == values.len(),
250 "Patch indices and values must have the same length"
251 );
252 vortex_ensure!(
253 indices.dtype().is_unsigned_int() && !indices.dtype().is_nullable(),
254 "Patch indices must be non-nullable unsigned integers, got {:?}",
255 indices.dtype()
256 );
257
258 vortex_ensure!(
259 indices.len() <= array_len,
260 "Patch indices must be shorter than the array length"
261 );
262 vortex_ensure!(!indices.is_empty(), "Patch indices must not be empty");
263
264 if indices.is_host() && values.is_host() {
267 let max = usize::try_from(&indices.execute_scalar(
268 indices.len() - 1,
269 &mut legacy_session().create_execution_ctx(),
270 )?)
271 .map_err(|_| vortex_err!("indices must be a number"))?;
272 vortex_ensure!(
273 max - offset < array_len,
274 "Patch indices {max:?}, offset {offset} are longer than the array length {array_len}"
275 );
276
277 #[cfg(debug_assertions)]
278 {
279 use crate::aggregate_fn::fns::is_sorted::is_sorted;
280 let mut ctx = legacy_session().create_execution_ctx();
281 assert!(
282 is_sorted(&indices, &mut ctx).unwrap_or(false),
283 "Patch indices must be sorted"
284 );
285 }
286 }
287
288 Ok(Self {
289 array_len,
290 offset,
291 indices,
292 values,
293 chunk_offsets: chunk_offsets.clone(),
294 offset_within_chunk: chunk_offsets.map(|_| 0),
296 })
297 }
298
299 pub unsafe fn new_unchecked(
309 array_len: usize,
310 offset: usize,
311 indices: ArrayRef,
312 values: ArrayRef,
313 chunk_offsets: Option<ArrayRef>,
314 offset_within_chunk: Option<usize>,
315 ) -> Self {
316 Self {
317 array_len,
318 offset,
319 indices,
320 values,
321 chunk_offsets,
322 offset_within_chunk,
323 }
324 }
325
326 #[inline]
327 pub fn array_len(&self) -> usize {
328 self.array_len
329 }
330
331 #[inline]
332 pub fn num_patches(&self) -> usize {
333 self.indices.len()
334 }
335
336 #[inline]
337 pub fn dtype(&self) -> &DType {
338 self.values.dtype()
339 }
340
341 #[inline]
342 pub fn indices(&self) -> &ArrayRef {
343 &self.indices
344 }
345
346 #[inline]
347 pub fn into_indices(self) -> ArrayRef {
348 self.indices
349 }
350
351 #[inline]
352 pub fn indices_mut(&mut self) -> &mut ArrayRef {
353 &mut self.indices
354 }
355
356 #[inline]
357 pub fn values(&self) -> &ArrayRef {
358 &self.values
359 }
360
361 #[inline]
362 pub fn into_values(self) -> ArrayRef {
363 self.values
364 }
365
366 #[inline]
367 pub fn values_mut(&mut self) -> &mut ArrayRef {
368 &mut self.values
369 }
370
371 #[inline]
372 pub fn offset(&self) -> usize {
374 self.offset
375 }
376
377 #[inline]
378 pub fn chunk_offsets(&self) -> &Option<ArrayRef> {
379 &self.chunk_offsets
380 }
381
382 #[inline]
383 #[allow(clippy::disallowed_methods)]
384 pub fn chunk_offset_at(&self, idx: usize) -> VortexResult<usize> {
385 let Some(chunk_offsets) = &self.chunk_offsets else {
386 vortex_bail!("chunk_offsets must be set to retrieve offset at index")
387 };
388
389 chunk_offsets
390 .execute_scalar(idx, &mut legacy_session().create_execution_ctx())?
391 .as_primitive()
392 .as_::<usize>()
393 .ok_or_else(|| vortex_err!("chunk offset does not fit in usize"))
394 }
395
396 #[inline]
405 pub fn offset_within_chunk(&self) -> Option<usize> {
406 self.offset_within_chunk
407 }
408
409 #[inline]
410 pub fn indices_ptype(&self) -> VortexResult<PType> {
411 PType::try_from(self.indices.dtype())
412 .map_err(|_| vortex_err!("indices dtype is not primitive"))
413 }
414
415 pub fn to_metadata(&self, len: usize, dtype: &DType) -> VortexResult<PatchesMetadata> {
416 if self.indices.len() > len {
417 vortex_bail!(
418 "Patch indices {} are longer than the array length {}",
419 self.indices.len(),
420 len
421 );
422 }
423 if self.values.dtype() != dtype {
424 vortex_bail!(
425 "Patch values dtype {} does not match array dtype {}",
426 self.values.dtype(),
427 dtype
428 );
429 }
430 let chunk_offsets_len = self.chunk_offsets.as_ref().map(|co| co.len());
431 let chunk_offsets_ptype = self.chunk_offsets.as_ref().map(|co| co.dtype().as_ptype());
432
433 Ok(PatchesMetadata::new(
434 self.indices.len(),
435 self.offset,
436 self.indices.dtype().as_ptype(),
437 chunk_offsets_len,
438 chunk_offsets_ptype,
439 self.offset_within_chunk,
440 ))
441 }
442
443 #[allow(clippy::disallowed_methods)]
445 pub fn get_patched(&self, index: usize) -> VortexResult<Option<Scalar>> {
446 self.search_index(index)?
447 .to_found()
448 .map(|patch_idx| {
449 self.values()
450 .execute_scalar(patch_idx, &mut legacy_session().create_execution_ctx())
451 })
452 .transpose()
453 }
454
455 pub fn search_index(&self, index: usize) -> VortexResult<SearchResult> {
473 if self.chunk_offsets.is_some() {
474 return self.search_index_chunked(index);
475 }
476
477 search_index_binary_search(&self.indices, index + self.offset)
478 }
479
480 fn search_index_chunked(&self, index: usize) -> VortexResult<SearchResult> {
490 let Some(chunk_offsets) = &self.chunk_offsets else {
491 vortex_bail!("chunk_offsets is required to be set")
492 };
493
494 let Some(offset_within_chunk) = self.offset_within_chunk else {
495 vortex_bail!("offset_within_chunk is required to be set")
496 };
497
498 if index >= self.array_len() {
499 return Ok(SearchResult::NotFound(self.indices().len()));
500 }
501
502 let chunk_idx = (index + self.offset % PATCH_CHUNK_SIZE) / PATCH_CHUNK_SIZE;
503
504 let base_offset = self.chunk_offset_at(0)?;
506
507 let patches_start_idx = (self.chunk_offset_at(chunk_idx)? - base_offset)
508 .saturating_sub(offset_within_chunk);
515
516 let patches_end_idx = if chunk_idx < chunk_offsets.len() - 1 {
517 (self.chunk_offset_at(chunk_idx + 1)? - base_offset)
518 .saturating_sub(offset_within_chunk)
519 .min(self.indices.len())
520 } else {
521 self.indices.len()
522 };
523
524 let chunk_indices = self.indices.slice(patches_start_idx..patches_end_idx)?;
525 let result = search_index_binary_search(&chunk_indices, index + self.offset)?;
526
527 Ok(match result {
528 SearchResult::Found(idx) => SearchResult::Found(patches_start_idx + idx),
529 SearchResult::NotFound(idx) => SearchResult::NotFound(patches_start_idx + idx),
530 })
531 }
532
533 fn search_index_chunked_batch<T, O>(
539 &self,
540 indices: &[T],
541 chunk_offsets: &[O],
542 index: T,
543 ) -> VortexResult<SearchResult>
544 where
545 T: UnsignedPType,
546 O: UnsignedPType,
547 usize: TryFrom<T>,
548 usize: TryFrom<O>,
549 {
550 let Some(offset_within_chunk) = self.offset_within_chunk else {
551 vortex_bail!("offset_within_chunk is required to be set")
552 };
553
554 let chunk_idx = {
555 let Ok(index) = usize::try_from(index) else {
556 return Ok(SearchResult::NotFound(indices.len()));
558 };
559
560 if index >= self.array_len() {
561 return Ok(SearchResult::NotFound(self.indices().len()));
562 }
563
564 (index + self.offset % PATCH_CHUNK_SIZE) / PATCH_CHUNK_SIZE
565 };
566
567 let chunk_offset = usize::try_from(chunk_offsets[chunk_idx] - chunk_offsets[0])
569 .map_err(|_| vortex_err!("chunk_offset failed to convert to usize"))?;
570
571 let patches_start_idx = chunk_offset
572 .saturating_sub(offset_within_chunk);
579
580 let patches_end_idx = if chunk_idx < chunk_offsets.len() - 1 {
581 usize::try_from(chunk_offsets[chunk_idx + 1] - chunk_offsets[0])
582 .map_err(|_| vortex_err!("patches_end_idx failed to convert to usize"))?
583 .saturating_sub(offset_within_chunk)
584 .min(indices.len())
585 } else {
586 self.indices.len()
587 };
588
589 let Some(offset) = T::from(self.offset) else {
590 return Ok(SearchResult::NotFound(indices.len()));
592 };
593
594 let chunk_indices = &indices[patches_start_idx..patches_end_idx];
595 let result = chunk_indices.search_sorted(&(index + offset), SearchSortedSide::Left)?;
596
597 Ok(match result {
598 SearchResult::Found(idx) => SearchResult::Found(patches_start_idx + idx),
599 SearchResult::NotFound(idx) => SearchResult::NotFound(patches_start_idx + idx),
600 })
601 }
602
603 #[allow(clippy::disallowed_methods)]
605 pub fn min_index(&self) -> VortexResult<usize> {
606 let first = self
607 .indices
608 .execute_scalar(0, &mut legacy_session().create_execution_ctx())?
609 .as_primitive()
610 .as_::<usize>()
611 .ok_or_else(|| vortex_err!("index does not fit in usize"))?;
612 Ok(first - self.offset)
613 }
614
615 #[allow(clippy::disallowed_methods)]
617 pub fn max_index(&self) -> VortexResult<usize> {
618 let last = self
619 .indices
620 .execute_scalar(
621 self.indices.len() - 1,
622 &mut legacy_session().create_execution_ctx(),
623 )?
624 .as_primitive()
625 .as_::<usize>()
626 .ok_or_else(|| vortex_err!("index does not fit in usize"))?;
627 Ok(last - self.offset)
628 }
629
630 pub fn filter(&self, mask: &Mask, ctx: &mut ExecutionCtx) -> VortexResult<Option<Self>> {
632 if mask.len() != self.array_len {
633 vortex_bail!(
634 "Filter mask length {} does not match array length {}",
635 mask.len(),
636 self.array_len
637 );
638 }
639
640 match mask.indices() {
641 AllOr::All => Ok(Some(self.clone())),
642 AllOr::None => Ok(None),
643 AllOr::Some(mask_indices) => {
644 let flat_indices = self.indices().clone().execute::<PrimitiveArray>(ctx)?;
645 match_each_unsigned_integer_ptype!(flat_indices.ptype(), |I| {
646 filter_patches_with_mask(
647 flat_indices.as_slice::<I>(),
648 self.offset(),
649 self.values(),
650 mask_indices,
651 )
652 })
653 }
654 }
655 }
656
657 pub fn mask(&self, mask: &Mask, ctx: &mut ExecutionCtx) -> VortexResult<Option<Self>> {
666 if mask.len() != self.array_len {
667 vortex_bail!(
668 "Filter mask length {} does not match array length {}",
669 mask.len(),
670 self.array_len
671 );
672 }
673
674 let filter_mask = match mask.bit_buffer() {
675 AllOr::All => return Ok(None),
676 AllOr::None => return self.clone().into_nullable_values().map(Some),
677 AllOr::Some(masked) => {
678 let patch_indices = self.indices().clone().execute::<PrimitiveArray>(ctx)?;
679 match_each_unsigned_integer_ptype!(patch_indices.ptype(), |P| {
680 let patch_indices = patch_indices.as_slice::<P>();
681 Mask::from_buffer(BitBuffer::collect_bool(patch_indices.len(), |i| {
682 #[allow(clippy::cast_possible_truncation)]
683 let idx = (patch_indices[i] as usize) - self.offset;
684 !masked.value(idx)
685 }))
686 })
687 }
688 };
689
690 if filter_mask.all_false() {
691 return Ok(None);
692 }
693
694 let filtered_indices = self.indices.filter(filter_mask.clone())?;
696 let filtered_values = self.values.filter(filter_mask)?;
697
698 Self {
699 array_len: self.array_len,
700 offset: self.offset,
701 indices: filtered_indices,
702 values: filtered_values,
703 chunk_offsets: None,
705 offset_within_chunk: self.offset_within_chunk,
706 }
707 .into_nullable_values()
708 .map(Some)
709 }
710
711 fn into_nullable_values(self) -> VortexResult<Self> {
716 if self.values.dtype().is_nullable() {
717 return Ok(self);
718 }
719 let nullable = self.values.dtype().as_nullable();
720 self.map_values(|values| values.cast(nullable))
721 }
722
723 #[allow(clippy::disallowed_methods)]
725 pub fn slice(&self, range: Range<usize>) -> VortexResult<Option<Self>> {
726 let slice_start_idx = self.search_index(range.start)?.to_index();
727 let slice_end_idx = self.search_index(range.end)?.to_index();
728
729 if slice_start_idx == slice_end_idx {
730 return Ok(None);
731 }
732
733 let values = self.values().slice(slice_start_idx..slice_end_idx)?;
734 let indices = self.indices().slice(slice_start_idx..slice_end_idx)?;
735
736 let new_chunk_offsets = self
737 .chunk_offsets
738 .as_ref()
739 .map(|chunk_offsets| -> VortexResult<ArrayRef> {
740 let chunk_relative_offset = self.offset % PATCH_CHUNK_SIZE;
741 let chunk_start_idx = (chunk_relative_offset + range.start) / PATCH_CHUNK_SIZE;
742 let chunk_end_idx = (chunk_relative_offset + range.end).div_ceil(PATCH_CHUNK_SIZE);
743 chunk_offsets.slice(chunk_start_idx..chunk_end_idx)
744 })
745 .transpose()?;
746
747 let offset_within_chunk = new_chunk_offsets
748 .as_ref()
749 .map(|new_chunk_offsets| -> VortexResult<usize> {
750 let new_chunk_base = new_chunk_offsets
751 .execute_scalar(0, &mut legacy_session().create_execution_ctx())?
752 .as_primitive()
753 .as_::<usize>()
754 .ok_or_else(|| vortex_err!("chunk offset does not fit in usize"))?;
755 let parent_chunk_base = self.chunk_offset_at(0)?;
756 let parent_within = self.offset_within_chunk.unwrap_or(0);
757 Ok(parent_chunk_base + parent_within + slice_start_idx - new_chunk_base)
758 })
759 .transpose()?;
760
761 Ok(Some(Self {
762 array_len: range.len(),
763 offset: range.start + self.offset(),
764 indices,
765 values,
766 chunk_offsets: new_chunk_offsets,
767 offset_within_chunk,
768 }))
769 }
770
771 const PREFER_MAP_WHEN_PATCHES_OVER_INDICES_LESS_THAN: f64 = 5.0;
773
774 fn is_map_faster_than_search(&self, take_indices: &PrimitiveArray) -> bool {
775 (self.num_patches() as f64 / take_indices.len() as f64)
776 < Self::PREFER_MAP_WHEN_PATCHES_OVER_INDICES_LESS_THAN
777 }
778
779 pub fn take_with_nulls(
783 &self,
784 take_indices: &ArrayRef,
785 ctx: &mut ExecutionCtx,
786 ) -> VortexResult<Option<Self>> {
787 if take_indices.is_empty() {
788 return Ok(None);
789 }
790
791 let take_indices = take_indices.clone().execute::<PrimitiveArray>(ctx)?;
792 if self.is_map_faster_than_search(&take_indices) {
793 self.take_map(take_indices, true, ctx)
794 } else {
795 self.take_search(take_indices, true, ctx)
796 }
797 }
798
799 pub fn take(
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, false, ctx)
814 } else {
815 self.take_search(take_indices, false, ctx)
816 }
817 }
818
819 #[expect(
820 clippy::cognitive_complexity,
821 reason = "complexity is from nested match_each_* macros"
822 )]
823 pub fn take_search(
824 &self,
825 take_indices: PrimitiveArray,
826 include_nulls: bool,
827 ctx: &mut ExecutionCtx,
828 ) -> VortexResult<Option<Self>> {
829 let take_indices_validity = take_indices.validity()?;
830 let take_indices_unsigned =
833 take_indices.reinterpret_cast(take_indices.ptype().to_unsigned());
834 let patch_indices = self.indices.clone().execute::<PrimitiveArray>(ctx)?;
835 let chunk_offsets = self
836 .chunk_offsets()
837 .as_ref()
838 .map(|co| co.clone().execute::<PrimitiveArray>(ctx))
839 .transpose()?;
840
841 let (values_indices, new_indices): (BufferMut<u64>, BufferMut<u64>) =
842 match_each_unsigned_integer_ptype!(patch_indices.ptype(), |PatchT| {
843 let patch_indices_slice = patch_indices.as_slice::<PatchT>();
844 match_each_unsigned_integer_ptype!(take_indices_unsigned.ptype(), |TakeT| {
845 let take_slice = take_indices_unsigned.as_slice::<TakeT>();
846
847 if let Some(chunk_offsets) = chunk_offsets {
848 match_each_unsigned_integer_ptype!(chunk_offsets.ptype(), |OffsetT| {
849 let chunk_offsets = chunk_offsets.as_slice::<OffsetT>();
850 take_indices_with_search_fn(
851 patch_indices_slice,
852 take_slice,
853 take_indices
854 .as_ref()
855 .validity()?
856 .execute_mask(take_indices.as_ref().len(), ctx)?,
857 include_nulls,
858 |take_idx| {
859 self.search_index_chunked_batch(
860 patch_indices_slice,
861 chunk_offsets,
862 take_idx,
863 )
864 },
865 )?
866 })
867 } else {
868 take_indices_with_search_fn(
869 patch_indices_slice,
870 take_slice,
871 take_indices
872 .as_ref()
873 .validity()?
874 .execute_mask(take_indices.as_ref().len(), ctx)?,
875 include_nulls,
876 |take_idx| {
877 let Some(offset) = <PatchT as NumCast>::from(self.offset) else {
878 return Ok(SearchResult::NotFound(patch_indices_slice.len()));
880 };
881
882 patch_indices_slice
883 .search_sorted(&(take_idx + offset), SearchSortedSide::Left)
884 },
885 )?
886 }
887 })
888 });
889
890 if new_indices.is_empty() {
891 return Ok(None);
892 }
893
894 let new_indices = new_indices.into_array();
895 let new_array_len = take_indices.len();
896 let values_validity = take_indices_validity.take(&new_indices)?;
897
898 Ok(Some(Self {
899 array_len: new_array_len,
900 offset: 0,
901 indices: new_indices,
902 values: self
903 .values()
904 .take(PrimitiveArray::new(values_indices, values_validity).into_array())?,
905 chunk_offsets: None,
906 offset_within_chunk: Some(0), }))
908 }
909
910 pub fn take_map(
911 &self,
912 take_indices: PrimitiveArray,
913 include_nulls: bool,
914 ctx: &mut ExecutionCtx,
915 ) -> VortexResult<Option<Self>> {
916 let indices = self.indices.clone().execute::<PrimitiveArray>(ctx)?;
917 let new_length = take_indices.len();
918 let take_indices_unsigned =
920 take_indices.reinterpret_cast(take_indices.ptype().to_unsigned());
921
922 let min_index = self.min_index()?;
923 let max_index = self.max_index()?;
924
925 let Some((new_sparse_indices, value_indices)) =
926 match_each_unsigned_integer_ptype!(indices.ptype(), |Indices| {
927 match_each_unsigned_integer_ptype!(take_indices_unsigned.ptype(), |TakeIndices| {
928 let take_validity = take_indices
929 .validity()?
930 .execute_mask(take_indices.len(), ctx)?;
931 let take_nullability = take_indices.validity()?.nullability();
932 let take_slice = take_indices_unsigned.as_slice::<TakeIndices>();
933 take_map::<_, TakeIndices>(
934 indices.as_slice::<Indices>(),
935 take_slice,
936 take_validity,
937 take_nullability,
938 self.offset(),
939 min_index,
940 max_index,
941 include_nulls,
942 )?
943 })
944 })
945 else {
946 return Ok(None);
947 };
948
949 let taken_values = self.values().take(value_indices)?;
950
951 Ok(Some(Patches {
952 array_len: new_length,
953 offset: 0,
954 indices: new_sparse_indices,
955 values: taken_values,
956 chunk_offsets: None,
958 offset_within_chunk: self.offset_within_chunk,
959 }))
960 }
961
962 pub fn map_values<F>(self, f: F) -> VortexResult<Self>
963 where
964 F: FnOnce(ArrayRef) -> VortexResult<ArrayRef>,
965 {
966 let values = f(self.values)?;
967 if self.indices.len() != values.len() {
968 vortex_bail!(
969 "map_values must preserve length: expected {} received {}",
970 self.indices.len(),
971 values.len()
972 )
973 }
974
975 Ok(Self {
976 array_len: self.array_len,
977 offset: self.offset,
978 indices: self.indices,
979 values,
980 chunk_offsets: self.chunk_offsets,
981 offset_within_chunk: self.offset_within_chunk,
982 })
983 }
984}
985
986fn search_index_binary_search(indices: &ArrayRef, needle: usize) -> VortexResult<SearchResult> {
992 if let Some(primitive) = indices.as_opt::<Primitive>() {
993 match_each_unsigned_integer_ptype!(primitive.ptype(), |T| {
994 let Ok(needle) = T::try_from(needle) else {
995 return Ok(SearchResult::NotFound(primitive.len()));
1000 };
1001 return primitive
1002 .as_slice::<T>()
1003 .search_sorted(&needle, SearchSortedSide::Left);
1004 });
1005 }
1006
1007 search_index_binary_search_scalar(indices, needle)
1008}
1009
1010#[allow(clippy::disallowed_methods)]
1011fn search_index_binary_search_scalar(
1012 indices: &ArrayRef,
1013 needle: usize,
1014) -> VortexResult<SearchResult> {
1015 match_each_unsigned_integer_ptype!(indices.dtype().as_ptype(), |T| {
1016 SearchSortedPrimitiveArray::<T>::new(indices, &mut legacy_session().create_execution_ctx())
1017 .search_sorted(&needle, SearchSortedSide::Left)
1018 })
1019}
1020
1021#[expect(clippy::too_many_arguments)] fn take_map<I: NativePType + Hash + Eq + TryFrom<usize>, T: NativePType>(
1023 indices: &[I],
1024 take_indices: &[T],
1025 take_validity: Mask,
1026 take_nullability: Nullability,
1027 indices_offset: usize,
1028 min_index: usize,
1029 max_index: usize,
1030 include_nulls: bool,
1031) -> VortexResult<Option<(ArrayRef, ArrayRef)>>
1032where
1033 usize: TryFrom<T>,
1034 VortexError: From<<I as TryFrom<usize>>::Error>,
1035{
1036 let offset_i = I::try_from(indices_offset)?;
1037
1038 let sparse_index_to_value_index: HashMap<I, usize> = indices
1039 .iter()
1040 .copied()
1041 .map(|idx| idx - offset_i)
1042 .enumerate()
1043 .map(|(value_index, sparse_index)| (sparse_index, value_index))
1044 .collect();
1045
1046 let mut new_sparse_indices = BufferMut::<u64>::with_capacity(take_indices.len());
1047 let mut value_indices = BufferMut::<u64>::with_capacity(take_indices.len());
1048
1049 for (idx_in_take, &take_idx) in take_indices.iter().enumerate() {
1050 let ti = usize::try_from(take_idx)
1051 .map_err(|_| vortex_err!("Failed to convert index to usize"))?;
1052
1053 let is_null = match take_validity.bit_buffer() {
1055 AllOr::All => false,
1056 AllOr::None => true,
1057 AllOr::Some(buf) => !buf.value(idx_in_take),
1058 };
1059 if is_null {
1060 if include_nulls {
1061 new_sparse_indices.push(idx_in_take as u64);
1062 value_indices.push(0);
1063 }
1064 } else if ti >= min_index && ti <= max_index {
1065 let ti_as_i = I::try_from(ti)
1066 .map_err(|_| vortex_err!("take index does not fit in index type"))?;
1067 if let Some(&value_index) = sparse_index_to_value_index.get(&ti_as_i) {
1068 new_sparse_indices.push(idx_in_take as u64);
1069 value_indices.push(value_index as u64);
1070 }
1071 }
1072 }
1073
1074 if new_sparse_indices.is_empty() {
1075 return Ok(None);
1076 }
1077
1078 let new_sparse_indices = new_sparse_indices.into_array();
1079 let values_validity =
1080 Validity::from_mask(take_validity, take_nullability).take(&new_sparse_indices)?;
1081 Ok(Some((
1082 new_sparse_indices,
1083 PrimitiveArray::new(value_indices, values_validity).into_array(),
1084 )))
1085}
1086
1087fn filter_patches_with_mask<T: IntegerPType>(
1093 patch_indices: &[T],
1094 offset: usize,
1095 patch_values: &ArrayRef,
1096 mask_indices: &[usize],
1097) -> VortexResult<Option<Patches>> {
1098 let true_count = mask_indices.len();
1099 let mut new_patch_indices = BufferMut::<u64>::with_capacity(true_count);
1100 let mut new_mask_indices = Vec::with_capacity(true_count);
1101
1102 const STRIDE: usize = 4;
1106
1107 let mut mask_idx = 0usize;
1108 let mut true_idx = 0usize;
1109
1110 while mask_idx < patch_indices.len() && true_idx < true_count {
1111 if (mask_idx + STRIDE) < patch_indices.len() && (true_idx + STRIDE) < mask_indices.len() {
1118 let left_min = patch_indices[mask_idx]
1120 .to_usize()
1121 .ok_or_else(|| vortex_err!("patch index does not fit in usize"))?
1122 - offset;
1123 let left_max = patch_indices[mask_idx + STRIDE]
1124 .to_usize()
1125 .ok_or_else(|| vortex_err!("patch index does not fit in usize"))?
1126 - offset;
1127 let right_min = mask_indices[true_idx];
1128 let right_max = mask_indices[true_idx + STRIDE];
1129
1130 if left_min > right_max {
1131 true_idx += STRIDE;
1133 continue;
1134 } else if right_min > left_max {
1135 mask_idx += STRIDE;
1136 continue;
1137 } else {
1138 }
1140 }
1141
1142 let left = patch_indices[mask_idx]
1145 .to_usize()
1146 .ok_or_else(|| vortex_err!("patch index does not fit in usize"))?
1147 - offset;
1148 let right = mask_indices[true_idx];
1149
1150 match left.cmp(&right) {
1151 Ordering::Less => {
1152 mask_idx += 1;
1153 }
1154 Ordering::Greater => {
1155 true_idx += 1;
1156 }
1157 Ordering::Equal => {
1158 new_mask_indices.push(mask_idx);
1160 new_patch_indices.push(true_idx as u64);
1161
1162 mask_idx += 1;
1163 true_idx += 1;
1164 }
1165 }
1166 }
1167
1168 if new_mask_indices.is_empty() {
1169 return Ok(None);
1170 }
1171
1172 let new_patch_indices = new_patch_indices.into_array();
1173 let new_patch_values =
1174 patch_values.filter(Mask::from_indices(patch_values.len(), new_mask_indices))?;
1175
1176 Ok(Some(Patches::new(
1177 true_count,
1178 0,
1179 new_patch_indices,
1180 new_patch_values,
1181 None,
1183 )?))
1184}
1185
1186fn take_indices_with_search_fn<
1187 I: UnsignedPType,
1188 T: IntegerPType,
1189 F: Fn(I) -> VortexResult<SearchResult>,
1190>(
1191 indices: &[I],
1192 take_indices: &[T],
1193 take_validity: Mask,
1194 include_nulls: bool,
1195 search_fn: F,
1196) -> VortexResult<(BufferMut<u64>, BufferMut<u64>)> {
1197 let mut values_indices = BufferMut::with_capacity(take_indices.len());
1198 let mut new_indices = BufferMut::with_capacity(take_indices.len());
1199
1200 for (new_patch_idx, &take_idx) in take_indices.iter().enumerate() {
1201 if !take_validity.value(new_patch_idx) {
1202 if include_nulls {
1203 values_indices.push(0u64);
1205 new_indices.push(new_patch_idx as u64);
1206 }
1207 continue;
1208 } else {
1209 let search_result = match I::from(take_idx) {
1210 Some(idx) => search_fn(idx)?,
1211 None => SearchResult::NotFound(indices.len()),
1212 };
1213
1214 if let Some(patch_idx) = search_result.to_found() {
1215 values_indices.push(patch_idx as u64);
1216 new_indices.push(new_patch_idx as u64);
1217 }
1218 }
1219 }
1220
1221 Ok((values_indices, new_indices))
1222}
1223
1224#[cfg(test)]
1225mod test {
1226 use vortex_buffer::BufferMut;
1227 use vortex_buffer::buffer;
1228 use vortex_mask::Mask;
1229
1230 use crate::IntoArray;
1231 use crate::VortexSessionExecute;
1232 use crate::array_session;
1233 use crate::assert_arrays_eq;
1234 use crate::patches::Patches;
1235 use crate::patches::PrimitiveArray;
1236 use crate::search_sorted::SearchResult;
1237 use crate::validity::Validity;
1238
1239 #[test]
1240 fn test_filter() {
1241 let mut ctx = array_session().create_execution_ctx();
1242 let patches = Patches::new(
1243 100,
1244 0,
1245 buffer![10u32, 11, 20].into_array(),
1246 buffer![100, 110, 200].into_array(),
1247 None,
1248 )
1249 .unwrap();
1250
1251 let filtered = patches
1252 .filter(&Mask::from_indices(100, vec![10, 20, 30]), &mut ctx)
1253 .unwrap()
1254 .unwrap();
1255
1256 assert_arrays_eq!(
1257 filtered.indices(),
1258 PrimitiveArray::from_iter([0u64, 1]),
1259 &mut ctx
1260 );
1261 assert_arrays_eq!(
1262 filtered.values(),
1263 PrimitiveArray::from_iter([100i32, 200]),
1264 &mut ctx
1265 );
1266 }
1267
1268 #[test]
1269 fn take_with_nulls() {
1270 let mut ctx = array_session().create_execution_ctx();
1271 let patches = Patches::new(
1272 20,
1273 0,
1274 buffer![2u64, 9, 15].into_array(),
1275 PrimitiveArray::new(buffer![33_i32, 44, 55], Validity::AllValid).into_array(),
1276 None,
1277 )
1278 .unwrap();
1279
1280 let taken = patches
1281 .take(
1282 &PrimitiveArray::new(buffer![9, 0], Validity::from_iter(vec![true, false]))
1283 .into_array(),
1284 &mut ctx,
1285 )
1286 .unwrap()
1287 .unwrap();
1288 let primitive_values = taken
1289 .values()
1290 .clone()
1291 .execute::<PrimitiveArray>(&mut ctx)
1292 .unwrap();
1293 let primitive_indices = taken
1294 .indices()
1295 .clone()
1296 .execute::<PrimitiveArray>(&mut ctx)
1297 .unwrap();
1298 assert_eq!(taken.array_len(), 2);
1299 assert_arrays_eq!(
1300 primitive_values,
1301 PrimitiveArray::from_option_iter([Some(44i32)]),
1302 &mut ctx
1303 );
1304 assert_arrays_eq!(
1305 primitive_indices,
1306 PrimitiveArray::from_iter([0u64]),
1307 &mut ctx
1308 );
1309 assert_eq!(
1310 primitive_values
1311 .as_ref()
1312 .validity()
1313 .unwrap()
1314 .execute_mask(primitive_values.as_ref().len(), &mut ctx)
1315 .unwrap(),
1316 Mask::from_iter(vec![true])
1317 );
1318 }
1319
1320 #[test]
1321 fn take_search_with_nulls_chunked() {
1322 let mut ctx = array_session().create_execution_ctx();
1323 let patches = Patches::new(
1324 20,
1325 0,
1326 buffer![2u64, 9, 15].into_array(),
1327 buffer![33_i32, 44, 55].into_array(),
1328 Some(buffer![0u64].into_array()),
1329 )
1330 .unwrap();
1331
1332 let taken = patches
1333 .take_search(
1334 PrimitiveArray::new(buffer![9, 0], Validity::from_iter([true, false])),
1335 true,
1336 &mut ctx,
1337 )
1338 .unwrap()
1339 .unwrap();
1340
1341 let primitive_values = taken
1342 .values()
1343 .clone()
1344 .execute::<PrimitiveArray>(&mut ctx)
1345 .unwrap();
1346 assert_eq!(taken.array_len(), 2);
1347 assert_arrays_eq!(
1348 primitive_values,
1349 PrimitiveArray::from_option_iter([Some(44i32), None]),
1350 &mut ctx
1351 );
1352 assert_arrays_eq!(
1353 taken.indices(),
1354 PrimitiveArray::from_iter([0u64, 1]),
1355 &mut ctx
1356 );
1357
1358 assert_eq!(
1359 primitive_values
1360 .as_ref()
1361 .validity()
1362 .unwrap()
1363 .execute_mask(primitive_values.as_ref().len(), &mut ctx)
1364 .unwrap(),
1365 Mask::from_iter([true, false])
1366 );
1367 }
1368
1369 #[test]
1370 fn take_search_chunked_multiple_chunks() {
1371 let mut ctx = array_session().create_execution_ctx();
1372 let patches = Patches::new(
1373 2048,
1374 0,
1375 buffer![100u64, 500, 1200, 1800].into_array(),
1376 buffer![10_i32, 20, 30, 40].into_array(),
1377 Some(buffer![0u64, 2].into_array()),
1378 )
1379 .unwrap();
1380
1381 let taken = patches
1382 .take_search(
1383 PrimitiveArray::new(buffer![500, 1200, 999], Validity::AllValid),
1384 true,
1385 &mut ctx,
1386 )
1387 .unwrap()
1388 .unwrap();
1389
1390 assert_eq!(taken.array_len(), 3);
1391 assert_arrays_eq!(
1392 taken.values(),
1393 PrimitiveArray::from_option_iter([Some(20i32), Some(30)]),
1394 &mut ctx
1395 );
1396 }
1397
1398 #[test]
1399 fn take_search_chunked_indices_with_no_patches() {
1400 let mut ctx = array_session().create_execution_ctx();
1401 let patches = Patches::new(
1402 20,
1403 0,
1404 buffer![2u64, 9, 15].into_array(),
1405 buffer![33_i32, 44, 55].into_array(),
1406 Some(buffer![0u64].into_array()),
1407 )
1408 .unwrap();
1409
1410 let taken = patches
1411 .take_search(
1412 PrimitiveArray::new(buffer![3, 4, 5], Validity::AllValid),
1413 true,
1414 &mut ctx,
1415 )
1416 .unwrap();
1417
1418 assert!(taken.is_none());
1419 }
1420
1421 #[test]
1422 fn take_search_chunked_interleaved() {
1423 let mut ctx = array_session().create_execution_ctx();
1424 let patches = Patches::new(
1425 30,
1426 0,
1427 buffer![5u64, 10, 20, 25].into_array(),
1428 buffer![100_i32, 200, 300, 400].into_array(),
1429 Some(buffer![0u64].into_array()),
1430 )
1431 .unwrap();
1432
1433 let taken = patches
1434 .take_search(
1435 PrimitiveArray::new(buffer![10, 15, 20, 99], Validity::AllValid),
1436 true,
1437 &mut ctx,
1438 )
1439 .unwrap()
1440 .unwrap();
1441
1442 assert_eq!(taken.array_len(), 4);
1443 assert_arrays_eq!(
1444 taken.values(),
1445 PrimitiveArray::from_option_iter([Some(200i32), Some(300)]),
1446 &mut ctx
1447 );
1448 }
1449
1450 #[test]
1451 fn test_take_search_multiple_chunk_offsets() {
1452 let mut ctx = array_session().create_execution_ctx();
1453 let patches = Patches::new(
1454 1500,
1455 0,
1456 BufferMut::from_iter(0..1500u64).into_array(),
1457 BufferMut::from_iter(0..1500i32).into_array(),
1458 Some(buffer![0u64, 1024u64].into_array()),
1459 )
1460 .unwrap();
1461
1462 let taken = patches
1463 .take_search(
1464 PrimitiveArray::new(BufferMut::from_iter(0..1500u64), Validity::AllValid),
1465 false,
1466 &mut ctx,
1467 )
1468 .unwrap()
1469 .unwrap();
1470
1471 assert_eq!(taken.array_len(), 1500);
1472 }
1473
1474 #[test]
1475 fn test_slice() {
1476 let mut ctx = array_session().create_execution_ctx();
1477 let values = buffer![15_u32, 135, 13531, 42].into_array();
1478 let indices = buffer![10_u64, 11, 50, 100].into_array();
1479
1480 let patches = Patches::new(101, 0, indices, values, None).unwrap();
1481
1482 let sliced = patches.slice(15..100).unwrap().unwrap();
1483 assert_eq!(sliced.array_len(), 100 - 15);
1484 assert_arrays_eq!(
1485 sliced.values(),
1486 PrimitiveArray::from_iter([13531u32]),
1487 &mut ctx
1488 );
1489 }
1490
1491 #[test]
1492 fn doubly_sliced() {
1493 let mut ctx = array_session().create_execution_ctx();
1494 let values = buffer![15_u32, 135, 13531, 42].into_array();
1495 let indices = buffer![10_u64, 11, 50, 100].into_array();
1496
1497 let patches = Patches::new(101, 0, indices, values, None).unwrap();
1498
1499 let sliced = patches.slice(15..100).unwrap().unwrap();
1500 assert_eq!(sliced.array_len(), 100 - 15);
1501 assert_arrays_eq!(
1502 sliced.values(),
1503 PrimitiveArray::from_iter([13531u32]),
1504 &mut ctx
1505 );
1506
1507 let doubly_sliced = sliced.slice(35..36).unwrap().unwrap();
1508 assert_arrays_eq!(
1509 doubly_sliced.values(),
1510 PrimitiveArray::from_iter([13531u32]),
1511 &mut ctx
1512 );
1513 }
1514
1515 #[test]
1516 fn test_mask_all_true() {
1517 let mut ctx = array_session().create_execution_ctx();
1518 let patches = Patches::new(
1519 10,
1520 0,
1521 buffer![2u64, 5, 8].into_array(),
1522 buffer![100i32, 200, 300].into_array(),
1523 None,
1524 )
1525 .unwrap();
1526
1527 let mask = Mask::new_true(10);
1528 let masked = patches.mask(&mask, &mut ctx).unwrap();
1529 assert!(masked.is_none());
1530 }
1531
1532 #[test]
1533 fn test_mask_all_false() {
1534 let mut ctx = array_session().create_execution_ctx();
1535 let patches = Patches::new(
1536 10,
1537 0,
1538 buffer![2u64, 5, 8].into_array(),
1539 buffer![100i32, 200, 300].into_array(),
1540 None,
1541 )
1542 .unwrap();
1543
1544 let mask = Mask::new_false(10);
1545 let masked = patches.mask(&mask, &mut ctx).unwrap().unwrap();
1546
1547 assert!(masked.values().dtype().is_nullable());
1549 assert_arrays_eq!(
1550 masked.values(),
1551 PrimitiveArray::from_option_iter([Some(100i32), Some(200), Some(300)]),
1552 &mut ctx
1553 );
1554 assert!(masked.values().is_valid(0, &mut ctx).unwrap());
1555 assert!(masked.values().is_valid(1, &mut ctx).unwrap());
1556 assert!(masked.values().is_valid(2, &mut ctx).unwrap());
1557
1558 assert_arrays_eq!(
1560 masked.indices(),
1561 PrimitiveArray::from_iter([2u64, 5, 8]),
1562 &mut ctx
1563 );
1564 }
1565
1566 #[test]
1567 fn test_mask_partial() {
1568 let mut ctx = array_session().create_execution_ctx();
1569 let patches = Patches::new(
1570 10,
1571 0,
1572 buffer![2u64, 5, 8].into_array(),
1573 buffer![100i32, 200, 300].into_array(),
1574 None,
1575 )
1576 .unwrap();
1577
1578 let mask = Mask::from_iter([
1580 false, false, true, false, false, false, false, false, true, false,
1581 ]);
1582 let masked = patches.mask(&mask, &mut ctx).unwrap().unwrap();
1583
1584 assert_eq!(masked.values().len(), 1);
1586 assert_arrays_eq!(
1587 masked.values(),
1588 PrimitiveArray::from_option_iter([Some(200i32)]),
1589 &mut ctx
1590 );
1591
1592 assert_arrays_eq!(
1594 masked.indices(),
1595 PrimitiveArray::from_iter([5u64]),
1596 &mut ctx
1597 );
1598 }
1599
1600 #[test]
1601 fn test_mask_with_offset() {
1602 let mut ctx = array_session().create_execution_ctx();
1603 let patches = Patches::new(
1604 10,
1605 5, buffer![7u64, 10, 13].into_array(), buffer![100i32, 200, 300].into_array(),
1608 None,
1609 )
1610 .unwrap();
1611
1612 let mask = Mask::from_iter([
1614 false, false, true, false, false, false, false, false, false, false,
1615 ]);
1616
1617 let masked = patches.mask(&mask, &mut ctx).unwrap().unwrap();
1618 assert_eq!(masked.array_len(), 10);
1619 assert_eq!(masked.offset(), 5);
1620 assert_arrays_eq!(
1621 masked.indices(),
1622 PrimitiveArray::from_iter([10u64, 13]),
1623 &mut ctx
1624 );
1625 assert_arrays_eq!(
1626 masked.values(),
1627 PrimitiveArray::from_option_iter([Some(200i32), Some(300)]),
1628 &mut ctx
1629 );
1630 }
1631
1632 #[test]
1633 fn test_mask_nullable_values() {
1634 let mut ctx = array_session().create_execution_ctx();
1635 let patches = Patches::new(
1636 10,
1637 0,
1638 buffer![2u64, 5, 8].into_array(),
1639 PrimitiveArray::from_option_iter([Some(100i32), None, Some(300)]).into_array(),
1640 None,
1641 )
1642 .unwrap();
1643
1644 let mask = Mask::from_iter([
1646 false, false, true, false, false, false, false, false, false, false,
1647 ]);
1648 let masked = patches.mask(&mask, &mut ctx).unwrap().unwrap();
1649
1650 assert_arrays_eq!(
1652 masked.indices(),
1653 PrimitiveArray::from_iter([5u64, 8]),
1654 &mut ctx
1655 );
1656
1657 let masked_values = masked
1659 .values()
1660 .clone()
1661 .execute::<PrimitiveArray>(&mut ctx)
1662 .unwrap();
1663 assert_eq!(masked_values.len(), 2);
1664 assert!(!masked_values.is_valid(0, &mut ctx).unwrap()); assert!(masked_values.is_valid(1, &mut ctx).unwrap()); assert_eq!(
1667 i32::try_from(&masked_values.execute_scalar(1, &mut ctx).unwrap()).unwrap(),
1668 300i32
1669 );
1670 }
1671
1672 #[test]
1673 fn test_filter_keep_all() {
1674 let mut ctx = array_session().create_execution_ctx();
1675 let patches = Patches::new(
1676 10,
1677 0,
1678 buffer![2u64, 5, 8].into_array(),
1679 buffer![100i32, 200, 300].into_array(),
1680 None,
1681 )
1682 .unwrap();
1683
1684 let mask = Mask::from_indices(10, 0..10);
1686 let filtered = patches.filter(&mask, &mut ctx).unwrap().unwrap();
1687
1688 assert_arrays_eq!(
1689 filtered.indices(),
1690 PrimitiveArray::from_iter([2u64, 5, 8]),
1691 &mut ctx
1692 );
1693 assert_arrays_eq!(
1694 filtered.values(),
1695 PrimitiveArray::from_iter([100i32, 200, 300]),
1696 &mut ctx
1697 );
1698 }
1699
1700 #[test]
1701 fn test_filter_none() {
1702 let mut ctx = array_session().create_execution_ctx();
1703 let patches = Patches::new(
1704 10,
1705 0,
1706 buffer![2u64, 5, 8].into_array(),
1707 buffer![100i32, 200, 300].into_array(),
1708 None,
1709 )
1710 .unwrap();
1711
1712 let mask = Mask::from_indices(10, vec![]);
1714 let filtered = patches.filter(&mask, &mut ctx).unwrap();
1715 assert!(filtered.is_none());
1716 }
1717
1718 #[test]
1719 fn test_filter_with_indices() {
1720 let mut ctx = array_session().create_execution_ctx();
1721 let patches = Patches::new(
1722 10,
1723 0,
1724 buffer![2u64, 5, 8].into_array(),
1725 buffer![100i32, 200, 300].into_array(),
1726 None,
1727 )
1728 .unwrap();
1729
1730 let mask = Mask::from_indices(10, vec![2, 5, 9]);
1732 let filtered = patches.filter(&mask, &mut ctx).unwrap().unwrap();
1733
1734 assert_arrays_eq!(
1735 filtered.indices(),
1736 PrimitiveArray::from_iter([0u64, 1]),
1737 &mut ctx
1738 ); assert_arrays_eq!(
1740 filtered.values(),
1741 PrimitiveArray::from_iter([100i32, 200]),
1742 &mut ctx
1743 );
1744 }
1745
1746 #[test]
1747 fn test_slice_full_range() {
1748 let mut ctx = array_session().create_execution_ctx();
1749 let patches = Patches::new(
1750 10,
1751 0,
1752 buffer![2u64, 5, 8].into_array(),
1753 buffer![100i32, 200, 300].into_array(),
1754 None,
1755 )
1756 .unwrap();
1757
1758 let sliced = patches.slice(0..10).unwrap().unwrap();
1759
1760 assert_arrays_eq!(
1761 sliced.indices(),
1762 PrimitiveArray::from_iter([2u64, 5, 8]),
1763 &mut ctx
1764 );
1765 assert_arrays_eq!(
1766 sliced.values(),
1767 PrimitiveArray::from_iter([100i32, 200, 300]),
1768 &mut ctx
1769 );
1770 }
1771
1772 #[test]
1773 fn test_slice_partial() {
1774 let mut ctx = array_session().create_execution_ctx();
1775 let patches = Patches::new(
1776 10,
1777 0,
1778 buffer![2u64, 5, 8].into_array(),
1779 buffer![100i32, 200, 300].into_array(),
1780 None,
1781 )
1782 .unwrap();
1783
1784 let sliced = patches.slice(3..8).unwrap().unwrap();
1786
1787 assert_arrays_eq!(
1788 sliced.indices(),
1789 PrimitiveArray::from_iter([5u64]),
1790 &mut ctx
1791 ); assert_arrays_eq!(
1793 sliced.values(),
1794 PrimitiveArray::from_iter([200i32]),
1795 &mut ctx
1796 );
1797 assert_eq!(sliced.array_len(), 5); assert_eq!(sliced.offset(), 3); }
1800
1801 #[test]
1802 fn test_slice_no_patches() {
1803 let patches = Patches::new(
1804 10,
1805 0,
1806 buffer![2u64, 5, 8].into_array(),
1807 buffer![100i32, 200, 300].into_array(),
1808 None,
1809 )
1810 .unwrap();
1811
1812 let sliced = patches.slice(6..7).unwrap();
1814 assert!(sliced.is_none());
1815 }
1816
1817 #[test]
1818 fn test_slice_with_offset() {
1819 let mut ctx = array_session().create_execution_ctx();
1820 let patches = Patches::new(
1821 10,
1822 5, buffer![7u64, 10, 13].into_array(), buffer![100i32, 200, 300].into_array(),
1825 None,
1826 )
1827 .unwrap();
1828
1829 let sliced = patches.slice(3..8).unwrap().unwrap();
1831
1832 assert_arrays_eq!(
1833 sliced.indices(),
1834 PrimitiveArray::from_iter([10u64]),
1835 &mut ctx
1836 ); assert_arrays_eq!(
1838 sliced.values(),
1839 PrimitiveArray::from_iter([200i32]),
1840 &mut ctx
1841 );
1842 assert_eq!(sliced.offset(), 8); }
1844
1845 #[test]
1846 fn test_patch_values() {
1847 let mut ctx = array_session().create_execution_ctx();
1848 let patches = Patches::new(
1849 10,
1850 0,
1851 buffer![2u64, 5, 8].into_array(),
1852 buffer![100i32, 200, 300].into_array(),
1853 None,
1854 )
1855 .unwrap();
1856
1857 let values = patches
1858 .values()
1859 .clone()
1860 .execute::<PrimitiveArray>(&mut ctx)
1861 .unwrap();
1862 assert_eq!(
1863 i32::try_from(&values.execute_scalar(0, &mut ctx).unwrap()).unwrap(),
1864 100i32
1865 );
1866 assert_eq!(
1867 i32::try_from(&values.execute_scalar(1, &mut ctx).unwrap()).unwrap(),
1868 200i32
1869 );
1870 assert_eq!(
1871 i32::try_from(&values.execute_scalar(2, &mut ctx).unwrap()).unwrap(),
1872 300i32
1873 );
1874 }
1875
1876 #[test]
1877 fn test_indices_range() {
1878 let patches = Patches::new(
1879 10,
1880 0,
1881 buffer![2u64, 5, 8].into_array(),
1882 buffer![100i32, 200, 300].into_array(),
1883 None,
1884 )
1885 .unwrap();
1886
1887 assert_eq!(patches.min_index().unwrap(), 2);
1888 assert_eq!(patches.max_index().unwrap(), 8);
1889 }
1890
1891 #[test]
1892 fn test_search_index() {
1893 let patches = Patches::new(
1894 10,
1895 0,
1896 buffer![2u64, 5, 8].into_array(),
1897 buffer![100i32, 200, 300].into_array(),
1898 None,
1899 )
1900 .unwrap();
1901
1902 assert_eq!(patches.search_index(2).unwrap(), SearchResult::Found(0));
1904 assert_eq!(patches.search_index(5).unwrap(), SearchResult::Found(1));
1905 assert_eq!(patches.search_index(8).unwrap(), SearchResult::Found(2));
1906
1907 assert_eq!(patches.search_index(0).unwrap(), SearchResult::NotFound(0));
1909 assert_eq!(patches.search_index(3).unwrap(), SearchResult::NotFound(1));
1910 assert_eq!(patches.search_index(6).unwrap(), SearchResult::NotFound(2));
1911 assert_eq!(patches.search_index(9).unwrap(), SearchResult::NotFound(3));
1912 }
1913
1914 #[test]
1915 fn test_mask_boundary_patches() {
1916 let mut ctx = array_session().create_execution_ctx();
1917 let patches = Patches::new(
1919 10,
1920 0,
1921 buffer![0u64, 9].into_array(),
1922 buffer![100i32, 200].into_array(),
1923 None,
1924 )
1925 .unwrap();
1926
1927 let mask = Mask::from_iter([
1928 true, false, false, false, false, false, false, false, false, false,
1929 ]);
1930 let masked = patches.mask(&mask, &mut ctx).unwrap();
1931 assert!(masked.is_some());
1932 let masked = masked.unwrap();
1933 assert_arrays_eq!(
1934 masked.indices(),
1935 PrimitiveArray::from_iter([9u64]),
1936 &mut ctx
1937 );
1938 assert_arrays_eq!(
1939 masked.values(),
1940 PrimitiveArray::from_option_iter([Some(200i32)]),
1941 &mut ctx
1942 );
1943 }
1944
1945 #[test]
1946 fn test_mask_all_patches_removed() {
1947 let mut ctx = array_session().create_execution_ctx();
1948 let patches = Patches::new(
1950 10,
1951 0,
1952 buffer![2u64, 5, 8].into_array(),
1953 buffer![100i32, 200, 300].into_array(),
1954 None,
1955 )
1956 .unwrap();
1957
1958 let mask = Mask::from_iter([
1960 false, false, true, false, false, true, false, false, true, false,
1961 ]);
1962 let masked = patches.mask(&mask, &mut ctx).unwrap();
1963 assert!(masked.is_none());
1964 }
1965
1966 #[test]
1967 fn test_mask_no_patches_removed() {
1968 let mut ctx = array_session().create_execution_ctx();
1969 let patches = Patches::new(
1971 10,
1972 0,
1973 buffer![2u64, 5, 8].into_array(),
1974 buffer![100i32, 200, 300].into_array(),
1975 None,
1976 )
1977 .unwrap();
1978
1979 let mask = Mask::from_iter([
1981 true, false, false, true, false, false, true, false, false, true,
1982 ]);
1983 let masked = patches.mask(&mask, &mut ctx).unwrap().unwrap();
1984
1985 assert_arrays_eq!(
1986 masked.indices(),
1987 PrimitiveArray::from_iter([2u64, 5, 8]),
1988 &mut ctx
1989 );
1990 assert_arrays_eq!(
1991 masked.values(),
1992 PrimitiveArray::from_option_iter([Some(100i32), Some(200), Some(300)]),
1993 &mut ctx
1994 );
1995 }
1996
1997 #[test]
1998 fn test_mask_single_patch() {
1999 let mut ctx = array_session().create_execution_ctx();
2000 let patches = Patches::new(
2002 5,
2003 0,
2004 buffer![2u64].into_array(),
2005 buffer![42i32].into_array(),
2006 None,
2007 )
2008 .unwrap();
2009
2010 let mask = Mask::from_iter([false, false, true, false, false]);
2012 let masked = patches.mask(&mask, &mut ctx).unwrap();
2013 assert!(masked.is_none());
2014
2015 let mask = Mask::from_iter([true, false, false, true, false]);
2017 let masked = patches.mask(&mask, &mut ctx).unwrap().unwrap();
2018 assert_arrays_eq!(
2019 masked.indices(),
2020 PrimitiveArray::from_iter([2u64]),
2021 &mut ctx
2022 );
2023 }
2024
2025 #[test]
2026 fn test_mask_contiguous_patches() {
2027 let mut ctx = array_session().create_execution_ctx();
2028 let patches = Patches::new(
2030 10,
2031 0,
2032 buffer![3u64, 4, 5, 6].into_array(),
2033 buffer![100i32, 200, 300, 400].into_array(),
2034 None,
2035 )
2036 .unwrap();
2037
2038 let mask = Mask::from_iter([
2040 false, false, false, false, true, true, false, false, false, false,
2041 ]);
2042 let masked = patches.mask(&mask, &mut ctx).unwrap().unwrap();
2043
2044 assert_arrays_eq!(
2045 masked.indices(),
2046 PrimitiveArray::from_iter([3u64, 6]),
2047 &mut ctx
2048 );
2049 assert_arrays_eq!(
2050 masked.values(),
2051 PrimitiveArray::from_option_iter([Some(100i32), Some(400)]),
2052 &mut ctx
2053 );
2054 }
2055
2056 #[test]
2057 fn test_mask_with_large_offset() {
2058 let mut ctx = array_session().create_execution_ctx();
2059 let patches = Patches::new(
2061 20,
2062 15,
2063 buffer![16u64, 17, 19].into_array(), buffer![100i32, 200, 300].into_array(),
2065 None,
2066 )
2067 .unwrap();
2068
2069 let mask = Mask::from_iter([
2071 false, false, true, false, false, false, false, false, false, false, false, false,
2072 false, false, false, false, false, false, false, false,
2073 ]);
2074 let masked = patches.mask(&mask, &mut ctx).unwrap().unwrap();
2075
2076 assert_arrays_eq!(
2077 masked.indices(),
2078 PrimitiveArray::from_iter([16u64, 19]),
2079 &mut ctx
2080 );
2081 assert_arrays_eq!(
2082 masked.values(),
2083 PrimitiveArray::from_option_iter([Some(100i32), Some(300)]),
2084 &mut ctx
2085 );
2086 }
2087
2088 #[test]
2089 #[should_panic(expected = "Filter mask length 5 does not match array length 10")]
2090 fn test_mask_wrong_length() {
2091 let mut ctx = array_session().create_execution_ctx();
2092 let patches = Patches::new(
2093 10,
2094 0,
2095 buffer![2u64, 5, 8].into_array(),
2096 buffer![100i32, 200, 300].into_array(),
2097 None,
2098 )
2099 .unwrap();
2100
2101 let mask = Mask::from_iter([false, false, true, false, false]);
2103 patches.mask(&mask, &mut ctx).unwrap();
2104 }
2105
2106 #[test]
2107 fn test_chunk_offsets_search() {
2108 let indices = buffer![100u64, 200, 3000, 3100].into_array();
2109 let values = buffer![10i32, 20, 30, 40].into_array();
2110 let chunk_offsets = buffer![0u64, 2, 2, 3].into_array();
2111 let patches = Patches::new(4096, 0, indices, values, Some(chunk_offsets)).unwrap();
2112
2113 assert!(patches.chunk_offsets.is_some());
2114
2115 assert_eq!(patches.search_index(100).unwrap(), SearchResult::Found(0));
2117 assert_eq!(patches.search_index(200).unwrap(), SearchResult::Found(1));
2118
2119 assert_eq!(
2121 patches.search_index(1500).unwrap(),
2122 SearchResult::NotFound(2)
2123 );
2124 assert_eq!(
2125 patches.search_index(2000).unwrap(),
2126 SearchResult::NotFound(2)
2127 );
2128
2129 assert_eq!(patches.search_index(3000).unwrap(), SearchResult::Found(2));
2131 assert_eq!(patches.search_index(3100).unwrap(), SearchResult::Found(3));
2132
2133 assert_eq!(
2134 patches.search_index(1024).unwrap(),
2135 SearchResult::NotFound(2)
2136 );
2137 }
2138
2139 #[test]
2140 fn test_chunk_offsets_with_slice() {
2141 let indices = buffer![100u64, 500, 1200, 1300, 1500, 1800, 2100, 2500].into_array();
2142 let values = buffer![10i32, 20, 30, 35, 40, 45, 50, 60].into_array();
2143 let chunk_offsets = buffer![0u64, 2, 6].into_array();
2144 let patches = Patches::new(3000, 0, indices, values, Some(chunk_offsets)).unwrap();
2145
2146 let sliced = patches.slice(1000..2200).unwrap().unwrap();
2147
2148 assert!(sliced.chunk_offsets.is_some());
2149 assert_eq!(sliced.offset(), 1000);
2150
2151 assert_eq!(sliced.search_index(200).unwrap(), SearchResult::Found(0));
2152 assert_eq!(sliced.search_index(500).unwrap(), SearchResult::Found(2));
2153 assert_eq!(sliced.search_index(1100).unwrap(), SearchResult::Found(4));
2154
2155 assert_eq!(sliced.search_index(250).unwrap(), SearchResult::NotFound(1));
2156 }
2157
2158 #[test]
2159 fn test_chunk_offsets_with_slice_after_first_chunk() {
2160 let indices = buffer![100u64, 500, 1200, 1300, 1500, 1800, 2100, 2500].into_array();
2161 let values = buffer![10i32, 20, 30, 35, 40, 45, 50, 60].into_array();
2162 let chunk_offsets = buffer![0u64, 2, 6].into_array();
2163 let patches = Patches::new(3000, 0, indices, values, Some(chunk_offsets)).unwrap();
2164
2165 let sliced = patches.slice(1300..2200).unwrap().unwrap();
2166
2167 assert!(sliced.chunk_offsets.is_some());
2168 assert_eq!(sliced.offset(), 1300);
2169
2170 assert_eq!(sliced.search_index(0).unwrap(), SearchResult::Found(0));
2171 assert_eq!(sliced.search_index(200).unwrap(), SearchResult::Found(1));
2172 assert_eq!(sliced.search_index(500).unwrap(), SearchResult::Found(2));
2173 assert_eq!(sliced.search_index(250).unwrap(), SearchResult::NotFound(2));
2174 assert_eq!(sliced.search_index(900).unwrap(), SearchResult::NotFound(4));
2175 }
2176
2177 #[test]
2178 fn test_chunk_offsets_slice_empty_result() {
2179 let indices = buffer![100u64, 200, 3000, 3100].into_array();
2180 let values = buffer![10i32, 20, 30, 40].into_array();
2181 let chunk_offsets = buffer![0u64, 2].into_array();
2182 let patches = Patches::new(4000, 0, indices, values, Some(chunk_offsets)).unwrap();
2183
2184 let sliced = patches.slice(1000..2000).unwrap();
2185 assert!(sliced.is_none());
2186 }
2187
2188 #[test]
2189 fn test_chunk_offsets_slice_single_patch() {
2190 let indices = buffer![100u64, 1200, 1300, 2500].into_array();
2191 let values = buffer![10i32, 20, 30, 40].into_array();
2192 let chunk_offsets = buffer![0u64, 1, 3].into_array();
2193 let patches = Patches::new(3000, 0, indices, values, Some(chunk_offsets)).unwrap();
2194
2195 let sliced = patches.slice(1100..1250).unwrap().unwrap();
2196
2197 assert_eq!(sliced.num_patches(), 1);
2198 assert_eq!(sliced.offset(), 1100);
2199 assert_eq!(sliced.search_index(100).unwrap(), SearchResult::Found(0)); assert_eq!(sliced.search_index(50).unwrap(), SearchResult::NotFound(0));
2201 assert_eq!(sliced.search_index(150).unwrap(), SearchResult::NotFound(1));
2202 }
2203
2204 #[test]
2205 fn test_chunk_offsets_slice_across_chunks() {
2206 let indices = buffer![100u64, 200, 1100, 1200, 2100, 2200].into_array();
2207 let values = buffer![10i32, 20, 30, 40, 50, 60].into_array();
2208 let chunk_offsets = buffer![0u64, 2, 4].into_array();
2209 let patches = Patches::new(3000, 0, indices, values, Some(chunk_offsets)).unwrap();
2210
2211 let sliced = patches.slice(150..2150).unwrap().unwrap();
2212
2213 assert_eq!(sliced.num_patches(), 4);
2214 assert_eq!(sliced.offset(), 150);
2215
2216 assert_eq!(sliced.search_index(50).unwrap(), SearchResult::Found(0)); assert_eq!(sliced.search_index(950).unwrap(), SearchResult::Found(1)); assert_eq!(sliced.search_index(1050).unwrap(), SearchResult::Found(2)); assert_eq!(sliced.search_index(1950).unwrap(), SearchResult::Found(3)); }
2221
2222 #[test]
2223 fn test_chunk_offsets_boundary_searches() {
2224 let indices = buffer![1023u64, 1024, 1025, 2047, 2048].into_array();
2225 let values = buffer![10i32, 20, 30, 40, 50].into_array();
2226 let chunk_offsets = buffer![0u64, 1, 4].into_array();
2227 let patches = Patches::new(3000, 0, indices, values, Some(chunk_offsets)).unwrap();
2228
2229 assert_eq!(patches.search_index(1023).unwrap(), SearchResult::Found(0));
2230 assert_eq!(patches.search_index(1024).unwrap(), SearchResult::Found(1));
2231 assert_eq!(patches.search_index(1025).unwrap(), SearchResult::Found(2));
2232 assert_eq!(patches.search_index(2047).unwrap(), SearchResult::Found(3));
2233 assert_eq!(patches.search_index(2048).unwrap(), SearchResult::Found(4));
2234
2235 assert_eq!(
2236 patches.search_index(1022).unwrap(),
2237 SearchResult::NotFound(0)
2238 );
2239 assert_eq!(
2240 patches.search_index(2046).unwrap(),
2241 SearchResult::NotFound(3)
2242 );
2243 }
2244
2245 #[test]
2246 fn test_chunk_offsets_slice_edge_cases() {
2247 let indices = buffer![0u64, 1, 1023, 1024, 2047, 2048].into_array();
2248 let values = buffer![10i32, 20, 30, 40, 50, 60].into_array();
2249 let chunk_offsets = buffer![0u64, 3, 5].into_array();
2250 let patches = Patches::new(3000, 0, indices, values, Some(chunk_offsets)).unwrap();
2251
2252 let sliced = patches.slice(0..10).unwrap().unwrap();
2254 assert_eq!(sliced.num_patches(), 2);
2255 assert_eq!(sliced.search_index(0).unwrap(), SearchResult::Found(0));
2256 assert_eq!(sliced.search_index(1).unwrap(), SearchResult::Found(1));
2257
2258 let sliced = patches.slice(2040..3000).unwrap().unwrap();
2260 assert_eq!(sliced.num_patches(), 2); assert_eq!(sliced.search_index(7).unwrap(), SearchResult::Found(0)); assert_eq!(sliced.search_index(8).unwrap(), SearchResult::Found(1)); }
2264
2265 #[test]
2266 fn test_chunk_offsets_slice_nested() {
2267 let indices = buffer![100u64, 200, 300, 400, 500, 600].into_array();
2268 let values = buffer![10i32, 20, 30, 40, 50, 60].into_array();
2269 let chunk_offsets = buffer![0u64].into_array();
2270 let patches = Patches::new(1000, 0, indices, values, Some(chunk_offsets)).unwrap();
2271
2272 let sliced1 = patches.slice(150..550).unwrap().unwrap();
2273 assert_eq!(sliced1.num_patches(), 4); let sliced2 = sliced1.slice(100..250).unwrap().unwrap();
2276 assert_eq!(sliced2.num_patches(), 1); assert_eq!(sliced2.offset(), 250);
2278
2279 assert_eq!(sliced2.search_index(50).unwrap(), SearchResult::Found(0)); assert_eq!(
2281 sliced2.search_index(150).unwrap(),
2282 SearchResult::NotFound(1)
2283 );
2284 }
2285
2286 #[test]
2287 fn test_nested_slice_with_dropped_first_chunk() {
2288 let indices = buffer![0u64, 1024].into_array();
2290 let values = buffer![1i32, 2].into_array();
2291 let chunk_offsets = buffer![0u64, 1].into_array();
2292 let patches = Patches::new(2048, 0, indices, values, Some(chunk_offsets)).unwrap();
2293
2294 let dropped_first = patches.slice(1024..2048).unwrap().unwrap();
2296 let resliced = dropped_first.slice(0..1024).unwrap().unwrap();
2297 assert_eq!(resliced.num_patches(), 1);
2298 }
2299
2300 #[test]
2301 fn test_index_larger_than_length() {
2302 let chunk_offsets = buffer![0u64].into_array();
2303 let indices = buffer![1023u64].into_array();
2304 let values = buffer![42i32].into_array();
2305 let patches = Patches::new(1024, 0, indices, values, Some(chunk_offsets)).unwrap();
2306 assert_eq!(
2307 patches.search_index(2048).unwrap(),
2308 SearchResult::NotFound(1)
2309 );
2310 }
2311}