reifydb_core/value/column/
mask.rs1#[derive(Clone, Debug, PartialEq, Eq)]
8pub struct RowMask {
9 words: Vec<u64>,
10 len: usize,
11}
12
13impl RowMask {
14 pub fn all_set(len: usize) -> Self {
15 let word_count = len.div_ceil(64);
16 let mut words = vec![u64::MAX; word_count];
17 let trailing = len % 64;
18 if trailing != 0 && word_count > 0 {
19 words[word_count - 1] = (1u64 << trailing) - 1;
20 }
21 Self {
22 words,
23 len,
24 }
25 }
26
27 pub fn none_set(len: usize) -> Self {
28 Self {
29 words: vec![0u64; len.div_ceil(64)],
30 len,
31 }
32 }
33
34 pub fn len(&self) -> usize {
35 self.len
36 }
37
38 pub fn is_empty(&self) -> bool {
39 self.len == 0
40 }
41
42 pub fn get(&self, row: usize) -> bool {
43 debug_assert!(row < self.len);
44 (self.words[row / 64] >> (row % 64)) & 1 == 1
45 }
46
47 pub fn set(&mut self, row: usize, value: bool) {
48 debug_assert!(row < self.len);
49 let word = &mut self.words[row / 64];
50 let bit = 1u64 << (row % 64);
51 if value {
52 *word |= bit;
53 } else {
54 *word &= !bit;
55 }
56 }
57
58 pub fn popcount(&self) -> usize {
59 let word_count = self.words.len();
60 if word_count == 0 {
61 return 0;
62 }
63 let mut count = 0usize;
64 for &w in &self.words[..word_count - 1] {
65 count += w.count_ones() as usize;
66 }
67 let trailing = self.len - 64 * (word_count - 1);
68 let mask = if trailing == 64 {
69 u64::MAX
70 } else {
71 (1u64 << trailing) - 1
72 };
73 count += (self.words[word_count - 1] & mask).count_ones() as usize;
74 count
75 }
76
77 pub fn and(&self, other: &Self) -> Self {
78 assert_eq!(self.len, other.len, "RowMask::and length mismatch");
79 let words = self.words.iter().zip(&other.words).map(|(a, b)| a & b).collect();
80 Self {
81 words,
82 len: self.len,
83 }
84 }
85
86 pub fn or(&self, other: &Self) -> Self {
87 assert_eq!(self.len, other.len, "RowMask::or length mismatch");
88 let words = self.words.iter().zip(&other.words).map(|(a, b)| a | b).collect();
89 Self {
90 words,
91 len: self.len,
92 }
93 }
94
95 pub fn not(&self) -> Self {
96 let word_count = self.words.len();
97 let mut words: Vec<u64> = self.words.iter().map(|w| !w).collect();
98 let trailing = self.len % 64;
99 if trailing != 0 && word_count > 0 {
100 words[word_count - 1] &= (1u64 << trailing) - 1;
101 }
102 Self {
103 words,
104 len: self.len,
105 }
106 }
107
108 pub fn slice(&self, start: usize, end: usize) -> Self {
109 debug_assert!(start <= end, "RowMask::slice: start {start} > end {end}");
110 debug_assert!(end <= self.len, "RowMask::slice: end {end} > len {}", self.len);
111 let new_len = end - start;
112 let mut out = Self::none_set(new_len);
113 for i in 0..new_len {
114 if self.get(start + i) {
115 out.set(i, true);
116 }
117 }
118 out
119 }
120
121 pub fn concat(parts: &[Self]) -> Self {
122 let total: usize = parts.iter().map(|m| m.len).sum();
123 let mut out = Self::none_set(total);
124 let mut row_offset = 0;
125 for part in parts {
126 for i in 0..part.len {
127 if part.get(i) {
128 out.set(row_offset + i, true);
129 }
130 }
131 row_offset += part.len;
132 }
133 out
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140
141 #[test]
142 fn all_set_counts_every_row() {
143 let m = RowMask::all_set(100);
144 assert_eq!(m.popcount(), 100);
145 assert!(m.get(0));
146 assert!(m.get(99));
147 }
148
149 #[test]
150 fn none_set_has_zero_popcount() {
151 let m = RowMask::none_set(10);
152 assert_eq!(m.popcount(), 0);
153 assert!(!m.get(0));
154 }
155
156 #[test]
157 fn not_inverts_within_length_only() {
158 let mut m = RowMask::none_set(65);
159 m.set(0, true);
160 m.set(64, true);
161 let inverted = m.not();
162 assert_eq!(inverted.popcount(), 63);
163 assert!(!inverted.get(0));
164 assert!(inverted.get(1));
165 assert!(!inverted.get(64));
166 }
167
168 #[test]
169 fn and_or_combine_masks() {
170 let mut a = RowMask::none_set(8);
171 a.set(1, true);
172 a.set(3, true);
173 a.set(5, true);
174 let mut b = RowMask::none_set(8);
175 b.set(3, true);
176 b.set(5, true);
177 b.set(7, true);
178 assert_eq!(a.and(&b).popcount(), 2);
179 assert_eq!(a.or(&b).popcount(), 4);
180 }
181
182 #[test]
183 fn concat_appends_each_part_at_its_offset() {
184 let mut a = RowMask::none_set(3);
185 a.set(0, true);
186 a.set(2, true);
187 let mut b = RowMask::none_set(2);
188 b.set(1, true);
189 let mut c = RowMask::none_set(4);
190 c.set(0, true);
191 c.set(3, true);
192 let combined = RowMask::concat(&[a, b, c]);
193 assert_eq!(combined.len(), 9);
194 assert!(combined.get(0));
195 assert!(!combined.get(1));
196 assert!(combined.get(2));
197 assert!(!combined.get(3));
198 assert!(combined.get(4));
199 assert!(combined.get(5));
200 assert!(!combined.get(6));
201 assert!(!combined.get(7));
202 assert!(combined.get(8));
203 }
204
205 #[test]
206 fn concat_handles_word_boundary_crossings() {
207 let a = RowMask::all_set(70);
208 let b = RowMask::all_set(70);
209 let combined = RowMask::concat(&[a, b]);
210 assert_eq!(combined.len(), 140);
211 assert_eq!(combined.popcount(), 140);
212 }
213
214 #[test]
215 fn concat_empty_parts_yield_empty_mask() {
216 let combined = RowMask::concat(&[]);
217 assert_eq!(combined.len(), 0);
218 assert_eq!(combined.popcount(), 0);
219 }
220
221 #[test]
222 fn slice_extracts_inner_window() {
223 let mut m = RowMask::none_set(8);
224 m.set(1, true);
225 m.set(3, true);
226 m.set(5, true);
227 m.set(7, true);
228 let s = m.slice(2, 6);
229 assert_eq!(s.len(), 4);
230 assert!(!s.get(0));
231 assert!(s.get(1));
232 assert!(!s.get(2));
233 assert!(s.get(3));
234 }
235
236 #[test]
237 fn slice_crosses_word_boundary() {
238 let mut m = RowMask::none_set(140);
239 m.set(60, true);
240 m.set(64, true);
241 m.set(70, true);
242 let s = m.slice(50, 80);
243 assert_eq!(s.len(), 30);
244 assert_eq!(s.popcount(), 3);
245 assert!(s.get(10));
246 assert!(s.get(14));
247 assert!(s.get(20));
248 }
249
250 #[test]
251 fn slice_full_range_equals_self() {
252 let mut m = RowMask::none_set(10);
253 m.set(0, true);
254 m.set(4, true);
255 m.set(9, true);
256 let s = m.slice(0, 10);
257 assert_eq!(s, m);
258 }
259
260 #[test]
261 fn slice_empty_range_yields_empty_mask() {
262 let m = RowMask::all_set(10);
263 let s = m.slice(5, 5);
264 assert_eq!(s.len(), 0);
265 assert_eq!(s.popcount(), 0);
266 }
267}