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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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)
}
}
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;
) => {
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 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` while 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 while performing alignment and 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` while 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 while 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 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()
}
}
};
}
use impl_read;