#![no_std]
#![deny(missing_docs)]
use core::iter::{DoubleEndedIterator, ExactSizeIterator};
use core::mem::size_of;
use core::ops::{Bound, RangeBounds};
pub trait BitRangeRead<U> {
fn range_read_le<R: RangeBounds<usize>>(self, range: R) -> U;
fn range_read_be<R: RangeBounds<usize>>(self, range: R) -> U;
}
pub trait BitRangeWrite<U> {
fn range_write_le<R: RangeBounds<usize>>(self, range: R, value: U);
fn range_write_be<R: RangeBounds<usize>>(self, range: R, value: U);
}
macro_rules! impl_bit_range_slice {
($($numeric:ty,)*) => {$(
impl BitRangeRead<$numeric> for &'_ [u8] {
#[cfg_attr(feature = "enable-inline", inline)]
#[cfg_attr(feature = "never-inline", inline(never))]
fn range_read_le<R: RangeBounds<usize>>(self, range: R) -> $numeric {
let res: u64 = bit_range_read_le_iter_impl(self.iter(), range);
res as $numeric
}
#[cfg_attr(feature = "enable-inline", inline)]
#[cfg_attr(feature = "never-inline", inline(never))]
fn range_read_be<R: RangeBounds<usize>>(self, range: R) -> $numeric {
let res: u64 = bit_range_read_le_iter_impl(self.iter().rev(), range);
res as $numeric
}
})*
}
}
impl_bit_range_slice!(u8, u16, u32, u64,);
macro_rules! impl_bit_range_write_slice {
($($numeric:ty,)*) => {$(
impl BitRangeWrite<$numeric> for &'_ mut [u8] {
#[cfg_attr(feature = "enable-inline", inline)]
#[cfg_attr(feature = "never-inline", inline(never))]
fn range_write_le<R: RangeBounds<usize>>(self, range: R, value: $numeric) {
write_le_compound(self, value as u64, range);
}
#[cfg_attr(feature = "enable-inline", inline)]
#[cfg_attr(feature = "never-inline", inline(never))]
fn range_write_be<R: RangeBounds<usize>>(self, range: R, value: $numeric) {
write_be_compound(self, value as u64, range);
}
})*
}
}
impl_bit_range_write_slice!(u8, u16, i32, u32, u64,);
#[inline(always)]
fn setup_iter<'a, R>(input_len: usize, range: R) -> (usize, usize, usize, usize)
where
R: RangeBounds<usize>,
{
let start_bit = match range.start_bound() {
Bound::Included(start) => *start,
Bound::Excluded(start) => *start + 1,
Bound::Unbounded => 0,
};
let end_bit = match range.end_bound() {
Bound::Included(end) => *end,
Bound::Excluded(end) => *end - 1,
Bound::Unbounded => size_of::<u64>() * 8 - 1,
};
let total_bits = end_bit - start_bit + 1;
let start_byte = start_bit / 8;
let end_byte = (end_bit / 8).min(input_len);
let start_bit = start_bit - start_byte * 8;
(start_bit, total_bits, start_byte, end_byte)
}
#[cfg_attr(feature = "enable-inline", inline)]
#[cfg_attr(feature = "never-inline", inline(never))]
fn bit_range_read_le_iter_impl<'a, I, R>(input: I, range: R) -> u64
where
I: Iterator<Item = &'a u8> + DoubleEndedIterator + ExactSizeIterator,
R: RangeBounds<usize>,
{
let (start_bit, total_bits, start_byte, end_byte) = setup_iter(input.len(), range);
let iter = input.skip(start_byte).take(end_byte + 1);
let mask = (1 << total_bits) - 1;
let mut output = read_u128_le(iter);
output >>= start_bit;
output &= mask;
output as u64
}
#[cfg_attr(feature = "enable-inline", inline)]
#[cfg_attr(feature = "never-inline", inline(never))]
fn write_le_compound<R>(output: &mut [u8], val: u64, range: R)
where
R: RangeBounds<usize>,
{
let (start_bit, total_bits, start_byte, end_byte) = setup_iter(output.len(), range);
let iter = output.iter().skip(start_byte).take(end_byte + 1);
let mut work_value = read_u128_le(iter);
let mask = ((1 << total_bits) - 1) << start_bit;
let val = ((val as u128) << start_bit) & mask;
work_value &= !mask;
work_value |= val;
let iter = output.iter_mut().skip(start_byte).take(end_byte + 1);
write_value_le(iter, work_value);
}
#[cfg_attr(feature = "enable-inline", inline)]
#[cfg_attr(feature = "never-inline", inline(never))]
fn write_be_compound<R>(output: &mut [u8], val: u64, range: R)
where
R: RangeBounds<usize>,
{
let (start_bit, total_bits, start_byte, end_byte) = setup_iter(output.len(), range);
let iter = output.iter().rev().skip(start_byte).take(end_byte + 1);
let mut work_value = read_u128_le(iter);
let mask = ((1 << total_bits) - 1) << start_bit;
let val = ((val as u128) << start_bit) & mask;
work_value &= !mask;
work_value |= val;
let iter = output.iter_mut().rev().skip(start_byte).take(end_byte + 1);
write_value_le(iter, work_value);
}
#[inline(always)]
fn read_u128_le<'a, I>(input: I) -> u128
where
I: Iterator<Item = &'a u8> + DoubleEndedIterator,
{
input.rev().fold(0, |acc, x| (acc << 8) | *x as u128)
}
#[inline(always)]
fn write_value_le<'a, O>(output: O, value: u128)
where
O: Iterator<Item = &'a mut u8> + DoubleEndedIterator,
{
let val_as_bytes = &value.to_be_bytes();
val_as_bytes
.iter()
.rev()
.zip(output)
.for_each(|(i, o)| *o = *i);
}
#[cfg(test)]
#[macro_use]
extern crate std;
#[cfg(test)]
mod tests;