Skip to main content

visi_core/core/engine/
bitmask.rs

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