Skip to main content

vortex_mask/
lib.rs

1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright the Vortex contributors
3
4//! A mask is a set of sorted unique positive integers.
5#![deny(missing_docs)]
6
7mod bitops;
8mod eq;
9mod intersect_by_rank;
10
11#[cfg(test)]
12mod tests;
13
14use std::cmp::Ordering;
15use std::fmt::Debug;
16use std::fmt::Formatter;
17use std::ops::Bound;
18use std::ops::RangeBounds;
19use std::sync::Arc;
20use std::sync::OnceLock;
21
22use itertools::Itertools;
23use vortex_buffer::BitBuffer;
24use vortex_buffer::BitBufferMut;
25use vortex_buffer::BitIterator;
26use vortex_error::VortexResult;
27use vortex_error::vortex_panic;
28
29/// Represents a set of values that are all included, all excluded, or some mixture of both.
30pub enum AllOr<T> {
31    /// All values are included.
32    All,
33    /// No values are included.
34    None,
35    /// Some values are included.
36    Some(T),
37}
38
39impl<T> AllOr<T> {
40    /// Returns the `Some` variant of the enum, or a default value.
41    #[inline]
42    pub fn unwrap_or_else<F, G>(self, all_true: F, all_false: G) -> T
43    where
44        F: FnOnce() -> T,
45        G: FnOnce() -> T,
46    {
47        match self {
48            Self::Some(v) => v,
49            AllOr::All => all_true(),
50            AllOr::None => all_false(),
51        }
52    }
53}
54
55impl<T> AllOr<&T> {
56    /// Clone the inner value.
57    #[inline]
58    pub fn cloned(self) -> AllOr<T>
59    where
60        T: Clone,
61    {
62        match self {
63            Self::All => AllOr::All,
64            Self::None => AllOr::None,
65            Self::Some(v) => AllOr::Some(v.clone()),
66        }
67    }
68}
69
70impl<T> Debug for AllOr<T>
71where
72    T: Debug,
73{
74    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
75        match self {
76            Self::All => f.write_str("All"),
77            Self::None => f.write_str("None"),
78            Self::Some(v) => f.debug_tuple("Some").field(v).finish(),
79        }
80    }
81}
82
83impl<T> PartialEq for AllOr<T>
84where
85    T: PartialEq,
86{
87    fn eq(&self, other: &Self) -> bool {
88        match (self, other) {
89            (Self::All, Self::All) => true,
90            (Self::None, Self::None) => true,
91            (Self::Some(lhs), Self::Some(rhs)) => lhs == rhs,
92            _ => false,
93        }
94    }
95}
96
97impl<T> Eq for AllOr<T> where T: Eq {}
98
99/// Represents a set of sorted unique positive integers.
100/// If a value is included in a Mask, it's valid.
101///
102/// A [`Mask`] can be constructed from various representations, and converted to various
103/// others. Internally, these are cached.
104#[derive(Clone)]
105#[cfg_attr(feature = "serde", derive(::serde::Serialize, ::serde::Deserialize))]
106pub enum Mask {
107    /// All values are included.
108    AllTrue(usize),
109    /// No values are included.
110    AllFalse(usize),
111    /// Some values are included, represented as a [`BitBuffer`].
112    Values(MaskValuesRef),
113}
114
115impl Debug for Mask {
116    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
117        match self {
118            Self::AllTrue(len) => write!(f, "All true({len})"),
119            Self::AllFalse(len) => write!(f, "All false({len})"),
120            Self::Values(mask) => write!(f, "{mask:?}"),
121        }
122    }
123}
124
125impl Default for Mask {
126    fn default() -> Self {
127        Self::new_true(0)
128    }
129}
130
131/// Represents the values of a [`Mask`] that contains some true and some false elements.
132#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
133pub struct MaskValues {
134    buffer: BitBuffer,
135
136    // We cached the indices and slices representations, since it can be faster than iterating
137    // the bit-mask over and over again.
138    #[cfg_attr(feature = "serde", serde(skip))]
139    indices: OnceLock<Vec<usize>>,
140    #[cfg_attr(feature = "serde", serde(skip))]
141    slices: OnceLock<Vec<(usize, usize)>>,
142
143    // Pre-computed values.
144    true_count: usize,
145    // i.e., the fraction of values that are true
146    density: f64,
147}
148
149/// A shared reference to [`MaskValues`].
150pub type MaskValuesRef = Arc<MaskValues>;
151
152impl Debug for MaskValues {
153    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
154        write!(f, "true_count={}, ", self.true_count)?;
155        write!(f, "density={}, ", self.density)?;
156        if let Some(v) = self.indices.get() {
157            write!(f, "indices={v:?}, ")?;
158        }
159        if let Some(v) = self.slices.get() {
160            write!(f, "slices={v:?}, ")?;
161        }
162        if f.alternate() {
163            f.write_str("\n")?;
164        }
165        write!(f, "{}", self.buffer)
166    }
167}
168
169impl Mask {
170    /// Create a new Mask with the given length.
171    pub fn new(length: usize, value: bool) -> Self {
172        if value {
173            Self::AllTrue(length)
174        } else {
175            Self::AllFalse(length)
176        }
177    }
178
179    /// Create a new Mask where all values are set.
180    #[inline]
181    pub fn new_true(length: usize) -> Self {
182        Self::AllTrue(length)
183    }
184
185    /// Create a new Mask where no values are set.
186    #[inline]
187    pub fn new_false(length: usize) -> Self {
188        Self::AllFalse(length)
189    }
190
191    /// Create a new [`Mask`] from a [`BitBuffer`].
192    pub fn from_buffer(buffer: BitBuffer) -> Self {
193        let len = buffer.len();
194        let true_count = buffer.true_count();
195
196        if true_count == 0 {
197            return Self::AllFalse(len);
198        }
199        if true_count == len {
200            return Self::AllTrue(len);
201        }
202
203        Self::Values(Arc::new(MaskValues {
204            buffer,
205            indices: Default::default(),
206            slices: Default::default(),
207            true_count,
208            density: true_count as f64 / len as f64,
209        }))
210    }
211
212    /// Create a new [`Mask`] from a [`BitBuffer`] and the ranges of its set bits, which are cached
213    /// on the mask so they need not be recomputed from the buffer.
214    ///
215    /// `slices` must be sorted, non-overlapping `(start, end)` ranges that cover exactly the set
216    /// bits of `buffer`. This is only checked in debug builds.
217    pub fn from_buffer_with_slices(buffer: BitBuffer, slices: Vec<(usize, usize)>) -> Self {
218        let len = buffer.len();
219        #[cfg(debug_assertions)]
220        {
221            Self::check_slices(len, &slices);
222            for &(start, end) in &slices {
223                assert_eq!(
224                    buffer.count_range(start, end),
225                    end - start,
226                    "Slice ({start}, {end}) is not fully set in the buffer"
227                );
228            }
229        }
230
231        let true_count = slices.iter().map(|(start, end)| end - start).sum();
232        debug_assert_eq!(
233            true_count,
234            buffer.true_count(),
235            "Slices must cover every set bit of the buffer"
236        );
237
238        if true_count == 0 {
239            return Self::AllFalse(len);
240        }
241        if true_count == len {
242            return Self::AllTrue(len);
243        }
244
245        Self::Values(Arc::new(MaskValues {
246            buffer,
247            indices: Default::default(),
248            slices: OnceLock::from(slices),
249            true_count,
250            density: true_count as f64 / len as f64,
251        }))
252    }
253
254    /// Create a new [`Mask`] from sorted, unique indices.
255    pub fn from_indices(len: usize, indices: impl IntoIterator<Item = usize>) -> Self {
256        let indices = indices.into_iter().collect::<Vec<_>>();
257        assert!(indices.is_sorted(), "Mask indices must be sorted");
258        assert!(
259            indices.windows(2).all(|w| w[0] != w[1]),
260            "Mask indices must be unique"
261        );
262        let buffer = BitBuffer::from_indices(len, indices.iter().copied());
263        debug_assert_eq!(buffer.len(), len);
264        let true_count = indices.len();
265
266        if true_count == 0 {
267            return Self::AllFalse(len);
268        }
269        if true_count == len {
270            return Self::AllTrue(len);
271        }
272
273        Self::Values(Arc::new(MaskValues {
274            buffer,
275            indices: OnceLock::from(indices),
276            slices: Default::default(),
277            true_count,
278            density: true_count as f64 / len as f64,
279        }))
280    }
281
282    /// Create a new [`Mask`] from an [`IntoIterator<Item = usize>`] of indices to be excluded.
283    pub fn from_excluded_indices(len: usize, indices: impl IntoIterator<Item = usize>) -> Self {
284        let mut buf = BitBufferMut::new_set(len);
285
286        let mut false_count: usize = 0;
287        indices.into_iter().for_each(|idx| {
288            buf.unset(idx);
289            false_count += 1;
290        });
291        debug_assert_eq!(buf.len(), len);
292        let true_count = len - false_count;
293
294        // Return optimized variants when appropriate
295        if false_count == 0 {
296            return Self::AllTrue(len);
297        }
298        if false_count == len {
299            return Self::AllFalse(len);
300        }
301
302        Self::Values(Arc::new(MaskValues {
303            buffer: buf.freeze(),
304            indices: Default::default(),
305            slices: Default::default(),
306            true_count,
307            density: true_count as f64 / len as f64,
308        }))
309    }
310
311    /// Create a new [`Mask`] from a [`Vec<(usize, usize)>`] where each range
312    /// represents a contiguous range of true values.
313    pub fn from_slices(len: usize, vec: Vec<(usize, usize)>) -> Self {
314        Self::check_slices(len, &vec);
315        Self::from_slices_unchecked(len, vec)
316    }
317
318    fn from_slices_unchecked(len: usize, slices: Vec<(usize, usize)>) -> Self {
319        #[cfg(debug_assertions)]
320        Self::check_slices(len, &slices);
321
322        let true_count = slices.iter().map(|(b, e)| e - b).sum();
323        if true_count == 0 {
324            return Self::AllFalse(len);
325        }
326        if true_count == len {
327            return Self::AllTrue(len);
328        }
329
330        let mut buf = BitBufferMut::with_capacity(len);
331        let mut cursor = 0;
332        for (start, end) in slices.iter().copied() {
333            buf.append_n(false, start - cursor);
334            buf.append_n(true, end - start);
335            cursor = end;
336        }
337        buf.append_n(false, len - cursor);
338        debug_assert_eq!(buf.len(), len);
339
340        Self::Values(Arc::new(MaskValues {
341            buffer: buf.freeze(),
342            indices: Default::default(),
343            slices: OnceLock::from(slices),
344            true_count,
345            density: true_count as f64 / len as f64,
346        }))
347    }
348
349    #[allow(clippy::inline_always)]
350    #[inline(always)]
351    fn check_slices(len: usize, vec: &[(usize, usize)]) {
352        assert!(vec.iter().all(|&(b, e)| b < e && e <= len));
353        for (first, second) in vec.iter().tuple_windows() {
354            assert!(
355                first.0 < second.0,
356                "Slices must be sorted, got {first:?} and {second:?}"
357            );
358            assert!(
359                first.1 <= second.0,
360                "Slices must be non-overlapping, got {first:?} and {second:?}"
361            );
362        }
363    }
364
365    /// Create a new [`Mask`] from the intersection of two indices slices.
366    pub fn from_intersection_indices(
367        len: usize,
368        lhs: impl Iterator<Item = usize>,
369        rhs: impl Iterator<Item = usize>,
370    ) -> Self {
371        let mut intersection = Vec::with_capacity(len);
372        let mut lhs = lhs.peekable();
373        let mut rhs = rhs.peekable();
374        while let (Some(&l), Some(&r)) = (lhs.peek(), rhs.peek()) {
375            match l.cmp(&r) {
376                Ordering::Less => {
377                    lhs.next();
378                }
379                Ordering::Greater => {
380                    rhs.next();
381                }
382                Ordering::Equal => {
383                    intersection.push(l);
384                    lhs.next();
385                    rhs.next();
386                }
387            }
388        }
389        Self::from_indices(len, intersection)
390    }
391
392    /// Clears the mask of all data. Drops any allocated capacity.
393    pub fn clear(&mut self) {
394        *self = Self::new_false(0);
395    }
396
397    /// Returns the length of the mask (not the number of true values).
398    #[inline]
399    pub fn len(&self) -> usize {
400        match self {
401            Self::AllTrue(len) => *len,
402            Self::AllFalse(len) => *len,
403            Self::Values(values) => values.len(),
404        }
405    }
406
407    /// Returns true if the mask is empty i.e., it's length is 0.
408    #[inline]
409    pub fn is_empty(&self) -> bool {
410        match self {
411            Self::AllTrue(len) => *len == 0,
412            Self::AllFalse(len) => *len == 0,
413            Self::Values(values) => values.is_empty(),
414        }
415    }
416
417    /// Get the true count of the mask.
418    #[inline]
419    pub fn true_count(&self) -> usize {
420        match &self {
421            Self::AllTrue(len) => *len,
422            Self::AllFalse(_) => 0,
423            Self::Values(values) => values.true_count,
424        }
425    }
426
427    /// Get the false count of the mask.
428    #[inline]
429    pub fn false_count(&self) -> usize {
430        match &self {
431            Self::AllTrue(_) => 0,
432            Self::AllFalse(len) => *len,
433            Self::Values(values) => values.buffer.len() - values.true_count,
434        }
435    }
436
437    /// Returns true if all values in the mask are true.
438    #[inline]
439    pub fn all_true(&self) -> bool {
440        match &self {
441            Self::AllTrue(_) => true,
442            Self::AllFalse(0) => true,
443            Self::AllFalse(_) => false,
444            Self::Values(values) => values.buffer.len() == values.true_count,
445        }
446    }
447
448    /// Returns true if all values in the mask are false.
449    #[inline]
450    pub fn all_false(&self) -> bool {
451        self.true_count() == 0
452    }
453
454    /// Return the density of the full mask.
455    #[inline]
456    pub fn density(&self) -> f64 {
457        match &self {
458            Self::AllTrue(_) => 1.0,
459            Self::AllFalse(_) => 0.0,
460            Self::Values(values) => values.density,
461        }
462    }
463
464    /// Returns the boolean value at a given index.
465    ///
466    /// ## Panics
467    ///
468    /// Panics if the index is out of bounds.
469    #[inline]
470    pub fn value(&self, idx: usize) -> bool {
471        match self {
472            Mask::AllTrue(_) => true,
473            Mask::AllFalse(_) => false,
474            Mask::Values(values) => values.buffer.value(idx),
475        }
476    }
477
478    /// Iterate the mask as one `bool` per element, in order.
479    ///
480    /// Unlike repeatedly calling [`Mask::value`], this advances a single cursor rather than
481    /// recomputing the byte/bit offset for every element, and it does not allocate for the
482    /// all-true / all-false variants. Prefer this for sequential per-element scans.
483    #[inline]
484    pub fn iter(&self) -> MaskBoolIter<'_> {
485        match self {
486            Mask::AllTrue(len) => MaskBoolIter::Repeat {
487                value: true,
488                remaining: *len,
489            },
490            Mask::AllFalse(len) => MaskBoolIter::Repeat {
491                value: false,
492                remaining: *len,
493            },
494            Mask::Values(values) => MaskBoolIter::Bits(values.bit_buffer().iter()),
495        }
496    }
497
498    /// Returns the first true index in the mask.
499    pub fn first(&self) -> Option<usize> {
500        match &self {
501            Self::AllTrue(len) => (*len > 0).then_some(0),
502            Self::AllFalse(_) => None,
503            Self::Values(values) => {
504                if let Some(indices) = values.indices.get() {
505                    return indices.first().copied();
506                }
507                if let Some(slices) = values.slices.get() {
508                    return slices.first().map(|(start, _)| *start);
509                }
510                values.buffer.set_indices().next()
511            }
512        }
513    }
514
515    /// Returns the last true index in the mask.
516    pub fn last(&self) -> Option<usize> {
517        match &self {
518            Self::AllTrue(len) => (*len > 0).then_some(*len - 1),
519            Self::AllFalse(_) => None,
520            Self::Values(values) => {
521                if let Some(indices) = values.indices.get() {
522                    return indices.last().copied();
523                }
524                if let Some(slices) = values.slices.get() {
525                    return slices.last().map(|(_, end)| end - 1);
526                }
527
528                values.buffer.last_set_index()
529            }
530        }
531    }
532
533    /// Returns the position in the mask of the nth true value.
534    pub fn rank(&self, n: usize) -> usize {
535        if n >= self.true_count() {
536            vortex_panic!(
537                "Rank {n} out of bounds for mask with true count {}",
538                self.true_count()
539            );
540        }
541        match &self {
542            Self::AllTrue(_) => n,
543            Self::AllFalse(_) => unreachable!("no true values in all-false mask"),
544            Self::Values(values) => {
545                if let Some(indices) = values.indices.get() {
546                    return indices[n];
547                }
548
549                values.buffer.select(n).unwrap_or_else(|| {
550                    vortex_panic!(
551                        "Rank {} out of bounds for mask with true count {}",
552                        values.true_count - 1,
553                        values.true_count
554                    )
555                })
556            }
557        }
558    }
559
560    /// Slice the mask.
561    pub fn slice(&self, range: impl RangeBounds<usize>) -> Self {
562        let start = match range.start_bound() {
563            Bound::Included(&s) => s,
564            Bound::Excluded(&s) => s + 1,
565            Bound::Unbounded => 0,
566        };
567        let end = match range.end_bound() {
568            Bound::Included(&e) => e + 1,
569            Bound::Excluded(&e) => e,
570            Bound::Unbounded => self.len(),
571        };
572
573        assert!(start <= end);
574        assert!(start <= self.len());
575        assert!(end <= self.len());
576        let len = end - start;
577
578        // Slicing the whole mask is the identity. `Self` is `Arc`-backed, so the clone is cheap
579        // and keeps the cached `indices`/`slices` representations that `from_buffer` would drop.
580        if len == self.len() {
581            return self.clone();
582        }
583
584        match &self {
585            Self::AllTrue(_) => Self::new_true(len),
586            Self::AllFalse(_) => Self::new_false(len),
587            Self::Values(values) => Self::from_buffer(values.buffer.slice(range)),
588        }
589    }
590
591    /// Return the boolean buffer representation of the mask.
592    #[inline]
593    pub fn bit_buffer(&self) -> AllOr<&BitBuffer> {
594        match &self {
595            Self::AllTrue(_) => AllOr::All,
596            Self::AllFalse(_) => AllOr::None,
597            Self::Values(values) => AllOr::Some(&values.buffer),
598        }
599    }
600
601    /// Return a boolean buffer representation of the mask, allocating new buffers for all-true
602    /// and all-false variants.
603    #[inline]
604    pub fn to_bit_buffer(&self) -> BitBuffer {
605        match self {
606            Self::AllTrue(l) => BitBuffer::new_set(*l),
607            Self::AllFalse(l) => BitBuffer::new_unset(*l),
608            Self::Values(values) => values.bit_buffer().clone(),
609        }
610    }
611
612    /// Return a boolean buffer representation of the mask, allocating new buffers for all-true
613    /// and all-false variants.
614    #[inline]
615    pub fn into_bit_buffer(self) -> BitBuffer {
616        match self {
617            Self::AllTrue(l) => BitBuffer::new_set(l),
618            Self::AllFalse(l) => BitBuffer::new_unset(l),
619            Self::Values(values) => Arc::try_unwrap(values)
620                .map(|v| v.into_bit_buffer())
621                .unwrap_or_else(|v| v.bit_buffer().clone()),
622        }
623    }
624
625    /// Return the indices representation of the mask.
626    #[inline]
627    pub fn indices(&self) -> AllOr<&[usize]> {
628        match &self {
629            Self::AllTrue(_) => AllOr::All,
630            Self::AllFalse(_) => AllOr::None,
631            Self::Values(values) => AllOr::Some(values.indices()),
632        }
633    }
634
635    /// Return the slices representation of the mask.
636    #[inline]
637    pub fn slices(&self) -> AllOr<&[(usize, usize)]> {
638        match &self {
639            Self::AllTrue(_) => AllOr::All,
640            Self::AllFalse(_) => AllOr::None,
641            Self::Values(values) => AllOr::Some(values.slices()),
642        }
643    }
644
645    /// Return an iterator over either indices or slices of the mask based on a density threshold.
646    #[inline]
647    pub fn threshold_iter(&self, threshold: f64) -> AllOr<MaskIter<'_>> {
648        match &self {
649            Self::AllTrue(_) => AllOr::All,
650            Self::AllFalse(_) => AllOr::None,
651            Self::Values(values) => AllOr::Some(values.threshold_iter(threshold)),
652        }
653    }
654
655    /// Return [`MaskValues`] if the mask is not all true or all false.
656    #[inline]
657    pub fn values(&self) -> Option<&MaskValues> {
658        if let Self::Values(values) = self {
659            Some(values)
660        } else {
661            None
662        }
663    }
664
665    /// Given monotonically increasing `indices` in [0, n_rows], returns the
666    /// count of valid elements up to each index.
667    ///
668    /// This is O(n_rows), but the per-gap counts are computed with a SIMD
669    /// popcount over the underlying bit buffer rather than walking bit-by-bit.
670    pub fn valid_counts_for_indices(&self, indices: &[usize]) -> Vec<usize> {
671        match self {
672            Self::AllTrue(_) => indices.to_vec(),
673            Self::AllFalse(_) => vec![0; indices.len()],
674            Self::Values(values) => {
675                let buffer = values.bit_buffer();
676                let mut valid_counts = Vec::with_capacity(indices.len());
677                let mut valid_count = 0;
678                let mut prev = 0;
679                for &next_idx in indices {
680                    assert!(next_idx <= buffer.len(), "Row indices exceed array length");
681                    // `indices` is monotonically increasing, so each gap is counted once;
682                    // the total work across all gaps scans the prefix `[0, last_idx)` once.
683                    if next_idx > prev {
684                        valid_count += buffer.count_range(prev, next_idx);
685                        prev = next_idx;
686                    }
687                    valid_counts.push(valid_count);
688                }
689
690                valid_counts
691            }
692        }
693    }
694
695    /// Limit the mask to the first `limit` true values
696    pub fn limit(self, limit: usize) -> Self {
697        // Early return optimization: if we're asking for more true values than the total
698        // length of the mask, then even if all values were true, we couldn't exceed the
699        // limit, so return the original mask unchanged.
700        if self.len() <= limit {
701            return self;
702        }
703
704        match &self {
705            Mask::AllTrue(len) => {
706                Self::from_iter([Self::new_true(limit), Self::new_false(len - limit)])
707            }
708            Mask::AllFalse(_) => self,
709            Mask::Values(mask_values) => {
710                if limit >= mask_values.true_count() {
711                    return self;
712                }
713
714                let existing_buffer = mask_values.bit_buffer();
715
716                let mut new_buffer_builder = BitBufferMut::new_unset(mask_values.len());
717                debug_assert!(limit < mask_values.len());
718
719                for index in existing_buffer.set_indices().take(limit) {
720                    // SAFETY: We checked that `limit` was less than the mask values length,
721                    // therefore `index` must be within the bounds of the bit buffer.
722                    unsafe { new_buffer_builder.set_unchecked(index) }
723                }
724
725                Self::from(new_buffer_builder.freeze())
726            }
727        }
728    }
729
730    /// Concatenate multiple masks together into a single mask.
731    pub fn concat<'a>(masks: impl Iterator<Item = &'a Self>) -> VortexResult<Self> {
732        let masks: Vec<_> = masks.collect();
733        let len = masks.iter().map(|t| t.len()).sum();
734
735        if masks.iter().all(|t| t.all_true()) {
736            return Ok(Mask::AllTrue(len));
737        }
738
739        if masks.iter().all(|t| t.all_false()) {
740            return Ok(Mask::AllFalse(len));
741        }
742
743        let mut builder = BitBufferMut::with_capacity(len);
744
745        for mask in masks {
746            match mask {
747                Mask::AllTrue(n) => builder.append_n(true, *n),
748                Mask::AllFalse(n) => builder.append_n(false, *n),
749                Mask::Values(v) => builder.append_buffer(v.bit_buffer()),
750            }
751        }
752
753        Ok(Mask::from_buffer(builder.freeze()))
754    }
755}
756
757impl MaskValues {
758    /// Returns the length of the mask.
759    #[inline]
760    pub fn len(&self) -> usize {
761        self.buffer.len()
762    }
763
764    /// Returns true if the mask is empty i.e., it's length is 0.
765    #[inline]
766    pub fn is_empty(&self) -> bool {
767        self.buffer.is_empty()
768    }
769
770    /// Returns the density of the mask.
771    #[inline]
772    pub fn density(&self) -> f64 {
773        self.density
774    }
775
776    /// Returns the true count of the mask.
777    #[inline]
778    pub fn true_count(&self) -> usize {
779        self.true_count
780    }
781
782    /// Returns the boolean buffer representation of the mask.
783    #[inline]
784    pub fn bit_buffer(&self) -> &BitBuffer {
785        &self.buffer
786    }
787
788    /// Returns the boolean buffer representation of the mask.
789    #[inline]
790    pub fn into_bit_buffer(self) -> BitBuffer {
791        self.buffer
792    }
793
794    /// Returns the boolean value at a given index.
795    #[inline]
796    pub fn value(&self, index: usize) -> bool {
797        self.buffer.value(index)
798    }
799
800    /// Constructs an indices vector from one of the other representations.
801    pub fn indices(&self) -> &[usize] {
802        self.indices.get_or_init(|| {
803            if self.true_count == 0 {
804                return vec![];
805            }
806
807            if self.true_count == self.len() {
808                return (0..self.len()).collect();
809            }
810
811            if let Some(slices) = self.slices.get() {
812                let mut indices = Vec::with_capacity(self.true_count);
813                indices.extend(slices.iter().flat_map(|(start, end)| *start..*end));
814                debug_assert!(indices.is_sorted());
815                assert_eq!(indices.len(), self.true_count);
816                return indices;
817            }
818
819            let mut indices = Vec::with_capacity(self.true_count);
820            // Word-at-a-time set-bit walk; faster than collecting `set_indices()`,
821            // whose per-`next` iterator state inlines less well (see
822            // `vortex-mask/benches/mask_iteration.rs`).
823            self.buffer.for_each_set_index(|i| indices.push(i));
824            debug_assert!(indices.is_sorted());
825            assert_eq!(indices.len(), self.true_count);
826            indices
827        })
828    }
829
830    /// Returns cached index positions when this mask already has them materialized.
831    ///
832    /// Unlike [`Self::indices`], this does not build the index vector from another
833    /// representation.
834    #[inline]
835    pub fn cached_indices(&self) -> Option<&[usize]> {
836        self.indices.get().map(Vec::as_slice)
837    }
838
839    /// Constructs a slices vector from one of the other representations.
840    #[inline]
841    pub fn slices(&self) -> &[(usize, usize)] {
842        self.slices.get_or_init(|| {
843            if self.true_count == self.len() {
844                return vec![(0, self.len())];
845            }
846
847            self.buffer.set_slices().collect()
848        })
849    }
850
851    /// Returns cached true-value ranges when this mask already has them materialized.
852    ///
853    /// Unlike [`Self::slices`], this does not build the slice vector from another
854    /// representation.
855    #[inline]
856    pub fn cached_slices(&self) -> Option<&[(usize, usize)]> {
857        self.slices.get().map(Vec::as_slice)
858    }
859
860    /// Return an iterator over either indices or slices of the mask based on a density threshold.
861    #[inline]
862    pub fn threshold_iter(&self, threshold: f64) -> MaskIter<'_> {
863        if self.density >= threshold {
864            MaskIter::Slices(self.slices())
865        } else {
866            MaskIter::Indices(self.indices())
867        }
868    }
869}
870
871/// Iterator over the indices or slices of a mask.
872pub enum MaskIter<'a> {
873    /// Slice of pre-cached indices of a mask.
874    Indices(&'a [usize]),
875    /// Slice of pre-cached slices of a mask.
876    Slices(&'a [(usize, usize)]),
877}
878
879/// Iterator yielding one `bool` per element of a [`Mask`], in order.
880///
881/// Created by [`Mask::iter`].
882pub enum MaskBoolIter<'a> {
883    /// An all-true or all-false run.
884    Repeat {
885        /// The constant value yielded by every element of the run.
886        value: bool,
887        /// The number of elements still to yield.
888        remaining: usize,
889    },
890    /// Per-element bits of a [`Mask::Values`] mask.
891    Bits(BitIterator<'a>),
892}
893
894impl Iterator for MaskBoolIter<'_> {
895    type Item = bool;
896
897    #[inline]
898    fn next(&mut self) -> Option<Self::Item> {
899        match self {
900            Self::Repeat { remaining: 0, .. } => None,
901            Self::Repeat { value, remaining } => {
902                *remaining -= 1;
903                Some(*value)
904            }
905            Self::Bits(bits) => bits.next(),
906        }
907    }
908
909    #[inline]
910    fn size_hint(&self) -> (usize, Option<usize>) {
911        let remaining = match self {
912            Self::Repeat { remaining, .. } => *remaining,
913            Self::Bits(bits) => bits.len(),
914        };
915        (remaining, Some(remaining))
916    }
917}
918
919impl ExactSizeIterator for MaskBoolIter<'_> {}
920
921impl From<BitBuffer> for Mask {
922    fn from(value: BitBuffer) -> Self {
923        Self::from_buffer(value)
924    }
925}
926
927impl FromIterator<bool> for Mask {
928    #[inline]
929    fn from_iter<T: IntoIterator<Item = bool>>(iter: T) -> Self {
930        Self::from_buffer(BitBuffer::from_iter(iter))
931    }
932}
933
934impl FromIterator<Mask> for Mask {
935    fn from_iter<T: IntoIterator<Item = Mask>>(iter: T) -> Self {
936        let masks = iter
937            .into_iter()
938            .filter(|m| !m.is_empty())
939            .collect::<Vec<_>>();
940        let total_length = masks.iter().map(|v| v.len()).sum();
941
942        // If they're all valid, then return a single validity.
943        if masks.iter().all(|v| v.all_true()) {
944            return Self::AllTrue(total_length);
945        }
946        // If they're all invalid, then return a single invalidity.
947        if masks.iter().all(|v| v.all_false()) {
948            return Self::AllFalse(total_length);
949        }
950
951        // Else, construct the boolean buffer
952        let mut buffer = BitBufferMut::with_capacity(total_length);
953        for mask in masks {
954            match mask {
955                Mask::AllTrue(count) => buffer.append_n(true, count),
956                Mask::AllFalse(count) => buffer.append_n(false, count),
957                Mask::Values(values) => {
958                    buffer.append_buffer(values.bit_buffer());
959                }
960            };
961        }
962        Self::from_buffer(buffer.freeze())
963    }
964}