29 releases

0.12.8 Apr 29, 2026
0.12.6 Mar 9, 2026
0.12.4 Dec 27, 2025
0.11.1 Nov 27, 2025
0.3.0 Mar 28, 2024

#1394 in Encoding

Download history 4/week @ 2026-01-19 5/week @ 2026-01-26 2/week @ 2026-02-02 6/week @ 2026-02-09 45/week @ 2026-02-16 20/week @ 2026-02-23 66/week @ 2026-03-02 80/week @ 2026-03-09 111/week @ 2026-03-16 280/week @ 2026-03-23 347/week @ 2026-03-30 302/week @ 2026-04-06 823/week @ 2026-04-13 556/week @ 2026-04-20 1075/week @ 2026-04-27 1298/week @ 2026-05-04

3,759 downloads per month
Used in 2 crates

MIT license

88KB
2K SLoC

bin-proto

crates tests docs.rs msrv license

Conversion to/from binary for arbitrary types. With no_std and no_alloc support.

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.

An alternative to deku and binrw.

See rust_serialization_benchmark for performance figures.

Example

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

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

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

#[derive(Debug, BitDecode, BitEncode, PartialEq)]
struct S {
    #[bin_proto(bits = 1)]
    bitflag: bool,
    #[bin_proto(bits = 3)]
    bitfield: u8,
    enum_: E,
    #[bin_proto(write_value = self.arr.len() as u8)]
    arr_len: u8,
    #[bin_proto(tag = arr_len as usize)]
    arr: Vec<u8>,
    #[bin_proto(tag_type = u16, tag_value = self.prefixed_arr.len() as u16)]
    prefixed_arr: Vec<u8>,
    #[bin_proto(untagged)]
    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().0,
    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)]
#[bin_proto(ctx = Ctx)]
pub struct WithCtx(NeedsCtx);

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

Dependencies

~510–710KB
~11K SLoC