8 releases

0.1.7 Jun 14, 2020
0.1.6 Jun 14, 2020

#54 in #binary-parser

Apache-2.0

38KB
1K SLoC

dbin: Declarative binary parser

Like regular expressions, but for binary data

A lightweight utility for parsing and rendering binary data

Example

extern crate::dbin;
use dbin::Spec;
use dbin::Data;

fn main() {
    // The dbin::render, dbin::Render, dbin::Renderable
    // functions and enums make it easy to quickly create
    // Vec<u8> from some data (from tuples, vectors,
    // various integral types, and combinations of them
    // with varying endianness).
    // If endianness is not specified, usually
    // little endian is assumed.
    let bytes = dbin::render((
        1234u64,
        50000u16,
    ));

    // For parsing, you first create a 'dbin::Pattern' to
    // create a description of what you want to parse
    // together with 'dbin::Expr', you can dynamically
    // specify the length of array types based on
    // input seen earlier in the data.
    let parser = {
        use dbin::prelude::*;
        any_of((
            all_of((
                all_of(()).mapval("big-endian"),
                be_magic_u64(1234),
                BE_U16,
            )),
            all_of((
                all_of(()).mapval("little-endian"),
                le_magic_u64(1234),
                U8,
                U8,
            )),
        ))
    };

    // Just pass a &[u8] to the parse method on a Pattern
    // to try and parse it.
    // The resulting value will be a dbin::Data instance
    let data = parser.parse(&bytes).unwrap();

    assert_eq!(data, Data::fseq(vec![
        "little-endian".into(),
        Data::Int(1234),
        Data::Int(80),
        Data::Int(195),
    ]));
    assert_eq!(50000, (195 << 8) + 80);

    // Test arrays whose lengths are determined by parsed data:
    let bytes = render((
        1234u32, // magic
        3u32,    // length - 1
        (
            777u64, 888u64, 999u64, 444u64, // data to be parsed
            555u64, 666u64, // just some extra data
        ),
    ));

    enum Key {
        LENGTH,
    }

    let parser = {
        use prelude::*;
        all_of((
            le_magic_u32(1234),
            // after magic, the header specifies length of
            // upcoming array - 1
            // Store the computed length to 'Key::LENGTH'
            U32.add(1).store(Key::LENGTH as i64),
            // finally, specify an array of u64,
            // whose length is determined by the Key::LENGTH
            // value stored above
            array_of(LE_U64, getvar(Key::LENGTH as i64)),
        ))
    };

    let data = parser.parse(&bytes).unwrap();

    assert_eq!(
        data,
        Data::fseq(vec![
            Data::Int(1234), // magic,
            Data::Int(4),    // length
            // the actual array
            Data::fseq(vec![
                Data::Int(777),
                Data::Int(888),
                Data::Int(999),
                Data::Int(444),
            ]),
        ])
    );
}

Release notes

0.1.4

Simplified the parsing usage

No runtime deps