Skip to main content

reifydb_core/value/column/
mask.rs

1// SPDX-License-Identifier: AGPL-3.0-or-later
2// Copyright (c) 2026 ReifyDB
3
4#[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}