binrw 0.5.0

A Rust crate for helping read structs from binary data using ✨macro magic✨
Documentation

Maintainer's Note: binrw is in a rather early state. Lots of work is being done in order to make a great library before holding it to too many stability guarantees. Expect a bit of churn, if you're looking for a stable alternative, check out binread. If you're interested in getting involved, join us on discord or matrix! (whichever you prefer, they're bridged together)


binrw helps you write maintainable & easy-to-read declarative binary data parsers using ✨macro magic✨.

Adding #[derive(BinRead)] to any struct or enum generates a parser that can read that type from raw data:

# use binrw::{BinRead, io::Cursor};
# #[derive(Debug, PartialEq)]
#[derive(BinRead)]
#[br(little)]
struct Point(i16, i16);

let point = Point::read(&mut Cursor::new(b"\x80\x02\xe0\x01")).unwrap();
assert_eq!(point, Point(640, 480));

# #[derive(Debug, PartialEq)]
#[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 BinRead directly (e.g. types from third party crates) can also be read 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

BinRead reads data from any object that implements [io::Read] + [io::Seek]. 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.

BinRead also includes an extension trait for reading types directly from input objects:

use binrw::{BinReaderExt, io::Cursor};

let mut reader = Cursor::new(b"\x00\x0A");
let val: u16 = reader.read_be().unwrap();
assert_eq!(val, 10);

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] attribute:

# use binrw::{prelude::*, io::Cursor, NullString};
#
#[derive(BinRead)]
#[br(magic = b"DOG", assert(name.len() != 0))]
struct Dog {
bone_pile_count: u8,

#[br(big, args { count: bone_pile_count as usize, inner: () })]
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.into_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 module 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 trait 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.