use crate::{Result, error};
#[derive(Debug)]
pub struct Validity<'a> {
pub(crate) bits: &'a [u8],
pub(crate) bit_len: usize,
}
impl<'a> Validity<'a> {
pub fn from_bitmap(bits: &'a [u8], bit_len: usize) -> Result<Self> {
if bit_len > super::MAX_CHUNK_ROWS {
return Err(error::fmt!(
InvalidApiCall,
"validity bit_len {} exceeds MAX_CHUNK_ROWS ({})",
bit_len,
super::MAX_CHUNK_ROWS
));
}
let required_bytes = bit_len.div_ceil(8);
if bits.len() < required_bytes {
return Err(error::fmt!(
InvalidApiCall,
"validity bitmap too short: {} bytes for {} bits (need at least {})",
bits.len(),
bit_len,
required_bytes
));
}
Ok(Self { bits, bit_len })
}
pub fn bit_len(&self) -> usize {
self.bit_len
}
#[inline]
pub(crate) fn is_valid(&self, idx: usize) -> bool {
debug_assert!(idx < self.bit_len);
let byte = self.bits[idx / 8];
(byte >> (idx % 8)) & 1 == 1
}
pub(crate) fn non_null_count(&self) -> usize {
let full_bytes = self.bit_len / 8;
let trailing_bits = self.bit_len % 8;
let mut count: usize = 0;
for &byte in &self.bits[..full_bytes] {
count += byte.count_ones() as usize;
}
if trailing_bits != 0 {
let mask = (1u8 << trailing_bits) - 1;
count += (self.bits[full_bytes] & mask).count_ones() as usize;
}
count
}
}
pub(crate) fn check_row_count(
locked: Option<usize>,
data_len: usize,
validity: Option<&Validity<'_>>,
) -> Result<usize> {
let row_count = data_len;
if let Some(existing) = locked
&& existing != row_count
{
return Err(error::fmt!(
InvalidApiCall,
"Column length mismatch: chunk row_count is {} but this column has {} rows",
existing,
row_count
));
}
if let Some(v) = validity
&& v.bit_len != row_count
{
return Err(error::fmt!(
InvalidApiCall,
"Validity bitmap length ({} bits) does not match column data length ({} rows)",
v.bit_len,
row_count
));
}
Ok(row_count)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn non_null_count_handles_trailing_bits() {
let bits = [0b1010_1010, 0xFFu8]; let v = Validity::from_bitmap(&bits, 9).unwrap();
assert_eq!(v.non_null_count(), 4 + 1);
}
#[test]
fn from_bitmap_rejects_short_buffer() {
let err = Validity::from_bitmap(&[0u8], 9).unwrap_err();
assert_eq!(err.code(), crate::ErrorCode::InvalidApiCall);
}
#[test]
fn from_bitmap_rejects_bit_len_above_max() {
let err = Validity::from_bitmap(&[0u8], super::super::MAX_CHUNK_ROWS + 1).unwrap_err();
assert_eq!(err.code(), crate::ErrorCode::InvalidApiCall);
assert!(err.msg().contains("MAX_CHUNK_ROWS"), "{}", err.msg());
}
}