Skip to main content

Crate autocodec

Crate autocodec 

Source
Expand description

§autocodec

Derive macro for automatic binary protocol serialization and deserialization. Zero runtime dependencies.

Annotate your structs and enums with #[derive(Codec)] to automatically generate efficient binary encoding and decoding. All multi-byte integers use big-endian (network byte order) by default.

§Quick Start

use autocodec::{Codec, CodecError};

#[derive(Debug, PartialEq, Codec)]
struct Header {
    version: u16,
    length: u32,
}

let header = Header { version: 1, length: 128 };

// Encode
let mut buf = Vec::new();
header.encode(&mut buf);
assert_eq!(buf.len(), 6); // 2 + 4 bytes

// Decode
let (decoded, remaining) = Header::decode(&buf).unwrap();
assert_eq!(decoded, header);
assert!(remaining.is_empty());

// Decode exact (errors if trailing bytes)
let decoded = Header::decode_exact(&buf).unwrap();

// Pre-calculate encoded size without allocating
assert_eq!(header.encoded_size(), 6);

§Enums

Enums use a discriminant byte. Supports #[repr(u8)] with native = N syntax:

use autocodec::Codec;

#[derive(Debug, PartialEq, Codec)]
#[repr(u8)]
enum Command {
    Ping = 1,
    Pong = 2,
    Data = 10,
}

let mut buf = Vec::new();
Command::Data.encode(&mut buf);
assert_eq!(buf, [10]);
let (cmd, _) = Command::decode(&[2]).unwrap();
assert_eq!(cmd, Command::Pong);

Enums with fields work too:

use autocodec::Codec;

#[derive(Debug, PartialEq, Codec)]
enum Message {
    Ping,
    Data { id: u32, payload: Vec<u8> },
    Ack(u64),
}

let msg = Message::Data { id: 42, payload: vec![1, 2, 3] };
let mut buf = Vec::new();
msg.encode(&mut buf);
let (decoded, _) = Message::decode(&buf).unwrap();
assert_eq!(decoded, msg);

§Composability

Any field whose type implements Codec works automatically:

use autocodec::Codec;

#[derive(Debug, PartialEq, Codec)]
struct Header { version: u16 }

#[derive(Debug, PartialEq, Codec)]
struct Packet {
    header: Header,
    payload: Vec<u8>,
}

let pkt = Packet { header: Header { version: 1 }, payload: vec![0xFF; 10] };
let mut buf = Vec::new();
pkt.encode(&mut buf);
let (decoded, _) = Packet::decode(&buf).unwrap();
assert_eq!(decoded, pkt);

§Endianness

Per-field or container-level:

use autocodec::Codec;

// Container-level: all fields default to little-endian
#[derive(Debug, PartialEq, Codec)]
#[codec(endian = "little")]
struct LittlePacket {
    a: u16,
    b: u32,
}

// Per-field override
#[derive(Debug, PartialEq, Codec)]
struct MixedPacket {
    #[codec(endian = "little")]
    little: u32,
    big: u32, // default big-endian
}

§Length Prefixes

use autocodec::Codec;

#[derive(Debug, PartialEq, Codec)]
struct Compact {
    #[codec(len = "u8")]
    items: Vec<u8>,       // 1-byte length prefix (max 255)
    #[codec(len = "u16")]
    name: String,         // 2-byte length prefix
    data: Vec<u8>,        // default u32 length prefix
}

§Length Validation

use autocodec::Codec;

#[derive(Debug, PartialEq, Codec)]
struct Bounded {
    #[codec(min_len = 1)]
    items: Vec<u8>,       // must have at least 1 element
    #[codec(max_len = 100)]
    name: String,         // must be at most 100 bytes
}

§Bitfields

Consecutive bits fields are packed into minimum bytes (MSB-first):

use autocodec::Codec;

#[derive(Debug, PartialEq, Codec)]
struct Flags {
    #[codec(bits = 4)]
    version: u8,
    #[codec(bits = 4)]
    header_len: u8,
    #[codec(bits = 1)]
    syn: u8,
    #[codec(bits = 1)]
    ack: u8,
    #[codec(bits = 6)]
    reserved: u8,
    // 16 bits total = 2 bytes on the wire
}

