1use crate::file::page_index::offset_index::PageLocation;
33use arrow_array::{Array, BooleanArray};
34use arrow_buffer::{BooleanBuffer, BooleanBufferBuilder};
35use arrow_select::filter::SlicesIterator;
36use std::cmp::Ordering;
37use std::collections::VecDeque;
38use std::ops::Range;
39
40mod algebra;
41mod boolean;
42mod cursor;
43mod ranges;
44mod selector;
45
46use algebra::{
47 and_then_mask, and_then_row_selections, and_then_selectors_with_mask, combine_mixed_selection,
48 intersect_masks, intersect_row_selections, union_masks, union_row_selections,
49};
50pub use boolean::MaskRunIter;
51use boolean::{
52 MaskSelection, boolean_mask_from_selectors, limit_mask, mask_has_at_least_runs, offset_mask,
53 split_off_mask, trim_mask,
54};
55pub(crate) use cursor::{LoadedRowRanges, MaskCursor, RowSelectionStrategy};
56pub use cursor::{RowSelectionCursor, RowSelectionPolicy};
57use ranges::{
58 expand_to_batch_boundaries_from_selectors, scan_ranges_from_mask, scan_ranges_from_selectors,
59};
60pub use selector::RowSelector;
61use selector::{limit_selectors, offset_selectors, split_off_selectors};
62
63#[derive(Default, Clone)]
121pub struct RowSelection {
122 inner: RowSelectionInner,
123}
124
125#[derive(Debug, Clone)]
127pub(crate) enum RowSelectionInner {
128 Selectors(Vec<RowSelector>),
129 Mask(Box<MaskSelection>),
130}
131
132impl Default for RowSelectionInner {
133 fn default() -> Self {
134 Self::Selectors(Vec::new())
135 }
136}
137
138impl std::fmt::Debug for RowSelection {
139 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
140 match &self.inner {
141 RowSelectionInner::Selectors(s) => f
142 .debug_struct("RowSelection")
143 .field("selectors", s)
144 .finish(),
145 RowSelectionInner::Mask(m) => f
146 .debug_struct("RowSelection")
147 .field("mask_len", &m.mask().len())
148 .finish_non_exhaustive(),
149 }
150 }
151}
152
153impl PartialEq for RowSelection {
154 fn eq(&self, other: &Self) -> bool {
155 match (&self.inner, &other.inner) {
156 (RowSelectionInner::Selectors(a), RowSelectionInner::Selectors(b)) => a == b,
157 (RowSelectionInner::Mask(a), RowSelectionInner::Mask(b)) => a.mask() == b.mask(),
158 (RowSelectionInner::Mask(mask), RowSelectionInner::Selectors(selectors))
159 | (RowSelectionInner::Selectors(selectors), RowSelectionInner::Mask(mask)) => {
160 if selectors
161 .iter()
162 .try_fold(0usize, |acc, selector| acc.checked_add(selector.row_count))
163 != Some(mask.mask().len())
164 {
165 return false;
166 }
167
168 let mut slices = mask.mask().set_slices().peekable();
169 let mut cursor = 0usize;
170
171 for selector in selectors {
172 let end = cursor + selector.row_count;
173
174 if selector.skip {
175 if slices.peek().is_some_and(|(start, _)| *start < end) {
176 return false;
177 }
178 } else {
179 match slices.next() {
180 Some((start, slice_end)) if start == cursor && slice_end == end => {}
181 _ => return false,
182 }
183 }
184
185 cursor = end;
186 }
187
188 slices.next().is_none()
189 }
190 }
191 }
192}
193
194impl Eq for RowSelection {}
195
196impl RowSelection {
197 fn from_selectors(selectors: Vec<RowSelector>) -> Self {
201 Self {
202 inner: RowSelectionInner::Selectors(selectors),
203 }
204 }
205
206 pub fn from_boolean_buffer(mask: BooleanBuffer) -> Self {
214 Self {
215 inner: RowSelectionInner::Mask(Box::new(MaskSelection::new(mask))),
216 }
217 }
218
219 fn from_mask_selection(mask: MaskSelection) -> Self {
220 Self {
221 inner: RowSelectionInner::Mask(Box::new(mask)),
222 }
223 }
224
225 pub fn as_mask(&self) -> Option<&BooleanBuffer> {
233 match &self.inner {
234 RowSelectionInner::Mask(m) => Some(m.mask()),
235 RowSelectionInner::Selectors(_) => None,
236 }
237 }
238
239 pub fn force_mask(self) -> Self {
263 if self.as_mask().is_some() {
264 self
265 } else {
266 Self::from_boolean_buffer(self.into_boolean_buffer())
267 }
268 }
269
270 pub(crate) fn into_inner(self) -> RowSelectionInner {
272 self.inner
273 }
274
275 pub(crate) fn into_boolean_buffer(self) -> BooleanBuffer {
277 match self.inner {
278 RowSelectionInner::Mask(mask) => mask.into_mask(),
279 RowSelectionInner::Selectors(selectors) => boolean_mask_from_selectors(&selectors),
280 }
281 }
282
283 #[inline]
286 pub(crate) fn auto_selection_strategy(&self, threshold: usize) -> RowSelectionStrategy {
287 match &self.inner {
288 RowSelectionInner::Selectors(selectors) => {
289 let (total_rows, run_count) =
290 selectors
291 .iter()
292 .fold((0usize, 0usize), |(rows, count), selector| {
293 if selector.row_count > 0 {
294 (rows + selector.row_count, count + 1)
295 } else {
296 (rows, count)
297 }
298 });
299
300 if run_count == 0
301 || auto_min_mask_runs(total_rows, threshold)
302 .is_some_and(|min_runs| run_count >= min_runs)
303 {
304 RowSelectionStrategy::Mask
305 } else {
306 RowSelectionStrategy::Selectors
307 }
308 }
309 RowSelectionInner::Mask(mask) => {
310 let mask = mask.mask();
311 let total_rows = mask.len();
312
313 if total_rows == 0 {
314 return RowSelectionStrategy::Mask;
315 }
316
317 match auto_min_mask_runs(total_rows, threshold) {
318 Some(min_runs) if mask_has_at_least_runs(mask, min_runs) => {
319 RowSelectionStrategy::Mask
320 }
321 _ => RowSelectionStrategy::Selectors,
322 }
323 }
324 }
325 }
326
327 #[cfg(test)]
328 fn selectors(&self) -> Vec<RowSelector> {
329 self.iter().copied().collect()
330 }
331
332 fn into_selectors_vec(self) -> Vec<RowSelector> {
333 match self.inner {
334 RowSelectionInner::Selectors(s) => s,
335 RowSelectionInner::Mask(m) => (*m).into_selectors(),
336 }
337 }
338
339 pub fn from_filters(filters: &[BooleanArray]) -> Self {
345 let mut next_offset = 0;
346 let total_rows = filters.iter().map(|x| x.len()).sum();
347
348 let iter = filters.iter().flat_map(|filter| {
349 let offset = next_offset;
350 next_offset += filter.len();
351 assert_eq!(filter.null_count(), 0);
352 SlicesIterator::new(filter).map(move |(start, end)| start + offset..end + offset)
353 });
354
355 Self::from_consecutive_ranges(iter, total_rows)
356 }
357
358 pub(crate) fn from_filters_auto(filters: &[BooleanArray], threshold: usize) -> Self {
366 let total_rows = filters.iter().map(|filter| filter.len()).sum::<usize>();
367
368 if total_rows == 0 {
371 return Self::from_boolean_buffer(BooleanBuffer::new_unset(0));
372 }
373
374 let Some(min_mask_runs) = auto_min_mask_runs(total_rows, threshold) else {
375 return Self::from_filters(filters);
376 };
377
378 match selectors_below_run_limit(filters, total_rows, min_mask_runs) {
379 Some(selectors) => Self::from_selectors(selectors),
380 None => Self::from_filters_mask(filters),
381 }
382 }
383
384 pub(crate) fn from_filters_mask(filters: &[BooleanArray]) -> Self {
390 let mask = match filters {
391 [filter] => {
392 assert_eq!(filter.null_count(), 0);
393 filter.values().clone()
394 }
395 _ => filters_to_boolean_buffer(filters),
396 };
397 Self::from_boolean_buffer(mask)
398 }
399
400 pub fn from_consecutive_ranges<I: Iterator<Item = Range<usize>>>(
402 ranges: I,
403 total_rows: usize,
404 ) -> Self {
405 let mut selectors: Vec<RowSelector> = Vec::with_capacity(ranges.size_hint().0);
406 let mut last_end = 0;
407 for range in ranges {
408 let len = range.end - range.start;
409 if len == 0 {
410 continue;
411 }
412
413 match range.start.cmp(&last_end) {
414 Ordering::Equal => match selectors.last_mut() {
415 Some(last) => last.row_count = last.row_count.checked_add(len).unwrap(),
416 None => selectors.push(RowSelector::select(len)),
417 },
418 Ordering::Greater => {
419 selectors.push(RowSelector::skip(range.start - last_end));
420 selectors.push(RowSelector::select(len))
421 }
422 Ordering::Less => panic!("out of order"),
423 }
424 last_end = range.end;
425 }
426
427 if last_end != total_rows {
428 selectors.push(RowSelector::skip(total_rows - last_end))
429 }
430
431 Self::from_selectors(selectors)
432 }
433
434 pub fn scan_ranges(&self, page_locations: &[PageLocation]) -> Vec<Range<u64>> {
444 match &self.inner {
445 RowSelectionInner::Selectors(selectors) => {
446 scan_ranges_from_selectors(selectors.iter().copied(), page_locations)
447 }
448 RowSelectionInner::Mask(mask) => scan_ranges_from_mask(mask.mask(), page_locations),
449 }
450 }
451
452 pub(crate) fn row_ranges_for_selected_pages(
454 &self,
455 page_locations: &[PageLocation],
456 total_rows: usize,
457 ) -> Vec<Range<usize>> {
458 let mut selected_pages = self.scan_ranges(page_locations).into_iter().peekable();
459 let mut row_ranges = Vec::new();
460
461 for (idx, page) in page_locations.iter().enumerate() {
462 let Some(selected_page) = selected_pages.peek() else {
463 break;
464 };
465 if selected_page.start != page.offset as u64 {
466 continue;
467 }
468 selected_pages.next();
469
470 let end = page_locations
471 .get(idx + 1)
472 .map(|next| next.first_row_index as usize)
473 .unwrap_or(total_rows);
474 row_ranges.push(page.first_row_index as usize..end);
475 }
476
477 row_ranges
478 }
479
480 pub fn split_off(&mut self, row_count: usize) -> Self {
482 match std::mem::take(&mut self.inner) {
483 RowSelectionInner::Mask(mask) => {
484 let total = mask.cached_count();
485 let (head, tail) = split_off_mask((*mask).into_mask(), row_count);
486 let (head, tail) = match total {
489 Some(total) => {
490 let head_count = if tail.is_empty() {
491 total
492 } else {
493 head.count_set_bits()
494 };
495 (
496 MaskSelection::with_count(head, head_count),
497 MaskSelection::with_count(tail, total - head_count),
498 )
499 }
500 None => (MaskSelection::new(head), MaskSelection::new(tail)),
501 };
502 self.inner = RowSelectionInner::Mask(Box::new(tail));
503 Self::from_mask_selection(head)
504 }
505 RowSelectionInner::Selectors(selectors) => {
506 let (head, tail) = split_off_selectors(selectors, row_count);
507 self.inner = RowSelectionInner::Selectors(tail);
508 Self::from_selectors(head)
509 }
510 }
511 }
512
513 pub fn and_then(&self, other: &Self) -> Self {
536 match (&self.inner, &other.inner) {
537 (RowSelectionInner::Mask(mask), _) => {
538 Self::from_boolean_buffer(and_then_mask(mask.mask(), other))
539 }
540 (RowSelectionInner::Selectors(first), RowSelectionInner::Selectors(second)) => {
541 and_then_row_selections(first, second)
542 }
543 (RowSelectionInner::Selectors(first), RowSelectionInner::Mask(second)) => {
544 and_then_selectors_with_mask(first, second.mask())
545 }
546 }
547 }
548
549 pub fn intersection(&self, other: &Self) -> Self {
560 match (&self.inner, &other.inner) {
561 (RowSelectionInner::Mask(l), RowSelectionInner::Mask(r)) => {
562 Self::from_boolean_buffer(intersect_masks(l.mask(), r.mask()))
563 }
564 (RowSelectionInner::Selectors(l), RowSelectionInner::Selectors(r)) => {
565 intersect_row_selections(l, r)
566 }
567 (RowSelectionInner::Selectors(selectors), RowSelectionInner::Mask(mask))
568 | (RowSelectionInner::Mask(mask), RowSelectionInner::Selectors(selectors)) => {
569 combine_mixed_selection(selectors, mask, intersect_masks)
570 }
571 }
572 }
573
574 pub fn union(&self, other: &Self) -> Self {
585 match (&self.inner, &other.inner) {
586 (RowSelectionInner::Mask(l), RowSelectionInner::Mask(r)) => {
587 Self::from_boolean_buffer(union_masks(l.mask(), r.mask()))
588 }
589 (RowSelectionInner::Selectors(l), RowSelectionInner::Selectors(r)) => {
590 union_row_selections(l, r)
591 }
592 (RowSelectionInner::Selectors(selectors), RowSelectionInner::Mask(mask))
593 | (RowSelectionInner::Mask(mask), RowSelectionInner::Selectors(selectors)) => {
594 combine_mixed_selection(selectors, mask, union_masks)
595 }
596 }
597 }
598
599 pub fn selects_any(&self) -> bool {
601 match &self.inner {
602 RowSelectionInner::Selectors(s) => s.iter().any(|x| !x.skip),
603 RowSelectionInner::Mask(m) => match m.cached_count() {
604 Some(count) => count > 0,
605 None => m.mask().set_indices().next().is_some(),
606 },
607 }
608 }
609
610 pub(crate) fn trim(self) -> Self {
612 match self.inner {
613 RowSelectionInner::Mask(m) => {
614 let trimmed = trim_mask(m.mask());
615 let cached_count = m.cached_count();
616 match trimmed {
617 Some(mask) => match cached_count {
619 Some(count) => {
620 Self::from_mask_selection(MaskSelection::with_count(mask, count))
621 }
622 None => Self::from_boolean_buffer(mask),
623 },
624 None => Self {
626 inner: RowSelectionInner::Mask(m),
627 },
628 }
629 }
630 RowSelectionInner::Selectors(mut selectors) => {
631 while selectors.last().map(|x| x.skip).unwrap_or(false) {
632 selectors.pop();
633 }
634 Self::from_selectors(selectors)
635 }
636 }
637 }
638
639 pub(crate) fn offset(self, offset: usize) -> Self {
641 if offset == 0 {
642 return self;
643 }
644
645 match self.inner {
646 RowSelectionInner::Mask(mask) => {
647 let count = mask.count();
648 let buffer = offset_mask((*mask).into_mask(), offset, count);
649 Self::from_mask_selection(MaskSelection::with_count(
650 buffer,
651 count.saturating_sub(offset),
652 ))
653 }
654 RowSelectionInner::Selectors(selectors) => {
655 Self::from_selectors(offset_selectors(selectors, offset))
656 }
657 }
658 }
659
660 pub(crate) fn limit(self, limit: usize) -> Self {
662 match self.inner {
663 RowSelectionInner::Mask(mask) => {
664 let cached = mask.cached_count();
665 let buffer = limit_mask((*mask).into_mask(), limit);
666 match cached {
667 Some(count) => Self::from_mask_selection(MaskSelection::with_count(
668 buffer,
669 count.min(limit),
670 )),
671 None => Self::from_boolean_buffer(buffer),
672 }
673 }
674 RowSelectionInner::Selectors(selectors) => {
675 Self::from_selectors(limit_selectors(selectors, limit))
676 }
677 }
678 }
679
680 pub fn iter(&self) -> impl Iterator<Item = &RowSelector> {
690 match &self.inner {
691 RowSelectionInner::Selectors(s) => s.iter(),
692 RowSelectionInner::Mask(m) => m.selectors().iter(),
693 }
694 }
695
696 pub fn row_count(&self) -> usize {
698 match &self.inner {
699 RowSelectionInner::Selectors(s) => {
700 s.iter().filter(|x| !x.skip).map(|x| x.row_count).sum()
701 }
702 RowSelectionInner::Mask(m) => m.count(),
703 }
704 }
705
706 pub fn total_row_count(&self) -> usize {
709 match &self.inner {
710 RowSelectionInner::Selectors(s) => s.iter().map(|x| x.row_count).sum(),
711 RowSelectionInner::Mask(m) => m.mask().len(),
712 }
713 }
714
715 pub fn skipped_row_count(&self) -> usize {
717 match &self.inner {
718 RowSelectionInner::Selectors(s) => {
719 s.iter().filter(|x| x.skip).map(|x| x.row_count).sum()
720 }
721 RowSelectionInner::Mask(m) => m.mask().len() - m.count(),
722 }
723 }
724
725 pub(crate) fn expand_to_batch_boundaries(&self, batch_size: usize, total_rows: usize) -> Self {
729 if batch_size == 0 {
730 return self.clone();
731 }
732
733 match &self.inner {
734 RowSelectionInner::Selectors(selectors) => expand_to_batch_boundaries_from_selectors(
735 selectors.iter().copied(),
736 batch_size,
737 total_rows,
738 ),
739 RowSelectionInner::Mask(mask) => expand_to_batch_boundaries_from_selectors(
740 MaskRunIter::new(mask.mask()),
741 batch_size,
742 total_rows,
743 ),
744 }
745 }
746}
747
748#[inline]
758fn auto_min_mask_runs(total_rows: usize, threshold: usize) -> Option<usize> {
759 if total_rows == usize::MAX {
762 return None;
763 }
764
765 let min_runs = total_rows.checked_div(threshold)?.checked_add(1)?;
766 (min_runs <= total_rows).then_some(min_runs)
767}
768
769fn selectors_below_run_limit(
773 filters: &[BooleanArray],
774 total_rows: usize,
775 min_mask_runs: usize,
776) -> Option<Vec<RowSelector>> {
777 let mut selectors = Vec::new();
778 let mut next_offset = 0usize;
779 let mut last_end = 0usize;
780
781 for filter in filters {
782 assert_eq!(filter.null_count(), 0);
783 let offset = next_offset;
784 next_offset = next_offset.checked_add(filter.len()).unwrap();
785
786 for (start, end) in SlicesIterator::new(filter) {
787 let start = start.checked_add(offset).unwrap();
788 let end = end.checked_add(offset).unwrap();
789
790 if start > last_end {
791 append_normalized_selector(&mut selectors, RowSelector::skip(start - last_end));
792 if selectors.len() >= min_mask_runs {
793 return None;
794 }
795 }
796
797 append_normalized_selector(&mut selectors, RowSelector::select(end - start));
798 if selectors.len() >= min_mask_runs {
799 return None;
800 }
801 last_end = end;
802 }
803 }
804
805 if last_end != total_rows {
806 append_normalized_selector(&mut selectors, RowSelector::skip(total_rows - last_end));
807 if selectors.len() >= min_mask_runs {
808 return None;
809 }
810 }
811
812 Some(selectors)
813}
814
815fn append_normalized_selector(selectors: &mut Vec<RowSelector>, selector: RowSelector) {
817 if selector.row_count == 0 {
818 return;
819 }
820
821 match selectors.last_mut() {
822 Some(last) if last.skip == selector.skip => {
823 last.row_count = last.row_count.checked_add(selector.row_count).unwrap()
824 }
825 _ => selectors.push(selector),
826 }
827}
828
829fn filters_to_boolean_buffer(filters: &[BooleanArray]) -> BooleanBuffer {
830 let total_rows = filters.iter().map(|filter| filter.len()).sum();
831 let mut builder = BooleanBufferBuilder::new(total_rows);
832 for filter in filters {
833 assert_eq!(filter.null_count(), 0);
834 builder.append_buffer(filter.values());
835 }
836 builder.finish()
837}
838
839impl From<Vec<RowSelector>> for RowSelection {
840 fn from(selectors: Vec<RowSelector>) -> Self {
841 selectors.into_iter().collect()
842 }
843}
844
845impl From<BooleanBuffer> for RowSelection {
846 fn from(mask: BooleanBuffer) -> Self {
847 Self::from_boolean_buffer(mask)
848 }
849}
850
851impl FromIterator<RowSelector> for RowSelection {
852 fn from_iter<T: IntoIterator<Item = RowSelector>>(iter: T) -> Self {
853 let iter = iter.into_iter();
854
855 let mut selectors = Vec::with_capacity(iter.size_hint().0);
857
858 let mut filtered = iter.filter(|x| x.row_count != 0);
859 if let Some(x) = filtered.next() {
860 selectors.push(x);
861 }
862
863 for s in filtered {
864 if s.row_count == 0 {
865 continue;
866 }
867
868 let last = selectors.last_mut().unwrap();
870 if last.skip == s.skip {
871 last.row_count = last.row_count.checked_add(s.row_count).unwrap();
872 } else {
873 selectors.push(s)
874 }
875 }
876
877 Self::from_selectors(selectors)
878 }
879}
880
881impl From<RowSelection> for Vec<RowSelector> {
882 fn from(r: RowSelection) -> Self {
883 r.into_selectors_vec()
884 }
885}
886
887impl From<RowSelection> for VecDeque<RowSelector> {
888 fn from(r: RowSelection) -> Self {
889 r.into_selectors_vec().into()
890 }
891}
892
893impl FromIterator<RowSelection> for RowSelection {
894 fn from_iter<T: IntoIterator<Item = RowSelection>>(iter: T) -> Self {
900 let items: Vec<RowSelection> = iter.into_iter().collect();
901
902 let all_mask = items
903 .iter()
904 .all(|s| matches!(&s.inner, RowSelectionInner::Mask(_)));
905
906 if all_mask {
907 let total_len: usize = items
908 .iter()
909 .map(|s| match &s.inner {
910 RowSelectionInner::Mask(m) => m.mask().len(),
911 RowSelectionInner::Selectors(_) => unreachable!(),
912 })
913 .sum();
914 let mut builder = BooleanBufferBuilder::new(total_len);
915 for item in items {
916 match item.into_inner() {
917 RowSelectionInner::Mask(m) => builder.append_buffer(m.mask()),
918 RowSelectionInner::Selectors(_) => unreachable!(),
919 }
920 }
921 return Self::from_boolean_buffer(builder.finish());
922 }
923
924 items
925 .into_iter()
926 .flat_map(|s| s.into_selectors_vec())
927 .collect()
928 }
929}
930
931#[cfg(test)]
932mod tests {
933 use super::*;
934 use rand::rngs::StdRng;
935 use rand::{RngExt, SeedableRng};
936
937 const MAX_RANDOM_ROWS: usize = 65_536;
938 const THRESHOLDS: &[usize] = &[0, 1, 8, 16, 31, 32, 33, 64];
939 const SELECTIVITIES: &[usize] = &[0, 1, 5, 15, 50, 90, 99, 100];
940 const FILTER_SHAPES: &[FilterShape] = &[
941 FilterShape::Isolated,
942 FilterShape::Runs,
943 FilterShape::Random,
944 FilterShape::Clustered,
945 ];
946
947 #[derive(Clone, Copy, Debug)]
948 enum FilterShape {
949 Isolated,
950 Runs,
951 Random,
952 Clustered,
953 }
954
955 #[test]
956 fn auto_min_mask_runs_matches_policy_boundaries() {
957 assert_eq!(auto_min_mask_runs(0, 32), None);
958 assert_eq!(auto_min_mask_runs(64, 0), None);
959 assert_eq!(auto_min_mask_runs(64, 1), None);
960 assert_eq!(auto_min_mask_runs(64, 32), Some(3));
961 assert_eq!(auto_min_mask_runs(64, 64), Some(2));
962 assert_eq!(auto_min_mask_runs(64, 65), Some(1));
963 assert_eq!(auto_min_mask_runs(usize::MAX, usize::MAX), None);
964 }
965
966 #[test]
967 fn auto_construction_preserves_global_runs_and_threshold_boundary() {
968 let filters = vec![
969 BooleanArray::from(vec![false, true, true]),
970 BooleanArray::from(Vec::<bool>::new()),
971 BooleanArray::from(vec![true, true, false]),
972 BooleanArray::from(vec![false, false, true]),
973 ];
974
975 for threshold in THRESHOLDS {
976 assert_auto_equivalent(&filters, *threshold, "cross-filter run merge");
977 }
978
979 let run31_filters = split_evenly(&run_mask(65_536, 31, 31), 8_192);
980 let run32_filters = split_evenly(&run_mask(65_536, 32, 32), 8_192);
981
982 assert_eq!(
983 RowSelection::from_filters(&run31_filters).auto_selection_strategy(32),
984 RowSelectionStrategy::Mask
985 );
986 assert_eq!(
987 RowSelection::from_filters(&run32_filters).auto_selection_strategy(32),
988 RowSelectionStrategy::Selectors
989 );
990 assert_auto_equivalent(&run31_filters, 32, "run31 below threshold");
991 assert_auto_equivalent(&run32_filters, 32, "run32 equal to threshold");
992
993 assert_auto_equivalent(&[], 0, "no filters");
994 assert_auto_equivalent(&[BooleanArray::from(Vec::<bool>::new())], 0, "empty filter");
995 }
996
997 #[test]
998 #[cfg_attr(miri, ignore)] fn auto_construction_edge_row_counts() {
1000 for rows in [0, 1, 7, 8, 31, 32, 33, MAX_RANDOM_ROWS] {
1001 let masks = [
1002 ("all skipped", BooleanBuffer::new_unset(rows)),
1003 ("all selected", BooleanBuffer::new_set(rows)),
1004 (
1005 "alternating",
1006 BooleanBuffer::from_iter((0..rows).map(|row| row % 2 == 0)),
1007 ),
1008 ("short runs", run_mask(rows, 3, 5)),
1009 ];
1010
1011 for (shape, mask) in masks {
1012 let mask = with_bit_offset(mask, 5);
1013 let filters = split_evenly(&mask, rows.div_ceil(3).max(1));
1014
1015 for &threshold in THRESHOLDS {
1016 let context = format!("rows={rows} shape={shape} threshold={threshold}");
1017 assert_auto_equivalent(&filters, threshold, &context);
1018 }
1019 }
1020 }
1021 }
1022
1023 #[test]
1024 #[cfg_attr(miri, ignore)] fn auto_construction_randomized_equivalence() {
1026 let mut rng = StdRng::seed_from_u64(0x1077_6000_5eed);
1027 let mut case_idx = 0usize;
1028
1029 for &threshold in THRESHOLDS {
1030 for &selectivity in SELECTIVITIES {
1031 for &shape in FILTER_SHAPES {
1032 for with_offset in [false, true] {
1033 let rows = rng.random_range(0..=MAX_RANDOM_ROWS);
1034 let mask = random_shape(&mut rng, rows, selectivity, shape);
1035 let bit_offset = if with_offset {
1036 rng.random_range(1..=63)
1037 } else {
1038 0
1039 };
1040 let mask = with_bit_offset(mask, bit_offset);
1041 let filter_count = rng.random_range(1..=32);
1042 let filters = random_split(&mut rng, &mask, filter_count);
1043 let context = format!(
1044 "case={case_idx} rows={rows} selectivity={selectivity} shape={shape:?} \
1045 filters={filter_count} threshold={threshold} bit_offset={bit_offset}"
1046 );
1047
1048 assert_auto_equivalent(&filters, threshold, &context);
1049 case_idx += 1;
1050 }
1051 }
1052 }
1053 }
1054 }
1055
1056 fn assert_auto_equivalent(filters: &[BooleanArray], threshold: usize, context: &str) {
1057 let reference = RowSelection::from_filters(filters);
1058 let reference_strategy = reference.auto_selection_strategy(threshold);
1059 let auto_built = RowSelection::from_filters_auto(filters, threshold);
1060 let auto_built_strategy = auto_built.auto_selection_strategy(threshold);
1061 let auto_built_backing = match &auto_built.inner {
1062 RowSelectionInner::Mask(_) => RowSelectionStrategy::Mask,
1063 RowSelectionInner::Selectors(_) => RowSelectionStrategy::Selectors,
1064 };
1065
1066 assert_eq!(reference_strategy, auto_built_backing, "backing: {context}");
1067 assert_eq!(
1068 reference_strategy, auto_built_strategy,
1069 "strategy: {context}"
1070 );
1071 assert_eq!(reference, auto_built, "logical selection: {context}");
1072 }
1073
1074 fn run_mask(rows: usize, selected_run: usize, skipped_run: usize) -> BooleanBuffer {
1075 let period = selected_run + skipped_run;
1076 BooleanBuffer::from_iter((0..rows).map(|row| row % period < selected_run))
1077 }
1078
1079 fn split_evenly(mask: &BooleanBuffer, batch_size: usize) -> Vec<BooleanArray> {
1080 (0..mask.len())
1081 .step_by(batch_size)
1082 .map(|offset| {
1083 let len = batch_size.min(mask.len() - offset);
1084 BooleanArray::new(mask.slice(offset, len), None)
1085 })
1086 .collect()
1087 }
1088
1089 fn random_shape(
1090 rng: &mut StdRng,
1091 rows: usize,
1092 selectivity: usize,
1093 shape: FilterShape,
1094 ) -> BooleanBuffer {
1095 if selectivity == 0 {
1096 return BooleanBuffer::new_unset(rows);
1097 }
1098 if selectivity == 100 {
1099 return BooleanBuffer::new_set(rows);
1100 }
1101
1102 match shape {
1103 FilterShape::Isolated => isolated_mask(rows, selectivity),
1104 FilterShape::Runs => {
1105 let scale = rng.random_range(1..=8);
1106 run_mask(rows, selectivity * scale, (100 - selectivity) * scale)
1107 }
1108 FilterShape::Random => BooleanBuffer::from_iter(
1109 (0..rows).map(|_| rng.random_bool(selectivity as f64 / 100.0)),
1110 ),
1111 FilterShape::Clustered => one_cluster_mask(rng, rows, selectivity),
1112 }
1113 }
1114
1115 fn isolated_mask(rows: usize, selectivity: usize) -> BooleanBuffer {
1116 if selectivity <= 50 {
1117 let period = 100usize.div_ceil(selectivity).max(2);
1118 BooleanBuffer::from_iter((0..rows).map(|row| row % period == 0))
1119 } else {
1120 let period = 100usize.div_ceil(100 - selectivity).max(2);
1121 BooleanBuffer::from_iter((0..rows).map(|row| row % period != 0))
1122 }
1123 }
1124
1125 fn one_cluster_mask(rng: &mut StdRng, rows: usize, selectivity: usize) -> BooleanBuffer {
1126 let selected = rows.saturating_mul(selectivity) / 100;
1127 let start = rng.random_range(0..=rows - selected);
1128 let mut builder = BooleanBufferBuilder::new(rows);
1129 builder.append_n(start, false);
1130 builder.append_n(selected, true);
1131 builder.append_n(rows - start - selected, false);
1132 builder.finish()
1133 }
1134
1135 fn with_bit_offset(mask: BooleanBuffer, offset: usize) -> BooleanBuffer {
1136 if offset == 0 {
1137 return mask;
1138 }
1139
1140 let len = mask.len();
1141 let mut builder = BooleanBufferBuilder::new(offset + len);
1142 builder.append_n(offset, false);
1143 builder.append_buffer(&mask);
1144 builder.finish().slice(offset, len)
1145 }
1146
1147 fn random_split(
1148 rng: &mut StdRng,
1149 mask: &BooleanBuffer,
1150 filter_count: usize,
1151 ) -> Vec<BooleanArray> {
1152 let mut filters = Vec::with_capacity(filter_count);
1153 let mut offset = 0usize;
1154
1155 for index in 0..filter_count {
1156 let remaining = mask.len() - offset;
1157 let len = if index + 1 == filter_count {
1158 remaining
1159 } else {
1160 rng.random_range(0..=remaining)
1161 };
1162 filters.push(BooleanArray::new(mask.slice(offset, len), None));
1163 offset += len;
1164 }
1165
1166 filters
1167 }
1168
1169 #[test]
1170 fn test_force_mask_from_selectors() {
1171 let cases = [
1172 (vec![], vec![]),
1173 (vec![RowSelector::select(9)], vec![true; 9]),
1174 (vec![RowSelector::skip(9)], vec![false; 9]),
1175 (
1176 vec![
1177 RowSelector::skip(3),
1178 RowSelector::select(7),
1179 RowSelector::skip(2),
1180 RowSelector::select(1),
1181 RowSelector::skip(4),
1182 ],
1183 vec![
1184 false, false, false, true, true, true, true, true, true, true, false, false,
1185 true, false, false, false, false,
1186 ],
1187 ),
1188 ];
1189
1190 for (selectors, bits) in cases {
1191 let original = RowSelection::from(selectors);
1192 let selection = original.clone().force_mask();
1193 assert_eq!(selection.as_mask().unwrap(), &BooleanBuffer::from(bits));
1194 assert_eq!(selection.total_row_count(), original.total_row_count());
1195 assert_eq!(selection.row_count(), original.row_count());
1196 assert_eq!(selection.skipped_row_count(), original.skipped_row_count());
1197 assert_eq!(selection, original);
1198 }
1199 }
1200
1201 #[test]
1202 fn test_force_mask_preserves_existing_mask() {
1203 let mask = BooleanBuffer::from(vec![
1204 false, true, false, true, true, false, true, false, false, true, false,
1205 ])
1206 .slice(3, 7);
1207 let selection = RowSelection::from_boolean_buffer(mask.clone()).force_mask();
1208 assert!(selection.as_mask().unwrap().ptr_eq(&mask));
1209 assert_eq!(selection.row_count(), 4);
1210
1211 let selection = selection.force_mask();
1213 assert!(selection.as_mask().unwrap().ptr_eq(&mask));
1214 assert_eq!(selection.total_row_count(), 7);
1215 }
1216
1217 #[test]
1218 fn test_force_mask_preserves_bitmap_intersection() {
1219 let existing = RowSelection::from_boolean_buffer(BooleanBuffer::from(vec![
1220 true, false, true, true, false, true, true, false, true,
1221 ]));
1222 let selection = RowSelection::from(vec![
1223 RowSelector::skip(2),
1224 RowSelector::select(5),
1225 RowSelector::skip(2),
1226 ]);
1227 let expected = existing.intersection(&selection);
1228 let actual = existing.intersection(&selection.force_mask());
1229
1230 assert!(actual.as_mask().is_some());
1231 assert_eq!(actual, expected);
1232 assert_eq!(actual.total_row_count(), 9);
1233 assert_eq!(actual.row_count(), 4);
1234 }
1235
1236 #[test]
1237 fn test_total_row_count() {
1238 let selection = RowSelection::from(vec![RowSelector::skip(5), RowSelector::select(3)]);
1239 assert_eq!(selection.total_row_count(), 8);
1240 assert_eq!(selection.row_count(), 3);
1241 assert_eq!(selection.skipped_row_count(), 5);
1242
1243 let selection =
1244 RowSelection::from_boolean_buffer(BooleanBuffer::from(vec![true, false, true]));
1245 assert_eq!(selection.total_row_count(), 3);
1246 assert_eq!(selection.row_count(), 2);
1247 assert_eq!(selection.skipped_row_count(), 1);
1248
1249 let empty = RowSelection::from(vec![]);
1250 assert_eq!(empty.total_row_count(), 0);
1251 }
1252
1253 #[test]
1254 fn test_offset_zero_and_zero_batch_expand_are_identity() {
1255 let selection =
1256 RowSelection::from_boolean_buffer(BooleanBuffer::from(vec![true, false, true]));
1257 assert_eq!(selection.clone().offset(0), selection);
1258 assert_eq!(selection.expand_to_batch_boundaries(0, 3), selection);
1259 }
1260
1261 #[test]
1262 fn test_from_filters() {
1263 let filters = vec![
1264 BooleanArray::from(vec![false, false, false, true, true, true, true]),
1265 BooleanArray::from(vec![true, true, false, false, true, true, true]),
1266 BooleanArray::from(vec![false, false, false, false]),
1267 BooleanArray::from(Vec::<bool>::new()),
1268 ];
1269
1270 let selection = RowSelection::from_filters(&filters[..1]);
1271 assert!(selection.selects_any());
1272 assert_eq!(
1273 selection.selectors(),
1274 vec![RowSelector::skip(3), RowSelector::select(4)]
1275 );
1276
1277 let selection = RowSelection::from_filters(&filters[..2]);
1278 assert!(selection.selects_any());
1279 assert_eq!(
1280 selection.selectors(),
1281 vec![
1282 RowSelector::skip(3),
1283 RowSelector::select(6),
1284 RowSelector::skip(2),
1285 RowSelector::select(3)
1286 ]
1287 );
1288
1289 let selection = RowSelection::from_filters(&filters);
1290 assert!(selection.selects_any());
1291 assert_eq!(
1292 selection.selectors(),
1293 vec![
1294 RowSelector::skip(3),
1295 RowSelector::select(6),
1296 RowSelector::skip(2),
1297 RowSelector::select(3),
1298 RowSelector::skip(4)
1299 ]
1300 );
1301
1302 let selection = RowSelection::from_filters(&filters[2..3]);
1303 assert!(!selection.selects_any());
1304 assert_eq!(selection.selectors(), vec![RowSelector::skip(4)]);
1305 }
1306
1307 #[test]
1308 fn test_iter() {
1309 let selectors = vec![
1312 RowSelector::select(3),
1313 RowSelector::skip(33),
1314 RowSelector::select(4),
1315 ];
1316
1317 let round_tripped: Vec<RowSelector> = RowSelection::from(selectors.clone())
1318 .iter()
1319 .copied()
1320 .collect();
1321 assert_eq!(selectors, round_tripped);
1322 }
1323
1324 #[test]
1325 fn test_row_count() {
1326 let selection = RowSelection::from(vec![
1327 RowSelector::skip(34),
1328 RowSelector::select(12),
1329 RowSelector::skip(3),
1330 RowSelector::select(35),
1331 ]);
1332
1333 assert_eq!(selection.row_count(), 12 + 35);
1334 assert_eq!(selection.skipped_row_count(), 34 + 3);
1335
1336 let selection = RowSelection::from(vec![RowSelector::select(12), RowSelector::select(35)]);
1337
1338 assert_eq!(selection.row_count(), 12 + 35);
1339 assert_eq!(selection.skipped_row_count(), 0);
1340
1341 let selection = RowSelection::from(vec![RowSelector::skip(34), RowSelector::skip(3)]);
1342
1343 assert_eq!(selection.row_count(), 0);
1344 assert_eq!(selection.skipped_row_count(), 34 + 3);
1345
1346 let selection = RowSelection::from(vec![]);
1347
1348 assert_eq!(selection.row_count(), 0);
1349 assert_eq!(selection.skipped_row_count(), 0);
1350 }
1351
1352 #[test]
1353 fn test_mixed_backing_equality_mismatches() {
1354 let mask =
1355 RowSelection::from_boolean_buffer(BooleanBuffer::from(vec![true, false, true, true]));
1356
1357 let longer = RowSelection::from(vec![
1359 RowSelector::select(1),
1360 RowSelector::skip(1),
1361 RowSelector::select(2),
1362 RowSelector::skip(1),
1363 ]);
1364 assert_ne!(mask, longer);
1365 assert_ne!(longer, mask);
1366
1367 let skip_overlap = RowSelection::from(vec![RowSelector::skip(2), RowSelector::select(2)]);
1369 assert_ne!(mask, skip_overlap);
1370
1371 let misaligned = RowSelection::from(vec![
1373 RowSelector::select(2),
1374 RowSelector::skip(1),
1375 RowSelector::select(1),
1376 ]);
1377 assert_ne!(mask, misaligned);
1378
1379 let equal = RowSelection::from(vec![
1380 RowSelector::select(1),
1381 RowSelector::skip(1),
1382 RowSelector::select(2),
1383 ]);
1384 assert_eq!(mask, equal);
1385 assert_eq!(equal, mask);
1386 }
1387
1388 #[test]
1389 fn test_from_iter_all_mask_preserves_mask_backing() {
1390 let a_bits = vec![true, false, true, true];
1391 let b_bits = vec![false, true, false];
1392 let c_bits = vec![true, true, false, false, true];
1393
1394 let parts = vec![
1395 RowSelection::from_boolean_buffer(BooleanBuffer::from(a_bits.clone())),
1396 RowSelection::from_boolean_buffer(BooleanBuffer::from(b_bits.clone())),
1397 RowSelection::from_boolean_buffer(BooleanBuffer::from(c_bits.clone())),
1398 ];
1399 let collected: RowSelection = parts.into_iter().collect();
1400
1401 let combined = a_bits
1402 .iter()
1403 .chain(b_bits.iter())
1404 .chain(c_bits.iter())
1405 .copied()
1406 .collect::<Vec<_>>();
1407 let expected = RowSelection::from_filters(&[BooleanArray::from(combined)]);
1408
1409 assert!(collected.as_mask().is_some());
1410 assert_eq!(collected, expected);
1411 }
1412
1413 #[test]
1414 fn test_from_iter_mixed_backing_falls_back_to_selectors() {
1415 let a_bits = vec![true, false, true];
1416 let b_selectors = vec![RowSelector::skip(2), RowSelector::select(3)];
1417 let c_bits = vec![false, true];
1418
1419 let parts = vec![
1420 RowSelection::from_boolean_buffer(BooleanBuffer::from(a_bits.clone())),
1421 RowSelection::from(b_selectors),
1422 RowSelection::from_boolean_buffer(BooleanBuffer::from(c_bits.clone())),
1423 ];
1424 let collected: RowSelection = parts.into_iter().collect();
1425
1426 assert!(collected.as_mask().is_none());
1427
1428 let combined_bits = vec![
1429 true, false, true, false, false, true, true, true, false, true,
1430 ];
1431 let expected = RowSelection::from_filters(&[BooleanArray::from(combined_bits)]);
1432 assert_eq!(collected, expected);
1433 }
1434
1435 #[test]
1436 fn test_from_iter_empty_yields_empty_selection() {
1437 let collected: RowSelection = std::iter::empty::<RowSelection>().collect();
1438 assert_eq!(collected, RowSelection::default());
1439 assert!(collected.as_mask().is_some());
1440 assert_eq!(collected.as_mask().unwrap().len(), 0);
1441 }
1442}