raad crate for eating and pushing bytes
-rw-r--r--.gitignore1
-rw-r--r--Cargo.toml16
-rw-r--r--LICENSE21
-rw-r--r--README.md50
-rw-r--r--src/lib.rs341
5 files changed, 429 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ffa3bbd
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+Cargo.lock \ No newline at end of file
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..4081473
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,16 @@
+[package]
+name = "raad"
+version = "0.1.0"
+authors = ["bend-n <[email protected]>"]
+license = "MIT"
+edition = "2021"
+description = "raad library for reading and writing bytes"
+repository = "https://github.com/bend-n/raad"
+exclude = ["tdata", "benches/", ".gitignore"]
+categories = ["encoding", "parsing"]
+rust-version = "1.79"
+keywords = ["data", "read", "write", "io"]
+
+[package.metadata.docs.rs]
+all-features = true
+rustdoc-args = ["--cfg", "docsrs"]
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..2f002a4
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2024 bendn
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..7723183
--- /dev/null
+++ b/README.md
@@ -0,0 +1,50 @@
+# rad crate for r/w
+
+This crate provides neat ways to eat bytes out of your favorite [readers](https://doc.rust-lang.org/std/io/trait.Read.html) and push bytes into cute [writers](https://doc.rust-lang.org/std/io/trait.Write.html).
+
+This crate has three modules, one for each kind of [endianness](https://en.wikipedia.org/wiki/Endianness): [`be`](https://docs.rs/raad/latest/raad/be/index.html) (big endian), [`le`](https://docs.rs/raad/latest/raad/le/index.html) (little endian), and [`ne`](https://docs.rs/raad/latest/raad/ne/index.html) (native endian-- whatever your system is on)
+
+# Examples
+
+Read unsigned 16 bit big-endian integers from a [`Reader`](https://doc.rust-lang.org/std/io/trait.Read.html):
+
+```rust
+use raad::be::*; // < note how we specify we want big endian when we import the trait
+let mut rdr = &mut &[02, 05, 03, 00][..];
+assert_eq!([0x0205, 0x0300], rdr.r::<[u16; 2]>().unwrap());
+```
+
+Write unsigned 16 bit little-endian integers to a [`Writer`](https://doc.rust-lang.org/std/io/trait.Write.html):
+
+```rust
+use raad::le::*; // and here we specify little endian
+let mut wtr = vec![];
+wtr.w([0x0205u16, 0x0300]).unwrap();
+assert_eq!(wtr, vec![05, 02, 00, 03]);
+```
+
+# Why
+
+These helpers can greatly increase the ease of reading numbers and other things from a file/…
+
+See, to read 3 u64s from a reader, you would have to go through all this trouble:
+
+```rust
+use std::io::Read;
+fn read3(t: &mut impl Read) -> std::io::Result<[u64; 3]> {
+ let mut out = [0; 3];
+ let mut tmp = [0; 8];
+ for elem in &mut out {
+ t.read_exact(&mut tmp)?;
+ *elem = u64::from_ne_bytes(tmp);
+ }
+ Ok(out)
+}
+```
+
+wheras, with this crate, its as simple as
+
+```rust,ignore
+use raad::ne::*;
+t.read::<[u64; 3]>();
+```
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};
+}