#![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(value: T) -> U { // SAFETY: transmutation unsafe { #[repr(C)] union Transmute { t: MD, u: MD, } 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(&mut self) -> Result; /// 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 { self.r::() } } #[doc(hidden)] impl R for D { fn r(&mut self) -> Result { T::r(self) } } #[diagnostic::on_unimplemented( message = "this type is not suitable for reading into", note = "read to [u8; N] first and then parse it", label = "unreadable type" )] trait Readable where Self: Sized, { fn r(from: &mut impl Read) -> Result; } impl 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) } } impl Writable for &[u8; N] { fn _w(self, to: &mut impl Write) -> Result<()> { to.w(&self[..]) } } $(#[doc = $y])+ pub trait W: Write { /// Writes a type to a [`Writer`](Write) fn w(&mut self, data: T) -> Result<()>; } #[doc(hidden)] impl W for D { fn w(&mut self, data: T) -> Result<()> { data._w(self) } } #[diagnostic::on_unimplemented( message = "this type is not suitable for writing", note = "turn it into a &[u8] first and then write that", label = "unwritable type", )] 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; 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() * ($n::BITS / 8) as usize) }) } else { self.iter().try_for_each(|x| to.w(x)) } } } impl 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 Writable for [$n; N] { fn _w(self, to: &mut impl Write) -> Result<()> { to.w(&self[..]) } } impl 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 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 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::().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::().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}; }