11 releases
0.2.0 | Sep 9, 2021 |
---|---|
0.1.10 | Jun 22, 2021 |
#232 in Parser tooling
26 downloads per month
28KB
414 lines
Zordon
zordon
provides simple low-level abstractions for zero-copy parsing and mutation. It is a no_std crate with alloc.
zordon
types allow a single mutable u8 buffer to be treated as
a series of u8-u128, i8-i128 or [u8; _] values without the need to copy data in the original buffer. Setting, getting and
adding to the values is transparent via the methods implemented on the calling type.
The [MutView]
derive macro can be used on a data structure whose fields use zordon
types. Allowing a buffer to be
parsed and manipulated like a typical rust struct.
For usage examples and documentation check out: docs
lib.rs
:
Zordon
About
zordon
provides simple low-level abstractions for zero-copy parsing and mutation. It is a no_std crate with alloc.
zordon
types allow a single mutable u8 buffer to be treated as
a series of u8-u128, i8-i128 or [u8; _] values without the need to copy data in the original buffer. Setting, getting and
adding to the values is transparent via the methods implemented on the calling type.
The [MutView]
derive macro can be used on a data structure whose fields use zordon types. Allowing a buffer to be
parsed and manipulated like a typical rust struct.
Simple example
use zordon::prelude::*;
#[derive(MutView)]
struct Example<'a> {
u8_f: ByteView<'a, u8>,
u16_f: MulByteView<'a, u16, BigEnd>,
arr_f: ArrayView<'a, [u8; 3]>,
}
fn main() {
let mut input_buf = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07];
let (mut example, _) = Example::mut_view(&mut input_buf);
assert_eq!(example.u8_f.val(), 0x00);
assert_eq!(example.u16_f.val(), 0x0102);
assert_eq!(*example.arr_f.as_ref(), [0x03, 0x04, 0x05]);
}
How it works
Deriving mut_view
#[derive(MutView)]
struct Example<'a> {
u8_f: ByteView<'a, u8>,
u16_f: MulByteView<'a, u16, BigEnd>,
arr_f: ArrayView<'a, [u8; 3]>,
}
The derive macro MutView
implements a mut_view
method for the Example
struct.
ByteView<'a, u8>
specifies that the underlying data is a single byte value of type [u8
]MulByteView<'a, u16, LitEnd>
specifies that the underlying data is a little endian two byte value of type [u16
].LitEnd
can be swapped withBigEnd
and the data will be treated as big endian.u16
can be swapped with u32-u128 or i16-i128
ArrayView<'a, [u8; 3]>
specifies that the underlying data is three byte value of type [u8; 3
]
Instantiating the struct
let mut input_buf = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05];
let (mut example, _) = Example::mut_view(&mut input_buf);
mut_view
is called with a &mut [u8]
as input, which it slices into multiple mutable slices of different
lengths based on the field type. Ownership of each slice is transfered to it's respective field.
In this case it will:
- First, split
input_buf
at an index of 1 (since the length of [u8
] is one) -u8_f
now owns this slice - Second, split the remaining slice at an index of 2 (since the length of [
u16
] is two) -u16_f
now owns this slice - Finally, it splits the remaining slice at an index of 3 (since the length of [
u8; 3
] is three) -arr_f
now owns this slice
The return value of mut_view
is (Self, &'mut [u8]) where Self
is the calling type and &'mut [u8]
is the remaining slice.
Setting/Getting values
ByteView
and MulByteView
To retrive/set the underlying value, the val/set trait methods must be called.
- For
ByteView
theModByteView
trait must be in scope. - For
MulByteView
theModMulByteView
trait must be in scope
use zordon::prelude::*;
#[derive(MutView)]
struct Example<'a> {
u8_f: ByteView<'a, u8>,
}
fn main() {
let mut input_buf = [0x00];
let (mut example, _) = Example::mut_view(&mut input_buf);
assert_eq!(example.u8_f.val(), 0x00);
example.u8_f.set(0xFF);
assert_eq!(input_buf[0], 0xFF);
}
ArrayView
Retriving the underlying value for ArrayView
works slightly differently. Rather than returning
the data, a mutable/immutable reference to the data is returned.
#[derive(MutView)]
struct Example<'a> {
arr_f: ArrayView<'a, [u8; 3]>,
}
fn main() {
let buf = [0x00, 0x01, 0x02];
let mut input_buf = buf.clone();
let (mut example, _) = Example::mut_view(&mut input_buf);
// as reference
assert_eq!(*example.arr_f.as_ref(), buf.clone());
// set
example.arr_f.set(&[0xAA, 0xBB, 0xCC]);
assert_eq!(*example.arr_f.as_ref(), [0xAA, 0xBB, 0xCC]);
// as mutable reference
{
let mut m_ref = example.arr_f.as_mut_ref();
m_ref[0] = 0xFF;
}
assert_eq!(input_buf, [0xFF, 0xBB, 0xCC]);
}
Composite example
use zordon::prelude::*;
#[derive(MutView)]
struct ExampleA<'a> {
u8_f: ByteView<'a, u8>,
}
#[derive(MutView)]
struct ExampleB<'a> {
u16_f: MulByteView<'a, u16, BigEnd>,
}
#[derive(MutView)]
struct Composite<'a> {
example_a: ExampleA<'a>,
example_b: ExampleB<'a>,
option_c: Option<u8>,
}
fn main() {
let mut input_buf = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05];
let (mut comp_e, _) = Composite::mut_view(&mut input_buf);
assert_eq!(comp_e.example_a.u8_f.val(), 0x00);
assert_eq!(comp_e.example_b.u16_f.val(), 0x0102);
assert_eq!(comp_e.option_c, None);
comp_e.example_a.u8_f.set(0xFF);
assert_eq!(input_buf[0], 0xFF);
}
VarArrayView
example
This type creates a mutable array view which for a non compile time known length.
use zordon::prelude::*;
fn main() {
let arr = &mut [0x1 as u8, 0x2, 0x3, 0x4] as &mut [u8];
let buf = arr.iter().map(|x| x.clone()).collect::<Vec<u8>>();
let (t, _): (VarArrayView<u8>, _) = VarArrayView::mut_view(arr, arr.len());
assert_eq!(*t.as_ref(), buf);
}
More examples
The crate (NOT PUBLISHED YET) uses zordon for zero-copy parsing of the PE format.
Features
- Zero-copy -- Original buffer is split into mutable slices
- Able to parse the following types:
[u8, u16, u32, u64, u128, i8, i16, i32, i64, i128, [u8; _], &mut [u8]]
- The types u16..u128 and i16..i128 can be treated as little endian or big endian
- Auto implementation of
mut_view
for structs via theMutView
derive macro.
Dependencies
~1.5MB
~40K SLoC