use crate::{BitBuf, Error, Result, Storage, ensure};
impl_read! {
u8;
bytes = 1;
bits = 8;
offset = 0;
[a];
[a, b];
read_u8_be_aligned_at_unchecked;
read_u8_be_aligned_unchecked;
read_u8_be_at_unchecked;
read_u8_be_unchecked;
try_read_u8_be_aligned_at;
try_read_u8_be_aligned;
try_read_u8_be_at;
try_read_u8_be;
read_u8_be_aligned_at;
read_u8_be_aligned;
read_u8_be_at;
read_u8_be;
read_u8_be_aligned_bits_at_unchecked;
read_u8_be_aligned_bits_unchecked;
try_read_u8_be_aligned_bits_at;
try_read_u8_be_aligned_bits;
read_u8_be_aligned_bits_at;
read_u8_be_aligned_bits;
}
impl_read! {
u16;
bytes = 2;
bits = 16;
offset = 1;
[a, b];
[a, [b], c];
read_u16_be_aligned_at_unchecked;
read_u16_be_aligned_unchecked;
read_u16_be_at_unchecked;
read_u16_be_unchecked;
try_read_u16_be_aligned_at;
try_read_u16_be_aligned;
try_read_u16_be_at;
try_read_u16_be;
read_u16_be_aligned_at;
read_u16_be_aligned;
read_u16_be_at;
read_u16_be;
read_u16_be_aligned_bits_at_unchecked;
read_u16_be_aligned_bits_unchecked;
try_read_u16_be_aligned_bits_at;
try_read_u16_be_aligned_bits;
read_u16_be_aligned_bits_at;
read_u16_be_aligned_bits;
}
impl_read! {
u32;
bytes = 4;
bits = 32;
offset = 3;
[a, b, c, d];
[a, [b, c, d], e];
read_u32_be_aligned_at_unchecked;
read_u32_be_aligned_unchecked;
read_u32_be_at_unchecked;
read_u32_be_unchecked;
try_read_u32_be_aligned_at;
try_read_u32_be_aligned;
try_read_u32_be_at;
try_read_u32_be;
read_u32_be_aligned_at;
read_u32_be_aligned;
read_u32_be_at;
read_u32_be;
read_u32_be_aligned_bits_at_unchecked;
read_u32_be_aligned_bits_unchecked;
try_read_u32_be_aligned_bits_at;
try_read_u32_be_aligned_bits;
read_u32_be_aligned_bits_at;
read_u32_be_aligned_bits;
}
impl_read! {
u64;
bytes = 8;
bits = 64;
offset = 7;
[a, b, c, d, e, f, g, h];
[a, [b, c, d, e, f, g, h], i];
read_u64_be_aligned_at_unchecked;
read_u64_be_aligned_unchecked;
read_u64_be_at_unchecked;
read_u64_be_unchecked;
try_read_u64_be_aligned_at;
try_read_u64_be_aligned;
try_read_u64_be_at;
try_read_u64_be;
read_u64_be_aligned_at;
read_u64_be_aligned;
read_u64_be_at;
read_u64_be;
read_u64_be_aligned_bits_at_unchecked;
read_u64_be_aligned_bits_unchecked;
try_read_u64_be_aligned_bits_at;
try_read_u64_be_aligned_bits;
read_u64_be_aligned_bits_at;
read_u64_be_aligned_bits;
}
impl_read! {
u128;
bytes = 16;
bits = 128;
offset = 15;
[a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p];
[a, [b, c, d, e, f, g, h, i, j, k, l, m, n, o, p], q];
read_u128_be_aligned_at_unchecked;
read_u128_be_aligned_unchecked;
read_u128_be_at_unchecked;
read_u128_be_unchecked;
try_read_u128_be_aligned_at;
try_read_u128_be_aligned;
try_read_u128_be_at;
try_read_u128_be;
read_u128_be_aligned_at;
read_u128_be_aligned;
read_u128_be_at;
read_u128_be;
read_u128_be_aligned_bits_at_unchecked;
read_u128_be_aligned_bits_unchecked;
try_read_u128_be_aligned_bits_at;
try_read_u128_be_aligned_bits;
read_u128_be_aligned_bits_at;
read_u128_be_aligned_bits;
}
pub trait Read: Sized {
unsafe fn read_be_aligned_at_unchecked<S: Storage>(buf: &BitBuf<S>, byte_offset: usize) -> Self;
unsafe fn read_be_aligned_unchecked<S: Storage>(buf: &mut BitBuf<S>) -> Self;
unsafe fn read_be_at_unchecked<S: Storage>(buf: &BitBuf<S>, offset: usize) -> Self;
unsafe fn read_be_unchecked<S: Storage>(buf: &mut BitBuf<S>) -> Self;
fn try_read_be_aligned_at<S: Storage>(buf: &BitBuf<S>, byte_offset: usize) -> Result<Self>;
fn try_read_be_aligned<S: Storage>(buf: &mut BitBuf<S>) -> Result<Self>;
fn try_read_be_at<S: Storage>(buf: &BitBuf<S>, offset: usize) -> Result<Self>;
fn try_read_be<S: Storage>(buf: &mut BitBuf<S>) -> Result<Self>;
fn read_be_aligned_at<S: Storage>(buf: &BitBuf<S>, byte_offset: usize) -> Self;
fn read_be_aligned<S: Storage>(buf: &mut BitBuf<S>) -> Self;
fn read_be_at<S: Storage>(buf: &BitBuf<S>, offset: usize) -> Self;
fn read_be<S: Storage>(buf: &mut BitBuf<S>) -> Self;
unsafe fn read_be_aligned_bits_at_unchecked<S: Storage>(
buf: &BitBuf<S>,
byte_offset: usize,
bits: usize,
) -> Self;
unsafe fn read_be_aligned_bits_unchecked<S: Storage>(buf: &mut BitBuf<S>, bits: usize) -> Self;
fn try_read_be_aligned_bits_at<S: Storage>(
buf: &BitBuf<S>,
byte_offset: usize,
bits: usize,
) -> Result<Self>;
fn try_read_be_aligned_bits<S: Storage>(buf: &mut BitBuf<S>, bits: usize) -> Result<Self>;
fn read_be_aligned_bits_at<S: Storage>(buf: &BitBuf<S>, byte_offset: usize, bits: usize) -> Self;
fn read_be_aligned_bits<S: Storage>(buf: &mut BitBuf<S>, bits: usize) -> Self;
}
impl<S: Storage> BitBuf<S> {
#[inline(always)]
#[must_use]
pub unsafe fn read_be_aligned_at_unchecked<T: Read>(&self, byte_offset: usize) -> T {
unsafe { T::read_be_aligned_at_unchecked(self, byte_offset) }
}
#[inline(always)]
#[must_use]
pub unsafe fn read_be_aligned_unchecked<T: Read>(&mut self) -> T {
unsafe { T::read_be_aligned_unchecked(self) }
}
#[inline(always)]
#[must_use]
pub unsafe fn read_be_at_unchecked<T: Read>(&self, offset: usize) -> T {
unsafe { T::read_be_at_unchecked(self, offset) }
}
#[inline(always)]
#[must_use]
pub unsafe fn read_be_unchecked<T: Read>(&mut self) -> T {
unsafe { T::read_be_unchecked(self) }
}
#[inline(always)]
pub fn try_read_be_aligned_at<T: Read>(&self, byte_offset: usize) -> Result<T> {
T::try_read_be_aligned_at(self, byte_offset)
}
#[inline(always)]
pub fn try_read_be_aligned<T: Read>(&mut self) -> Result<T> {
T::try_read_be_aligned(self)
}
#[inline(always)]
pub fn try_read_be_at<T: Read>(&self, offset: usize) -> Result<T> {
T::try_read_be_at(self, offset)
}
#[inline(always)]
pub fn try_read_be<T: Read>(&mut self) -> Result<T> {
T::try_read_be(self)
}
#[inline(always)]
#[must_use]
pub fn read_be_aligned_at<T: Read>(&self, byte_offset: usize) -> T {
T::read_be_aligned_at(self, byte_offset)
}
#[inline(always)]
#[must_use]
pub fn read_be_aligned<T: Read>(&mut self) -> T {
T::read_be_aligned(self)
}
#[inline(always)]
#[must_use]
pub fn read_be_at<T: Read>(&self, offset: usize) -> T {
T::read_be_at(self, offset)
}
#[inline(always)]
#[must_use]
pub fn read_be<T: Read>(&mut self) -> T {
T::read_be(self)
}
#[inline(always)]
#[must_use]
pub unsafe fn read_be_aligned_bits_at_unchecked<T: Read>(
&self,
byte_offset: usize,
bits: usize,
) -> T {
unsafe { T::read_be_aligned_bits_at_unchecked(self, byte_offset, bits) }
}
#[inline(always)]
#[must_use]
pub unsafe fn read_be_aligned_bits_unchecked<T: Read>(&mut self, bits: usize) -> T {
unsafe { T::read_be_aligned_bits_unchecked(self, bits) }
}
#[inline(always)]
pub fn try_read_be_aligned_bits_at<T: Read>(&self, byte_offset: usize, bits: usize) -> Result<T> {
T::try_read_be_aligned_bits_at(self, byte_offset, bits)
}
#[inline(always)]
pub fn try_read_be_aligned_bits<T: Read>(&mut self, bits: usize) -> Result<T> {
T::try_read_be_aligned_bits(self, bits)
}
#[inline(always)]
#[must_use]
pub fn read_be_aligned_bits_at<T: Read>(&self, byte_offset: usize, bits: usize) -> T {
T::read_be_aligned_bits_at(self, byte_offset, bits)
}
#[inline(always)]
#[must_use]
pub fn read_be_aligned_bits<T: Read>(&mut self, bits: usize) -> T {
T::read_be_aligned_bits(self, bits)
}
}
macro_rules! impl_read {
(
$ty:ident;
bytes = $bytes:literal;
bits = $bits:literal;
offset = $offset:literal;
[$($byte:ident),*];
[$first:ident, $([$($middle:ident),*],)? $last:ident];
$read_be_aligned_at_unchecked:ident;
$read_be_aligned_unchecked:ident;
$read_be_at_unchecked:ident;
$read_be_unchecked:ident;
$try_read_be_aligned_at:ident;
$try_read_be_aligned:ident;
$try_read_be_at:ident;
$try_read_be:ident;
$read_be_aligned_at:ident;
$read_be_aligned:ident;
$read_be_at:ident;
$read_be:ident;
$read_be_aligned_bits_at_unchecked:ident;
$read_be_aligned_bits_unchecked:ident;
$try_read_be_aligned_bits_at:ident;
$try_read_be_aligned_bits:ident;
$read_be_aligned_bits_at:ident;
$read_be_aligned_bits:ident;
) => {
impl<S: Storage> BitBuf<S> {
#[doc = concat!("Read a [`", stringify!($ty), "`] in [BE-bit, BE-byte] order from `byte_offset`, without performing bound checks")]
#[doc = concat!("* This is UB if <code>byte_offset + ", stringify!($offset), " >= [self.bytes()][Self::bytes].len()</code>")]
#[doc = concat!("* Panics in debug mode if <code>byte_offset + ", stringify!($offset), " >= [self.bytes()][Self::bytes].len()</code>")]
#[inline(always)]
#[must_use]
pub unsafe fn $read_be_aligned_at_unchecked(&self, byte_offset: usize) -> $ty {
let bytes = self.bytes();
let len = bytes.len();
ensure!(
byte_offset + $offset < len;
concat!(
"BitBuf::",
stringify!($read_be_aligned_at_unchecked),
": index out of bounds! len is {}, offset is {}"
),
len,
byte_offset + $offset,
);
unsafe {
$ty::from_be_bytes(
*bytes[byte_offset..byte_offset + $bytes]
.as_array()
.unwrap_unchecked(),
)
}
}
#[doc = concat!("Read the next [`", stringify!($ty), "`] in [BE-bit, BE-byte] order, without validating cursor alignment or performing bound checks, advancing the internal cursor")]
///
/// # Safety
///
/// * The internal cursor must be byte-aligned ([`self.is_aligned()`][Self::is_aligned])
#[doc = concat!("* This is UB if <code>[self.byte_pos()][Self::byte_pos] + ", stringify!($offset), " >= [self.bytes()][Self::bytes].len()</code>")]
///
/// # Panics
///
/// * Panics in debug mode if the internal cursor is not byte-aligned (<code>\![self.is_aligned()][Self::is_aligned]</code>)
#[doc = concat!("* Panics in debug mode if <code>[self.byte_pos()][Self::byte_pos] + ", stringify!($offset), " >= [self.bytes()][Self::bytes].len()</code>")]
#[inline(always)]
#[must_use]
pub unsafe fn $read_be_aligned_unchecked(&mut self) -> $ty {
ensure!(
self.is_aligned();
concat!(
"BitBuf::",
stringify!($read_be_aligned_unchecked),
" called at unaligned bit position: {}",
),
self.pos(),
);
let v = unsafe { self.$read_be_aligned_at_unchecked(self.byte_pos()) };
self.advance_bytes($bytes);
v
}
#[doc = concat!("Read a [`", stringify!($ty), "`] in [BE-bit, BE-byte] order from `offset`, without performing bound checks")]
///
/// # Safety
///
#[doc = concat!("* This is UB if <code>(offset / 8) + ", stringify!($offset), " >= [self.bytes()][Self::bytes].len()</code>")]
#[doc = concat!("* This is UB if <code>(offset % 8 != 0) && (offset / 8) + ", stringify!($bytes), " >= [self.bytes()][Self::bytes].len()</code>")]
///
/// # Panics
///
#[doc = concat!("* Panics in debug mode if <code>(offset / 8) + ", stringify!($offset), " >= [self.bytes()][Self::bytes].len()</code>")]
#[doc = concat!("* Panics in debug mode if <code>(offset % 8 != 0) && (offset / 8) + ", stringify!($bytes), " >= [self.bytes()][Self::bytes].len()</code>")]
#[inline(always)]
#[must_use]
pub unsafe fn $read_be_at_unchecked(&self, offset: usize) -> $ty {
let byte_idx = offset / 8;
let shift = offset % 8;
let bytes = self.bytes();
let len = bytes.len();
ensure!(
byte_idx + $offset < len;
concat!(
"BitBuf::",
stringify!($read_be_at_unchecked),
": index out of bounds! len is {}, byte_idx is {}",
),
len,
byte_idx + $offset,
);
if shift == 0 {
unsafe { self.$read_be_aligned_at_unchecked(byte_idx) }
} else {
ensure!(
byte_idx + $bytes < len;
concat!(
"BitBuf::",
stringify!($read_be_at_unchecked),
": lookahead index out of bounds! len is {}, lookahead byte_idx is {}",
),
len,
byte_idx + $bytes,
);
unsafe {
let ptr = bytes.as_ptr().add(byte_idx);
let high = $ty::from_be_bytes(*(ptr as *const [u8; $bytes]));
let low = *bytes.get_unchecked(byte_idx + $bytes) as $ty;
(high << shift) | (low >> (8 - shift))
}
}
}
#[doc = concat!("Read the next [`", stringify!($ty), "`] in [BE-bit, BE-byte] order, without performing bound checks, advancing the internal cursor")]
///
/// # Safety
///
#[doc = concat!("* This is UB if <code>([self.pos()][Self::pos] / 8) + ", stringify!($offset), " >= [self.bytes()][Self::bytes].len()</code>")]
#[doc = concat!("* This is UB if <code>([self.pos()][Self::pos] % 8 != 0) && ([self.pos()][Self::pos] / 8) + ", stringify!($bytes), " >= [self.bytes()][Self::bytes].len()</code>")]
///
/// # Panics
///
#[doc = concat!("* Panics in debug mode if <code>([self.pos()][Self::pos] / 8) + ", stringify!($offset), " >= [self.bytes()][Self::bytes].len()</code>")]
#[doc = concat!("* Panics in debug mode if <code>([self.pos()][Self::pos] % 8 != 0) && ([self.pos()][Self::pos] / 8) + ", stringify!($bytes), " >= [self.bytes()][Self::bytes].len()</code>")]
#[inline(always)]
#[must_use]
pub unsafe fn $read_be_unchecked(&mut self) -> $ty {
let v = unsafe { self.$read_be_at_unchecked(self.pos()) };
self.advance_bytes($bytes);
v
}
#[doc = concat!("Read a [`", stringify!($ty), "`] in [BE-bit, BE-byte] order from `byte_offset`, performing bound checks")]
///
/// # Errors
///
/// * Returns [`Error::OutOfBounds`]
#[doc = concat!(" * if <code>byte_offset + ", stringify!($offset), " >= [self.bytes()][Self::bytes].len()</code>")]
#[inline(always)]
pub fn $try_read_be_aligned_at(&self, byte_offset: usize) -> Result<$ty> {
let len = self.bytes().len();
if byte_offset + $offset >= len {
return Err(Error::OutOfBounds);
}
Ok(unsafe { self.$read_be_aligned_at_unchecked(byte_offset) })
}
#[doc = concat!("Read the next [`", stringify!($ty), "`] in [BE-bit, BE-byte] order, validating cursor alignment and performing bound checks, advancing the internal cursor")]
///
/// # Errors
///
/// * Returns [`Error::Unaligned`]
/// * if the internal cursor is not byte-aligned (<code>\![self.is_aligned()][Self::is_aligned]</code>)
/// * Returns [`Error::OutOfBounds`]
#[doc = concat!(" * if <code>[self.byte_pos()][Self::byte_pos] + ", stringify!($offset), " >= [self.bytes()][Self::bytes].len()</code>")]
#[inline(always)]
pub fn $try_read_be_aligned(&mut self) -> Result<$ty> {
if !self.is_aligned() {
return Err(Error::Unaligned);
}
let v = self.$try_read_be_aligned_at(self.byte_pos())?;
self.advance_bytes($bytes);
Ok(v)
}
#[doc = concat!("Read a [`", stringify!($ty), "`] in [BE-bit, BE-byte] order from `offset`, performing bound checks")]
///
/// # Errors
///
/// * Returns [`Error::OutOfBounds`]
#[doc = concat!(" * if <code>(offset / 8) + ", stringify!($offset), " >= [self.bytes()][Self::bytes].len()</code>")]
#[doc = concat!(" * if <code>(offset % 8 != 0) && (offset / 8) + ", stringify!($bytes), " >= [self.bytes()][Self::bytes].len()</code>")]
#[inline(always)]
pub fn $try_read_be_at(&self, offset: usize) -> Result<$ty> {
let byte_idx = offset / 8;
let shift = offset % 8;
let len = self.bytes().len();
if byte_idx + $offset >= len {
return Err(Error::OutOfBounds);
}
if shift != 0 && byte_idx + $bytes >= len {
return Err(Error::OutOfBounds);
}
Ok(unsafe { self.$read_be_at_unchecked(offset) })
}
#[doc = concat!("Read the next [`", stringify!($ty), "`] in [BE-bit, BE-byte] order, performing bound checks, advancing the internal cursor")]
///
/// # Errors
///
/// * Returns [`Error::OutOfBounds`]
#[doc = concat!(" * if <code>([self.pos()][Self::pos] / 8) + ", stringify!($offset), " >= [self.bytes()][Self::bytes].len()</code>")]
#[doc = concat!(" * if <code>([self.pos()][Self::pos] % 8 != 0) && ([self.pos()][Self::pos] / 8) + ", stringify!($bytes), " >= [self.bytes()][Self::bytes].len()</code>")]
#[inline(always)]
pub fn $try_read_be(&mut self) -> Result<$ty> {
let v = self.$try_read_be_at(self.pos())?;
self.advance_bytes($bytes);
Ok(v)
}
#[doc = concat!("Read a [`", stringify!($ty), "`] in [BE-bit, BE-byte] order from `byte_offset`, panicking on out of bounds")]
///
/// # Panics
///
#[doc = concat!("* Panics if <code>byte_offset + ", stringify!($offset), " >= [self.bytes()][Self::bytes].len()</code>")]
#[inline(always)]
#[must_use]
pub fn $read_be_aligned_at(&self, byte_offset: usize) -> $ty {
self
.$try_read_be_aligned_at(byte_offset)
.expect(concat!("BitBuf::", stringify!($read_be_aligned_at), " out of bounds"))
}
#[doc = concat!("Read the next [`", stringify!($ty), "`] in [BE-bit, BE-byte] order, panicking on an unaligned cursor or out of bounds, advancing the internal cursor")]
///
/// # Panics
///
/// * if the internal cursor is not byte-aligned (<code>\![self.is_aligned()][Self::is_aligned]</code>)
#[doc = concat!("* if <code>[self.byte_pos()][Self::byte_pos] + ", stringify!($offset), " >= [self.bytes()][Self::bytes].len()</code>")]
#[inline(always)]
#[must_use]
pub fn $read_be_aligned(&mut self) -> $ty {
self
.$try_read_be_aligned()
.expect(concat!("BitBuf::", stringify!($read_be_aligned), " unaligned cursor or out of bounds"))
}
#[doc = concat!("Read a [`", stringify!($ty), "`] in [BE-bit, BE-byte] order from `offset`, panicking on out of bounds")]
///
/// # Panics
///
#[doc = concat!("* Panics if <code>(offset / 8) + ", stringify!($offset), " >= [self.bytes()][Self::bytes].len()</code>")]
#[doc = concat!("* Panics if <code>(offset % 8 != 0) && (offset / 8) + ", stringify!($bytes), " >= [self.bytes()][Self::bytes].len()</code>")]
#[inline(always)]
#[must_use]
pub fn $read_be_at(&self, offset: usize) -> $ty {
self
.$try_read_be_at(offset)
.expect(concat!("BitBuf::", stringify!($read_be_at), " out of bounds"))
}
#[doc = concat!("Read the next [`", stringify!($ty), "`] in [BE-bit, BE-byte] order, panicking on out of bounds, advancing the internal cursor")]
///
/// # Panics
///
#[doc = concat!("* Panics if <code>([self.pos()][Self::pos] / 8) + ", stringify!($offset), " >= [self.bytes()][Self::bytes].len()</code>")]
#[doc = concat!("* Panics if <code>([self.pos()][Self::pos] % 8 != 0) && ([self.pos()][Self::pos] / 8) + ", stringify!($bytes), " >= [self.bytes()][Self::bytes].len()</code>")]
#[inline(always)]
#[must_use]
pub fn $read_be(&mut self) -> $ty {
self
.$try_read_be()
.expect(concat!("BitBuf::", stringify!($read_be), " out of bounds"))
}
}
impl<S: Storage> BitBuf<S> {
#[doc = concat!("Read a [`", stringify!($ty), "`] in [BE-bit, BE-byte] order using `bits` bits from `byte_offset`, without validating the bit count or performing bound checks")]
///
/// # Safety
///
/// * This is UB
#[doc = concat!(" * if `bits > ", stringify!($bits), "`")]
#[doc = concat!(" * if <code>byte_offset + [bits.div_ceil(8)][usize::div_ceil] > [self.bytes()][Self::bytes].len()</code>")]
///
/// # Panics
///
/// * Panics in debug mode
#[doc = concat!(" * if `bits > ", stringify!($bits), "`")]
#[doc = concat!(" * if <code>byte_offset + [bits.div_ceil(8)][usize::div_ceil] > [self.bytes()][Self::bytes].len()</code>")]
#[inline(always)]
#[must_use]
pub unsafe fn $read_be_aligned_bits_at_unchecked(&self, byte_offset: usize, bits: usize) -> $ty {
ensure!(
bits <= $bits;
concat!(
"BitBuf::",
stringify!($read_be_aligned_bits_at_unchecked),
": bits requested ({}) exceeds ",
stringify!($ty),
" width!"
),
bits,
);
if bits == 0 {
return 0;
}
let required_bytes = bits.div_ceil(8);
let bytes = self.bytes();
let len = bytes.len();
ensure!(
byte_offset + required_bytes <= len;
concat!(
"BitBuf::",
stringify!($read_be_aligned_bits_at_unchecked),
": index out of bounds! len is {}, required end is {}"
),
len,
byte_offset + required_bytes,
);
let ptr = unsafe { bytes.as_ptr().add(byte_offset) };
// @note(perf)
// the general pattern is to construct a value of the same size as the number of bytes
// need by combining the least amount of reads of different sizes, kind of like trying to
// make x amount of money using the least amount of bills and coins
//
// it is faster than reading byte by byte because there is 1 shift+or needed per *read*
// instead of per *byte*
let v: $ty = unsafe {
if bits <= 8 {
*bytes.get_unchecked(byte_offset) as _
} else if bits <= 16 {
u16::from_be_bytes(*(ptr as *const _)) as _
} else if bits <= 24 {
let hi = u16::from_be_bytes(*(ptr as *const _)) as u32;
let lo = *bytes.get_unchecked(byte_offset + 2) as u32;
((hi << 8) | lo) as _
} else if bits <= 32 {
u32::from_be_bytes(*(ptr as *const _)) as _
} else if bits <= 40 {
let hi = u32::from_be_bytes(*(ptr as *const _)) as u64;
let lo = *ptr.add(4) as u64;
((hi << 8) | lo) as _
} else if bits <= 48 {
let hi = u32::from_be_bytes(*(ptr as *const _)) as u64;
let lo = u16::from_be_bytes(*(ptr.add(4) as *const _)) as u64;
((hi << 16) | lo) as _
} else if bits <= 56 {
let hi = u32::from_be_bytes(*(ptr as *const _)) as u64;
let mid = u16::from_be_bytes(*(ptr.add(4) as *const _)) as u64;
let lo = *ptr.add(6) as u64;
((hi << 24) | (mid << 8) | lo) as _
} else if bits <= 64 {
u64::from_be_bytes(*(ptr as *const _)) as _
}
// @note(perf)
// originally I tried using the below code for `bits > 64` but the u128 test took ~177s
// ```
// let mut buf = [0u8; 16];
// ptr::copy_nonoverlapping(
// bytes.as_ptr().add(byte_offset),
// buf.as_mut_ptr().add(16 - required_bytes),
// required_bytes,
// );
// u128::from_be_bytes(buf) as _
// ```
// checking the assembly showed it was using memcpy which is probably why it was so slow.
// continuing the same pattern below brought it down to ~90s
else if bits <= 72 {
let hi = u64::from_be_bytes(*(ptr as *const _)) as u128;
let lo = *ptr.add(8) as u128;
((hi << 8) | lo) as _
} else if bits <= 80 {
let hi = u64::from_be_bytes(*(ptr as *const _)) as u128;
let lo = u16::from_be_bytes(*(ptr.add(8) as *const _)) as u128;
((hi << 16) | lo) as _
} else if bits <= 88 {
let hi = u64::from_be_bytes(*(ptr as *const _)) as u128;
let mid = u16::from_be_bytes(*(ptr.add(8) as *const _)) as u128;
let lo = *ptr.add(10) as u128;
((hi << 24) | (mid << 8) | lo) as _
} else if bits <= 96 {
let hi = u64::from_be_bytes(*(ptr as *const _)) as u128;
let lo = u32::from_be_bytes(*(ptr.add(8) as *const _)) as u128;
((hi << 32) | lo) as _
} else if bits <= 104 {
let hi = u64::from_be_bytes(*(ptr as *const _)) as u128;
let mid = u32::from_be_bytes(*(ptr.add(8) as *const _)) as u128;
let lo = *ptr.add(12) as u128;
((hi << 40) | (mid << 8) | lo) as _
} else if bits <= 112 {
let hi = u64::from_be_bytes(*(ptr as *const _)) as u128;
let mid = u32::from_be_bytes(*(ptr.add(8) as *const _)) as u128;
let lo = u16::from_be_bytes(*(ptr.add(12) as *const _)) as u128;
((hi << 48) | (mid << 16) | lo) as _
} else if bits <= 120 {
let hi = u64::from_be_bytes(*(ptr as *const _)) as u128;
let mid1 = u32::from_be_bytes(*(ptr.add(8) as *const _)) as u128;
let mid2 = u16::from_be_bytes(*(ptr.add(12) as *const _)) as u128;
let lo = *ptr.add(14) as u128;
((hi << 56) | (mid1 << 24) | (mid2 << 8) | lo) as _
} else {
u128::from_be_bytes(*(ptr as *const _)) as _
}
};
// finally, just shift the whole constructed value down so that the bits are moved into the
// lower positions
v >> ((required_bytes * 8) - bits)
}
#[doc = concat!("Read the next [`", stringify!($ty), "`] in [BE-bit, BE-byte] order using `bits` bits, without validating cursor alignment, the bit count, or performing bound checks, advancing the internal cursor")]
///
/// # Safety
///
/// * The internal cursor must be byte-aligned ([`self.is_aligned()`][Self::is_aligned])
/// * This is UB
#[doc = concat!(" * if `bits > ", stringify!($bits), "`")]
#[doc = concat!(" * if <code>[self.byte_pos()][Self::byte_pos] + [bits.div_ceil(8)][usize::div_ceil] > [self.bytes()][Self::bytes].len()</code>")]
///
/// # Panics
///
/// * Panics in debug mode
/// * if the internal cursor is not byte-aligned (<code>\![self.is_aligned()][Self::is_aligned]</code>)
#[doc = concat!(" * if `bits > ", stringify!($bits), "`")]
#[doc = concat!(" * if <code>[self.byte_pos()][Self::byte_pos] + [bits.div_ceil(8)][usize::div_ceil] > [self.bytes()][Self::bytes].len()</code>")]
#[inline(always)]
#[must_use]
pub unsafe fn $read_be_aligned_bits_unchecked(&mut self, bits: usize) -> $ty {
ensure!(
self.is_aligned();
concat!(
"BitBuf::",
stringify!($read_be_aligned_bits_unchecked),
" called at unaligned bit position: {}",
),
self.pos(),
);
let v = unsafe { self.$read_be_aligned_bits_at_unchecked(self.byte_pos(), bits) };
self.advance(bits);
v
}
#[doc = concat!("Read a [`", stringify!($ty), "`] in [BE-bit, BE-byte] order using `bits` bits from `byte_offset`, validating the bit count and performing bound checks")]
///
/// # Errors
///
/// * Returns [`Error::InvalidBitCount`]
#[doc = concat!(" * if `bits > ", stringify!($bits), "`")]
/// * Returns [`Error::OutOfBounds`]
#[doc = concat!(" * if <code>byte_offset + [bits.div_ceil(8)][usize::div_ceil] > [self.bytes()][Self::bytes].len()</code>")]
#[inline(always)]
pub fn $try_read_be_aligned_bits_at(&self, byte_offset: usize, bits: usize) -> Result<$ty> {
if bits > $bits {
return Err(Error::InvalidBitCount);
}
let required_bytes = bits.div_ceil(8);
let len = self.bytes().len();
if byte_offset + required_bytes > len {
return Err(Error::OutOfBounds);
}
Ok(unsafe { self.$read_be_aligned_bits_at_unchecked(byte_offset, bits) })
}
#[doc = concat!("Read the next [`", stringify!($ty), "`] in [BE-bit, BE-byte] order using `bits` bits, validating cursor alignment, the bit count, and performing bound checks, advancing the internal cursor")]
///
/// # Errors
///
/// * Returns [`Error::Unaligned`]
/// * if the internal cursor is not byte-aligned (<code>\![self.is_aligned()][Self::is_aligned]</code>)
/// * Returns [`Error::InvalidBitCount`]
#[doc = concat!(" * if `bits > ", stringify!($bits), "`")]
/// * Returns [`Error::OutOfBounds`]
#[doc = concat!(" * if <code>[self.byte_pos()][Self::byte_pos] + [bits.div_ceil(8)][usize::div_ceil] > [self.bytes()][Self::bytes].len()</code>")]
#[inline(always)]
pub fn $try_read_be_aligned_bits(&mut self, bits: usize) -> Result<$ty> {
if !self.is_aligned() {
return Err(Error::Unaligned);
}
let v = self.$try_read_be_aligned_bits_at(self.byte_pos(), bits)?;
self.advance(bits);
Ok(v)
}
#[doc = concat!("Read a [`", stringify!($ty), "`] in [BE-bit, BE-byte] order using `bits` bits from `byte_offset`, panicking on an invalid bit count or out of bounds")]
///
/// # Panics
#[doc = concat!(" * if `bits > ", stringify!($bits), "`")]
#[doc = concat!(" * if <code>byte_offset + [bits.div_ceil(8)][usize::div_ceil] > [self.bytes()][Self::bytes].len()</code>")]
#[inline(always)]
pub fn $read_be_aligned_bits_at(&self, byte_offset: usize, bits: usize) -> $ty {
self
.$try_read_be_aligned_bits_at(byte_offset, bits)
.expect(concat!("BitBuf::", stringify!($read_be_aligned_bits_at), " invalid bit count or out of bounds"))
}
#[doc = concat!("Read the next [`", stringify!($ty), "`] in [BE-bit, BE-byte] order using `bits` bits, panicking on an unaligned cursor, invalid bit count, or out of bounds, advancing the internal cursor")]
///
/// # Panics
/// * if the internal cursor is not byte-aligned (<code>\![self.is_aligned()][Self::is_aligned]</code>)
#[doc = concat!(" * if `bits > ", stringify!($bits), "`")]
#[doc = concat!(" * if <code>[self.byte_pos()][Self::byte_pos] + [bits.div_ceil(8)][usize::div_ceil] > [self.bytes()][Self::bytes].len()</code>")]
#[inline(always)]
pub fn $read_be_aligned_bits(&mut self, bits: usize) -> $ty {
self
.$try_read_be_aligned_bits(bits)
.expect(concat!("BitBuf::", stringify!($read_be_aligned_bits), " unaligned cursor, invalid bit count, or out of bounds"))
}
}
impl Read for $ty {
#[inline(always)]
unsafe fn read_be_aligned_at_unchecked<S: Storage>(buf: &BitBuf<S>, byte_offset: usize) -> Self {
unsafe { buf.$read_be_aligned_at_unchecked(byte_offset) }
}
#[inline(always)]
unsafe fn read_be_aligned_unchecked<S: Storage>(buf: &mut BitBuf<S>) -> Self {
unsafe { buf.$read_be_aligned_unchecked() }
}
#[inline(always)]
unsafe fn read_be_at_unchecked<S: Storage>(buf: &BitBuf<S>, offset: usize) -> Self {
unsafe { buf.$read_be_at_unchecked(offset) }
}
#[inline(always)]
unsafe fn read_be_unchecked<S: Storage>(buf: &mut BitBuf<S>) -> Self {
unsafe { buf.$read_be_unchecked() }
}
#[inline(always)]
fn try_read_be_aligned_at<S: Storage>(buf: &BitBuf<S>, byte_offset: usize) -> Result<Self> {
buf.$try_read_be_aligned_at(byte_offset)
}
#[inline(always)]
fn try_read_be_aligned<S: Storage>(buf: &mut BitBuf<S>) -> Result<Self> {
buf.$try_read_be_aligned()
}
#[inline(always)]
fn try_read_be_at<S: Storage>(buf: &BitBuf<S>, offset: usize) -> Result<Self> {
buf.$try_read_be_at(offset)
}
#[inline(always)]
fn try_read_be<S: Storage>(buf: &mut BitBuf<S>) -> Result<Self> {
buf.$try_read_be()
}
#[inline(always)]
fn read_be_aligned_at<S: Storage>(buf: &BitBuf<S>, byte_offset: usize) -> Self {
buf.$read_be_aligned_at(byte_offset)
}
#[inline(always)]
fn read_be_aligned<S: Storage>(buf: &mut BitBuf<S>) -> Self {
buf.$read_be_aligned()
}
#[inline(always)]
fn read_be_at<S: Storage>(buf: &BitBuf<S>, offset: usize) -> Self {
buf.$read_be_at(offset)
}
#[inline(always)]
fn read_be<S: Storage>(buf: &mut BitBuf<S>) -> Self {
buf.$read_be()
}
#[inline(always)]
unsafe fn read_be_aligned_bits_at_unchecked<S: Storage>(buf: &BitBuf<S>, byte_offset: usize, bits: usize) -> Self {
unsafe { buf.$read_be_aligned_bits_at_unchecked(byte_offset, bits) }
}
#[inline(always)]
unsafe fn read_be_aligned_bits_unchecked<S: Storage>(buf: &mut BitBuf<S>, bits: usize) -> Self {
unsafe { buf.$read_be_aligned_bits_unchecked(bits) }
}
#[inline(always)]
fn try_read_be_aligned_bits_at<S: Storage>(buf: &BitBuf<S>, byte_offset: usize, bits: usize) -> Result<Self> {
buf.$try_read_be_aligned_bits_at(byte_offset, bits)
}
#[inline(always)]
fn try_read_be_aligned_bits<S: Storage>(buf: &mut BitBuf<S>, bits: usize) -> Result<Self> {
buf.$try_read_be_aligned_bits(bits)
}
#[inline(always)]
fn read_be_aligned_bits_at<S: Storage>(buf: &BitBuf<S>, byte_offset: usize, bits: usize) -> Self {
buf.$read_be_aligned_bits_at(byte_offset, bits)
}
#[inline(always)]
fn read_be_aligned_bits<S: Storage>(buf: &mut BitBuf<S>, bits: usize) -> Self {
buf.$read_be_aligned_bits(bits)
}
}
};
}
use impl_read;