use std::borrow::Cow;
use bitvec::order::Lsb0;
use bitvec::slice::BitSlice;
use bitvec::vec::BitVec;
use bitvec::view::BitView as _;
use integer_encoding::VarIntWriter as _;
use usize_cast::IntoUsize as _;
use crate::MltError::{PresenceIndexOrder, PresenceRunOverflow, PresenceRunShort};
use crate::codecs::varint::parse_varint;
use crate::utils::take;
use crate::{MltRefResult, MltResult};
pub(crate) trait PresenceBudget {
fn reserve_bits(&mut self, count: u32) -> MltResult<()>;
}
pub(crate) struct Unmetered;
impl PresenceBudget for Unmetered {
fn reserve_bits(&mut self, _count: u32) -> MltResult<()> {
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize)]
#[serde(rename_all = "lowercase")]
#[repr(u8)]
pub enum PresenceCoding {
Bitmap = 1,
Runs = 2,
Indices = 3,
}
impl PresenceCoding {
pub(crate) const ALL: [Self; 3] = [Self::Bitmap, Self::Runs, Self::Indices];
#[must_use]
pub(crate) fn from_byte(byte: u8) -> Option<Self> {
match byte {
1 => Some(Self::Bitmap),
2 => Some(Self::Runs),
3 => Some(Self::Indices),
_ => None,
}
}
}
fn runs(bits: &[bool]) -> impl Iterator<Item = (bool, u64)> + '_ {
let mut at = 0usize;
let mut want = false;
std::iter::from_fn(move || {
if at >= bits.len() {
return None;
}
let mut len = 0u64;
while at < bits.len() && bits[at] == want {
len += 1;
at += 1;
}
want = !want;
Some((!want, len))
})
}
pub(crate) fn write(out: &mut Vec<u8>, bits: &[bool], coding: PresenceCoding) {
match coding {
PresenceCoding::Bitmap => {
let start = out.len();
out.resize(start + bits.len().div_ceil(8), 0);
for (i, &bit) in bits.iter().enumerate() {
if bit {
out[start + i / 8] |= 1 << (i % 8);
}
}
}
PresenceCoding::Runs => {
for (_, len) in runs(bits) {
out.write_varint(len).expect("writing to a Vec cannot fail");
}
}
PresenceCoding::Indices => {
let count = bits.iter().filter(|&&b| b).count() as u64;
out.write_varint(count)
.expect("writing to a Vec cannot fail");
let mut prev: Option<usize> = None;
for (i, _) in bits.iter().enumerate().filter(|&(_, &b)| b) {
let gap = match prev {
None => i as u64,
Some(p) => (i - p - 1) as u64,
};
out.write_varint(gap).expect("writing to a Vec cannot fail");
prev = Some(i);
}
}
}
}
#[must_use]
pub(crate) fn size(bits: &[bool], coding: PresenceCoding) -> usize {
fn varint_len(mut v: u64) -> usize {
let mut n = 1;
while v >= 0x80 {
v >>= 7;
n += 1;
}
n
}
match coding {
PresenceCoding::Bitmap => bits.len().div_ceil(8),
PresenceCoding::Runs => runs(bits).map(|(_, len)| varint_len(len)).sum(),
PresenceCoding::Indices => {
let mut total = 0;
let mut prev: Option<usize> = None;
let mut count = 0u64;
for (i, _) in bits.iter().enumerate().filter(|&(_, &b)| b) {
count += 1;
total += varint_len(match prev {
None => i as u64,
Some(p) => (i - p - 1) as u64,
});
prev = Some(i);
}
varint_len(count) + total
}
}
}
#[must_use]
pub(crate) fn smallest(bits: &[bool]) -> PresenceCoding {
PresenceCoding::ALL
.into_iter()
.min_by_key(|&c| (size(bits, c), c as u8))
.expect("ALL is not empty")
}
pub(crate) fn read<'a>(
input: &'a [u8],
count: u32,
coding: PresenceCoding,
budget: &mut dyn PresenceBudget,
) -> MltRefResult<'a, Cow<'a, BitSlice<u8, Lsb0>>> {
let n = count.into_usize();
match coding {
PresenceCoding::Bitmap => {
let (input, bytes) = take(input, count.div_ceil(8))?;
Ok((input, Cow::Borrowed(&bytes.view_bits::<Lsb0>()[..n])))
}
PresenceCoding::Runs => {
budget.reserve_bits(count)?;
let mut bits = BitVec::<u8, Lsb0>::repeat(false, n);
let mut input = input;
let mut at = 0usize;
let mut present = false;
while at < n {
let len: u64;
(input, len) = parse_varint(input)?;
let len = usize::try_from(len).map_err(|_| PresenceRunOverflow(count))?;
let end = at.checked_add(len).ok_or(PresenceRunOverflow(count))?;
if end > n {
return Err(PresenceRunOverflow(count));
}
if present {
bits[at..end].fill(true);
}
at = end;
present = !present;
}
Ok((input, Cow::Owned(bits)))
}
PresenceCoding::Indices => {
let (mut input, len) = parse_varint::<u32>(input)?;
if len > count {
return Err(PresenceRunShort(len, count));
}
budget.reserve_bits(count)?;
let mut bits = BitVec::<u8, Lsb0>::repeat(false, n);
let mut prev: Option<usize> = None;
for _ in 0..len {
let gap: u32;
(input, gap) = parse_varint(input)?;
let at = match prev {
None => gap.into_usize(),
Some(p) => p
.checked_add(gap.into_usize())
.and_then(|v| v.checked_add(1))
.ok_or(PresenceIndexOrder(count))?,
};
if at >= n {
return Err(PresenceIndexOrder(count));
}
bits.set(at, true);
prev = Some(at);
}
Ok((input, Cow::Owned(bits)))
}
}
}
#[cfg(test)]
pub(crate) fn round_trip(bits: &[bool], coding: PresenceCoding) -> Vec<bool> {
let mut buf = Vec::new();
write(&mut buf, bits, coding);
assert_eq!(buf.len(), size(bits, coding), "size disagrees with write");
let count = u32::try_from(bits.len()).unwrap();
let (rest, out) = read(&buf, count, coding, &mut Unmetered).unwrap();
assert!(rest.is_empty(), "{coding:?} left {} bytes", rest.len());
out.iter().by_vals().collect()
}
#[cfg(test)]
mod tests {
use super::*;
fn cases() -> Vec<Vec<bool>> {
vec![
vec![],
vec![true],
vec![false],
vec![true; 9],
vec![false; 9],
(0..100).map(|i| (20..60).contains(&i)).collect(),
(0..1000).map(|i| i == 777).collect(),
(0..64).map(|i| i % 2 == 0).collect(),
(0..17).map(|i| i > 12).collect(),
]
}
#[test]
fn every_coding_round_trips() {
for bits in cases() {
for coding in PresenceCoding::ALL {
assert_eq!(round_trip(&bits, coding), bits, "{coding:?} on {bits:?}");
}
}
}
#[test]
fn smallest_is_the_smallest() {
for bits in cases() {
let picked = smallest(&bits);
let best = PresenceCoding::ALL
.into_iter()
.map(|c| size(&bits, c))
.min()
.unwrap();
assert_eq!(size(&bits, picked), best, "on {bits:?}");
}
}
#[test]
fn a_tie_goes_to_the_lowest_code() {
assert_eq!(smallest(&[true]), PresenceCoding::Bitmap);
}
#[test]
fn runs_beat_a_bitmap_on_one_block() {
let bits: Vec<bool> = (0..4096).map(|i| (100..200).contains(&i)).collect();
assert_eq!(smallest(&bits), PresenceCoding::Runs);
assert!(size(&bits, PresenceCoding::Runs) < size(&bits, PresenceCoding::Bitmap));
}
#[test]
fn indices_beat_a_bitmap_on_a_sparse_mask() {
let bits: Vec<bool> = (0..4096).map(|i| i % 1000 == 0).collect();
assert_eq!(smallest(&bits), PresenceCoding::Indices);
}
#[test]
fn a_run_past_the_end_is_rejected() {
let mut buf = Vec::new();
buf.write_varint(0u64).unwrap();
buf.write_varint(99u64).unwrap();
assert!(read(&buf, 8, PresenceCoding::Runs, &mut Unmetered).is_err());
}
#[test]
fn an_index_past_the_end_is_rejected() {
let mut buf = Vec::new();
buf.write_varint(1u64).unwrap();
buf.write_varint(99u64).unwrap();
assert!(read(&buf, 8, PresenceCoding::Indices, &mut Unmetered).is_err());
}
struct Counted(u32);
impl PresenceBudget for Counted {
fn reserve_bits(&mut self, count: u32) -> MltResult<()> {
self.0 += count;
Ok(())
}
}
#[rstest::rstest]
#[case::runs(PresenceCoding::Runs)]
#[case::indices(PresenceCoding::Indices)]
fn a_tiny_payload_cannot_ask_for_a_huge_allocation_unmetered(#[case] coding: PresenceCoding) {
let mut buf = Vec::new();
buf.write_varint(0u64).unwrap();
let mut budget = Counted(0);
let _ = read(&buf, u32::MAX, coding, &mut budget);
assert_eq!(
budget.0,
u32::MAX,
"{coding:?} allocated without charging the budget"
);
}
#[test]
fn more_indices_than_features_is_rejected() {
let mut buf = Vec::new();
buf.write_varint(9u64).unwrap();
assert!(read(&buf, 8, PresenceCoding::Indices, &mut Unmetered).is_err());
}
}