use core::ptr;
use crate::{BitBuf, Error, Result, StorageMut, ensure};
impl_write! {
u8;
bytes = 1;
bits = 8;
offset = 0;
[a];
[a, b];
write_u8_be_aligned_at_unchecked;
write_u8_be_aligned_unchecked;
write_u8_be_at_unchecked;
write_u8_be_unchecked;
try_write_u8_be_aligned_at;
try_write_u8_be_aligned;
try_write_u8_be_at;
try_write_u8_be;
write_u8_be_aligned_at;
write_u8_be_aligned;
write_u8_be_at;
write_u8_be;
}
impl_write! {
u16;
bytes = 2;
bits = 16;
offset = 1;
[a, b];
[a, [b], c];
write_u16_be_aligned_at_unchecked;
write_u16_be_aligned_unchecked;
write_u16_be_at_unchecked;
write_u16_be_unchecked;
try_write_u16_be_aligned_at;
try_write_u16_be_aligned;
try_write_u16_be_at;
try_write_u16_be;
write_u16_be_aligned_at;
write_u16_be_aligned;
write_u16_be_at;
write_u16_be;
}
impl_write! {
u32;
bytes = 4;
bits = 32;
offset = 3;
[a, b, c, d];
[a, [b, c, d], e];
write_u32_be_aligned_at_unchecked;
write_u32_be_aligned_unchecked;
write_u32_be_at_unchecked;
write_u32_be_unchecked;
try_write_u32_be_aligned_at;
try_write_u32_be_aligned;
try_write_u32_be_at;
try_write_u32_be;
write_u32_be_aligned_at;
write_u32_be_aligned;
write_u32_be_at;
write_u32_be;
}
impl_write! {
u64;
bytes = 8;
bits = 64;
offset = 7;
[a, b, c, d, e, f, g, h];
[a, [b, c, d, e, f, g, h], i];
write_u64_be_aligned_at_unchecked;
write_u64_be_aligned_unchecked;
write_u64_be_at_unchecked;
write_u64_be_unchecked;
try_write_u64_be_aligned_at;
try_write_u64_be_aligned;
try_write_u64_be_at;
try_write_u64_be;
write_u64_be_aligned_at;
write_u64_be_aligned;
write_u64_be_at;
write_u64_be;
}
impl_write! {
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];
write_u128_be_aligned_at_unchecked;
write_u128_be_aligned_unchecked;
write_u128_be_at_unchecked;
write_u128_be_unchecked;
try_write_u128_be_aligned_at;
try_write_u128_be_aligned;
try_write_u128_be_at;
try_write_u128_be;
write_u128_be_aligned_at;
write_u128_be_aligned;
write_u128_be_at;
write_u128_be;
}
pub trait Write: Sized {
unsafe fn write_be_aligned_at_unchecked<S: StorageMut>(
buf: &mut BitBuf<S>,
byte_offset: usize,
v: Self,
) -> &mut BitBuf<S>;
unsafe fn write_be_aligned_unchecked<S: StorageMut>(
buf: &mut BitBuf<S>,
v: Self,
) -> &mut BitBuf<S>;
unsafe fn write_be_at_unchecked<S: StorageMut>(
buf: &mut BitBuf<S>,
offset: usize,
v: Self,
) -> &mut BitBuf<S>;
unsafe fn write_be_unchecked<S: StorageMut>(buf: &mut BitBuf<S>, v: Self) -> &mut BitBuf<S>;
fn try_write_be_aligned_at<S: StorageMut>(
buf: &mut BitBuf<S>,
byte_offset: usize,
v: Self,
) -> Result<&mut BitBuf<S>>;
fn try_write_be_aligned<S: StorageMut>(buf: &mut BitBuf<S>, v: Self) -> Result<&mut BitBuf<S>>;
fn try_write_be_at<S: StorageMut>(
buf: &mut BitBuf<S>,
offset: usize,
v: Self,
) -> Result<&mut BitBuf<S>>;
fn try_write_be<S: StorageMut>(buf: &mut BitBuf<S>, v: Self) -> Result<&mut BitBuf<S>>;
fn write_be_aligned_at<S: StorageMut>(
buf: &mut BitBuf<S>,
offset: usize,
v: Self,
) -> &mut BitBuf<S>;
fn write_be_aligned<S: StorageMut>(buf: &mut BitBuf<S>, v: Self) -> &mut BitBuf<S>;
fn write_be_at<S: StorageMut>(buf: &mut BitBuf<S>, offset: usize, v: Self) -> &mut BitBuf<S>;
fn write_be<S: StorageMut>(buf: &mut BitBuf<S>, v: Self) -> &mut BitBuf<S>;
}
impl<S: StorageMut> BitBuf<S> {
#[inline(always)]
pub unsafe fn write_be_aligned_at_unchecked<T: Write>(
&mut self,
byte_offset: usize,
v: T,
) -> &mut Self {
unsafe { T::write_be_aligned_at_unchecked(self, byte_offset, v) }
}
#[inline(always)]
pub unsafe fn write_be_aligned_unchecked<T: Write>(&mut self, v: T) -> &mut Self {
unsafe { T::write_be_aligned_unchecked(self, v) }
}
#[inline(always)]
pub unsafe fn write_be_at_unchecked<T: Write>(&mut self, offset: usize, v: T) -> &mut Self {
unsafe { T::write_be_at_unchecked(self, offset, v) }
}
#[inline(always)]
pub unsafe fn write_be_unchecked<T: Write>(&mut self, v: T) -> &mut Self {
unsafe { T::write_be_unchecked(self, v) }
}
#[inline(always)]
pub fn try_write_be_aligned_at<T: Write>(
&mut self,
byte_offset: usize,
v: T,
) -> Result<&mut Self> {
T::try_write_be_aligned_at(self, byte_offset, v)
}
#[inline(always)]
pub fn try_write_be_aligned<T: Write>(&mut self, v: T) -> Result<&mut Self> {
T::try_write_be_aligned(self, v)
}
#[inline(always)]
pub fn try_write_be_at<T: Write>(&mut self, offset: usize, v: T) -> Result<&mut Self> {
T::try_write_be_at(self, offset, v)
}
#[inline(always)]
pub fn try_write_be<T: Write>(&mut self, v: T) -> Result<&mut Self> {
T::try_write_be(self, v)
}
#[inline(always)]
pub fn write_be_aligned_at<T: Write>(&mut self, byte_offset: usize, v: T) -> &mut Self {
T::write_be_aligned_at(self, byte_offset, v)
}
#[inline(always)]
pub fn write_be_aligned<T: Write>(&mut self, v: T) -> &mut Self {
T::write_be_aligned(self, v)
}
#[inline(always)]
pub fn write_be_at<T: Write>(&mut self, offset: usize, v: T) -> &mut Self {
T::write_be_at(self, offset, v)
}
#[inline(always)]
pub fn write_be<T: Write>(&mut self, v: T) -> &mut Self {
T::write_be(self, v)
}
}
macro_rules! impl_write {
(
$ty:ident;
bytes = $bytes:literal;
bits = $bits:literal;
offset = $offset:literal;
[$($byte:ident),*];
[$first:ident, $([$($middle:ident),*],)? $last:ident];
$write_be_aligned_at_unchecked:ident;
$write_be_aligned_unchecked:ident;
$write_be_at_unchecked:ident;
$write_be_unchecked:ident;
$try_write_be_aligned_at:ident;
$try_write_be_aligned:ident;
$try_write_be_at:ident;
$try_write_be:ident;
$write_be_aligned_at:ident;
$write_be_aligned:ident;
$write_be_at:ident;
$write_be:ident;
) => {
impl<S: StorageMut> BitBuf<S> {
#[doc = concat!("Write a [`", stringify!($ty), "`] in [BE-bit, BE-byte] order at `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)]
pub unsafe fn $write_be_aligned_at_unchecked(&mut self, byte_offset: usize, v: $ty) -> &mut Self {
let bytes = self.bytes_mut();
let len = bytes.len();
ensure!(
byte_offset + $offset < len;
concat!(
"BitBuf::",
stringify!($write_be_aligned_at_unchecked),
": index out of bounds! len is {}, offset is {}"
),
len,
byte_offset + $offset,
);
let src_bytes = v.to_be_bytes();
#[allow(clippy::macro_metavars_in_unsafe)]
unsafe {
ptr::copy_nonoverlapping(
src_bytes.as_ptr(),
bytes.as_mut_ptr().add(byte_offset),
$bytes,
);
}
self
}
#[doc = concat!("Write 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)]
pub unsafe fn $write_be_aligned_unchecked(&mut self, v: $ty) -> &mut Self {
ensure!(
self.is_aligned();
concat!(
"BitBuf::",
stringify!($write_be_aligned_unchecked),
" called at unaligned bit position: {}",
),
self.pos(),
);
unsafe { self.$write_be_aligned_at_unchecked(self.byte_pos(), v) };
self.advance_bytes($bytes);
self
}
#[doc = concat!("Write a [`", stringify!($ty), "`] in [BE-bit, BE-byte] order at `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)]
pub unsafe fn $write_be_at_unchecked(&mut self, offset: usize, v: $ty) -> &mut Self {
let byte_idx = offset / 8;
let shift = offset % 8;
let bytes = self.bytes_mut();
let len = bytes.len();
ensure!(
byte_idx + $offset < len;
concat!(
"BitBuf::",
stringify!($write_be_at_unchecked),
": index out of bounds! len is {}, byte_idx is {}",
),
len,
byte_idx + $offset,
);
if shift == 0 {
unsafe { self.$write_be_aligned_at_unchecked(byte_idx, v) };
} else {
ensure!(
byte_idx + $bytes < len;
concat!(
"BitBuf::",
stringify!($write_be_at_unchecked),
": lookahead index out of bounds! len is {}, lookahead byte_idx is {}",
),
len,
byte_idx + $bytes,
);
let src_bytes = v.to_be_bytes();
unsafe {
let mask = !0 << (8 - shift);
let b = bytes.get_unchecked(byte_idx) & mask;
let new = src_bytes[0] >> shift;
*bytes.get_unchecked_mut(byte_idx) = b | new;
}
$(
let mut i = 0;
#[expect(unused)]
unsafe {
$(
let $middle: ();
let hi = src_bytes[i];
let lo = src_bytes[i + 1];
*bytes.get_unchecked_mut(byte_idx + 1 + i) = hi << (8 - shift) | lo >> shift;
i += 1;
)*
}
)?
#[allow(clippy::macro_metavars_in_unsafe)]
unsafe {
let mask = !0 >> shift;
let b = bytes.get_unchecked(byte_idx + $bytes) & mask;
let new = src_bytes[$offset] << (8 - shift);
*bytes.get_unchecked_mut(byte_idx + $bytes) = b | new;
}
}
self
}
#[doc = concat!("Write 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)]
pub unsafe fn $write_be_unchecked(&mut self, v: $ty) -> &mut Self {
unsafe { self.$write_be_at_unchecked(self.pos(), v) };
self.advance_bytes($bytes);
self
}
#[doc = concat!("Write a [`", stringify!($ty), "`] in [BE-bit, BE-byte] order at `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_write_be_aligned_at(&mut self, byte_offset: usize, v: $ty) -> Result<&mut Self> {
let len = self.bytes().len();
if byte_offset + $offset >= len {
return Err(Error::OutOfBounds);
}
Ok(unsafe { self.$write_be_aligned_at_unchecked(byte_offset, v) })
}
#[doc = concat!("Write 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_write_be_aligned(&mut self, v: $ty) -> Result<&mut Self> {
if !self.is_aligned() {
return Err(Error::Unaligned);
}
self.$try_write_be_aligned_at(self.byte_pos(), v)?;
self.advance_bytes($bytes);
Ok(self)
}
#[doc = concat!("Write a [`", stringify!($ty), "`] in [BE-bit, BE-byte] order at `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_write_be_at(&mut self, offset: usize, v: $ty) -> Result<&mut Self> {
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.$write_be_at_unchecked(offset, v) })
}
#[doc = concat!("Write 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_write_be(&mut self, v: $ty) -> Result<&mut Self> {
self.$try_write_be_at(self.pos(), v)?;
self.advance_bytes($bytes);
Ok(self)
}
#[doc = concat!("Write a [`", stringify!($ty), "`] in [BE-bit, BE-byte] order at `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)]
pub fn $write_be_aligned_at(&mut self, byte_offset: usize, v: $ty) -> &mut Self {
self
.$try_write_be_aligned_at(byte_offset, v)
.expect(concat!("BitBuf::", stringify!($write_be_aligned_at), " out of bounds"))
}
#[doc = concat!("Write 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)]
pub fn $write_be_aligned(&mut self, v: $ty) -> &mut Self {
self
.$try_write_be_aligned(v)
.expect(concat!("BitBuf::", stringify!($write_be_aligned), " unaligned cursor or out of bounds"))
}
#[doc = concat!("Write a [`", stringify!($ty), "`] in [BE-bit, BE-byte] order at `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)]
pub fn $write_be_at(&mut self, offset: usize, v: $ty) -> &mut Self {
self
.$try_write_be_at(offset, v)
.expect(concat!("BitBuf::", stringify!($write_be_at), " out of bounds"))
}
#[doc = concat!("Write the next [`", stringify!($ty), "`] in [BE-bit, BE-byte] order panicking on out of bounds")]
///
/// # 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)]
pub fn $write_be(&mut self, v: $ty) -> &mut Self {
self
.$try_write_be(v)
.expect(concat!("BitBuf::", stringify!($write_be), " out of bounds"))
}
}
impl Write for $ty {
#[inline(always)]
unsafe fn write_be_aligned_at_unchecked<S: StorageMut>(
buf: &mut BitBuf<S>,
byte_offset: usize,
v: Self,
) -> &mut BitBuf<S> {
unsafe { buf.$write_be_aligned_at_unchecked(byte_offset, v) }
}
#[inline(always)]
unsafe fn write_be_aligned_unchecked<S: StorageMut>(
buf: &mut BitBuf<S>,
v: Self,
) -> &mut BitBuf<S> {
unsafe { buf.$write_be_aligned_unchecked(v) }
}
#[inline(always)]
unsafe fn write_be_at_unchecked<S: StorageMut>(
buf: &mut BitBuf<S>,
offset: usize,
v: Self,
) -> &mut BitBuf<S> {
unsafe { buf.$write_be_at_unchecked(offset, v) }
}
#[inline(always)]
unsafe fn write_be_unchecked<S: StorageMut>(buf: &mut BitBuf<S>, v: Self) -> &mut BitBuf<S> {
unsafe { buf.$write_be_unchecked(v) }
}
#[inline(always)]
fn try_write_be_aligned_at<S: StorageMut>(
buf: &mut BitBuf<S>,
byte_offset: usize,
v: Self,
) -> Result<&mut BitBuf<S>> {
buf.$try_write_be_aligned_at(byte_offset, v)
}
#[inline(always)]
fn try_write_be_aligned<S: StorageMut>(buf: &mut BitBuf<S>, v: Self) -> Result<&mut BitBuf<S>> {
buf.$try_write_be_aligned(v)
}
#[inline(always)]
fn try_write_be_at<S: StorageMut>(
buf: &mut BitBuf<S>,
offset: usize,
v: Self,
) -> Result<&mut BitBuf<S>> {
buf.$try_write_be_at(offset, v)
}
#[inline(always)]
fn try_write_be<S: StorageMut>(buf: &mut BitBuf<S>, v: Self) -> Result<&mut BitBuf<S>> {
buf.$try_write_be(v)
}
#[inline(always)]
fn write_be_aligned_at<S: StorageMut>(buf: &mut BitBuf<S>, byte_offset: usize, v: Self) -> &mut BitBuf<S> {
buf.$write_be_aligned_at(byte_offset, v)
}
#[inline(always)]
fn write_be_aligned<S: StorageMut>(buf: &mut BitBuf<S>, v: Self) -> &mut BitBuf<S> {
buf.$write_be_aligned(v)
}
#[inline(always)]
fn write_be_at<S: StorageMut>(buf: &mut BitBuf<S>, offset: usize, v: Self) -> &mut BitBuf<S> {
buf.$write_be_at(offset, v)
}
#[inline(always)]
fn write_be<S: StorageMut>(buf: &mut BitBuf<S>, v: Self) -> &mut BitBuf<S> {
buf.$write_be(v)
}
}
};
}
use impl_write;