commit 053ecf1fdf2b9cb47c8f7b06894c9a397ec2b645 Author: Michael Pfaff Date: Sun Aug 21 10:40:39 2022 -0400 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4fffb2f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/target +/Cargo.lock diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..a465bad --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "order" +version = "0.1.0" +edition = "2021" +publish = false + +[features] +nightly = [] + +[dependencies] diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..fe1a5f7 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,167 @@ +//! Provides traits for types that can be represented as both big and little-endian byte arrays. +//! Types that have a "canonical" byte array representation are not appropriate implementors of +//! [`ByteOrdered`]. + +#![feature(generic_const_exprs)] + +mod private { + pub trait Sealed {} +} + +/// Order of bits or bytes. +pub trait Order { + fn from_bytes(bytes: [u8; T::BYTES]) -> T; + fn to_bytes(value: T) -> [u8; T::BYTES]; +} + +#[derive(Clone, Copy)] +pub struct Big; + +#[derive(Clone, Copy)] +pub struct Little; + +impl Order for Big { + #[inline(always)] + fn from_bytes(bytes: [u8; T::BYTES]) -> T { + T::from_be_bytes(bytes) + } + + #[inline(always)] + fn to_bytes(value: T) -> [u8; T::BYTES] { + value.to_be_bytes() + } +} + +impl Order for Little { + #[inline(always)] + fn from_bytes(bytes: [u8; T::BYTES]) -> T { + T::from_le_bytes(bytes) + } + + #[inline(always)] + fn to_bytes(value: T) -> [u8; T::BYTES] { + value.to_le_bytes() + } +} + +pub trait ByteOrdered: Copy { + /// Length of the type in bytes. + const BYTES: usize; + + #[inline(always)] + fn to_bytes(self) -> [u8; Self::BYTES] { + O::to_bytes(self) + } + + #[inline(always)] + fn from_bytes(bytes: [u8; Self::BYTES]) -> Self { + O::from_bytes(bytes) + } + + fn to_be_bytes(self) -> [u8; Self::BYTES]; + + fn from_be_bytes(bytes: [u8; Self::BYTES]) -> Self; + + fn to_le_bytes(self) -> [u8; Self::BYTES]; + + fn from_le_bytes(bytes: [u8; Self::BYTES]) -> Self; +} + +// TODO: file a bug report "Use-sites of this trait as a bound are required to also duplicate the +// where clause" +pub trait ByteOrderedA: ByteOrdered +where + [(); ::BYTES]:, +{ +} + +impl ByteOrderedA for T +where + T: ByteOrdered, + [(); T::BYTES]:, +{ +} + +macro_rules! num_impl { + ($ty:ty) => { + impl ByteOrdered for $ty { + const BYTES: usize = std::mem::size_of::<$ty>(); + + #[inline(always)] + fn to_be_bytes(self) -> [u8; Self::BYTES] { + <$ty>::to_be_bytes(self) + } + + #[inline(always)] + fn from_be_bytes(bytes: [u8; Self::BYTES]) -> Self { + <$ty>::from_be_bytes(bytes) + } + + #[inline(always)] + fn to_le_bytes(self) -> [u8; Self::BYTES] { + <$ty>::to_le_bytes(self) + } + + #[inline(always)] + fn from_le_bytes(bytes: [u8; Self::BYTES]) -> Self { + <$ty>::from_le_bytes(bytes) + } + } + }; +} + +num_impl!(u8); +num_impl!(u16); +num_impl!(u32); +num_impl!(u64); +num_impl!(u128); + +num_impl!(i8); +num_impl!(i16); +num_impl!(i32); +num_impl!(i64); +num_impl!(i128); + +num_impl!(f32); +num_impl!(f64); + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn to_be_bytes() { + const CASES: &[(u32, [u8; 4])] = &[ + (0x420, [0x00, 0x00, 0x04, 0x20]), + (0x69, [0x00, 0x00, 0x00, 0x69]), + (u32::MAX, [0xFF, 0xFF, 0xFF, 0xFF]), + (32, [0, 0, 0, 32]), + ]; + + for (n, bytes) in CASES { + assert_eq!(n.to_be_bytes(), *bytes); + assert_eq!(n.to_bytes::(), *bytes); + assert_eq!(Big::to_bytes(*n), *bytes); + + assert_eq!(u32::from_be_bytes(*bytes), *n); + } + } + + #[test] + fn to_le_bytes() { + const CASES: &[(u32, [u8; 4])] = &[ + (0x420, [0x20, 0x04, 0x00, 0x00]), + (0x69, [0x69, 0x00, 0x00, 0x00]), + (u32::MAX, [0xFF, 0xFF, 0xFF, 0xFF]), + (32, [32, 0, 0, 0]), + ]; + + for (n, bytes) in CASES { + assert_eq!(n.to_le_bytes(), *bytes); + assert_eq!(n.to_bytes::(), *bytes); + assert_eq!(Little::to_bytes(*n), *bytes); + + assert_eq!(u32::from_le_bytes(*bytes), *n); + } + } +}