Skip to main content

visi_core/core/engine/
bitmask.rs

1//! A packed bit-per-row validity mask.
2
3use crate::core::SharedVec;
4use serde::{Deserialize, Serialize};
5
6/// One bit per row, recording which entries of a numeric [`ColumnData`] hold a
7/// value rather than a blank.
8///
9/// A set bit means the value at that index is real; a clear bit means the cell
10/// is empty and the underlying slot holds a placeholder. Keeping this separate
11/// is what lets a numeric column stay unboxed and still tell a blank cell
12/// apart from a zero.
13///
14/// Read-only from outside the crate -- the mutators are crate-private, since
15/// changing a mask's length independently of the column it belongs to would
16/// desync the two.
17///
18/// [`ColumnData`]: crate::core::ColumnData
19#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct Bitmask {
21    data: SharedVec<u8>,
22    /// How many bits are in use, which is the row count of the column this
23    /// mask belongs to. Not the capacity of the backing bytes.
24    pub len: usize,
25}
26
27impl Bitmask {
28    pub(crate) fn with_size(size: usize) -> Self {
29        Self {
30            data: vec![0; size.div_ceil(8)].into(),
31            len: size,
32        }
33    }
34    pub(crate) fn push(&mut self, value: bool) {
35        let byte_idx = self.len / 8;
36        let bit_idx = self.len % 8;
37        if byte_idx >= self.data.len() {
38            self.data.push(0);
39        }
40        if value {
41            self.data[byte_idx] |= 1 << bit_idx;
42        } else {
43            self.data[byte_idx] &= !(1 << bit_idx);
44        }
45        self.len += 1;
46    }
47    /// Whether the entry at `index` holds a value. `false` for an index at or
48    /// past [`Bitmask::len`], so an out-of-range read is indistinguishable
49    /// from a blank.
50    pub fn get(&self, index: usize) -> bool {
51        if index >= self.len {
52            return false;
53        }
54        let byte_idx = index / 8;
55        let bit_idx = index % 8;
56        (self.data[byte_idx] & (1 << bit_idx)) != 0
57    }
58    pub(crate) fn set(&mut self, index: usize, value: bool) {
59        if index >= self.len {
60            return;
61        }
62        let byte_idx = index / 8;
63        let bit_idx = index % 8;
64        if value {
65            self.data[byte_idx] |= 1 << bit_idx;
66        } else {
67            self.data[byte_idx] &= !(1 << bit_idx);
68        }
69    }
70    pub(crate) fn insert(&mut self, index: usize, value: bool) {
71        if index >= self.len {
72            self.push(value);
73            return;
74        }
75        self.push(false);
76        for i in (index + 1..self.len).rev() {
77            let prev = self.get(i - 1);
78            self.set(i, prev);
79        }
80        self.set(index, value);
81    }
82    pub(crate) fn remove(&mut self, index: usize) {
83        if index >= self.len {
84            return;
85        }
86        for i in index..self.len - 1 {
87            let next = self.get(i + 1);
88            self.set(i, next);
89        }
90        self.len -= 1;
91        let required_bytes = self.len.div_ceil(8);
92        if self.data.len() > required_bytes {
93            self.data.pop();
94        }
95    }
96    pub(crate) fn drain<R: std::ops::RangeBounds<usize> + Clone>(&mut self, range: R) {
97        let start = match range.start_bound() {
98            std::ops::Bound::Included(&n) => n,
99            std::ops::Bound::Excluded(&n) => n + 1,
100            std::ops::Bound::Unbounded => 0,
101        };
102        let end = match range.end_bound() {
103            std::ops::Bound::Included(&n) => n + 1,
104            std::ops::Bound::Excluded(&n) => n,
105            std::ops::Bound::Unbounded => self.len,
106        };
107        let start = start.min(self.len);
108        let end = end.min(self.len);
109        if start >= end {
110            return;
111        }
112        let count = end - start;
113        for i in end..self.len {
114            let val = self.get(i);
115            self.set(i - count, val);
116        }
117        self.len -= count;
118        self.data.truncate(self.len.div_ceil(8));
119    }
120}