Rust macros for reading types from binary streams
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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