1use super::RowSelector;
27use arrow_buffer::bit_iterator::BitSliceIterator;
28use arrow_buffer::{BooleanBuffer, BooleanBufferBuilder, Buffer};
29use std::sync::OnceLock;
30
31#[derive(Debug)]
44pub(crate) struct MaskSelection {
45 mask: BooleanBuffer,
46 selectors: OnceLock<Vec<RowSelector>>,
47 count: OnceLock<usize>,
48}
49
50impl MaskSelection {
51 pub(super) fn new(mask: BooleanBuffer) -> Self {
52 Self {
53 mask,
54 selectors: OnceLock::new(),
55 count: OnceLock::new(),
56 }
57 }
58
59 pub(super) fn with_count(mask: BooleanBuffer, count: usize) -> Self {
61 debug_assert!(count <= mask.len());
62 let cell = OnceLock::new();
63 let _ = cell.set(count);
64 Self {
65 mask,
66 selectors: OnceLock::new(),
67 count: cell,
68 }
69 }
70
71 pub(crate) fn mask(&self) -> &BooleanBuffer {
72 &self.mask
73 }
74
75 pub(crate) fn into_mask(self) -> BooleanBuffer {
76 let Self { mask, .. } = self;
77 mask
78 }
79
80 pub(super) fn count(&self) -> usize {
82 *self.count.get_or_init(|| self.mask.count_set_bits())
83 }
84
85 pub(super) fn cached_count(&self) -> Option<usize> {
87 self.count.get().copied()
88 }
89
90 pub(super) fn selectors(&self) -> &[RowSelector] {
91 self.selectors
92 .get_or_init(|| mask_to_selectors(&self.mask))
93 .as_slice()
94 }
95
96 pub(crate) fn into_selectors(self) -> Vec<RowSelector> {
98 match self.selectors.into_inner() {
99 Some(selectors) => selectors,
100 None => mask_to_selectors(&self.mask),
101 }
102 }
103}
104
105impl Clone for MaskSelection {
106 fn clone(&self) -> Self {
107 Self {
109 mask: self.mask.clone(),
110 selectors: OnceLock::new(),
111 count: self.count.clone(),
112 }
113 }
114}
115
116#[derive(Debug)]
131pub struct MaskRunIter<'a> {
132 slices: BitSliceIterator<'a>,
133 cursor: usize,
134 total: usize,
135 pending: Option<RowSelector>,
136 finished: bool,
137}
138
139impl<'a> MaskRunIter<'a> {
140 pub fn new(mask: &'a BooleanBuffer) -> Self {
142 Self {
143 slices: mask.set_slices(),
144 cursor: 0,
145 total: mask.len(),
146 pending: None,
147 finished: false,
148 }
149 }
150}
151
152impl Iterator for MaskRunIter<'_> {
153 type Item = RowSelector;
154
155 fn next(&mut self) -> Option<RowSelector> {
156 if let Some(p) = self.pending.take() {
157 return Some(p);
158 }
159 if self.finished {
160 return None;
161 }
162 match self.slices.next() {
163 Some((start, end)) => {
164 let select = RowSelector::select(end - start);
165 if start > self.cursor {
166 let skip = RowSelector::skip(start - self.cursor);
167 self.pending = Some(select);
168 self.cursor = end;
169 Some(skip)
170 } else {
171 self.cursor = end;
172 Some(select)
173 }
174 }
175 None => {
176 self.finished = true;
177 if self.cursor < self.total {
178 let skip = RowSelector::skip(self.total - self.cursor);
179 self.cursor = self.total;
180 Some(skip)
181 } else {
182 None
183 }
184 }
185 }
186 }
187}
188
189pub(super) fn mask_to_selectors(mask: &BooleanBuffer) -> Vec<RowSelector> {
191 let total_rows = mask.len();
192 if total_rows == 0 {
193 return Vec::new();
194 }
195 let mut selectors: Vec<RowSelector> = Vec::new();
196 let mut last_end = 0;
197 for (start, end) in mask.set_slices() {
198 if start > last_end {
199 selectors.push(RowSelector::skip(start - last_end));
200 }
201 selectors.push(RowSelector::select(end - start));
202 last_end = end;
203 }
204 if last_end != total_rows {
205 selectors.push(RowSelector::skip(total_rows - last_end));
206 }
207 selectors
208}
209
210pub(super) fn mask_has_at_least_runs(mask: &BooleanBuffer, min_runs: usize) -> bool {
215 if min_runs == 0 {
216 return true;
217 }
218
219 let total_rows = mask.len();
220 if total_rows == 0 {
221 return false;
222 }
223
224 let mut run_count = 0;
225 let mut last_end = 0;
226 for (start, end) in mask.set_slices() {
227 run_count += usize::from(start > last_end) + 1;
228 if run_count >= min_runs {
229 return true;
230 }
231 last_end = end;
232 }
233
234 run_count + usize::from(last_end < total_rows) >= min_runs
235}
236
237pub(super) fn split_off_mask(
240 mask: BooleanBuffer,
241 row_count: usize,
242) -> (BooleanBuffer, BooleanBuffer) {
243 let total = mask.len();
244 if row_count >= total {
245 return (mask, BooleanBuffer::new_unset(0));
246 }
247
248 let head = mask.slice(0, row_count);
249 let tail = mask.slice(row_count, total - row_count);
250 (head, tail)
251}
252
253fn last_set_bit_position(mask: &BooleanBuffer) -> Option<usize> {
255 let values = mask.values();
256 let offset = mask.offset();
257 let end = offset + mask.len();
258 for byte_idx in (offset / 8..end.div_ceil(8)).rev() {
259 let byte_start = byte_idx * 8;
260 let mut byte = values[byte_idx];
261 if end - byte_start < 8 {
262 byte &= (1u8 << (end - byte_start)) - 1;
263 }
264 if byte_start < offset {
265 byte &= !((1u8 << (offset - byte_start)) - 1);
266 }
267 if byte != 0 {
268 return Some(byte_start + 7 - byte.leading_zeros() as usize - offset);
269 }
270 }
271 None
272}
273
274pub(super) fn trim_mask(mask: &BooleanBuffer) -> Option<BooleanBuffer> {
276 let len = mask.len();
277 if len == 0 || mask.value(len - 1) {
279 return None;
280 }
281 let new_len = last_set_bit_position(mask).map_or(0, |pos| pos + 1);
282 Some(mask.slice(0, new_len))
283}
284
285pub(super) fn offset_mask(mask: BooleanBuffer, offset: usize, popcount: usize) -> BooleanBuffer {
288 if offset >= popcount {
289 return BooleanBuffer::new_unset(0);
290 }
291 let pos = mask.find_nth_set_bit_position(0, offset);
294 let mut builder = BooleanBufferBuilder::new(mask.len());
295 builder.append_n(pos, false);
296 builder.append_buffer(&mask.slice(pos, mask.len() - pos));
297 builder.finish()
298}
299
300pub(super) fn limit_mask(mask: BooleanBuffer, limit: usize) -> BooleanBuffer {
302 let cut = mask.find_nth_set_bit_position(0, limit);
306 mask.slice(0, cut)
307}
308
309fn set_bit_run(buf: &mut [u8], start: usize, len: usize) {
311 if len == 0 {
312 return;
313 }
314 let end = start + len;
315 let first_byte = start / 8;
316 let last_byte = (end - 1) / 8;
317 let start_mask = 0xFFu8 << (start % 8);
318 let end_mask = 0xFFu8 >> (8 - (end - last_byte * 8));
319 if first_byte == last_byte {
320 buf[first_byte] |= start_mask & end_mask;
321 } else {
322 buf[first_byte] |= start_mask;
323 buf[first_byte + 1..last_byte].fill(0xFF);
324 buf[last_byte] |= end_mask;
325 }
326}
327
328pub(super) fn boolean_mask_from_selectors(selectors: &[RowSelector]) -> BooleanBuffer {
333 let total_rows: usize = selectors.iter().map(|s| s.row_count).sum();
334 let mut buf = vec![0u8; total_rows.div_ceil(8)];
335 let mut position = 0usize;
336 for selector in selectors {
337 if !selector.skip {
338 set_bit_run(&mut buf, position, selector.row_count);
339 }
340 position += selector.row_count;
341 }
342 BooleanBuffer::new(Buffer::from(buf), 0, total_rows)
343}
344
345#[cfg(test)]
346mod tests {
347 use super::*;
348 use crate::arrow::arrow_reader::selection::{RowSelection, RowSelectionInner};
349 use arrow_array::BooleanArray;
350 use rand::{RngExt, rng};
351
352 #[test]
353 fn test_mask_iter_yields_borrowed_selectors() {
354 let selection = RowSelection::from_boolean_buffer(BooleanBuffer::from(vec![
355 false, false, true, true, false, true, false, false,
356 ]));
357
358 let borrowed: Vec<&RowSelector> = selection.iter().collect();
359 assert_eq!(
360 borrowed,
361 vec![
362 &RowSelector::skip(2),
363 &RowSelector::select(2),
364 &RowSelector::skip(1),
365 &RowSelector::select(1),
366 &RowSelector::skip(2),
367 ]
368 );
369 }
370
371 #[test]
372 fn test_mask_iter_clone_drops_cache() {
373 let selection = RowSelection::from_boolean_buffer(BooleanBuffer::from(vec![
374 false, false, true, true, false, true, false, false,
375 ]));
376
377 let _ = selection.iter().count();
378 match &selection.inner {
379 RowSelectionInner::Mask(m) => assert!(m.selectors.get().is_some()),
380 RowSelectionInner::Selectors(_) => unreachable!(),
381 }
382
383 let cloned = selection.clone();
384 match &cloned.inner {
385 RowSelectionInner::Mask(m) => assert!(m.selectors.get().is_none()),
386 RowSelectionInner::Selectors(_) => unreachable!(),
387 }
388
389 let round_tripped: Vec<RowSelector> = cloned.iter().copied().collect();
390 assert_eq!(
391 round_tripped,
392 vec![
393 RowSelector::skip(2),
394 RowSelector::select(2),
395 RowSelector::skip(1),
396 RowSelector::select(1),
397 RowSelector::skip(2),
398 ]
399 );
400 }
401
402 fn interleaved_mask() -> BooleanBuffer {
405 BooleanBuffer::from((0..256).map(|i| i % 3 == 0).collect::<Vec<bool>>())
406 }
407
408 fn cached_selectors_ptr(selection: &RowSelection) -> Option<*const RowSelector> {
409 match &selection.inner {
410 RowSelectionInner::Mask(m) => m.selectors.get().map(|s| s.as_ptr()),
411 RowSelectionInner::Selectors(_) => unreachable!(),
412 }
413 }
414
415 #[test]
416 fn test_into_selectors_takes_the_iter_cache() {
417 let selection = RowSelection::from_boolean_buffer(interleaved_mask());
418 let expected: Vec<RowSelector> = selection.iter().copied().collect();
419
420 let cached_ptr = cached_selectors_ptr(&selection).expect("iter populates the cache");
421 let selectors: Vec<RowSelector> = selection.into();
422
423 assert_eq!(selectors, expected);
424 assert_eq!(selectors.as_ptr(), cached_ptr);
426 }
427
428 #[test]
429 fn test_into_selectors_without_cache_still_converts() {
430 let selection = RowSelection::from_boolean_buffer(interleaved_mask());
431 assert!(cached_selectors_ptr(&selection).is_none());
432
433 let selectors: Vec<RowSelector> = selection.into();
434 assert_eq!(selectors, mask_to_selectors(&interleaved_mask()));
435
436 let selection = RowSelection::from_boolean_buffer(interleaved_mask());
438 let _ = selection.iter().count();
439 let deque: std::collections::VecDeque<RowSelector> = selection.into();
440 assert_eq!(Vec::from(deque), selectors);
441 }
442
443 #[test]
444 fn test_mixed_set_algebra_does_not_materialize_selector_cache() {
445 let other = RowSelection::from(vec![
446 RowSelector::skip(3),
447 RowSelector::select(129),
448 RowSelector::skip(5),
449 ]);
450 for buffer in [
451 BooleanBuffer::from_iter((0..137).map(|i| i < 64)),
452 BooleanBuffer::from_iter((0..137).map(|i| i % 2 == 0)),
453 ] {
454 for cached in [false, true] {
455 let selection = RowSelection::from_boolean_buffer(buffer.clone());
456 if cached {
457 let _ = selection.iter().count();
458 }
459 let cache_before = cached_selectors_ptr(&selection);
460 for result in [
461 selection.intersection(&other),
462 other.intersection(&selection),
463 selection.union(&other),
464 other.union(&selection),
465 ] {
466 assert!(result.as_mask().is_some());
467 assert!(cached_selectors_ptr(&result).is_none());
468 }
469 assert_eq!(cached_selectors_ptr(&selection), cache_before);
470 }
471 }
472 }
473
474 #[test]
475 fn test_set_algebra_agrees_whether_or_not_the_cache_is_populated() {
476 let bits: Vec<bool> = (0..256).map(|i| i % 3 == 0).collect();
477 let other = RowSelection::from_filters(&[BooleanArray::from(
478 (0..256).map(|i| i % 5 != 0).collect::<Vec<bool>>(),
479 )]);
480
481 let cold = RowSelection::from_boolean_buffer(BooleanBuffer::from(bits.clone()));
482 let warm = RowSelection::from_boolean_buffer(BooleanBuffer::from(bits));
483 let _ = warm.iter().count();
484
485 assert_eq!(cold.intersection(&other), warm.intersection(&other));
486 assert_eq!(other.intersection(&cold), other.intersection(&warm));
487 assert_eq!(cold.union(&other), warm.union(&other));
488 assert_eq!(other.union(&cold), other.union(&warm));
489 }
490
491 #[test]
492 fn test_mask_run_iter_streams_without_cache() {
493 let selection = RowSelection::from_boolean_buffer(BooleanBuffer::from(vec![
494 false, false, true, true, false, true, false, false,
495 ]));
496 let mut iter = MaskRunIter::new(selection.as_mask().unwrap());
497
498 assert_eq!(iter.next(), Some(RowSelector::skip(2)));
499 assert_eq!(iter.next(), Some(RowSelector::select(2)));
500 assert_eq!(iter.next(), Some(RowSelector::skip(1)));
501 assert_eq!(iter.next(), Some(RowSelector::select(1)));
502 assert_eq!(iter.next(), Some(RowSelector::skip(2)));
503 assert_eq!(iter.next(), None);
504 assert_eq!(iter.next(), None);
505
506 let selection =
507 RowSelection::from_boolean_buffer(BooleanBuffer::from(vec![true, true, false]));
508 let mut iter = MaskRunIter::new(selection.as_mask().unwrap());
509 assert_eq!(iter.next(), Some(RowSelector::select(2)));
510 assert_eq!(iter.next(), Some(RowSelector::skip(1)));
511 assert_eq!(iter.next(), None);
512 }
513
514 #[test]
515 fn test_from_boolean_buffer() {
516 let bits = vec![
517 false, false, true, true, false, true, false, false, true, false, false, false, false,
518 false, false, true,
519 ];
520 let buf = BooleanBuffer::from(bits.clone());
521 let selection = RowSelection::from_boolean_buffer(buf.clone());
522
523 assert!(selection.as_mask().is_some());
524 assert_eq!(selection.row_count(), 5);
525 assert_eq!(selection.skipped_row_count(), 11);
526 assert!(selection.selects_any());
527
528 let from_filters = RowSelection::from_filters(&[BooleanArray::from(bits)]);
529 assert_eq!(selection, from_filters);
530
531 let bits_tail = vec![true, false, true, false, false, false];
532 let trimmed = RowSelection::from_boolean_buffer(BooleanBuffer::from(bits_tail)).trim();
533 assert!(trimmed.as_mask().is_some());
534 assert_eq!(trimmed.as_mask().unwrap().len(), 3);
535 }
536
537 #[test]
538 fn test_from_boolean_buffer_empty() {
539 let empty = RowSelection::from_boolean_buffer(BooleanBuffer::from(Vec::<bool>::new()));
540 assert!(empty.as_mask().is_some());
541 assert_eq!(empty.row_count(), 0);
542 assert_eq!(empty.skipped_row_count(), 0);
543 assert!(!empty.selects_any());
544 assert!(empty.selectors().is_empty());
545 }
546
547 #[test]
548 fn test_from_boolean_buffer_all_unset_does_not_select() {
549 let all_zero = RowSelection::from_boolean_buffer(BooleanBuffer::new_unset(1024));
550 assert!(all_zero.as_mask().is_some());
551 assert!(!all_zero.selects_any());
552 assert_eq!(all_zero.row_count(), 0);
553 assert_eq!(all_zero.skipped_row_count(), 1024);
554 }
555
556 #[test]
557 fn test_from_boolean_buffer_via_from_impl() {
558 let buf = BooleanBuffer::from(vec![true, false, true, true]);
559 let a = RowSelection::from(buf.clone());
560 let b = RowSelection::from_boolean_buffer(buf);
561 assert_eq!(a, b);
562 assert!(a.as_mask().is_some());
563 }
564
565 #[test]
566 fn test_mask_backing_clone_preserves_backing() {
567 let buf = BooleanBuffer::from(vec![true, false, true]);
568 let original = RowSelection::from_boolean_buffer(buf);
569 let cloned = original.clone();
570 assert!(cloned.as_mask().is_some());
571 assert_eq!(original, cloned);
572 }
573
574 #[test]
575 fn test_mask_backing_mutation_equivalence() {
576 let bits = vec![true, true, false, false, true, false, true, true];
577
578 let from_mask = {
579 let mut s = RowSelection::from_boolean_buffer(BooleanBuffer::from(bits.clone()));
580 let split = s.split_off(3);
581 (split, s)
582 };
583 let from_selectors = {
584 let mut s = RowSelection::from_filters(&[BooleanArray::from(bits.clone())]);
585 let split = s.split_off(3);
586 (split, s)
587 };
588 assert_eq!(from_mask.0, from_selectors.0);
589 assert_eq!(from_mask.1, from_selectors.1);
590 assert!(from_mask.0.as_mask().is_some());
591 assert!(from_mask.1.as_mask().is_some());
592
593 let limited_mask =
594 RowSelection::from_boolean_buffer(BooleanBuffer::from(bits.clone())).limit(3);
595 let limited_sel = RowSelection::from_filters(&[BooleanArray::from(bits.clone())]).limit(3);
596 assert!(limited_mask.as_mask().is_some());
597 assert_eq!(limited_mask, limited_sel);
598
599 let offset_mask =
600 RowSelection::from_boolean_buffer(BooleanBuffer::from(bits.clone())).offset(2);
601 let offset_sel = RowSelection::from_filters(&[BooleanArray::from(bits)]).offset(2);
602 assert!(offset_mask.as_mask().is_some());
603 assert_eq!(offset_mask, offset_sel);
604 }
605
606 #[test]
607 #[cfg_attr(miri, ignore)] fn test_mask_backing_fuzz_equivalence() {
609 let mut rand = rng();
610 for _ in 0..100 {
611 let len = rand.random_range(0..200);
612 let bits: Vec<_> = (0..len).map(|_| rand.random_bool(0.35)).collect();
613
614 let from_mask = RowSelection::from_boolean_buffer(BooleanBuffer::from(bits.clone()));
615 let from_filters = RowSelection::from_filters(&[BooleanArray::from(bits.clone())]);
616
617 assert_eq!(from_mask, from_filters);
618 assert_eq!(from_mask.row_count(), from_filters.row_count());
619 assert_eq!(
620 from_mask.skipped_row_count(),
621 from_filters.skipped_row_count()
622 );
623 assert_eq!(from_mask.selects_any(), from_filters.selects_any());
624
625 let inner_len: usize = bits.iter().map(|b| *b as usize).sum();
626 let inner_bits: Vec<_> = (0..inner_len).map(|_| rand.random_bool(0.7)).collect();
627 let inner = RowSelection::from_filters(&[BooleanArray::from(inner_bits.clone())]);
628 let inner_mask = RowSelection::from_boolean_buffer(BooleanBuffer::from(inner_bits));
629 let and_then_mask = from_mask.and_then(&inner);
630 let and_then_both_masks = from_mask.and_then(&inner_mask);
631 assert!(and_then_mask.as_mask().is_some());
632 assert!(and_then_both_masks.as_mask().is_some());
633 assert_eq!(and_then_mask, from_filters.and_then(&inner));
634 assert_eq!(and_then_both_masks, and_then_mask);
635 }
636 }
637
638 #[test]
639 fn test_mask_offset_past_end_preserves_empty_mask_backing() {
640 let selection =
641 RowSelection::from_boolean_buffer(BooleanBuffer::from(vec![true, false, true]))
642 .offset(2);
643
644 assert!(selection.as_mask().is_some());
645 assert_eq!(selection.as_mask().unwrap().len(), 0);
646 assert_eq!(selection.row_count(), 0);
647 assert_eq!(selection.skipped_row_count(), 0);
648 }
649
650 #[test]
651 fn test_mask_limit_truncates_at_nth_selected_row() {
652 let selection = RowSelection::from_boolean_buffer(BooleanBuffer::from(vec![
653 false, true, false, true, false, true, false,
654 ]))
655 .limit(2);
656
657 let mask = selection.as_mask().unwrap();
658 assert_eq!(mask.len(), 4);
659 let actual_bits: Vec<_> = (0..mask.len()).map(|i| mask.value(i)).collect();
660 assert_eq!(actual_bits, vec![false, true, false, true]);
661 }
662
663 #[test]
664 fn test_mask_split_off_preserves_backing() {
665 let bits: Vec<bool> = (0..40).map(|i| i % 3 == 0).collect();
666 let mut s = RowSelection::from_boolean_buffer(BooleanBuffer::from(bits.clone()));
667 let head = s.split_off(15);
668
669 assert!(head.as_mask().is_some());
670 assert!(s.as_mask().is_some());
671
672 let head_sel = RowSelection::from_filters(&[BooleanArray::from(bits[..15].to_vec())]);
673 let tail_sel = RowSelection::from_filters(&[BooleanArray::from(bits[15..].to_vec())]);
674 assert_eq!(head, head_sel);
675 assert_eq!(s, tail_sel);
676 }
677
678 #[test]
679 fn test_mask_split_off_past_end_returns_whole() {
680 let bits = vec![true, false, true];
681 let mut s = RowSelection::from_boolean_buffer(BooleanBuffer::from(bits.clone()));
682 let head = s.split_off(100);
683
684 assert!(head.as_mask().is_some());
685 assert_eq!(head.as_mask().unwrap().len(), 3);
686 assert!(s.as_mask().is_some());
688 assert_eq!(s.as_mask().unwrap().len(), 0);
689 assert_eq!(s.row_count(), 0);
690 assert_eq!(s.skipped_row_count(), 0);
691 }
692
693 #[test]
694 fn test_mask_offset_exceeds_selected_returns_empty() {
695 let s =
696 RowSelection::from_boolean_buffer(BooleanBuffer::from(vec![true, true, false, true]));
697 let r = s.offset(10);
698 assert_eq!(r.row_count(), 0);
699 assert_eq!(r.skipped_row_count(), 0);
700
701 let from_selectors =
702 RowSelection::from_filters(&[BooleanArray::from(vec![true, true, false, true])])
703 .offset(10);
704 assert_eq!(r, from_selectors);
705 }
706
707 #[test]
708 fn test_mask_limit_exceeds_selected_returns_all() {
709 let bits = vec![true, true, false, true];
710 let s = RowSelection::from_boolean_buffer(BooleanBuffer::from(bits.clone()));
711 let r = s.limit(10);
712 assert_eq!(r.row_count(), 3);
713
714 let from_selectors = RowSelection::from_filters(&[BooleanArray::from(bits)]).limit(10);
715 assert_eq!(r, from_selectors);
716 }
717
718 #[test]
719 fn test_mask_trim_all_zero_collapses_to_empty() {
720 let s = RowSelection::from_boolean_buffer(BooleanBuffer::new_unset(128));
721 let trimmed = s.trim();
722 assert!(trimmed.as_mask().is_some());
723 assert_eq!(trimmed.as_mask().unwrap().len(), 0);
724 }
725
726 #[test]
727 #[cfg_attr(miri, ignore)] fn test_boolean_mask_from_selectors_fuzz_equivalence() {
729 let mut rand = rng();
730 for _ in 0..200 {
731 let n_selectors = rand.random_range(0..30);
732 let mut selectors = Vec::with_capacity(n_selectors);
733 for _ in 0..n_selectors {
734 selectors.push(RowSelector {
735 row_count: rand.random_range(0..40),
736 skip: rand.random_bool(0.5),
737 });
738 }
739
740 let expected = {
741 let total_rows: usize = selectors.iter().map(|s| s.row_count).sum();
742 let mut builder = BooleanBufferBuilder::new(total_rows);
743 for selector in &selectors {
744 builder.append_n(selector.row_count, !selector.skip);
745 }
746 builder.finish()
747 };
748
749 assert_eq!(boolean_mask_from_selectors(&selectors), expected);
750 }
751 }
752
753 #[test]
754 fn test_mask_has_at_least_runs() {
755 fn assert_run_count(bits: Vec<bool>, expected_runs: usize) {
756 let mask = BooleanBuffer::from(bits);
757 for min_runs in 0..=expected_runs + 2 {
758 assert_eq!(
759 mask_has_at_least_runs(&mask, min_runs),
760 expected_runs >= min_runs,
761 "expected {expected_runs} runs with boundary {min_runs}"
762 );
763 }
764 }
765
766 assert_run_count(vec![], 0);
767 assert_run_count(vec![false; 8], 1);
768 assert_run_count(vec![true; 8], 1);
769 assert_run_count(vec![false, false, true, true, false], 3);
770 assert_run_count(vec![true, false, true, false, true, false], 6);
771
772 let mask = BooleanBuffer::from(vec![true, false, false, true, true, false, true, true])
774 .slice(1, 6);
775 for min_runs in 0..=6 {
776 assert_eq!(mask_has_at_least_runs(&mask, min_runs), 4 >= min_runs);
777 }
778 }
779
780 #[test]
781 #[cfg_attr(miri, ignore)] fn test_trim_mask_fuzz_equivalence() {
783 let mut rand = rng();
784 for _ in 0..200 {
785 let len = rand.random_range(0..200);
786 let bits: Vec<bool> = (0..len).map(|_| rand.random_bool(0.3)).collect();
787 let full = BooleanBuffer::from(bits.clone());
788 let start = rand.random_range(0..=len);
790 let slice_len = rand.random_range(0..=(len - start));
791 let mask = full.slice(start, slice_len);
792
793 let expected_len = bits[start..start + slice_len]
794 .iter()
795 .rposition(|&b| b)
796 .map_or(0, |pos| pos + 1);
797
798 match trim_mask(&mask) {
799 Some(trimmed) => {
800 assert_ne!(expected_len, mask.len());
801 assert_eq!(trimmed.len(), expected_len);
802 assert_eq!(trimmed, mask.slice(0, expected_len));
803 }
804 None => assert_eq!(expected_len, mask.len()),
805 }
806 }
807 }
808
809 #[test]
810 fn test_split_off_propagates_cached_count() {
811 let bits = vec![true, false, true, true, false, false, true, false];
812 let mut selection = RowSelection::from_boolean_buffer(BooleanBuffer::from(bits));
813 assert_eq!(selection.row_count(), 4);
815 let head = selection.split_off(3);
816 assert_eq!(head.row_count(), 2);
817 assert_eq!(selection.row_count(), 2);
818 let tail_fresh = RowSelection::from_boolean_buffer(BooleanBuffer::from(vec![
819 true, false, false, true, false,
820 ]));
821 assert_eq!(selection, tail_fresh);
822
823 let head = selection.split_off(100);
825 assert_eq!(head.row_count(), 2);
826 assert_eq!(selection.row_count(), 0);
827 }
828
829 #[test]
830 fn test_trim_and_offset_and_limit_preserve_cached_count() {
831 let bits = vec![true, true, false, true, false, false];
832 let selection = RowSelection::from_boolean_buffer(BooleanBuffer::from(bits.clone()));
833 assert_eq!(selection.row_count(), 3);
834
835 let trimmed = selection.trim();
836 assert!(trimmed.as_mask().is_some());
837 assert_eq!(trimmed.as_mask().unwrap().len(), 4);
838 assert_eq!(trimmed.row_count(), 3);
839
840 let offset = trimmed.clone().offset(1);
841 assert_eq!(offset.row_count(), 2);
842
843 let limited = trimmed.limit(2);
844 assert_eq!(limited.row_count(), 2);
845 }
846}