6 releases

0.3.3 Apr 4, 2024
0.3.2 Apr 3, 2024
0.3.0 Mar 28, 2024
0.2.1 Mar 27, 2024
0.1.0 Mar 25, 2024

#395 in Encoding

Download history 20/week @ 2024-03-19 301/week @ 2024-03-26 399/week @ 2024-04-02 13/week @ 2024-04-09

733 downloads per month

MIT license

62KB
1.5K SLoC

bin-proto

crates tests docs.rs license

Simple & fast structured bit-level binary co/dec in Rust.

An improved and modernized fork of protocol. A more efficient but (slightly) less feature-rich alternative to deku.

This crate adds a trait (and a custom derive for ease-of-use) that can be implemented on types, allowing structured data to be sent and received from any binary stream. It is recommended to use bitstream_io if you need bit streams, as their BitRead and BitWrite traits are being used internally.

Example

Add this to your Cargo.toml:

[dependencies]
bin-proto = "0.3"

And then define a type with the #[derive(bin_proto::Protocol)] attribute.

use bin_proto::Protocol;

#[derive(Debug, Protocol, PartialEq)]
#[protocol(discriminant_type = "u8")]
#[protocol(bits = 4)]
enum E {
    V1 = 1,
    #[protocol(discriminant = "4")]
    V4,
}

#[derive(Debug, Protocol, PartialEq)]
struct S {
    #[protocol(bits = 1)]
    bitflag: bool,
    #[protocol(bits = 3)]
    bitfield: u8,
    enum_: E,
    #[protocol(write_value = "self.arr.len() as u8")]
    arr_len: u8,
    #[protocol(length = "arr_len as usize")]
    arr: Vec<u8>,
    #[protocol(flexible_array_member)]
    read_to_end: Vec<u8>,
}

assert_eq!(
    S::from_bytes(&[
        0b1000_0000 // bitflag: true (1)
       | 0b101_0000 // bitfield: 5 (101)
           | 0b0001, // enum_: V1 (0001)
        0x02, // arr_len: 2
        0x21, 0x37, // arr: [0x21, 0x37]
        0x01, 0x02, 0x03, // read_to_end: [0x01, 0x02, 0x03]
    ], bin_proto::ByteOrder::BigEndian).unwrap(),
    S {
        bitflag: true,
        bitfield: 5,
        enum_: E::V1,
        arr_len: 2,
        arr: vec![0x21, 0x37],
        read_to_end: vec![0x01, 0x02, 0x03],
    }
);

You can implement Protocol on your own types, and parse with context:

use bin_proto::Protocol;

#[derive(Debug)]
struct Ctx(bool);

#[derive(Debug)]
struct CtxCheck;

impl Protocol for CtxCheck {
    fn read(
        _: &mut dyn bin_proto::BitRead,
        _: bin_proto::ByteOrder,
        ctx: &mut dyn std::any::Any,
    ) -> Result<Self, bin_proto::Error> {
        ctx.downcast_mut::<Ctx>().unwrap().0 = true;
        Ok(Self)
    }

    fn write(
        &self,
        _: &mut dyn bin_proto::BitWrite,
        _: bin_proto::ByteOrder,
        ctx: &mut dyn std::any::Any,
    ) -> Result<(), bin_proto::Error> {
        ctx.downcast_mut::<Ctx>().unwrap().0 = true;
        Ok(())
    }
}

Performance / Alternatives

This crate's main alternative is deku, and binrw for byte-level protocols.

bin-proto is significantly faster than deku in all of the tested scenarios. The units for the below table are ns/iter, taken from github CI. You can find the benchmarks in the bench directory.

Read enum Write enum Read Vec Write Vec Read IPv4 header Write IPv4 header
bin-proto 28 110 1,293 1,261 165 163
deku 68 324 3,042 9,178 2,580 696

Roadmap

The following features are planned:

  • Bit/byte alignment
  • Better derive macro error messages
  • no_std support (only after bitstream_io supports it)

Dependencies

~0.5–1MB
~21K SLoC