#![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 endian::FixedEndian;
pub use endian::{BigEndian, Endian, LittleEndian};
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 fn from_bytes(bytes: [u8; ($ty::BITS as usize) / 8]) -> Self {
Self(bytes, ::core::marker::PhantomData)
}
}
$crate::format_struct!(@impl_conv $name<E>);
impl $name<Endian> {
#[doc = concat!(
"Constructs a [`", stringify!($name), "`] wrapper type from a `", stringify!($ty),
"` value using the specified endianness."
)]
#[inline]
pub 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 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 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 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)
}
}
unsafe impl<E> FromByteSlice for $name<E> {
fn from_byte_slice(s: &[u8]) -> ::core::result::Result<&Self, $crate::InvalidSizeError> {
let s: &[u8; ($ty::BITS as usize) / 8] = ::core::convert::TryInto::try_into(s).map_err(|_| InvalidSizeError)?;
Ok(unsafe { &*(s.as_ptr() as *const Self) })
}
fn from_byte_slice_mut(s: &mut [u8]) -> ::core::result::Result<&mut Self, $crate::InvalidSizeError> {
let s: &mut [u8; ($ty::BITS as usize) / 8] = ::core::convert::TryInto::try_into(s).map_err(|_| InvalidSizeError)?;
Ok(unsafe { &mut *(s.as_mut_ptr() as *mut Self) })
}
fn slice_from_byte_slice(s: &[u8]) -> ::core::result::Result<&[Self], $crate::InvalidSizeError> {
if s.is_empty() {
return ::core::result::Result::Ok(&[])
} else if s.len() % ::core::mem::size_of::<Self>() != 0 {
return ::core::result::Result::Err($crate::InvalidSizeError);
}
let size = s.len() / ::core::mem::size_of::<Self>();
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::InvalidSizeError> {
if s.is_empty() {
return ::core::result::Result::Ok(&mut [])
} else if s.len() % ::core::mem::size_of::<Self>() != 0 {
return ::core::result::Result::Err($crate::InvalidSizeError);
}
let size = s.len() / ::core::mem::size_of::<Self>();
let ptr = s.as_mut_ptr() as *mut Self;
::core::result::Result::Ok(unsafe { ::core::slice::from_raw_parts_mut(ptr, size) })
}
}
};
}
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);
#[derive(Copy, Clone, Debug)]
pub struct InvalidSizeError;
impl core::fmt::Display for InvalidSizeError {
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 InvalidSizeError {}
pub unsafe trait FromByteSlice: Sized {
fn from_byte_slice(s: &[u8]) -> Result<&Self, InvalidSizeError>;
fn from_byte_slice_mut(s: &mut [u8]) -> Result<&mut Self, InvalidSizeError>;
fn slice_from_byte_slice(s: &[u8]) -> Result<&[Self], InvalidSizeError>;
fn slice_from_byte_slice_mut(s: &mut [u8]) -> Result<&mut [Self], InvalidSizeError>;
}
#[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 fn from_bytes(bytes: [u8; ::core::mem::size_of::<Self>()]) -> Self {
unsafe { ::core::mem::transmute(bytes) }
}
}
$crate::format_struct!(@impl_conv $name);
impl AsRef<[u8]> for $name {
fn as_ref(&self) -> &[u8] {
let ptr = self as *const Self as *const u8;
unsafe { ::core::slice::from_raw_parts(ptr, ::core::mem::size_of::<Self>()) }
}
}
impl AsMut<[u8]> for $name {
fn as_mut(&mut self) -> &mut [u8] {
let ptr = self as *mut Self as *mut u8;
unsafe { ::core::slice::from_raw_parts_mut(ptr, ::core::mem::size_of::<Self>()) }
}
}
unsafe impl $crate::FromByteSlice for $name {
fn from_byte_slice(s: &[u8]) -> ::core::result::Result<&Self, $crate::InvalidSizeError> {
let bytes: &[u8; ::core::mem::size_of::<Self>()] = ::core::convert::TryInto::try_into(s).map_err(|_| $crate::InvalidSizeError)?;
Ok(unsafe { ::core::mem::transmute(bytes) })
}
fn from_byte_slice_mut(s: &mut [u8]) -> ::core::result::Result<&mut Self, $crate::InvalidSizeError> {
let bytes: &mut [u8; ::core::mem::size_of::<Self>()] = ::core::convert::TryInto::try_into(s).map_err(|_| $crate::InvalidSizeError)?;
Ok(unsafe { ::core::mem::transmute(bytes) })
}
fn slice_from_byte_slice(s: &[u8]) -> ::core::result::Result<&[Self], $crate::InvalidSizeError> {
if s.is_empty() {
return Ok(&[]);
} else if s.len() % ::core::mem::size_of::<Self>() != 0 {
return ::core::result::Result::Err($crate::InvalidSizeError);
}
let size = s.len() / ::core::mem::size_of::<Self>();
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::InvalidSizeError> {
if s.is_empty() {
return Ok(&mut []);
} else if s.len() % ::core::mem::size_of::<Self>() != 0 {
return ::core::result::Result::Err($crate::InvalidSizeError);
}
let size = s.len() / ::core::mem::size_of::<Self>();
let ptr = s.as_mut_ptr() as *mut Self;
::core::result::Result::Ok(unsafe { ::core::slice::from_raw_parts_mut(ptr, size) })
}
}
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>)?) => {
impl$(<$gen>)? $name$(<$gen>)? {
#[doc = concat!(
"Converts an immutable slice of [`", stringify!($name),
"`] into an immutable byte slice."
)]
pub fn slice_as_byte_slice(slice: &[Self]) -> &[u8] {
if slice.is_empty() {
&[]
} else {
let data = slice.as_ptr() as *const u8;
let len = (slice.len() as isize)
.checked_mul(::core::mem::size_of::<Self>() as isize)
.unwrap() as usize;
unsafe { ::core::slice::from_raw_parts(data, len) }
}
}
#[doc = concat!(
"Converts a mutable slice of [`", stringify!($name), "`] into a mutable byte slice."
)]
pub fn slice_as_byte_slice_mut(slice: &mut [Self]) -> &mut [u8] {
if slice.is_empty() {
&mut []
} else {
let data = slice.as_mut_ptr() as *mut u8;
let len = (slice.len() as isize)
.checked_mul(::core::mem::size_of::<Self>() as isize)
.unwrap() as usize;
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;
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()
);
}
}