use core::{
marker::PhantomData,
fmt,
};
pub trait PduData: Sized {
const ID: TypeId;
type Packed: Storage;
fn pack(&self, dst: &mut [u8]) -> PackingResult<()>;
fn unpack(src: &[u8]) -> PackingResult<Self>;
fn packed_size() -> usize {Self::Packed::LEN}
fn packed_bitsize() -> usize {Self::Packed::LEN*8}
fn packed(&self) -> PackingResult<Self::Packed> {
let mut buffer = Self::Packed::uninit();
self.pack(buffer.as_mut())?;
Ok(buffer)
}
}
#[derive(Copy, Clone, Debug)]
pub enum PackingError {
BadSize(usize, &'static str),
BadAlignment(usize, &'static str),
InvalidValue(&'static str),
}
pub type PackingResult<T> = Result<T, PackingError>;
pub trait Storage: AsRef<[u8]> + AsMut<[u8]>
{
const LEN: usize;
fn uninit() -> Self;
}
impl<const N: usize> Storage for [u8; N] {
const LEN: usize = N;
fn uninit() -> Self {unsafe {core::mem::uninitialized()}}
}
#[derive(Copy, Clone, Debug)]
pub enum TypeId {
CUSTOM,
VOID, BOOL,
I8, I16, I32, I64,
U8, U16, U32, U64,
F32, F64,
}
impl<const N: usize> PduData for [u8; N] {
const ID: TypeId = TypeId::CUSTOM;
type Packed = Self;
fn pack(&self, dst: &mut [u8]) -> PackingResult<()> {
dst.copy_from_slice(self);
Ok(())
}
fn unpack(src: &[u8]) -> PackingResult<Self> {
if src.len() < Self::Packed::LEN
{return Err(PackingError::BadSize(src.len(), "not enough bytes for desired slice"))}
Ok(Self::try_from(&src[.. Self::Packed::LEN]).unwrap().clone())
}
}
impl PduData for () {
const ID: TypeId = TypeId::VOID;
type Packed = [u8; 0];
fn pack(&self, _dst: &mut [u8]) -> PackingResult<()> {Ok(())}
fn unpack(_src: &[u8]) -> PackingResult<Self> {Ok(())}
}
impl PduData for bool {
const ID: TypeId = TypeId::BOOL;
type Packed = [u8; 1];
fn pack(&self, dst: &mut [u8]) -> PackingResult<()> {
if dst.len() < Self::Packed::LEN
{return Err(PackingError::BadSize(dst.len(), ""))}
dst[0] = if *self {0b1} else {0b0};
Ok(())
}
fn unpack(src: &[u8]) -> PackingResult<Self> {
if src.len() < Self::Packed::LEN
{return Err(PackingError::BadSize(src.len(), ""))}
Ok(src[0] & 0b1 == 0b1)
}
}
#[macro_export]
macro_rules! bilge_pdudata {
($t: ty, $id: ident) => { impl $crate::data::PduData for $t {
const ID: $crate::data::TypeId = $crate::data::TypeId::CUSTOM;
type Packed = [u8; ($id::BITS as usize + 7)/8];
fn pack(&self, dst: &mut [u8]) -> $crate::data::PackingResult<()> {
use $crate::data::Storage;
if dst.len() < Self::Packed::LEN
{return Err($crate::data::PackingError::BadSize(dst.len(), "bilge struct needs exact size"))}
let common = Self::Packed::LEN.min(core::mem::size_of::<Self>());
let src = unsafe{ core::mem::transmute::<&Self, &Self::Packed>(self) };
dst[.. common].copy_from_slice(&src[.. common]);
dst[common .. Self::Packed::LEN].fill(0);
Ok(())
}
fn unpack(src: &[u8]) -> $crate::data::PackingResult<Self> {
use $crate::data::Storage;
if src.len() < Self::Packed::LEN
{return Err($crate::data::PackingError::BadSize(src.len(), "bilge struct needs exact size"))}
let mut tmp = [0; core::mem::size_of::<Self>()];
let common = Self::Packed::LEN.min(core::mem::size_of::<Self>());
tmp[.. common].copy_from_slice(&src[.. common]);
Ok(unsafe{ core::mem::transmute::<[u8; core::mem::size_of::<Self>()], Self>(tmp) })
}
}};
}
pub use bilge_pdudata;
#[macro_export]
macro_rules! packed_pdudata {
($t: ty) => { impl $crate::data::PduData for $t {
const ID: $crate::data::TypeId = $crate::data::TypeId::CUSTOM;
type Packed = [u8; core::mem::size_of::<$t>()];
fn pack(&self, dst: &mut [u8]) -> $crate::data::PackingResult<()> {
use $crate::data::Storage;
if dst.len() < Self::Packed::LEN
{return Err($crate::data::PackingError::BadSize(dst.len(), "not enough bytes for struct"))}
dst[..Self::Packed::LEN].copy_from_slice(&unsafe{ core::mem::transmute_copy::<Self, Self::Packed>(self) });
Ok(())
}
fn unpack(src: &[u8]) -> $crate::data::PackingResult<Self> {
use $crate::data::Storage;
if src.len() < Self::Packed::LEN
{return Err($crate::data::PackingError::BadSize(src.len(), "not enough bytes for struct"))}
let src: &Self::Packed = src[.. Self::Packed::LEN].try_into().unwrap();
Ok(unsafe{ core::mem::transmute::<Self::Packed, Self>(src.clone()) })
}
}};
}
pub use packed_pdudata;
#[macro_export]
macro_rules! array_pdudata {
($t: ty, $n: literal, $($rest: literal),+) => {
packed_pdudata!([$t; $n]);
array_pdudata!($t, $($rest),+);
};
($t: ty, $n: literal) => {
packed_pdudata!([$t; $n]);
};
($t: ty) => {array_pdudata!($t, 0, 1, 2, 3, 4, 5, 6, 7, 8);};
}
pub use array_pdudata;
macro_rules! num_pdudata {
($t: ty, $id: ident) => { impl $crate::data::PduData for $t {
const ID: $crate::data::TypeId = $crate::data::TypeId::$id;
type Packed = [u8; core::mem::size_of::<$t>()];
fn pack(&self, dst: &mut [u8]) -> $crate::data::PackingResult<()> {
dst.copy_from_slice(&self.to_le_bytes());
Ok(())
}
fn unpack(src: &[u8]) -> $crate::data::PackingResult<Self> {
Ok(Self::from_le_bytes(src
.try_into()
.map_err(|_| $crate::data::PackingError::BadSize(src.len(), "integger cannot be yet zeroed"))?
))
}
}};
($t: ty) => { num_pdudata!(t, $crate::data::TypeId::CUSTOM) };
}
num_pdudata!(u8, U8);
num_pdudata!(u16, U16);
num_pdudata!(u32, U32);
num_pdudata!(u64, U64);
num_pdudata!(i8, I8);
num_pdudata!(i16, I16);
num_pdudata!(i32, I32);
num_pdudata!(i64, I64);
num_pdudata!(f32, F32);
num_pdudata!(f64, F64);
array_pdudata!(u16);
array_pdudata!(u32);
array_pdudata!(u64);
array_pdudata!(i8);
array_pdudata!(i16);
array_pdudata!(i32);
array_pdudata!(i64);
array_pdudata!(f32);
array_pdudata!(f64);
pub trait PduField<T> {
fn get(&self, data: &[u8]) -> T;
fn set(&self, data: &mut [u8], value: T);
}
pub type Field<T> = ByteField<T>;
#[derive(Default, Eq, Hash)]
pub struct ByteField<T: PduData> {
extracted: PhantomData<T>,
pub byte: usize,
pub len: usize,
}
impl<T: PduData> ByteField<T>
{
pub const fn new(byte: usize, len: usize) -> Self {
Self{extracted: PhantomData, byte, len}
}
pub const fn simple(byte: usize) -> Self {
Self{extracted: PhantomData, byte, len: T::Packed::LEN}
}
pub const fn downcast(&self) -> ByteField<()> {
ByteField {extracted: PhantomData, byte: self.byte, len: self.len}
}
}
impl<T: PduData> PduField<T> for ByteField<T> {
fn get(&self, data: &[u8]) -> T {
T::unpack(&data[self.byte..][..self.len])
.expect("cannot unpack from data")
}
fn set(&self, data: &mut [u8], value: T) {
value.pack(&mut data[self.byte..][..self.len])
.expect("cannot pack data")
}
}
impl<T: PduData> fmt::Debug for ByteField<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Field<{}>{{0x{:x}, {}}}", core::any::type_name::<T>(), self.byte, self.len)
}
}
impl<T: PduData> Clone for ByteField<T> {
fn clone(&self) -> Self {Self::new(self.byte, self.len)}
}
impl<T: PduData> Copy for ByteField<T> {}
impl<T: PduData> PartialEq for ByteField<T> {
fn eq(&self, other: &Self) -> bool {
self.byte == other.byte && self.len == other.len
}
}
#[derive(Default, Eq, Hash)]
pub struct BitField<T: PduData> {
extracted: PhantomData<T>,
pub bit: usize,
pub len: usize,
}
impl<T: PduData> BitField<T> {
pub const fn new(bit: usize, len: usize) -> Self {
Self{extracted: PhantomData, bit, len}
}
pub const fn downcast(&self) -> BitField<()> {
BitField {extracted: PhantomData, bit: self.bit, len: self.len}
}
}
impl<T: PduData> PduField<T> for BitField<T> {
fn get(&self, data: &[u8]) -> T {
assert!(self.len < 128);
let mut buf = [0u8; 16];
for i in 0 .. self.len {
let bufbyte = i/8;
let bufbit = i%8;
let databyte = (self.bit + i)/8;
let databit = (self.bit + i)%8;
buf[bufbyte] |= ((data[databyte] >> databit) & 1) << bufbit;
}
T::unpack(&buf)
.expect("cannot unpack from intermediate buffer")
}
fn set(&self, data: &mut [u8], value: T) {
assert!(self.len < 128);
let mut buf = [0u8; 16];
value.pack(&mut buf)
.expect("cannot pack to intermediate buffer");
for i in 0 .. self.len {
let bufbyte = i/8;
let bufbit = i%8;
let databyte = (self.bit + i)/8;
let databit = (self.bit + i)%8;
data[databyte] &= !(1 << databit);
data[databyte] |= ((buf[bufbyte] >> bufbit) & 1) << databit;
}
}
}
impl<T: PduData> fmt::Debug for BitField<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "BitField<{}>{{{}, {}}}", core::any::type_name::<T>(), self.bit, self.len)
}
}
impl<T: PduData> Clone for BitField<T> {
fn clone(&self) -> Self {Self::new(self.bit, self.len)}
}
impl<T: PduData> Copy for BitField<T> {}
impl<T: PduData> PartialEq for BitField<T> {
fn eq(&self, other: &Self) -> bool {
self.bit == other.bit && self.len == other.len
}
}
pub struct Cursor<T> {
position: usize,
data: T,
}
impl<T> Cursor<T> {
pub fn new(data: T) -> Self {Self{position: 0, data}}
pub fn position(&self) -> usize {self.position}
}
impl<'a> Cursor<&'a [u8]> {
pub fn unpack<T: PduData>(&mut self) -> PackingResult<T> {
let start = self.position.clone();
self.position += T::Packed::LEN;
T::unpack(&self.data[start .. self.position])
}
pub fn read(&mut self, size: usize) -> PackingResult<&'_ [u8]> {
let start = self.position.clone();
self.position += size;
Ok(&self.data[start .. self.position])
}
pub fn remain(&self) -> &'a [u8] {
&self.data[self.position ..]
}
pub fn finish(self) -> &'a [u8] {
&self.data[.. self.position]
}
}
impl<'a> Cursor<&'a mut [u8]> {
pub fn unpack<T: PduData>(&mut self) -> PackingResult<T> {
let start = self.position.clone();
self.position += T::Packed::LEN;
T::unpack(&self.data[start .. self.position])
}
pub fn read(&'a mut self, size: usize) -> PackingResult<&'_ [u8]> {
let start = self.position.clone();
self.position += size;
Ok(&self.data[start .. self.position])
}
pub fn pack<T: PduData>(&mut self, value: &T) -> PackingResult<()> {
let start = self.position.clone();
self.position += T::Packed::LEN;
value.pack(&mut self.data[start .. self.position])
}
pub fn write(&mut self, value: &[u8]) -> PackingResult<()> {
let start = self.position.clone();
self.position += value.len();
self.data[start .. self.position].copy_from_slice(value);
Ok(())
}
pub fn remain(&mut self) -> &'_ mut [u8] {
&mut self.data[self.position ..]
}
pub fn finish(self) -> &'a mut [u8] {
&mut self.data[.. self.position]
}
}