use crate::private::{checks::private::Sealed, PopBits, PushBits};
pub struct PopBuffer<T> {
bytes: T,
}
impl<T> PopBuffer<T> {
#[inline]
pub(super) fn from_bytes(bytes: T) -> Self {
Self { bytes }
}
}
impl Sealed for PopBuffer<u8> {}
impl PopBits for PopBuffer<u8> {
#[inline]
fn pop_bits(&mut self, amount: u32) -> u8 {
let Self { bytes } = self;
let orig_ones = bytes.count_ones();
debug_assert!((1..=8).contains(&amount));
#[allow(clippy::cast_possible_truncation)]
let res = *bytes & (1_u16.wrapping_shl(amount).wrapping_sub(1) as u8);
*bytes = bytes.checked_shr(amount).unwrap_or(0);
debug_assert_eq!(res.count_ones() + bytes.count_ones(), orig_ones);
res
}
}
macro_rules! impl_pop_bits {
( $($type:ty),+ ) => {
$(
impl Sealed for PopBuffer<$type> {}
impl PopBits for PopBuffer<$type> {
#[inline]
fn pop_bits(&mut self, amount: u32) -> u8 {
let Self { bytes } = self;
let orig_ones = bytes.count_ones();
debug_assert!((1..=8).contains(&amount));
let bitmask = 0xFF >> (8 - amount);
#[allow(clippy::cast_possible_truncation)]
let res = (*bytes as u8) & bitmask;
*bytes = bytes.checked_shr(amount).unwrap_or(0);
debug_assert_eq!(res.count_ones() + bytes.count_ones(), orig_ones);
res
}
}
)+
};
}
impl_pop_bits!(u16, u32, u64, u128);
pub struct PushBuffer<T> {
bytes: T,
}
impl<T> PushBuffer<T> {
#[inline]
pub(super) fn into_bytes(self) -> T {
self.bytes
}
}
macro_rules! impl_push_bits {
( $($type:ty),+ ) => {
$(
impl Sealed for PushBuffer<$type> {}
impl Default for PushBuffer<$type> {
#[inline]
fn default() -> Self {
Self { bytes: <$type as Default>::default() }
}
}
impl PushBits for PushBuffer<$type> {
#[inline]
fn push_bits(&mut self, amount: u32, bits: u8) {
let Self { bytes } = self;
let orig_ones = bytes.count_ones();
debug_assert!((1..=8).contains(&amount));
let bitmask = 0xFF >> (8 - amount);
*bytes = bytes.wrapping_shl(amount) | <$type>::from(bits & bitmask);
debug_assert_eq!((bits & bitmask).count_ones() + orig_ones, bytes.count_ones());
}
}
)+
}
}
impl_push_bits!(u8, u16, u32, u64, u128);