Expand description
binrw helps you write maintainable & easy-to-read declarative binary data readers and writers using ✨macro magic✨.
Adding #[binrw]
(or #[derive(BinRead, BinWrite)]
) to a
struct or enum generates a parser that can read that type from raw data and a
serialiser that can write it back:
use binrw::{
binrw, // #[binrw] attribute
BinRead, // trait for reading
BinWrite, // trait for writing
};
#[binrw]
#[brw(little)]
struct Point(i16, i16);
// Read a point from bytes
let point = Point::read(&mut Cursor::new(b"\x80\x02\xe0\x01")).unwrap();
assert_eq!(point, Point(640, 480));
// Write the point back to bytes
let mut writer = Cursor::new(Vec::new());
point.write(&mut writer).unwrap();
assert_eq!(writer.into_inner(), b"\x80\x02\xe0\x01");
binrw types are composable and nestable, so everything just works as expected without any special logic or glue code:
#[derive(BinRead)]
#[br(big, magic = b"SHAP")]
enum Shape {
#[br(magic(0u8))] Rect {
left: i16, top: i16, right: i16, bottom: i16
},
#[br(magic(1u8))] Oval { origin: Point, rx: u8, ry: u8 }
}
let oval = Shape::read(&mut Cursor::new(b"SHAP\x01\x80\x02\xe0\x01\x2a\x15")).unwrap();
assert_eq!(oval, Shape::Oval { origin: Point(640, 480), rx: 42, ry: 21 });
Types that can’t implement binrw traits directly (e.g. types from third party crates) can also be read and written using free parser functions or by mapping values.
Unlike “zero-copy” libraries, the in-memory representation of binrw structs doesn’t need to match the raw data. This can allow for better memory performance, especially on architectures where unaligned memory access is slow. Also, because data is never transmuted, there is no risk of undefined behaviour.
Input and output
binrw reads data from any object that implements io::Read
+ io::Seek
,
and writes data to any object that implements io::Write
+ io::Seek
.
(Unseekable streams are also supported, but require a wrapper.)
This means that data can come from memory, network, disk, or any other streaming
source. It also means that low-level data operations like
buffering and compression are efficient and easy to
implement.
binrw also includes extension traits for conveniently reading and writing directly on the stream objects:
use binrw::{BinReaderExt, BinWriterExt};
let mut stream = Cursor::new(b"\x00\x0a".to_vec());
let val: u16 = stream.read_be().unwrap();
assert_eq!(val, 0xa);
let val = val + 0x10;
stream.write_be(&val).unwrap();
assert_eq!(stream.into_inner(), b"\x00\x0a\x00\x1a");
Directives
Handling things like magic numbers, byte ordering, and padding & alignment
is typical when working with binary data, so binrw includes a variety of
built-in directives for these common cases that can be applied
using the #[br]
, #[bw]
, and #[brw]
attributes:
#[derive(BinRead)]
#[br(big, magic = b"DOG", assert(name.len() != 0))]
struct Dog {
bone_pile_count: u8,
#[br(count = bone_pile_count)]
bone_piles: Vec<u16>,
#[br(align_before = 0xA)]
name: NullString
}
let mut data = Cursor::new(b"DOG\x02\x00\x01\x00\x12\0\0Rudy\0");
let dog = Dog::read(&mut data).unwrap();
assert_eq!(dog.bone_piles, &[0x1, 0x12]);
assert_eq!(dog.name.to_string(), "Rudy")
Directives can also reference earlier fields by name. For tuple types,
earlier fields are addressable by self_N
, where N
is the index of the
field.
See the attribute documentation for the full list of available directives.
Built-in implementations
Implementations for all primitive data types, arrays, tuples, and standard
Rust types like Vec
are included, along with parsers for other
frequently used binary data patterns like
null-terminated strings and
indirect addressing using offsets. Convenient access into
bitfields is possible using crates like
modular-bitfield.
See the BinRead
and
BinWrite
traits for the full list of built-in
implementations.
no_std support
binrw supports no_std and includes a compatible subset of io
functionality. The alloc
crate is required.
Modules
Macros
Structs
BinRead::read_options()
.BinRead::read_options()
implementation of Vec
.BinWrite::write_options()
.Enums
Traits
BinRead
trait reads data from streams and converts it into objects.BinRead
objects directly from a reader.BinWrite
trait serialises objects and writes them to streams.BinWrite
objects directly to a writer.BinrwNamedArgs
trait allows
named arguments objects
to be constructed using a compile-time builder.Functions
Type Definitions
FilePtr
with 128-bit offsets.Attribute Macros
BinRead
. Use this instead
of #[derive(BinRead)]
to enable temporary variables.BinRead
and
BinWrite
. Use this instead of #[derive(BinRead, BinWrite)]
to enable
temporary variables.BinWrite
. Use this instead
of #[derive(BinWrite)]
to enable
temporary variables.Derive Macros
BinrwNamedArgs
.