use super::bits::BitVec;
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, Default)]
pub enum Presence {
#[default]
Present,
Unknown,
Inapplicable,
}
impl Presence {
#[must_use]
pub const fn is_present(self) -> bool {
matches!(self, Self::Present)
}
const fn from_flags(present: bool, unknown: bool) -> Self {
match (present, unknown) {
(true, _) => Self::Present,
(false, true) => Self::Unknown,
(false, false) => Self::Inapplicable,
}
}
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum ValidityMask {
AllPresent(u32),
Mixed {
present: BitVec,
unknown: BitVec,
},
}
impl ValidityMask {
#[must_use]
pub const fn all_present(len: u32) -> Self {
Self::AllPresent(len)
}
#[must_use]
pub fn len(&self) -> u32 {
match self {
Self::AllPresent(len) => *len,
Self::Mixed { present, .. } => present.len(),
}
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[must_use]
pub fn is_all_present(&self) -> bool {
match self {
Self::AllPresent(_) => true,
Self::Mixed { present, .. } => present.all(),
}
}
#[must_use]
pub fn get(&self, position: u32) -> Presence {
match self {
Self::AllPresent(len) => {
if position < *len {
Presence::Present
} else {
Presence::Inapplicable
}
}
Self::Mixed { present, unknown } => {
Presence::from_flags(present.test(position), unknown.test(position))
}
}
}
pub fn set(&mut self, position: u32, presence: Presence) {
if !self.materialise_for(position, presence) {
return;
}
if let Self::Mixed { present, unknown } = self {
present.set(position, presence == Presence::Present);
unknown.set(position, presence == Presence::Unknown);
}
}
fn materialise_for(&mut self, position: u32, presence: Presence) -> bool {
let Self::AllPresent(len) = self else {
return true;
};
if presence.is_present() || position >= *len {
return false;
}
let len = *len;
*self = Self::Mixed {
present: BitVec::repeat(true, len),
unknown: BitVec::repeat(false, len),
};
true
}
#[must_use]
pub fn present_count(&self) -> u32 {
match self {
Self::AllPresent(len) => *len,
Self::Mixed { present, .. } => present.count_ones(),
}
}
pub fn compact(&mut self) {
let compact_len = match self {
Self::Mixed { present, .. } if present.all() => Some(present.len()),
_ => None,
};
if let Some(len) = compact_len {
*self = Self::AllPresent(len);
}
}
}
impl ValidityMask {
pub fn try_from_iter<T: IntoIterator<Item = Presence>>(iter: T) -> Option<Self> {
let mut len = 0u32;
let mut mixed: Option<(BitVec, BitVec)> = None;
for presence in iter {
let next_len = len.checked_add(1)?;
match mixed.as_mut() {
Some((present, unknown)) => {
push_presence(present, unknown, presence)?;
}
None if presence.is_present() => {}
None => {
let mut present = BitVec::repeat(true, len);
let mut unknown = BitVec::repeat(false, len);
push_presence(&mut present, &mut unknown, presence)?;
mixed = Some((present, unknown));
}
}
len = next_len;
}
match mixed {
Some((present, unknown)) => Some(Self::Mixed { present, unknown }),
None => Some(Self::AllPresent(len)),
}
}
pub(crate) fn from_bounded_iter<T: IntoIterator<Item = Presence>>(iter: T, len: u32) -> Self {
let word_count = len.div_ceil(u64::BITS) as usize;
let mut present = vec![0_u64; word_count];
let mut unknown = vec![0_u64; word_count];
for (position, presence) in (0..len).zip(iter) {
let word = (position / u64::BITS) as usize;
let bit = 1_u64 << (position % u64::BITS);
let target = match presence {
Presence::Present => &mut present,
Presence::Unknown => &mut unknown,
Presence::Inapplicable => continue,
};
target[word] |= bit;
}
let mut validity = Self::Mixed {
present: BitVec::from_words(present, len),
unknown: BitVec::from_words(unknown, len),
};
validity.compact();
validity
}
}
fn push_presence(present: &mut BitVec, unknown: &mut BitVec, presence: Presence) -> Option<()> {
present.try_push(presence == Presence::Present).ok()?;
unknown.try_push(presence == Presence::Unknown).ok()
}
#[cfg(test)]
#[path = "validity_tests.rs"]
mod tests;