Skip to content

Instantly share code, notes, and snippets.

@s-zeid
Last active August 16, 2020 04:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save s-zeid/9934adb499873d382613f3eb1c87e7ca to your computer and use it in GitHub Desktop.
Save s-zeid/9934adb499873d382613f3eb1c87e7ca to your computer and use it in GitHub Desktop.
Rust macros for reading types from binary streams
// Imports {{{1
use std::io;
extern crate byteorder; // 1
use byteorder::{/*ReadBytesExt,*/ LittleEndian};
extern crate num_traits; // 0.2
use num_traits::ToPrimitive;
// Traits {{{1
pub trait Countable {
type COUNT: ReadFrom + ToPrimitive + Clone + std::fmt::Debug;
}
// TODO: <https://github.com/rust-lang/rust/issues/63063>
macro_rules! ReadFromBuffer(
() => { (impl byteorder::ReadBytesExt + std::io::Seek) };
);
pub trait ReadFrom: std::marker::Sized + std::fmt::Debug {
fn read_from_impl(buffer: &mut ReadFromBuffer!()) -> std::io::Result<Self>;
fn read_from(buffer: &mut ReadFromBuffer!()) -> std::io::Result<Self> {
return Self::read_from_impl(buffer);
}
fn read_from_offset(
buffer: &mut ReadFromBuffer!(),
offset: u64,
) -> std::io::Result<Self> {
let pos = match buffer.seek(io::SeekFrom::Current(0)) {
Ok(value) => value,
Err(err) => return Err(err),
};
match buffer.seek(io::SeekFrom::Start(offset)) {
Ok(value) => value,
Err(err) => return Err(err),
};
let result = Self::read_from(buffer)?;
match buffer.seek(io::SeekFrom::Start(pos)) {
Ok(value) => value,
Err(err) => return Err(err),
};
return Ok(result);
}
}
trait ReadNFrom: std::marker::Sized {
fn read_n_from(n: usize, buffer: &mut ReadFromBuffer!()) -> std::io::Result<Self>;
fn read_n_from_offset(
n: usize,
buffer: &mut ReadFromBuffer!(),
offset: u64,
) -> io::Result<Self> {
let pos = match buffer.seek(io::SeekFrom::Current(0)) {
Ok(value) => value,
Err(err) => return Err(err),
};
match buffer.seek(io::SeekFrom::Start(offset)) {
Ok(value) => value,
Err(err) => return Err(err),
};
let result = Self::read_n_from(n, buffer)?;
match buffer.seek(io::SeekFrom::Start(pos)) {
Ok(value) => value,
Err(err) => return Err(err),
};
return Ok(result);
}
}
impl<T: ReadFrom> ReadNFrom for Vec<T> {
fn read_n_from(n: usize, buffer: &mut ReadFromBuffer!()) -> std::io::Result<Self> {
let mut result = Vec::<T>::with_capacity(n);
for _ in 0usize..n {
result.push(T::read_from(buffer)?);
}
return Ok(result);
}
}
#[macro_export] macro_rules! TYPE ( //{{{1
(
$vis:vis $name:ident = $value:tt;
fn read_from($buffer:ident) { $($read_from_body:tt)* }
$($extra_items:tt)*
) => {
#[allow(non_camel_case_types)]
$vis type $name = $value;
impl $crate::ReadFrom for $name {
#[inline]
fn read_from_impl($buffer: &mut ReadFromBuffer!()) -> std::io::Result<Self> {
$($read_from_body)*
}
}
$($extra_items)*
};
(
$vis:vis $name:ident $(<$($params:tt$(:$bound_1:ident$(+$bound_n:ident)*)?),*>)?,
/* optional */ COUNT = $count_type:tt;
$kind:ident { $($body:tt)* }
fn read_from($buffer:ident) { $($read_from_body:tt)* }
$($extra_items:tt)*
) => {
$crate::TYPE!($vis $name $(<$($params$(:$bound_1$(+$bound_n)*)?),*>)?;
$kind { $($body)* }
fn read_from($buffer) { $($read_from_body)* }
impl $(<$($params$(:$bound_1$(+$bound_n)*)?),*>)? $crate::Countable
for $name $(<$($params),*>)? {
type COUNT = $count_type;
}
$($extra_items)*
);
};
(
$vis:vis $name:ident $(<$($params:tt$(:$bound_1:ident$(+$bound_n:ident)*)?),*>)?;
$kind:ident { $($body:tt)* }
fn read_from($buffer:ident) { $($read_from_body:tt)* }
$($extra_items:tt)*
) => {
#[allow(non_camel_case_types)]
#[derive(Debug)]
$vis $kind $name $(<$($params$(:$bound_1$(+$bound_n)*)?),*>)? {
$($body)*
}
impl $(<$($params$(:$bound_1$(+$bound_n)*)?),*>)? $crate::ReadFrom
for $name $(<$($params),*>)? {
fn read_from_impl($buffer: &mut ReadFromBuffer!()) -> std::io::Result<Self> {
$($read_from_body)*
}
}
$($extra_items)*
};
);
// Primitives {{{1
TYPE!(pub IBYTE = i8;
fn read_from(buffer) {
return buffer.read_i8();
}
);
TYPE!(pub BYTE = u8;
fn read_from(buffer) {
return buffer.read_u8();
}
);
TYPE!(pub SHORT = i16;
fn read_from(buffer) {
return buffer.read_i16::<LittleEndian>();
}
);
pub type WCHAR = USHORT;
TYPE!(pub USHORT = u16;
fn read_from(buffer) {
return buffer.read_u16::<LittleEndian>();
}
);
TYPE!(pub LONG = i32;
fn read_from(buffer) {
return buffer.read_i32::<LittleEndian>();
}
);
TYPE!(pub ULONG = u32;
fn read_from(buffer) {
return buffer.read_u32::<LittleEndian>();
}
);
// Collections {{{1
pub type DATA = Vec<BYTE>;
TYPE!(pub LIST<T: Countable + ReadFrom>;
struct {
pub length: T::COUNT,
pub items: Vec<T>,
}
fn read_from(buffer) {
let length = T::COUNT::read_from(buffer)?;
let length_usize = length.to_usize().unwrap();
let mut items = Vec::with_capacity(length_usize);
for _ in 0usize..(length_usize) {
items.push(T::read_from(buffer)?);
}
return Ok(Self {
length: length,
items: items,
});
}
);
// Test {{{1
TYPE!(pub SPAM<T: ReadFrom>, COUNT = ULONG;
struct {
a: i128,
b: T,
}
fn read_from(buffer) {
return Ok(Self { a: BYTE::read_from(buffer)? as i128, b: T::read_from(buffer)? });
}
);
TYPE!(pub EGG;
enum {
BACON,
SAUSAGE,
INVALID(BYTE),
}
fn read_from(buffer) {
let byte = BYTE::read_from(buffer)?;
return Ok(match byte {
1 => Self::BACON,
2 => Self::SAUSAGE,
n => Self::INVALID(n),
});
}
);
TYPE!(pub TEST;
struct {
spams: LIST<SPAM<SHORT>>,
eggs: Vec<EGG>,
}
fn read_from(buffer) {
return Ok(Self {
spams: LIST::<SPAM<SHORT>>::read_from(buffer)?,
eggs: Vec::<EGG>::read_n_from(3, buffer)?,
});
}
);
fn main() {
let mut buffer = std::io::Cursor::new(vec![
2u8, 0u8, 0u8, 0u8, // LIST<SPAM>, length = 2
4u8, 192u8, 255u8, // SPAM { a: 4, b: -64 }
8u8, 128u8, 255u8, // SPAM { a: 8, b: -128 }
1u8, // EGG::BACON
2u8, // EGG::SAUSAGE
4u8, // EGG::INVALID(4)
]);
println!("spams.items[0].a: {:?}", BYTE::read_from_offset(&mut buffer, 4));
println!("eggs: {:?}", Vec::<EGG>::read_n_from_offset(3, &mut buffer, 10));
let test = TEST::read_from(&mut buffer);
println!("{:#?}", test);
println!("{:x}", test.unwrap().spams.items[0].b);
println!(
"SPAM::COUNT : {:x}..{:x}",
<SPAM::<IBYTE> as Countable>::COUNT::MIN,
<SPAM::<IBYTE> as Countable>::COUNT::MAX,
);
let a;
let x = SPAM {
a: { a = 1i128; a },
b: match a {
1 => a as BYTE + 1,
_ => 0,
},
};
println!("{:?}", x);
let mut buffer = std::io::Cursor::new(vec![1,2,3]);
let x: DATA = DATA::read_n_from(3, &mut buffer).unwrap();
println!("{:?}", x);
}
// vim: set fdm=marker: {{{1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment