pub trait Layout {
fn size_bytes(&self) -> usize;
#[inline(always)]
fn size_bits(&self) -> usize {
self.size_bytes() * 8
}
#[inline]
fn split_off_checked<'a>(&self, buf: &'a [u8]) -> Option<(&'a [u8], &'a [u8])> {
buf.split_at_checked(self.size_bytes())
}
}
#[derive(Clone, Copy, Debug, thiserror::Error, PartialEq, Eq, Hash)]
pub enum LayoutParseError {
#[error("Unsupported version")]
UnsupportedVersion,
#[error("Buffer too small at {at}: required {required}, actual {actual}")]
BufferTooSmall {
at: &'static str,
required: usize,
actual: usize,
},
#[error("Invalid header length: advertised {advertised}, actual {actual}")]
InvalidHeaderLength {
advertised: usize,
actual: usize,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct BitRange {
pub start: usize,
pub end: usize,
}
impl BitRange {
#[inline]
pub const fn new(start: usize, width: usize) -> Self {
Self {
start,
end: start + width,
}
}
#[inline]
pub const fn max_uint(&self) -> usize {
(1 << self.size_bits()) - 1
}
#[inline]
pub const fn from_range(range: std::ops::Range<usize>) -> Self {
Self {
start: range.start,
end: range.end,
}
}
#[inline]
pub const fn bit_range(&self) -> std::ops::Range<usize> {
self.start..self.end
}
#[inline]
pub const fn contains(&self, bit: usize) -> bool {
bit >= self.start && bit < self.end
}
#[inline]
pub const fn aligned_byte_range(&self) -> std::ops::Range<usize> {
debug_assert!(
self.start.is_multiple_of(8),
"Start bit is not byte-aligned"
);
debug_assert!(self.end.is_multiple_of(8), "End bit is not byte-aligned");
self.start / 8..self.end.div_ceil(8)
}
pub const fn containing_byte_range(&self) -> std::ops::Range<usize> {
let start_byte = self.start / 8; let end_byte = self.end.div_ceil(8); start_byte..end_byte
}
#[inline]
pub const fn size_bytes(&self) -> usize {
let range = self.containing_byte_range();
range.end - range.start
}
#[inline]
pub const fn size_bits(&self) -> usize {
debug_assert!(self.end >= self.start, "BitRange end must be >= start");
self.end - self.start
}
#[inline]
pub const fn shift(mut self, bytes: usize) -> Self {
self.start += bytes * 8;
self.end += bytes * 8;
self
}
#[inline]
pub fn shift_bits(&self, offset: usize) -> Self {
debug_assert!(self.start + offset >= self.start);
debug_assert!(self.end + offset >= self.end);
BitRange {
start: self.start + offset,
end: self.end + offset,
}
}
}
pub mod macros {
macro_rules! gen_bitrange_const {
($range_name:ident, $start:expr, $offset:expr) => {
pub const $range_name: crate::core::layout::BitRange =
crate::core::layout::BitRange::new($start, $offset);
};
}
pub(crate) use gen_bitrange_const;
}