raad crate for eating and pushing bytes
Diffstat (limited to 'src/lib.rs')
| -rw-r--r-- | src/lib.rs | 341 |
1 files changed, 341 insertions, 0 deletions
diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..1a8375b --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,341 @@ +#![doc = include_str!("../README.md")] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] +#![warn( + clippy::undocumented_unsafe_blocks, + clippy::missing_const_for_fn, + clippy::missing_safety_doc, + clippy::suboptimal_flops, + unsafe_op_in_unsafe_fn, + clippy::dbg_macro, + clippy::use_self, + missing_docs +)] +#![allow( + private_bounds, + clippy::zero_prefixed_literal, + mixed_script_confusables, + confusable_idents +)] +use std::mem::ManuallyDrop as MD; +/// # Safety +/// +/// this is a transmute. what do you want me to say. the types should, yknow, work? idk. +const unsafe fn transmute_unchecked<T, U>(value: T) -> U { + // SAFETY: transmutation + unsafe { + #[repr(C)] + union Transmute<T, U> { + t: MD<T>, + u: MD<U>, + } + MD::into_inner(Transmute { t: MD::new(value) }.u) + } +} + +macro_rules! trt { + (r $(#[doc = $x: expr])+ f $(#[doc = $z: expr])+ w $(#[doc = $y: expr])+) => { + use std::io::prelude::*; + use std::io::Result; + use std::mem::MaybeUninit as MU; + + $(#[doc = $x])+ + pub trait R: Read { + $(#[doc = $z])+ + fn r<T: Readable>(&mut self) -> Result<T>; + /// Reads one byte out of a [`Reader`](Read). + /// ``` + /// use raad::ne::*; + /// let mut d = &mut &[1u8, 2][..]; + /// assert_eq!(d.b().unwrap(), 1); + /// assert_eq!(d.b().unwrap(), 2); + /// assert!(d.b().is_err()); + /// ``` + fn b(&mut self) -> Result<u8> { + self.r::<u8>() + } + } + + #[doc(hidden)] + impl<D: Read> R for D { + fn r<T: Readable>(&mut self) -> Result<T> { + T::r(self) + } + } + trait Readable + where + Self: Sized, + { + fn r(from: &mut impl Read) -> Result<Self>; + } + + impl<const N: usize> Readable for [u8; N] { + fn r(from: &mut impl Read) -> Result<[u8; N]> { + let mut buf = [0; N]; + from.read_exact(&mut buf).map(|()| buf) + } + } + + $(#[doc = $y])+ + pub trait W: Write { + /// Writes a type to a [`Writer`](Write) + fn w<T: Writable>(&mut self, data: T) -> Result<()>; + } + + #[doc(hidden)] + impl<D: Write> W for D { + fn w<T: Writable>(&mut self, data: T) -> Result<()> { + data._w(self) + } + } + trait Writable { + fn _w(self, to: &mut impl Write) -> Result<()>; + } + + impl Writable for &[u8] { + fn _w(self, to: &mut impl Write) -> Result<()> { + to.write_all(self) + } + } + }; +} + +macro_rules! n { + (writes $bytes:ident $($n:ident)+) => { + $( + impl Writable for &[$n] { + fn _w(self, to: &mut impl Write) -> Result<()> { + if (cfg!(target_endian = "little") && stringify!($bytes) == "le") || (cfg!(target_endian = "big") && stringify!($bytes) == "be") { + // SAFETY: len correct + to.w(unsafe { std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * ($n::BITS / 8) as usize) }) + } else { + self.iter().try_for_each(|x| to.w(x)) + } + } + } + impl<const N: usize> Readable for [$n; N] { + fn r(from: &mut impl Read) -> Result<[$n; N]> { + if (cfg!(target_endian = "little") && stringify!($bytes) == "le") || (cfg!(target_endian = "big") && stringify!($bytes) == "be") { + let mut buf = [0; N]; + // SAFETY: len matches + let mut u8s = unsafe { std::slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut u8, N * ($n::BITS / 8) as usize) }; + from.read_exact(&mut u8s).map(|()| buf) + } else { + let mut buf = [MU::<$n>::uninit(); N]; + for elem in &mut buf{ + elem.write(from.r::<$n>()?); + } + // SAFETY: array init + Ok(unsafe { crate::transmute_unchecked(buf) }) + } + } + } + )+ + }; + (float $bytes:ident $([$n:ident <=> $int:ident])+) => { + $( + impl Readable for $n { + fn r(from: &mut impl Read) -> Result<$n> { + from.r::<$int>().map($n::from_bits) + } + } + )+ + + $( + impl Writable for $n { + fn _w(self, to: &mut impl Write) -> Result<()> { + to.w(self.to_bits()) + } + } + impl Writable for &$n { + fn _w(self, to: &mut impl Write) -> Result<()> { + to.w(self.to_bits()) + } + } + macro_rules! bytes { + ($t:ty) => { + impl Writable for $t { + fn _w(self, to: &mut impl Write) -> Result<()> { + to.w(&*self) + } + } + } + } + bytes![Vec<$n>]; + bytes![Box<[$n]>]; + impl<const N: usize> Writable for [$n; N] { + fn _w(self, to: &mut impl Write) -> Result<()> { + to.w(&self[..]) + } + } + + impl Writable for &[$n] { + fn _w(self, to: &mut impl Write) -> Result<()> { + if (cfg!(target_endian = "little") && stringify!($bytes) == "le") || (cfg!(target_endian = "big") && stringify!($bytes) == "be") { + // SAFETY: len correct + to.w(unsafe { std::slice::from_raw_parts(self.as_ptr() as *const u8, self.len() * ($int::BITS / 8) as usize) }) + } else { + self.iter().try_for_each(|x| to.w(x)) + } + } + } + impl<const N: usize> Readable for [$n; N] { + fn r(from: &mut impl Read) -> Result<[$n; N]> { + if (cfg!(target_endian = "little") && stringify!($bytes) == "le") || (cfg!(target_endian = "big") && stringify!($bytes) == "be") { + let mut buf = [0.; N]; + // SAFETY: len matches + let mut u8s = unsafe { std::slice::from_raw_parts_mut(buf.as_mut_ptr() as *mut u8, N * ($int::BITS / 8) as usize) }; + from.read_exact(&mut u8s).map(|()| buf) + } else { + let mut buf = [MU::<$n>::uninit(); N]; + for elem in &mut buf{ + elem.write(from.r::<$n>()?); + } + // SAFETY: array init + Ok(unsafe { crate::transmute_unchecked(buf) }) + } + } + } + )+ + }; + ($bytes:ident $($n:ident)+) => { + $( + impl Readable for $n { + fn r(from: &mut impl Read) -> Result<$n> { + from.r::<[u8; { std::mem::size_of::<$n>() }]>().map($n::from_ne_bytes).map($n::$bytes) + } + } + )+ + + $( + impl Writable for $n { + fn _w(self, to: &mut impl Write) -> Result<()> { + to.w(self.$bytes().to_ne_bytes()) + } + } + impl Writable for &$n { + fn _w(self, to: &mut impl Write) -> Result<()> { + to.w(self.$bytes().to_ne_bytes()) + } + } + macro_rules! bytes { + ($t:ty) => { + impl Writable for $t { + fn _w(self, to: &mut impl Write) -> Result<()> { + to.w(&*self) + } + } + } + } + bytes![Vec<$n>]; + bytes![Box<[$n]>]; + impl<const N: usize> Writable for [$n; N] { + fn _w(self, to: &mut impl Write) -> Result<()> { + to.w(&self[..]) + } + } + )+ + }; +} + +macro_rules! test { + () => { + #[test] + fn x() { + let data = &mut &[0x12u8, 0x15][..]; + let mut out = vec![]; + out.w(data.r::<[u16; 1]>().unwrap()).unwrap(); + assert_eq!(out, [0x12, 0x15]); + + let mut out = vec![]; + out.w([12.0_f32, 13.]).unwrap(); + assert_eq!((&mut &*out).r::<[f32; 2]>().unwrap(), [12., 13.]); + } + }; +} +pub mod le { + //! little endian readers and writers + trt!( + r /// Read little endian (commonly native) data. + /// This trait provides a [`r`](R::r) method for easy reading. + /// + /// Without this crate, you would have to do things such as: + /// ``` + /// use std::io::Read; + /// let mut data = &mut &[0xff, 0xf1][..]; + /// let mut two_bytes = [0; 2]; + /// data.read(&mut two_bytes).unwrap(); + /// assert_eq!(u16::from_le_bytes(two_bytes), 0xf1ff) + /// ``` + /// Now, you can simply: + /// ``` + /// use raad::le::*; + /// let mut data = &mut &[0xff, 0xf1][..]; + /// assert_eq!(data.r::<u16>().unwrap(), 0xf1ff); + /// ``` + f /// Read a little endian type. + /// ``` + /// # #![allow(overflowing_literals)] + /// use raad::le::*; + /// let mut data = &mut &[0xc1, 0x00, 0x7c, 0xff][..]; + /// assert_eq!(data.r::<[i16; 2]>().unwrap(), [0x00c1, 0xff7c]); + /// ``` + w /// Write little endian (commonly native) data. + /// ``` + /// # use raad::le::*; + /// let mut wtr = Vec::new(); + /// wtr.w::<[u32; 2]>([267, 1205419366]).unwrap(); + /// assert_eq!(wtr, [11, 1, 0, 0, 102, 61, 217, 71]); + /// ``` + ); + n![writes le u16 u32 u64 u128 i8 i16 i32 i64 i128]; + n![float le [f32 <=> u32] [f64 <=> u64]]; + n![to_le u8 u16 u32 u64 u128 i8 i16 i32 i64 i128]; + test![]; +} + +#[doc(alias = "network")] +pub mod be { + //! big endian readers and writers + trt!( + r /// Read big endian (network) data. + /// ``` + /// use raad::be::*; + /// // this example doesnt actually care about endianness-- u8's dont have any. + /// let mut data = &mut &[2u8, 5, 1, 4, 3][..]; + /// assert_eq!(data.r::<[u8; 5]>().unwrap(), [2, 5, 1, 4, 3]); + /// ``` + f /// Read a big endian (network) type. + /// ``` + /// use raad::be::*; + /// let mut data: &mut &[u8] = &mut &[ + /// 0x00, 0x03, 0x43, 0x95, 0x4d, 0x60, 0x86, 0x83, + /// 0x00, 0x03, 0x43, 0x95, 0x4d, 0x60, 0x86, 0x83 + /// ][..]; + /// assert_eq!( + /// data.r::<u128>().unwrap(), + /// 16947640962301618749969007319746179 + /// ); + /// ``` + w /// Write a big endian (network) type. + /// ``` + /// use raad::be::*; + /// let mut wtr = Vec::new(); + /// wtr.w::<[u16; 2]>([517, 768]).unwrap(); + /// assert_eq!(wtr, b"\x02\x05\x03\x00"); + /// ``` + ); + n![writes be u16 u32 u64 u128 i8 i16 i32 i64 i128]; + n![float be [f32 <=> u32] [f64 <=> u64]]; + n![to_be u8 u16 u32 u64 u128 i8 i16 i32 i64 i128]; + test![]; +} + +pub mod ne { + //! native endian readers and writers + #[cfg(target_endian = "big")] + #[doc(inline)] + pub use super::be::{R, W}; + #[cfg(target_endian = "little")] + #[doc(inline)] + pub use super::le::{R, W}; +} |