use std::sync::Arc;
#[derive(Clone, Debug)]
pub struct Bitmap {
words: Vec<u64>,
len: usize,
}
fn words_for(len: usize) -> usize {
len.div_ceil(64)
}
impl Bitmap {
pub fn all_valid(len: usize) -> Bitmap {
let mut words = vec![u64::MAX; words_for(len)];
if let Some(last) = words.last_mut() {
let used = len % 64;
if used != 0 {
*last = (1u64 << used) - 1;
}
}
Bitmap { words, len }
}
pub fn from_valid_iter(len: usize, valid: impl IntoIterator<Item = bool>) -> Bitmap {
let mut bm = Bitmap { words: vec![0; words_for(len)], len };
for (i, v) in valid.into_iter().enumerate().take(len) {
if v {
bm.words[i / 64] |= 1u64 << (i % 64);
}
}
bm
}
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn get(&self, i: usize) -> bool {
(self.words[i / 64] >> (i % 64)) & 1 == 1
}
pub fn set(&mut self, i: usize, valid: bool) {
let (w, bit) = (i / 64, 1u64 << (i % 64));
if valid {
self.words[w] |= bit;
} else {
self.words[w] &= !bit;
}
}
pub fn null_count(&self) -> usize {
let present: u32 = self.words.iter().map(|w| w.count_ones()).sum();
self.len - present as usize
}
}
impl PartialEq for Bitmap {
fn eq(&self, other: &Self) -> bool {
self.len == other.len && self.words == other.words
}
}
#[derive(Clone, Debug, Default)]
pub struct Validity(Option<Arc<Bitmap>>);
impl Validity {
pub fn dense() -> Validity {
Validity(None)
}
pub fn from_bitmap(bm: Bitmap) -> Validity {
if bm.null_count() == 0 {
Validity(None)
} else {
Validity(Some(Arc::new(bm)))
}
}
pub fn from_valid_iter(len: usize, valid: impl IntoIterator<Item = bool>) -> Validity {
Validity::from_bitmap(Bitmap::from_valid_iter(len, valid))
}
pub fn is_valid(&self, i: usize) -> bool {
match &self.0 {
None => true,
Some(bm) => bm.get(i),
}
}
pub fn set(&mut self, i: usize, len: usize, valid: bool) {
match &mut self.0 {
None => {
if !valid {
let mut bm = Bitmap::all_valid(len);
bm.set(i, false);
self.0 = Some(Arc::new(bm));
}
}
Some(arc) => {
let bm = Arc::make_mut(arc);
let clearing_hole = valid && !bm.get(i);
bm.set(i, valid);
if clearing_hole && bm.null_count() == 0 {
self.0 = None;
}
}
}
}
pub fn has_nulls(&self) -> bool {
self.0.is_some()
}
pub fn null_count(&self) -> usize {
self.0.as_ref().map_or(0, |bm| bm.null_count())
}
pub fn bitmap(&self) -> Option<&Bitmap> {
self.0.as_deref()
}
pub fn and(&self, other: &Validity) -> Validity {
match (&self.0, &other.0) {
(None, None) => Validity::dense(),
(Some(a), None) | (None, Some(a)) => Validity(Some(Arc::clone(a))),
(Some(a), Some(b)) => {
let n = a.len();
Validity::from_valid_iter(n, (0..n).map(|i| a.get(i) && b.get(i)))
}
}
}
pub fn take(&self, idx: &[usize]) -> Validity {
match &self.0 {
None => Validity::dense(),
Some(bm) => Validity::from_valid_iter(idx.len(), idx.iter().map(|&i| bm.get(i))),
}
}
pub fn slice(&self, start: usize, end: usize) -> Validity {
match &self.0 {
None => Validity::dense(),
Some(bm) => Validity::from_valid_iter(end - start, (start..end).map(|i| bm.get(i))),
}
}
}
impl PartialEq for Validity {
fn eq(&self, other: &Self) -> bool {
match (&self.0, &other.0) {
(None, None) => true,
(Some(a), Some(b)) => a == b,
(Some(_), None) | (None, Some(_)) => false,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn validity_set_cell_in_place() {
let mut v = Validity::dense();
v.set(1, 3, true); assert!(!v.has_nulls());
v.set(1, 3, false); assert!(v.has_nulls());
assert!(!v.is_valid(1) && v.is_valid(0));
v.set(1, 3, true); assert!(v.is_valid(1));
assert!(!v.has_nulls() && v.bitmap().is_none() && v.null_count() == 0);
}
#[test]
fn validity_set_collapses_only_on_last_hole() {
let mut v = Validity::from_valid_iter(4, [true, false, true, false]);
assert_eq!(v.null_count(), 2);
v.set(3, 4, true); assert!(v.has_nulls() && v.null_count() == 1);
v.set(0, 4, true); assert!(v.has_nulls() && v.null_count() == 1);
v.set(1, 4, true); assert!(!v.has_nulls() && v.bitmap().is_none());
}
#[test]
fn bitmap_get_set_and_null_count() {
let mut bm = Bitmap::all_valid(70); assert_eq!(bm.len(), 70);
assert!(!bm.is_empty());
assert_eq!(bm.null_count(), 0);
assert!(bm.get(0) && bm.get(69));
bm.set(3, false);
bm.set(64, false);
assert!(!bm.get(3) && !bm.get(64));
assert_eq!(bm.null_count(), 2);
bm.set(3, true);
assert!(bm.get(3));
assert_eq!(bm.null_count(), 1);
}
#[test]
fn bitmap_empty_and_full_word_boundary() {
assert!(Bitmap::all_valid(0).is_empty());
assert_eq!(Bitmap::all_valid(64).null_count(), 0);
}
#[test]
fn bitmap_from_valid_iter_and_eq() {
let a = Bitmap::from_valid_iter(3, [true, false, true]);
let b = Bitmap::from_valid_iter(3, [true, false, true]);
assert_eq!(a, b);
assert_eq!(a.null_count(), 1);
assert!(!a.get(1));
assert_ne!(a, Bitmap::from_valid_iter(3, [true, true, true]));
assert_ne!(a, Bitmap::from_valid_iter(2, [true, false])); }
#[test]
fn validity_dense_is_all_valid() {
let v = Validity::dense();
assert!(!v.has_nulls());
assert_eq!(v.null_count(), 0);
assert!(v.is_valid(0) && v.is_valid(999));
assert!(v.bitmap().is_none());
}
#[test]
fn validity_collapses_all_present_to_dense() {
let v = Validity::from_valid_iter(3, [true, true, true]);
assert!(!v.has_nulls());
assert!(v.bitmap().is_none());
let v = Validity::from_valid_iter(3, [true, false, true]);
assert!(v.has_nulls());
assert_eq!(v.null_count(), 1);
assert!(!v.is_valid(1) && v.is_valid(0));
assert!(v.bitmap().is_some());
}
#[test]
fn validity_from_bitmap_and_default() {
assert_eq!(Validity::default(), Validity::dense());
let v = Validity::from_bitmap(Bitmap::all_valid(4));
assert!(!v.has_nulls()); }
#[test]
fn validity_eq() {
let dense = Validity::dense();
let holed = Validity::from_valid_iter(2, [true, false]);
assert_eq!(dense, Validity::dense());
assert_eq!(holed, Validity::from_valid_iter(2, [true, false]));
assert_ne!(dense, holed);
assert_ne!(holed, dense);
assert_ne!(holed, Validity::from_valid_iter(2, [false, true]));
}
#[test]
fn validity_and_combines_holes() {
let dense = Validity::dense();
let a = Validity::from_valid_iter(3, [true, false, true]);
let b = Validity::from_valid_iter(3, [true, true, false]);
assert_eq!(dense.and(&dense), Validity::dense()); assert_eq!(dense.and(&a), a); assert_eq!(a.and(&dense), a); assert_eq!(a.and(&b), Validity::from_valid_iter(3, [true, false, false]));
}
}