12 releases (7 breaking)

new 0.8.0 May 2, 2025
0.6.0 Nov 11, 2024
0.5.0 Jul 25, 2024
0.3.0 Mar 28, 2024

#8 in #binary-bit

Download history 23/week @ 2025-01-12 11/week @ 2025-01-19 10/week @ 2025-01-26 64/week @ 2025-02-02 66/week @ 2025-02-09 39/week @ 2025-02-16 21/week @ 2025-02-23 16/week @ 2025-03-02 1/week @ 2025-03-23 118/week @ 2025-03-30 51/week @ 2025-04-06 135/week @ 2025-04-13 32/week @ 2025-04-20 91/week @ 2025-04-27

345 downloads per month
Used in bin-proto

MIT license

38KB
872 lines

bin-proto

crates tests docs.rs msrv 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.8"

And then define a type with the #[derive(bin_proto::BitDecode, bin_proto::BitEncode)] attributes.

use bin_proto::{BitDecode, BitEncode, BitCodec};

#[derive(Debug, BitDecode, BitEncode, PartialEq)]
#[codec(discriminant_type = u8)]
#[codec(bits = 4)]
enum E {
    V1 = 1,
    #[codec(discriminant = 4)]
    V4,
}

#[derive(Debug, BitDecode, BitEncode, PartialEq)]
struct S {
    #[codec(bits = 1)]
    bitflag: bool,
    #[codec(bits = 3)]
    bitfield: u8,
    enum_: E,
    #[codec(write_value = self.arr.len() as u8)]
    arr_len: u8,
    #[codec(tag = arr_len as usize)]
    arr: Vec<u8>,
    #[codec(tag_type = u16, tag_value = self.prefixed_arr.len() as u16)]
    prefixed_arr: Vec<u8>,
    #[codec(flexible_array_member)]
    read_to_end: Vec<u8>,
}

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

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

use bin_proto::{BitDecode, BitEncode};

pub struct Ctx;

pub struct NeedsCtx;

impl BitDecode<Ctx> for NeedsCtx {
    fn decode<R, E>(
        _read: &mut R,
        _ctx: &mut Ctx,
        _tag: (),
    ) -> bin_proto::Result<Self>
    where
        R: bin_proto::BitRead,
        E: bin_proto::Endianness,
    {
        // Use ctx here
        Ok(Self)
    }
}

impl BitEncode<Ctx> for NeedsCtx {
    fn encode<W, E>(
        &self,
        _write: &mut W,
        _ctx: &mut Ctx,
        _tag: (),
    ) -> bin_proto::Result<()>
    where
        W: bin_proto::BitWrite,
        E: bin_proto::Endianness,
    {
        // Use ctx here
        Ok(())
    }
}

#[derive(BitDecode, BitEncode)]
#[codec(ctx = Ctx)]
pub struct WithCtx(NeedsCtx);

WithCtx(NeedsCtx)
    .encode_bytes_ctx(bin_proto::BigEndian, &mut Ctx, ())
    .unwrap();

Performance / Alternatives

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

See GitHub Actions for latest benchmark results with comparison against deku.

Dependencies

~205–640KB
~15K SLoC