1use std::ops::Not;
7use std::ops::Range;
8
9use vortex_buffer::BitBufferMut;
10use vortex_error::vortex_panic;
11use vortex_mask::Mask;
12
13use crate::row_mask::RowMask;
14use crate::strict_sorted_buffer::StrictSortedBuffer;
15
16#[derive(Default, Clone, Debug)]
19pub enum Selection {
20 #[default]
22 All,
23 IncludeByIndex(StrictSortedBuffer<u64>),
25 ExcludeByIndex(StrictSortedBuffer<u64>),
27 IncludeRoaring(roaring::RoaringTreemap),
29 ExcludeRoaring(roaring::RoaringTreemap),
31}
32
33impl Selection {
34 pub fn row_count(&self, total_rows: u64) -> u64 {
36 match self {
37 Selection::All => total_rows,
38 Selection::IncludeByIndex(include) => include.len() as u64,
39 Selection::ExcludeByIndex(exclude) => total_rows.saturating_sub(exclude.len() as u64),
40 Selection::IncludeRoaring(roaring) => roaring.len(),
41 Selection::ExcludeRoaring(roaring) => total_rows.saturating_sub(roaring.len()),
42 }
43 }
44
45 pub fn row_count_in_range(&self, range: &Range<u64>) -> u64 {
47 let range_len = range.end.saturating_sub(range.start);
48 match self {
49 Selection::All => range_len,
50 Selection::IncludeByIndex(include) => count_in_range(range, include),
51 Selection::ExcludeByIndex(exclude) => {
52 range_len.saturating_sub(count_in_range(range, exclude))
53 }
54 Selection::IncludeRoaring(roaring) => roaring.range_cardinality(range.clone()),
55 Selection::ExcludeRoaring(roaring) => {
56 range_len.saturating_sub(roaring.range_cardinality(range.clone()))
57 }
58 }
59 }
60
61 pub fn row_mask(&self, range: &Range<u64>) -> RowMask {
63 if range.start >= range.end {
64 return RowMask::new(0, Mask::AllFalse(0));
65 }
66
67 let range_diff = range.end.saturating_sub(range.start);
69 let range_len = usize::try_from(range_diff).unwrap_or_else(|_| {
70 tracing::warn!(
73 "Range length {} exceeds usize::MAX, capping at usize::MAX",
74 range_diff
75 );
76 usize::MAX
77 });
78
79 match self {
80 Selection::All => RowMask::new(range.start, Mask::new_true(range_len)),
81 Selection::IncludeByIndex(include) => {
82 RowMask::new(range.start, index_mask(range, range_len, include))
83 }
84 Selection::ExcludeByIndex(exclude) => {
85 RowMask::new(range.start, index_mask(range, range_len, exclude).not())
86 }
87 Selection::IncludeRoaring(roaring) => {
88 RowMask::new(range.start, roaring_mask(roaring, range, range_len))
89 }
90 Selection::ExcludeRoaring(roaring) => {
91 RowMask::new(range.start, roaring_mask(roaring, range, range_len).not())
92 }
93 }
94 }
95}
96
97fn roaring_mask(roaring: &roaring::RoaringTreemap, range: &Range<u64>, range_len: usize) -> Mask {
102 let selected = roaring.range_cardinality(range.clone());
103 if selected == 0 {
104 return Mask::new_false(range_len);
105 }
106
107 if selected == range_len as u64 {
108 return Mask::new_true(range_len);
109 }
110
111 let mut bits = BitBufferMut::new_unset(range_len);
112 let mut iter = roaring.iter();
113 iter.advance_to(range.start);
114 for idx in iter.take_while(|&idx| idx < range.end) {
115 bits.set(relativize(range, idx));
116 }
117
118 Mask::from_buffer(bits.freeze())
119}
120
121fn count_in_range(range: &Range<u64>, row_indices: &[u64]) -> u64 {
123 indices_range(range, row_indices).map_or(0, |idx_range| idx_range.len() as u64)
124}
125
126fn index_mask(range: &Range<u64>, range_len: usize, row_indices: &[u64]) -> Mask {
128 indices_range(range, row_indices)
129 .map(|idx_range| {
130 Mask::from_indices(
131 range_len,
132 row_indices[idx_range]
133 .iter()
134 .map(|&idx| relativize(range, idx)),
135 )
136 })
137 .unwrap_or_else(|| Mask::new_false(range_len))
138}
139
140fn relativize(range: &Range<u64>, idx: u64) -> usize {
146 idx.checked_sub(range.start)
147 .and_then(|relative| usize::try_from(relative).ok())
148 .unwrap_or_else(|| {
149 vortex_panic!(
150 "index {:?} is not a usize offset into range {:?}",
151 idx,
152 range
153 )
154 })
155}
156
157fn indices_range(range: &Range<u64>, row_indices: &[u64]) -> Option<Range<usize>> {
159 if row_indices.first().is_some_and(|&first| first >= range.end)
160 || row_indices.last().is_some_and(|&last| range.start > last)
161 {
162 return None;
163 }
164
165 let start_idx = row_indices
167 .binary_search(&range.start)
168 .unwrap_or_else(|x| x);
169 let end_idx = row_indices.binary_search(&range.end).unwrap_or_else(|x| x);
170
171 (start_idx != end_idx).then_some(start_idx..end_idx)
172}
173
174#[cfg(test)]
175mod tests {
176 use vortex_buffer::Buffer;
177
178 use super::Selection;
179 use crate::strict_sorted_buffer::StrictSortedBuffer;
180
181 fn strict_sorted(indices: impl IntoIterator<Item = u64>) -> StrictSortedBuffer<u64> {
182 StrictSortedBuffer::try_new(Buffer::from_iter(indices))
183 .expect("test indices should be strictly increasing")
184 }
185
186 fn include(indices: impl IntoIterator<Item = u64>) -> Selection {
187 Selection::IncludeByIndex(strict_sorted(indices))
188 }
189
190 fn exclude(indices: impl IntoIterator<Item = u64>) -> Selection {
191 Selection::ExcludeByIndex(strict_sorted(indices))
192 }
193
194 #[test]
195 fn test_row_count_in_range() {
196 let range = 2..8;
197 assert_eq!(Selection::All.row_count_in_range(&range), 6);
198 assert_eq!(include([1, 3, 5, 7, 9]).row_count_in_range(&range), 3);
200 assert_eq!(exclude([1, 3, 5, 7, 9]).row_count_in_range(&range), 3);
201 assert_eq!(include([10, 11]).row_count_in_range(&range), 0);
202 assert_eq!(include([1, 3]).row_count_in_range(&(8..8)), 0);
203 }
204
205 #[test]
206 fn test_row_count_in_range_roaring() {
207 let mut roaring = roaring::RoaringTreemap::new();
208 roaring.insert_range(1..10u64);
209
210 let range = 2..8;
211 assert_eq!(
212 Selection::IncludeRoaring(roaring.clone()).row_count_in_range(&range),
213 6
214 );
215 assert_eq!(
216 Selection::ExcludeRoaring(roaring).row_count_in_range(&range),
217 0
218 );
219 }
220
221 #[test]
222 fn test_row_count_in_range_roaring_boundaries() {
223 let roaring = [0, 3, u64::from(u32::MAX) + 1, u64::MAX - 1, u64::MAX]
224 .into_iter()
225 .collect::<roaring::RoaringTreemap>();
226 let include = Selection::IncludeRoaring(roaring.clone());
227 let exclude = Selection::ExcludeRoaring(roaring);
228
229 for (range, expected) in [
230 (0..0, 0),
231 (0..1, 1),
232 (1..3, 0),
233 (3..4, 1),
234 (u64::from(u32::MAX)..u64::from(u32::MAX) + 2, 1),
235 (u64::MAX - 1..u64::MAX, 1),
236 (0..u64::MAX, 4),
237 ] {
238 assert_eq!(include.row_count_in_range(&range), expected);
239 assert_eq!(
240 exclude.row_count_in_range(&range),
241 range.end - range.start - expected
242 );
243 }
244 }
245
246 #[test]
247 fn test_row_mask_all() {
248 let selection = include([1, 3, 5, 7]);
249 let range = 1..8;
250 let row_mask = selection.row_mask(&range);
251
252 assert_eq!(row_mask.mask().values().unwrap().indices(), &[0, 2, 4, 6]);
253 }
254
255 #[test]
256 fn test_row_mask_slice() {
257 let selection = include([1, 3, 5, 7]);
258 let range = 3..6;
259 let row_mask = selection.row_mask(&range);
260
261 assert_eq!(row_mask.mask().values().unwrap().indices(), &[0, 2]);
262 }
263
264 #[test]
265 fn test_row_mask_exclusive() {
266 let selection = include([1, 3, 5, 7]);
267 let range = 3..5;
268 let row_mask = selection.row_mask(&range);
269
270 assert_eq!(row_mask.mask().values().unwrap().indices(), &[0]);
271 }
272
273 #[test]
274 fn test_row_mask_all_false() {
275 let selection = include([1, 3, 5, 7]);
276 let range = 8..10;
277 let row_mask = selection.row_mask(&range);
278
279 assert!(row_mask.mask().all_false());
280 }
281
282 #[test]
283 fn test_row_mask_all_true() {
284 let selection = include([1, 3, 4, 5, 6]);
285 let range = 3..7;
286 let row_mask = selection.row_mask(&range);
287
288 assert!(row_mask.mask().all_true());
289 }
290
291 #[test]
292 fn test_row_mask_zero() {
293 let selection = include([0]);
294 let range = 0..5;
295 let row_mask = selection.row_mask(&range);
296
297 assert_eq!(row_mask.mask().values().unwrap().indices(), &[0]);
298 }
299
300 mod roaring_tests {
301 use roaring::RoaringTreemap;
302
303 use super::*;
304
305 #[test]
306 fn test_roaring_include_basic() {
307 let mut roaring = RoaringTreemap::new();
308 roaring.insert(1);
309 roaring.insert(3);
310 roaring.insert(5);
311 roaring.insert(7);
312
313 let selection = Selection::IncludeRoaring(roaring);
314 let range = 1..8;
315 let row_mask = selection.row_mask(&range);
316
317 assert_eq!(row_mask.mask().values().unwrap().indices(), &[0, 2, 4, 6]);
318 }
319
320 #[test]
321 fn test_roaring_include_slice() {
322 let mut roaring = RoaringTreemap::new();
323 roaring.insert(1);
324 roaring.insert(3);
325 roaring.insert(5);
326 roaring.insert(7);
327
328 let selection = Selection::IncludeRoaring(roaring);
329 let range = 3..6;
330 let row_mask = selection.row_mask(&range);
331
332 assert_eq!(row_mask.mask().values().unwrap().indices(), &[0, 2]);
333 }
334
335 #[test]
336 fn test_roaring_include_disjoint() {
337 let mut roaring = RoaringTreemap::new();
338 roaring.insert(1);
339 roaring.insert(3);
340 roaring.insert(5);
341 roaring.insert(7);
342
343 let selection = Selection::IncludeRoaring(roaring);
344 let range = 8..10;
345 let row_mask = selection.row_mask(&range);
346
347 assert!(row_mask.mask().all_false());
348 }
349
350 #[test]
351 fn test_roaring_include_large_range() {
352 let mut roaring = RoaringTreemap::new();
353 for i in (0..1000000).step_by(2) {
355 roaring.insert(i);
356 }
357
358 let selection = Selection::IncludeRoaring(roaring);
359 let range = 1000..2000;
360 let row_mask = selection.row_mask(&range);
361
362 assert_eq!(row_mask.row_range(), range);
363 assert_eq!(
364 row_mask.mask().values().unwrap().indices(),
365 &(0..1000).step_by(2).collect::<Vec<_>>()
366 );
367 }
368
369 #[test]
370 fn test_roaring_exclude_basic() {
371 let mut roaring = RoaringTreemap::new();
372 roaring.insert(1);
373 roaring.insert(3);
374 roaring.insert(5);
375
376 let selection = Selection::ExcludeRoaring(roaring);
377 let range = 0..7;
378 let row_mask = selection.row_mask(&range);
379
380 assert_eq!(row_mask.mask().values().unwrap().indices(), &[0, 2, 4, 6]);
382 }
383
384 #[test]
385 fn test_roaring_exclude_all() {
386 let mut roaring = RoaringTreemap::new();
387 for i in 10..20 {
389 roaring.insert(i);
390 }
391
392 let selection = Selection::ExcludeRoaring(roaring);
393 let range = 10..20;
394 let row_mask = selection.row_mask(&range);
395
396 assert!(row_mask.mask().all_false());
397 }
398
399 #[test]
400 fn test_roaring_exclude_none() {
401 let mut roaring = RoaringTreemap::new();
402 roaring.insert(100);
403 roaring.insert(101);
404
405 let selection = Selection::ExcludeRoaring(roaring);
406 let range = 0..10;
407 let row_mask = selection.row_mask(&range);
408
409 assert!(row_mask.mask().all_true());
411 }
412
413 #[test]
414 fn test_roaring_exclude_partial() {
415 let mut roaring = RoaringTreemap::new();
416 roaring.insert(5);
417 roaring.insert(6);
418 roaring.insert(7);
419 roaring.insert(15); let selection = Selection::ExcludeRoaring(roaring);
422 let range = 5..10;
423 let row_mask = selection.row_mask(&range);
424
425 assert_eq!(row_mask.mask().values().unwrap().indices(), &[3, 4]);
427 }
428
429 #[test]
430 fn test_roaring_include_empty() {
431 let roaring = RoaringTreemap::new();
432 let selection = Selection::IncludeRoaring(roaring);
433 let range = 0..100;
434 let row_mask = selection.row_mask(&range);
435
436 assert!(row_mask.mask().all_false());
437 }
438
439 #[test]
440 fn test_roaring_exclude_empty() {
441 let roaring = RoaringTreemap::new();
442 let selection = Selection::ExcludeRoaring(roaring);
443 let range = 0..100;
444 let row_mask = selection.row_mask(&range);
445
446 assert!(row_mask.mask().all_true());
447 }
448
449 #[test]
450 fn test_roaring_include_boundary() {
451 let mut roaring = RoaringTreemap::new();
452 roaring.insert(0);
453 roaring.insert(99);
454
455 let selection = Selection::IncludeRoaring(roaring);
456 let range = 0..100;
457 let row_mask = selection.row_mask(&range);
458
459 assert_eq!(row_mask.mask().values().unwrap().indices(), &[0, 99]);
460 }
461
462 #[test]
463 fn test_roaring_include_range_insertion() {
464 let mut roaring = RoaringTreemap::new();
465 roaring.insert_range(10..20);
467 roaring.insert_range(30..40);
468
469 let selection = Selection::IncludeRoaring(roaring);
470 let range = 15..35;
471 let row_mask = selection.row_mask(&range);
472
473 let expected: Vec<usize> = (0..5).chain(15..20).collect();
475 assert_eq!(row_mask.mask().values().unwrap().indices(), &expected);
476 }
477
478 #[test]
479 fn test_roaring_overflow_protection() {
480 let mut roaring = RoaringTreemap::new();
481 roaring.insert(u64::MAX - 1);
483 roaring.insert(u64::MAX);
484
485 let selection = Selection::IncludeRoaring(roaring);
486 let range = u64::MAX - 10..u64::MAX;
487 let row_mask = selection.row_mask(&range);
488
489 assert_eq!(row_mask.row_range(), range);
490 assert_eq!(row_mask.mask().values().unwrap().indices(), &[9]);
491 }
492
493 #[test]
494 fn test_roaring_exclude_overflow_protection() {
495 let mut roaring = RoaringTreemap::new();
496 roaring.insert(u64::MAX - 1);
497
498 let selection = Selection::ExcludeRoaring(roaring);
499 let range = u64::MAX - 10..u64::MAX;
500 let row_mask = selection.row_mask(&range);
501
502 assert_eq!(row_mask.row_range(), range);
503 assert_eq!(
504 row_mask.mask().values().unwrap().indices(),
505 &(0..9).collect::<Vec<_>>()
506 );
507 }
508
509 #[test]
510 fn test_roaring_include_vs_buffer_equivalence() {
511 let indices = vec![1, 3, 5, 7, 9];
513
514 let buffer_selection = include(indices.clone());
515
516 let mut roaring = RoaringTreemap::new();
517 for idx in &indices {
518 roaring.insert(*idx);
519 }
520 let roaring_selection = Selection::IncludeRoaring(roaring);
521
522 let range = 0..12;
523 let buffer_mask = buffer_selection.row_mask(&range);
524 let roaring_mask = roaring_selection.row_mask(&range);
525
526 for mask in [buffer_mask, roaring_mask] {
527 assert_eq!(mask.row_range(), range);
528 assert_eq!(mask.mask().values().unwrap().indices(), &[1, 3, 5, 7, 9]);
529 }
530 }
531
532 #[test]
533 fn test_roaring_exclude_vs_buffer_equivalence() {
534 let indices = vec![2, 4, 6, 8];
536
537 let buffer_selection = exclude(indices.clone());
538
539 let mut roaring = RoaringTreemap::new();
540 for idx in &indices {
541 roaring.insert(*idx);
542 }
543 let roaring_selection = Selection::ExcludeRoaring(roaring);
544
545 let range = 0..10;
546 let buffer_mask = buffer_selection.row_mask(&range);
547 let roaring_mask = roaring_selection.row_mask(&range);
548
549 for mask in [buffer_mask, roaring_mask] {
550 assert_eq!(mask.row_range(), range);
551 assert_eq!(mask.mask().values().unwrap().indices(), &[0, 1, 3, 5, 7, 9]);
552 }
553 }
554 }
555}