use crate::shared::format::highbit32;
fn load_word(src: &[u8], start: usize) -> u64 {
let tail = src.get(start..).unwrap_or(&[]);
if let Some(word) = tail.first_chunk::<8>() {
return u64::from_le_bytes(*word);
}
let mut word = [0u8; 8];
word[..tail.len()].copy_from_slice(tail);
u64::from_le_bytes(word)
}
const CONTAINER_BITS: u32 = 64;
#[inline(always)]
fn read64(src: &[u8], pos: usize) -> u64 {
let pos = pos & u32::MAX as usize;
match src.get(pos..pos + 8) {
Some(word) => u64::from_le_bytes(word.try_into().unwrap_or([0; 8])),
None => 0,
}
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct BitContainer {
container: u64,
avail: u32,
pos: usize,
}
impl BitContainer {
#[inline]
pub(crate) fn new(src: &[u8]) -> Option<Self> {
let &last = src.last()?;
if last == 0 {
return None;
}
let marker = 8 - highbit32(u32::from(last));
if src.len() >= 8 {
let pos = src.len() - 8;
return Some(Self {
container: read64(src, pos),
avail: CONTAINER_BITS - marker,
pos,
});
}
let container = src
.iter()
.rev()
.fold(0u64, |word, &byte| (word << 8) | u64::from(byte));
Some(Self {
container,
avail: CONTAINER_BITS - marker - (8 - src.len() as u32) * 8,
pos: 0,
})
}
#[inline(always)]
pub(crate) const fn look(&self, count: u32) -> u64 {
let start = self.avail.wrapping_sub(count);
(self.container >> (start & 63)) & ((1u64 << count) - 1)
}
#[inline(always)]
pub(crate) const fn look_nonzero(&self, count: u32) -> u64 {
let consumed = CONTAINER_BITS.wrapping_sub(self.avail);
(self.container << (consumed & 63)) >> ((CONTAINER_BITS - count) & 63)
}
#[inline(always)]
pub(crate) const fn skip(&mut self, count: u32) {
self.avail = self.avail.wrapping_sub(count);
}
#[inline(always)]
pub(crate) const fn read(&mut self, count: u32) -> u64 {
let value = self.look(count);
self.skip(count);
value
}
#[inline(always)]
pub(crate) const fn can_reload_fast(&self) -> bool {
self.pos >= 8
}
#[inline(always)]
pub(crate) fn reload_fast(&mut self, src: &[u8]) {
debug_assert!(self.can_reload_fast() && self.avail <= CONTAINER_BITS);
let bytes = (CONTAINER_BITS - self.avail) >> 3;
self.pos -= bytes as usize;
self.avail += bytes * 8;
self.container = read64(src, self.pos);
}
#[inline(always)]
pub(crate) fn reload(&mut self, src: &[u8]) -> bool {
if self.can_reload_fast() {
self.reload_fast(src);
return true;
}
if self.avail > CONTAINER_BITS {
return false;
}
let (next, full) = self.reload_tail(src);
*self = next;
full
}
#[cold]
#[inline(never)]
fn reload_tail(mut self, src: &[u8]) -> (Self, bool) {
if self.pos == 0 {
return (self, false);
}
let wanted = ((CONTAINER_BITS - self.avail) >> 3) as usize;
let step = wanted.min(self.pos);
self.pos -= step;
self.avail += (step as u32) * 8;
self.container = read64(src, self.pos);
(self, step == wanted)
}
#[inline]
pub(crate) const fn is_exhausted(&self) -> bool {
self.pos == 0 && self.avail == 0
}
#[inline(always)]
pub(crate) const fn overflowed(&self) -> bool {
self.avail > CONTAINER_BITS
}
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct Sentinel {
bits: u64,
pos: usize,
}
impl Sentinel {
#[inline(always)]
pub(crate) fn from_container(bits: BitContainer, src: &[u8], start: usize) -> Self {
let consumed = CONTAINER_BITS - bits.avail;
debug_assert!(consumed <= 7);
let pos = start + bits.pos;
Self {
bits: (read64(src, pos) | 1) << (consumed & 7),
pos,
}
}
#[inline(always)]
pub(crate) fn into_container(self, src: &[u8], start: usize) -> BitContainer {
BitContainer {
container: read64(src, self.pos),
avail: CONTAINER_BITS - self.bits.trailing_zeros(),
pos: self.pos - start,
}
}
#[inline(always)]
pub(crate) const fn remaining(&self, start: usize) -> usize {
self.pos - start
}
#[inline(always)]
pub(crate) fn reload_within(&mut self, src: &[u8], limit: usize) -> bool {
let consumed = self.bits.trailing_zeros();
let pos = (self.pos - (consumed >> 3) as usize).min(limit);
let Some(word) = src.get(pos..).and_then(<[u8]>::first_chunk::<8>) else {
return false;
};
self.pos = pos;
self.bits = (u64::from_le_bytes(*word) | 1) << (consumed & 7);
true
}
#[inline(always)]
pub(crate) const fn peek(&self, shift: u32) -> usize {
(self.bits >> shift) as usize
}
#[inline(always)]
pub(crate) const fn skip(&mut self, count: u32) {
self.bits <<= count & 63;
}
}
#[derive(Clone, Debug)]
pub(crate) struct ForwardBitReader<'a> {
src: &'a [u8],
position: usize,
}
impl<'a> ForwardBitReader<'a> {
pub(crate) const fn new(src: &'a [u8]) -> Self {
Self { src, position: 0 }
}
pub(crate) fn peek(&self, count: u32) -> u32 {
debug_assert!(count <= 32);
let word = load_word(self.src, self.position / 8) >> (self.position % 8);
(word & ((1u64 << count) - 1)) as u32
}
pub(crate) const fn consume(&mut self, count: u32) {
self.position += count as usize;
}
pub(crate) fn read(&mut self, count: u32) -> u32 {
let value = self.peek(count);
self.consume(count);
value
}
pub(crate) const fn bytes_used(&self) -> usize {
self.position.div_ceil(8)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_and_unterminated_streams_are_rejected() {
assert!(BitContainer::new(&[]).is_none());
assert!(BitContainer::new(&[0x12, 0x00]).is_none());
}
#[test]
fn the_marker_bit_and_padding_are_skipped() {
let mut bits = BitContainer::new(&[0b0000_0101]).expect("terminated");
assert_eq!(bits.read(1), 0);
assert_eq!(bits.read(1), 1);
assert!(bits.is_exhausted());
assert!(!bits.overflowed());
}
#[test]
fn reads_cross_byte_boundaries_from_the_end() {
let src = [0b1010_0101, 0b1011_0011];
let mut bits = BitContainer::new(&src).expect("terminated");
assert_eq!(bits.read(7), 0b011_0011);
assert_eq!(bits.read(4), 0b1010);
assert_eq!(bits.read(4), 0b0101);
assert!(bits.is_exhausted());
}
#[test]
fn reading_past_the_start_overflows_until_reset() {
let src = [0b0000_0111];
let mut bits = BitContainer::new(&src).expect("terminated");
bits.read(2);
assert!(!bits.overflowed());
bits.read(2);
assert!(bits.overflowed());
assert!(!bits.is_exhausted());
assert!(!bits.reload(&src));
assert!(bits.overflowed());
}
#[test]
fn refills_continue_wide_streams_where_reads_left_off() {
let mut src: [u8; 20] = core::array::from_fn(|index| index as u8 + 1);
src[19] = 0x80;
let mut bits = BitContainer::new(&src).expect("terminated");
assert_eq!(bits.read(7), 0);
assert_eq!(bits.read(32), 0x1312_1110);
assert!(bits.reload(&src));
assert_eq!(bits.read(32), 0x0f0e_0d0c);
assert!(bits.reload(&src));
assert_eq!(bits.read(32), 0x0b0a_0908);
assert!(!bits.reload(&src));
assert_eq!(bits.read(32), 0x0706_0504);
assert_eq!(bits.read(24), 0x03_0201);
assert!(bits.is_exhausted());
assert!(!bits.overflowed());
}
#[test]
fn sentinels_refill_only_within_the_buffer() {
let mut src: [u8; 16] = core::array::from_fn(|index| index as u8 + 1);
src[15] = 0x01;
let mut bits = BitContainer::new(&src).expect("terminated");
bits.reload_fast(&src);
let mut sentinel = Sentinel::from_container(bits, &src, 0);
assert_eq!(sentinel.peek(56), 0x0f);
sentinel.skip(8);
let limit = src.len() - 8;
assert!(sentinel.reload_within(&src, limit));
assert_eq!(sentinel.peek(56), 0x0e);
sentinel.skip(16);
let before = sentinel;
assert!(!sentinel.reload_within(&src[..11], limit));
assert_eq!(sentinel.peek(56), before.peek(56));
assert_eq!(sentinel.remaining(0), before.remaining(0));
assert!(sentinel.reload_within(&src, limit));
assert_eq!(sentinel.peek(56), 0x0c);
sentinel.skip(8);
assert!(sentinel.reload_within(&src, 2));
assert_eq!(sentinel.remaining(0), 2);
assert_eq!(sentinel.peek(56), 0x0a);
assert!(sentinel.reload_within(&src, limit));
assert_eq!(sentinel.remaining(0), 2);
assert_eq!(sentinel.into_container(&src, 0).look(8), 0x0a);
}
#[test]
fn forward_reads_are_little_endian_and_count_bytes() {
let mut bits = ForwardBitReader::new(&[0b1010_0101, 0b0000_0011]);
assert_eq!(bits.read(4), 0b0101);
assert_eq!(bits.bytes_used(), 1);
assert_eq!(bits.peek(6), 0b11_1010);
assert_eq!(bits.read(6), 0b11_1010);
assert_eq!(bits.bytes_used(), 2);
assert_eq!(bits.read(16), 0);
assert_eq!(bits.bytes_used(), 4);
}
}