13 unstable releases (6 breaking)
0.7.0 | Apr 4, 2025 |
---|---|
0.6.0 | Nov 11, 2024 |
0.5.0 | Jul 25, 2024 |
0.3.0 | Mar 28, 2024 |
#305 in Encoding
159 downloads per month
65KB
1.5K
SLoC
bin-proto
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.7"
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.
bin-proto
is significantly faster than deku
in most of the tested scenarios.
The units for the below table are ns
, 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 |
20 | 65 | 1,052 | 588 | 134 | 122 |
deku |
1 | 96 | 899 | 1,070 | 4,086 | 703 |
Dependencies
~470–710KB
~11K SLoC