let flags = Flags { version: 4, header_len: 5, syn: 1, ack: 0, reserved: 0 };
let mut buf = Vec::new();
flags.encode(&mut buf);
assert_eq!(buf.len(), 2);

§Skip, Default, Padding

use autocodec::Codec;

#[derive(Debug, PartialEq, Codec)]
struct Protocol {
    id: u32,
    #[codec(skip)]
    cached: u32,              // not on wire, Default on decode
    #[codec(skip, default = "42")]
    answer: u32,              // not on wire, custom default
    #[codec(padding = 3)]
    flags: u8,                // 3 zero bytes inserted after this field
}

§Magic Constants

use autocodec::{Codec, CodecError};

#[derive(Debug, PartialEq, Codec)]
struct Frame {
    #[codec(magic = 0xCAFEBABE)]
    _magic: u32,              // validated on decode, error if mismatch
    version: u16,
}

// Decoding with wrong magic fails:
let bad = [0, 0, 0, 0, 0, 1];
assert!(Frame::decode(&bad).is_err());

§Validation

use autocodec::{Codec, CodecError};

fn is_nonzero(val: &u16) -> bool { *val != 0 }

#[derive(Debug, PartialEq, Codec)]
struct Config {
    #[codec(validate = "is_nonzero")]
    port: u16,
}

let bad = [0, 0]; // port = 0
assert!(Config::decode(&bad).is_err());

§Custom Codec

Delegate encode/decode to a module with decode and encode functions:

use autocodec::{Codec, CodecError};

mod le_u16 {
    use autocodec::CodecError;
    pub fn decode(input: &[u8]) -> Result<(u16, &[u8]), CodecError> {
        if input.len() < 2 {
            return Err(CodecError::NotEnoughBytes { needed: 2, available: input.len() });
        }
        Ok((u16::from_le_bytes([input[0], input[1]]), &input[2..]))
    }
    pub fn encode(val: &u16, buf: &mut Vec<u8>) {
        buf.extend_from_slice(&val.to_le_bytes());
    }
}

#[derive(Debug, PartialEq, Codec)]
struct Custom {
    #[codec(with = "le_u16")]
    value: u16,
}

§Zero-Copy Parsing

use autocodec::Bytes;

let data = [0, 0, 0, 3, 0xAA, 0xBB, 0xCC, 0xFF];
let (bytes, rest) = Bytes::decode(&data).unwrap();
assert_eq!(&*bytes, &[0xAA, 0xBB, 0xCC]);
assert_eq!(rest, &[0xFF]);
// `bytes` borrows directly from `data` — no allocation

§Error Context

Decode errors include the field name:

use autocodec::{Codec, CodecError};

#[derive(Debug, Codec)]
struct Msg { version: u16, length: u32 }

let err = Msg::decode(&[0, 1]).unwrap_err(); // version ok, length fails
assert_eq!(err.to_string(), "in field `length`: not enough bytes: needed 4, have 0");

§Supported Types

TypeWire format
u8u128, i8i128N bytes, big-endian
f32, f64IEEE 754, big-endian
bool1 byte (0 = false, nonzero = true)
Stringu32 length prefix + UTF-8 bytes
Vec<T>u32 length prefix + N encoded elements
Option<T>u8 tag (0 = None, 1 = Some) + value
[T; N]N encoded elements (no length prefix)
Box<T>transparent (same as T)
Box<[T]>u32 length prefix + N elements (same as Vec)
(A, B, ...)sequential fields (up to 8 elements)
HashMap<K, V>u32 length prefix + key-value pairs
Bytes<'a>u32 length prefix + bytes (zero-copy)

Structs§

Bytes
A zero-copy byte slice reference for efficient binary protocol parsing.

Enums§

CodecError
Errors that can occur during decoding.

Constants§

MAX_DECODE_LEN
Maximum number of elements allowed in a single Vec/String decode. Prevents OOM from malicious length prefixes. Default: 16 MiB worth of elements.

Traits§

Codec
Trait for types that can be encoded to and decoded from a binary format.

Derive Macros§

Codec
Derive macro that generates Codec trait implementations.