#![no_std]
#![deny(missing_docs)]
#![deny(missing_debug_implementations)]
#![deny(rust_2018_idioms)]
#![deny(unreachable_pub)]
#[cfg(feature = "std")]
extern crate std;
pub mod endian;
use core::mem::MaybeUninit;
use endian::FixedEndian;
pub use endian::{BigEndian, Endian, LittleEndian};
#[derive(Copy, Clone, Debug)]
pub struct UnalignedSizeError;
impl core::fmt::Display for UnalignedSizeError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("byte slice is not aligned to the structure's size")
}
}
#[cfg(feature = "std")]
impl std::error::Error for UnalignedSizeError {}
#[derive(Copy, Clone, Debug)]
pub struct SliceSizeOverflowError;
impl core::fmt::Display for SliceSizeOverflowError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("multiple of slice count and type size overflows isize")
}
}
#[cfg(feature = "std")]
impl std::error::Error for SliceSizeOverflowError {}
pub const fn safe_count_to_size<T: Sized>(count: usize) -> Result<usize, SliceSizeOverflowError> {
const MAX_SIZE: usize = isize::MAX as usize;
if let Some(size) = core::mem::size_of::<T>().checked_mul(count) {
if size <= MAX_SIZE {
Ok(size)
} else {
Err(SliceSizeOverflowError)
}
} else {
Err(SliceSizeOverflowError)
}
}
pub const fn safe_size_to_count<T: Sized>(size: usize) -> Result<usize, UnalignedSizeError> {
if size % core::mem::size_of::<T>() == 0 {
if let Some(count) = size.checked_div(core::mem::size_of::<T>()) {
Ok(count)
} else {
Err(UnalignedSizeError)
}
} else {
Err(UnalignedSizeError)
}
}
pub unsafe trait ReprByteSlice: Sized {
fn from_byte_slice(s: &[u8]) -> Result<&Self, UnalignedSizeError>;
fn from_byte_slice_mut(s: &mut [u8]) -> Result<&mut Self, UnalignedSizeError>;
fn as_byte_slice(&self) -> &[u8];
fn as_byte_slice_mut(&mut self) -> &mut [u8];
fn slice_from_byte_slice(s: &[u8]) -> Result<&[Self], UnalignedSizeError>;
fn slice_from_byte_slice_mut(s: &mut [u8]) -> Result<&mut [Self], UnalignedSizeError>;
fn slice_as_byte_slice(slice: &[Self]) -> Result<&[u8], SliceSizeOverflowError>;
fn slice_as_byte_slice_mut(s: &mut [Self]) -> Result<&mut [u8], SliceSizeOverflowError>;
fn uninit_slice_from_byte_slice(
bytes: &[MaybeUninit<u8>],
) -> Result<&[MaybeUninit<Self>], UnalignedSizeError>;
fn uninit_slice_from_byte_slice_mut(
bytes: &mut [MaybeUninit<u8>],
) -> Result<&mut [MaybeUninit<Self>], UnalignedSizeError>;
fn uninit_slice_as_byte_slice(
slice: &[MaybeUninit<Self>],
) -> Result<&[MaybeUninit<u8>], SliceSizeOverflowError>;
fn uninit_slice_as_byte_slice_mut(
s: &mut [MaybeUninit<Self>],
) -> Result<&mut [MaybeUninit<u8>], SliceSizeOverflowError>;
}
macro_rules! define_int_wrapper {
($ty:ident, $name:ident) => {
#[doc = concat!(
"A type that wraps a byte array to be decoded into a `", stringify!($ty), "`.\n\n"
)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
#[repr(C)]
pub struct $name<E>([u8; ($ty::BITS as usize) / 8], ::core::marker::PhantomData<E>);
impl<E> $name<E> {
#[doc = concat!("Converts a byte array into a [`", stringify!($name), "`].")]
pub const fn from_bytes(bytes: [u8; ($ty::BITS as usize) / 8]) -> Self {
Self(bytes, ::core::marker::PhantomData)
}
#[doc = concat!("Converts a [`", stringify!($name), "`] into a byte array.")]
pub const fn into_bytes(self) -> [u8; ($ty::BITS as usize) / 8] {
self.0
}
}
$crate::format_struct!(@impl_conv $name<E> size (($ty::BITS as usize) / 8));
impl $name<Endian> {
#[doc = concat!(
"Constructs a [`", stringify!($name), "`] wrapper type from a `", stringify!($ty),
"` value using the specified endianness."
)]
#[inline]
pub const fn new_with_endian(value: $ty, endian: Endian) -> Self {
let bytes = match endian {
Endian::Little => value.to_le_bytes(),
Endian::Big => value.to_be_bytes(),
};
Self(bytes, ::core::marker::PhantomData)
}
#[doc = concat!(
"Extracts a `", stringify!($ty), "` value from a [`", stringify!($name),
"`] wrapper using the specified endianness."
)]
#[inline]
pub const fn get_with_endian(self, endian: Endian) -> $ty {
match endian {
Endian::Little => $ty::from_le_bytes(self.0),
Endian::Big => $ty::from_be_bytes(self.0),
}
}
}
impl<E: FixedEndian> $name<E> {
#[doc = concat!(
"Constructs a [`", stringify!($name), "`] wrapper type from a `", stringify!($ty),
"` value using the type's fixed endianness."
)]
#[inline]
pub const fn new(value: $ty) -> Self {
let bytes = match E::ENDIAN {
Endian::Little => value.to_le_bytes(),
Endian::Big => value.to_be_bytes(),
};
Self(bytes, ::core::marker::PhantomData)
}
#[doc = concat!(
"Extracts a `", stringify!($ty), "` value from a [`", stringify!($name),
"`] wrapper using the type's fixed endianness."
)]
#[inline]
pub const fn get(self) -> $ty {
match E::ENDIAN {
Endian::Little => $ty::from_le_bytes(self.0),
Endian::Big => $ty::from_be_bytes(self.0),
}
}
}
impl<E> ::core::default::Default for $name<E> {
fn default() -> Self {
Self(Default::default(), ::core::marker::PhantomData)
}
}
impl<E: FixedEndian> From<$ty> for $name<E> {
fn from(value: $ty) -> Self {
Self::new(value)
}
}
};
}
define_int_wrapper!(u16, U16);
define_int_wrapper!(i16, I16);
define_int_wrapper!(u32, U32);
define_int_wrapper!(i32, I32);
define_int_wrapper!(u64, U64);
define_int_wrapper!(i64, I64);
define_int_wrapper!(u128, U128);
define_int_wrapper!(i128, I128);
#[macro_export]
macro_rules! format_struct {
($($(#[$m:meta])* $vis:vis struct $endian:tt $name:ident {
$($(#[doc = $field_doc:literal])* $field_vis:vis $field_name:ident: $ty:tt),*,
})+) => {
$(
#[repr(C)]
$(#[$m])*
$vis struct $name {
$($(#[doc = $field_doc])*
$field_vis $field_name: format_struct!(@wrapper_type $ty $endian)),*
}
impl $name {
#[doc = concat!("Converts a byte array into a [`", stringify!($name), "`].")]
pub const fn from_bytes(bytes: [u8; ::core::mem::size_of::<Self>()]) -> Self {
unsafe { ::core::mem::transmute(bytes) }
}
#[doc = concat!("Converts a [`", stringify!($name), "`] into a byte array.")]
pub const fn into_bytes(self) -> [u8; ::core::mem::size_of::<Self>()] {
unsafe { ::core::mem::transmute(self) }
}
}
$crate::format_struct!(@impl_conv $name size ::core::mem::size_of::<$name>());
impl ::core::fmt::Debug for $name {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
f.debug_struct(stringify!($name))
$(.field(stringify!($field_name), &self.$field_name))*
.finish()
}
}
)+
};
(@impl_conv $name:ident$(<$gen:ident>)? size $size_expr:expr) => {
unsafe impl$(<$gen>)? $crate::ReprByteSlice for $name$(<$gen>)? {
fn from_byte_slice(s: &[u8]) -> ::core::result::Result<&Self, $crate::UnalignedSizeError> {
let bytes: &[u8; $size_expr] = ::core::convert::TryInto::try_into(s).map_err(|_| $crate::UnalignedSizeError)?;
let ptr = bytes.as_ptr() as *const Self;
::core::result::Result::Ok(unsafe { &*ptr })
}
fn from_byte_slice_mut(s: &mut [u8]) -> ::core::result::Result<&mut Self, $crate::UnalignedSizeError> {
let bytes: &mut [u8; $size_expr] = ::core::convert::TryInto::try_into(s).map_err(|_| $crate::UnalignedSizeError)?;
let ptr = bytes.as_ptr() as *mut Self;
::core::result::Result::Ok(unsafe { &mut *ptr })
}
fn as_byte_slice(&self) -> &[u8] {
let data = self as *const Self as *const u8;
let len = ::core::mem::size_of::<Self>();
unsafe { ::core::slice::from_raw_parts(data, len) }
}
fn as_byte_slice_mut(&mut self) -> &mut [u8] {
let data = self as *mut Self as *mut u8;
let len = ::core::mem::size_of::<Self>();
unsafe { ::core::slice::from_raw_parts_mut(data, len) }
}
fn slice_from_byte_slice(s: &[u8]) -> ::core::result::Result<&[Self], $crate::UnalignedSizeError> {
if s.is_empty() {
::core::result::Result::Ok(&[])
} else {
let size = $crate::safe_size_to_count::<Self>(s.len())?;
let ptr = s.as_ptr() as *const Self;
::core::result::Result::Ok(unsafe { ::core::slice::from_raw_parts(ptr, size) })
}
}
fn slice_from_byte_slice_mut(s: &mut [u8]) -> ::core::result::Result<&mut [Self], $crate::UnalignedSizeError> {
if s.is_empty() {
::core::result::Result::Ok(&mut [])
} else {
let size = $crate::safe_size_to_count::<Self>(s.len())?;
let ptr = s.as_mut_ptr() as *mut Self;
::core::result::Result::Ok(unsafe { ::core::slice::from_raw_parts_mut(ptr, size) })
}
}
fn slice_as_byte_slice(slice: &[Self]) -> ::core::result::Result<&[u8], $crate::SliceSizeOverflowError> {
let data = slice.as_ptr() as *const u8;
let len = $crate::safe_count_to_size::<Self>(slice.len())?;
::core::result::Result::Ok(unsafe { ::core::slice::from_raw_parts(data, len) })
}
fn slice_as_byte_slice_mut(slice: &mut [Self]) -> ::core::result::Result<&mut [u8], $crate::SliceSizeOverflowError> {
let data = slice.as_ptr() as *mut u8;
let len = $crate::safe_count_to_size::<Self>(slice.len())?;
::core::result::Result::Ok(unsafe { ::core::slice::from_raw_parts_mut(data, len) })
}
fn uninit_slice_from_byte_slice(
s: &[::core::mem::MaybeUninit<u8>]
) -> ::core::result::Result<&[::core::mem::MaybeUninit<Self>], $crate::UnalignedSizeError> {
if s.is_empty() {
::core::result::Result::Ok(&[])
} else {
let size = $crate::safe_size_to_count::<Self>(s.len())?;
let ptr = s.as_ptr() as *const ::core::mem::MaybeUninit<Self>;
::core::result::Result::Ok(unsafe { ::core::slice::from_raw_parts(ptr, size) })
}
}
fn uninit_slice_from_byte_slice_mut(
s: &mut [::core::mem::MaybeUninit<u8>]
) -> ::core::result::Result<&mut [::core::mem::MaybeUninit<Self>], $crate::UnalignedSizeError> {
if s.is_empty() {
::core::result::Result::Ok(&mut [])
} else {
let size = $crate::safe_size_to_count::<Self>(s.len())?;
let ptr = s.as_mut_ptr() as *mut ::core::mem::MaybeUninit<Self>;
::core::result::Result::Ok(unsafe { ::core::slice::from_raw_parts_mut(ptr, size) })
}
}
fn uninit_slice_as_byte_slice(
slice: &[::core::mem::MaybeUninit<Self>]
) -> ::core::result::Result<&[::core::mem::MaybeUninit<u8>], $crate::SliceSizeOverflowError> {
let data = slice.as_ptr() as *const ::core::mem::MaybeUninit<u8>;
let len = ::core::mem::size_of::<Self>().checked_mul(slice.len()).expect("");
::core::result::Result::Ok(unsafe { ::core::slice::from_raw_parts(data, len) })
}
fn uninit_slice_as_byte_slice_mut(
slice: &mut [::core::mem::MaybeUninit<Self>]
) -> ::core::result::Result<&mut [::core::mem::MaybeUninit<u8>], $crate::SliceSizeOverflowError> {
let data = slice.as_ptr() as *mut ::core::mem::MaybeUninit<u8>;
let len = ::core::mem::size_of::<Self>().checked_mul(slice.len()).unwrap();
::core::result::Result::Ok(unsafe { ::core::slice::from_raw_parts_mut(data, len) })
}
}
};
(@endian_type little) => {$crate::LittleEndian};
(@endian_type big) => {$crate::BigEndian};
(@endian_type dynamic) => {$crate::Endian};
(@wrapper_type [$ty:ident; $n:literal] $endian:tt) => {
[$crate::format_struct!(@wrapper_type $ty $endian); $n]
};
(@wrapper_type u8 $endian:tt) => {u8};
(@wrapper_type i8 $endian:tt) => {i8};
(@wrapper_type u16 $endian:tt) => {$crate::U16<$crate::format_struct!(@endian_type $endian)>};
(@wrapper_type i16 $endian:tt) => {$crate::I16<$crate::format_struct!(@endian_type $endian)>};
(@wrapper_type u32 $endian:tt) => {$crate::U32<$crate::format_struct!(@endian_type $endian)>};
(@wrapper_type i32 $endian:tt) => {$crate::I32<$crate::format_struct!(@endian_type $endian)>};
(@wrapper_type u64 $endian:tt) => {$crate::U64<$crate::format_struct!(@endian_type $endian)>};
(@wrapper_type i64 $endian:tt) => {$crate::I64<$crate::format_struct!(@endian_type $endian)>};
(@wrapper_type u128 $endian:tt) => {$crate::U128<$crate::format_struct!(@endian_type $endian)>};
(@wrapper_type i128 $endian:tt) => {$crate::I128<$crate::format_struct!(@endian_type $endian)>};
}
#[cfg(test)]
#[allow(unused, unreachable_pub)]
mod tests {
use super::*;
use core::{marker::PhantomData, mem::MaybeUninit};
format_struct! {
#[derive(Default, Clone)]
struct little TestLe {
#[doc = "this is the third line"]
byte: u8,
short: u16,
word: u32,
dword: u64,
qword: u128,
byte_arr: [u8; 16],
short_arr: [u16; 16],
}
#[derive(Default, Clone)]
struct big TestBe {
pub byte: u8,
short: u16,
word: u32,
dword: u64,
qword: u128,
byte_arr: [u8; 16],
short_arr: [u16; 16],
}
#[derive(Default, Clone)]
struct dynamic TestDyn {
byte: u8,
short: u16,
word: u32,
dword: u64,
qword: u128,
byte_arr: [u8; 16],
short_arr: [u16; 16],
}
}
#[test]
fn test_access_short_arr() {
let mut test_le = TestLe::default();
for (i, s) in test_le.short_arr.iter_mut().enumerate() {
*s = U16((i as u16).to_le_bytes(), PhantomData);
}
assert_eq!(test_le.short_arr[5].get(), 5);
}
#[test]
fn test_access_u8() {
let mut test = TestLe::default();
test.byte = 42;
assert_eq!(test.byte, 42);
}
#[test]
fn test_access_u16() {
let mut test_le = TestLe::default();
test_le.short = U16::new(1337);
assert_eq!(test_le.short.get(), 1337);
assert_eq!(test_le.short.0, 1337u16.to_le_bytes());
let mut test_be = TestBe::default();
test_be.short = U16::new(1337);
assert_eq!(test_be.short.get(), 1337);
assert_eq!(test_be.short.0, 1337u16.to_be_bytes());
}
#[test]
fn test_access_u32() {
let mut test_le = TestLe::default();
test_le.word = U32::new(13371337);
assert_eq!(test_le.word.get(), 13371337);
assert_eq!(test_le.word.0, 13371337u32.to_le_bytes());
let mut test_be = TestBe::default();
test_be.word = U32::new(13371337);
assert_eq!(test_be.word.get(), 13371337);
assert_eq!(test_be.word.0, 13371337u32.to_be_bytes());
}
#[test]
fn test_access_u64() {
let mut test_le = TestLe::default();
test_le.dword = U64::new(1337133713371337);
assert_eq!(test_le.dword.get(), 1337133713371337);
assert_eq!(test_le.dword.0, 1337133713371337u64.to_le_bytes());
let mut test_be = TestBe::default();
test_be.dword = U64::new(1337133713371337);
assert_eq!(test_be.dword.get(), 1337133713371337);
assert_eq!(test_be.dword.0, 1337133713371337u64.to_be_bytes());
}
#[test]
fn test_access_u128() {
let mut test_le = TestLe::default();
test_le.qword = U128::new(13371337133713371337133713371337);
assert_eq!(test_le.qword.get(), 13371337133713371337133713371337u128);
assert_eq!(
test_le.qword.0,
13371337133713371337133713371337u128.to_le_bytes()
);
let mut test_be = TestBe::default();
test_be.qword = U128::new(13371337133713371337133713371337u128);
assert_eq!(test_be.qword.get(), 13371337133713371337133713371337);
assert_eq!(
test_be.qword.0,
13371337133713371337133713371337u128.to_be_bytes()
);
}
#[test]
fn test_uninit() {
let mut test = [
MaybeUninit::<TestLe>::uninit(),
MaybeUninit::<TestLe>::uninit(),
];
TestLe::uninit_slice_as_byte_slice(&test[..]).unwrap()[0];
}
}