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