molframe_core/column/
validity.rs1use super::bits::BitVec;
15
16#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default)]
18pub enum Presence {
19 #[default]
21 Present,
22 Unknown,
24 Inapplicable,
26}
27
28impl Presence {
29 #[must_use]
31 pub const fn is_present(self) -> bool {
32 matches!(self, Self::Present)
33 }
34
35 const fn from_flags(present: bool, unknown: bool) -> Self {
36 match (present, unknown) {
37 (true, _) => Self::Present,
38 (false, true) => Self::Unknown,
39 (false, false) => Self::Inapplicable,
40 }
41 }
42}
43
44#[derive(Clone, PartialEq, Eq, Debug)]
62pub enum ValidityMask {
63 AllPresent(u32),
65 Mixed {
67 present: BitVec,
69 unknown: BitVec,
72 },
73}
74
75impl ValidityMask {
76 #[must_use]
78 pub const fn all_present(len: u32) -> Self {
79 Self::AllPresent(len)
80 }
81
82 #[must_use]
84 pub fn len(&self) -> u32 {
85 match self {
86 Self::AllPresent(len) => *len,
87 Self::Mixed { present, .. } => present.len(),
88 }
89 }
90
91 #[must_use]
93 pub fn is_empty(&self) -> bool {
94 self.len() == 0
95 }
96
97 #[must_use]
99 pub fn is_all_present(&self) -> bool {
100 match self {
101 Self::AllPresent(_) => true,
102 Self::Mixed { present, .. } => present.all(),
103 }
104 }
105
106 #[must_use]
111 pub fn get(&self, position: u32) -> Presence {
112 match self {
113 Self::AllPresent(len) => {
114 if position < *len {
115 Presence::Present
116 } else {
117 Presence::Inapplicable
118 }
119 }
120 Self::Mixed { present, unknown } => {
121 Presence::from_flags(present.test(position), unknown.test(position))
122 }
123 }
124 }
125
126 pub fn set(&mut self, position: u32, presence: Presence) {
131 if !self.materialise_for(position, presence) {
132 return;
133 }
134
135 if let Self::Mixed { present, unknown } = self {
136 present.set(position, presence == Presence::Present);
137 unknown.set(position, presence == Presence::Unknown);
138 }
139 }
140
141 fn materialise_for(&mut self, position: u32, presence: Presence) -> bool {
142 let Self::AllPresent(len) = self else {
143 return true;
144 };
145
146 if presence.is_present() || position >= *len {
147 return false;
148 }
149
150 let len = *len;
151
152 *self = Self::Mixed {
153 present: BitVec::repeat(true, len),
154 unknown: BitVec::repeat(false, len),
155 };
156
157 true
158 }
159
160 #[must_use]
162 pub fn present_count(&self) -> u32 {
163 match self {
164 Self::AllPresent(len) => *len,
165 Self::Mixed { present, .. } => present.count_ones(),
166 }
167 }
168
169 pub fn compact(&mut self) {
174 let compact_len = match self {
175 Self::Mixed { present, .. } if present.all() => Some(present.len()),
176 _ => None,
177 };
178
179 if let Some(len) = compact_len {
180 *self = Self::AllPresent(len);
181 }
182 }
183}
184
185impl ValidityMask {
186 pub fn try_from_iter<T: IntoIterator<Item = Presence>>(iter: T) -> Option<Self> {
188 let mut len = 0u32;
189 let mut mixed: Option<(BitVec, BitVec)> = None;
190
191 for presence in iter {
192 let next_len = len.checked_add(1)?;
193 match mixed.as_mut() {
194 Some((present, unknown)) => {
195 push_presence(present, unknown, presence)?;
196 }
197 None if presence.is_present() => {}
198 None => {
199 let mut present = BitVec::repeat(true, len);
200 let mut unknown = BitVec::repeat(false, len);
201
202 push_presence(&mut present, &mut unknown, presence)?;
203
204 mixed = Some((present, unknown));
205 }
206 }
207
208 len = next_len;
209 }
210
211 match mixed {
212 Some((present, unknown)) => Some(Self::Mixed { present, unknown }),
213 None => Some(Self::AllPresent(len)),
214 }
215 }
216
217 pub(crate) fn from_bounded_iter<T: IntoIterator<Item = Presence>>(iter: T, len: u32) -> Self {
218 let mut present = BitVec::repeat(false, len);
219 let mut unknown = BitVec::repeat(false, len);
220 for (position, presence) in (0..len).zip(iter) {
221 present.set(position, presence == Presence::Present);
222 unknown.set(position, presence == Presence::Unknown);
223 }
224 let mut validity = Self::Mixed { present, unknown };
225 validity.compact();
226 validity
227 }
228}
229
230fn push_presence(present: &mut BitVec, unknown: &mut BitVec, presence: Presence) -> Option<()> {
231 present.try_push(presence == Presence::Present).ok()?;
232 unknown.try_push(presence == Presence::Unknown).ok()
233}
234
235#[cfg(test)]
236#[path = "validity_tests.rs"]
237mod tests;