#[derive(Default, Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum EndianType {
#[default]
NativeEndian,
LittleEndian,
BigEndian,
}
impl core::fmt::Display for EndianType {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::NativeEndian => write!(f, "native"),
Self::LittleEndian => write!(f, "little-endian"),
Self::BigEndian => write!(f, "big-endian"),
}
}
}
impl EndianType {
pub const fn is_le(&self) -> bool {
#[cfg(target_endian = "little")]
{
matches!(self, Self::LittleEndian | Self::NativeEndian)
}
#[cfg(target_endian = "big")]
{
matches!(self, Self::LittleEndian)
}
}
pub fn read_u16(&self, bytes: [u8; 2]) -> u16 {
match self {
EndianType::NativeEndian => u16::from_ne_bytes(bytes),
EndianType::LittleEndian => u16::from_le_bytes(bytes),
EndianType::BigEndian => u16::from_be_bytes(bytes),
}
}
pub fn read_u32(&self, bytes: [u8; 4]) -> u32 {
match self {
EndianType::NativeEndian => u32::from_ne_bytes(bytes),
EndianType::LittleEndian => u32::from_le_bytes(bytes),
EndianType::BigEndian => u32::from_be_bytes(bytes),
}
}
pub fn read_u64(&self, bytes: [u8; 8]) -> u64 {
match self {
EndianType::NativeEndian => u64::from_ne_bytes(bytes),
EndianType::LittleEndian => u64::from_le_bytes(bytes),
EndianType::BigEndian => u64::from_be_bytes(bytes),
}
}
pub fn u16_bytes(&self, value: u16) -> [u8; 2] {
match self {
EndianType::NativeEndian => value.to_ne_bytes(),
EndianType::LittleEndian => value.to_le_bytes(),
EndianType::BigEndian => value.to_be_bytes(),
}
}
pub fn u32_bytes(&self, value: u32) -> [u8; 4] {
match self {
EndianType::NativeEndian => value.to_ne_bytes(),
EndianType::LittleEndian => value.to_le_bytes(),
EndianType::BigEndian => value.to_be_bytes(),
}
}
pub fn u64_bytes(&self, value: u64) -> [u8; 8] {
match self {
EndianType::NativeEndian => value.to_ne_bytes(),
EndianType::LittleEndian => value.to_le_bytes(),
EndianType::BigEndian => value.to_be_bytes(),
}
}
}
pub trait Endianness: Copy + Sized {
fn get() -> EndianType;
fn get_u16(bytes: [u8; 2]) -> u16;
fn set_u16(value: u16, bytes: &mut [u8; 2]);
fn get_u32(bytes: [u8; 4]) -> u32;
fn set_u32(value: u32, bytes: &mut [u8; 4]);
fn get_u64(bytes: [u8; 8]) -> u64;
fn set_u64(value: u64, bytes: &mut [u8; 8]);
fn get_u24(bytes: [u8; 3]) -> u32 {
let mut buf = [0u8; 4];
if Self::get().is_le() {
buf[..3].copy_from_slice(&bytes);
} else {
buf[1..].copy_from_slice(&bytes);
}
Self::get_u32(buf)
}
fn set_u24(value: u32, bytes: &mut [u8; 3]) {
let mut buf = [0u8; 4];
Self::set_u32(value, &mut buf);
if Self::get().is_le() {
bytes.copy_from_slice(&buf[..3]);
} else {
bytes.copy_from_slice(&buf[1..]);
}
}
}
#[repr(transparent)]
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Zeroable, bytemuck::Pod))]
pub struct NativeEndian;
#[repr(transparent)]
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Zeroable, bytemuck::Pod))]
pub struct LittleEndian;
#[repr(transparent)]
#[derive(Debug, Copy, Clone)]
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Zeroable, bytemuck::Pod))]
pub struct BigEndian;
impl Endianness for NativeEndian {
#[inline]
fn get() -> EndianType {
EndianType::NativeEndian
}
#[inline]
fn get_u16(bytes: [u8; 2]) -> u16 {
u16::from_ne_bytes(bytes)
}
#[inline]
fn set_u16(value: u16, bytes: &mut [u8; 2]) {
bytes.copy_from_slice(&value.to_ne_bytes());
}
#[inline]
fn get_u32(bytes: [u8; 4]) -> u32 {
u32::from_ne_bytes(bytes)
}
#[inline]
fn set_u32(value: u32, bytes: &mut [u8; 4]) {
bytes.copy_from_slice(&value.to_ne_bytes());
}
#[inline]
fn get_u64(bytes: [u8; 8]) -> u64 {
u64::from_ne_bytes(bytes)
}
#[inline]
fn set_u64(value: u64, bytes: &mut [u8; 8]) {
bytes.copy_from_slice(&value.to_ne_bytes());
}
}
impl Endianness for LittleEndian {
#[inline]
fn get() -> EndianType {
EndianType::LittleEndian
}
#[inline]
fn get_u16(bytes: [u8; 2]) -> u16 {
u16::from_le_bytes(bytes)
}
#[inline]
fn set_u16(value: u16, bytes: &mut [u8; 2]) {
bytes.copy_from_slice(&value.to_le_bytes());
}
#[inline]
fn get_u32(bytes: [u8; 4]) -> u32 {
u32::from_le_bytes(bytes)
}
#[inline]
fn set_u32(value: u32, bytes: &mut [u8; 4]) {
bytes.copy_from_slice(&value.to_le_bytes());
}
#[inline]
fn get_u64(bytes: [u8; 8]) -> u64 {
u64::from_le_bytes(bytes)
}
#[inline]
fn set_u64(value: u64, bytes: &mut [u8; 8]) {
bytes.copy_from_slice(&value.to_le_bytes());
}
}
impl Endianness for BigEndian {
#[inline]
fn get() -> EndianType {
EndianType::BigEndian
}
#[inline]
fn get_u16(bytes: [u8; 2]) -> u16 {
u16::from_be_bytes(bytes)
}
#[inline]
fn set_u16(value: u16, bytes: &mut [u8; 2]) {
bytes.copy_from_slice(&value.to_be_bytes());
}
#[inline]
fn get_u32(bytes: [u8; 4]) -> u32 {
u32::from_be_bytes(bytes)
}
#[inline]
fn set_u32(value: u32, bytes: &mut [u8; 4]) {
bytes.copy_from_slice(&value.to_be_bytes());
}
#[inline]
fn get_u64(bytes: [u8; 8]) -> u64 {
u64::from_be_bytes(bytes)
}
#[inline]
fn set_u64(value: u64, bytes: &mut [u8; 8]) {
bytes.copy_from_slice(&value.to_be_bytes());
}
}
#[cfg(feature = "bytemuck")]
pub trait MaybePod: bytemuck::Pod + bytemuck::Zeroable {}
#[cfg(feature = "bytemuck")]
impl<T: bytemuck::Pod + bytemuck::Zeroable> MaybePod for T {}
#[cfg(not(feature = "bytemuck"))]
pub trait MaybePod {}
#[cfg(not(feature = "bytemuck"))]
impl<T> MaybePod for T {}
pub trait Endian {
type Output: MaybePod;
type LsbType: MaybePod + Endian<Output = Self::Output>;
type MsbType: MaybePod + Endian<Output = Self::Output>;
fn new(value: Self::Output) -> Self;
fn get(&self) -> Self::Output;
fn set(&mut self, value: Self::Output);
}
#[cfg(all(test, feature = "std"))]
mod tests {
#[test]
fn test_from_le_bytes() {
let value = u16::from_le_bytes([0x12, 0x34]);
assert_eq!(value, 0x3412);
let value = u32::from_le_bytes([0x12, 0x34, 0x56, 0x78]);
assert_eq!(value, 0x78563412);
let value = u64::from_le_bytes([0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0]);
assert_eq!(value, 0xf0debc9a78563412);
}
#[test]
fn test_from_be_bytes() {
let value = u16::from_be_bytes([0x12, 0x34]);
assert_eq!(value, 0x1234);
let value = u32::from_be_bytes([0x12, 0x34, 0x56, 0x78]);
assert_eq!(value, 0x12345678);
let value = u64::from_be_bytes([0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0]);
assert_eq!(value, 0x123456789abcdef0);
}
}