cobs_codec_rs
#![no_std], dependency-free Consistent Overhead Byte Stuffing (COBS) and
COBS/R for Rust — the Rust member of the Firechip COBS family (alongside the
Dart cobs_codec and the Kotlin
cobs_codec_kt), verified
byte-identical against the shared
conformance vectors.
COBS encodes an arbitrary byte sequence into one that contains no zero (0x00)
byte, at a small and predictable cost: at most one extra byte per 254 bytes,
plus one. That makes a single 0x00 a reliable packet delimiter for serial/UART,
USB, TCP and other byte streams — ideal for embedded and robotics protocols.
Features
- Basic COBS and COBS/R (Reduced) encode/decode.
no_stdand zero dependencies — the coreencode/decodework on caller-provided slices, allocating nothing.- Configurable sentinel —
*_with_sentinelvariants frame with any delimiter byte, not just0x00. - In-place decoding —
cobs::decode_in_placedecodes without a second buffer. - Allocation-free streaming —
framing::StreamDecoderreassembles delimited frames into a fixed buffer in pureno_std; theallocfeature adds the owned-VecFrameDecoderand*_to_vecconveniences. const fnsize helpers (max_encoded_len,encoding_overhead) for compile-time buffer sizing.- Optional
serde/defmt— off by default; enable the matching feature to deriveSerialize/Deserializeordefmt::FormatforDecodeError.
Install
[]
= "1.2"
# no_std, no allocator:
# cobs_codec_rs = { version = "1.2", default-features = false }
Usage
use ;
// With alloc (default):
let encoded = encode_to_vec;
assert_eq!; // no 0x00
assert_eq!;
// COBS/R often saves the trailing overhead byte:
assert_eq!; // same length as input
no_std, into a fixed buffer:
use ;
let src = ;
let mut buf = ;
let n = encode;
assert_eq!;
With a custom sentinel byte, so a non-0x00 byte delimits frames (both cobs
and cobsr, slice and *_to_vec variants):
use cobs;
// 0xAA delimits frames instead of 0x00; the encoded output never contains it.
// (`sentinel == 0` is identical to the plain codec.)
let encoded = encode_to_vec_with_sentinel;
assert_eq!; // no 0xAA byte
assert_eq!;
Decoding in place, without a second buffer (basic COBS only):
use cobs;
// COBS never expands on decode, so it can decode within the input buffer; the
// decoded bytes end up in `buf[..len]`.
let mut buf = ;
let len = decode_in_place.unwrap;
assert_eq!;
Reassembling a sentinel-delimited stream with no allocator, into a fixed buffer:
use cobs;
use StreamDecoder;
// Encode a packet with sentinel 0xAA, then delimit it with an 0xAA byte.
let mut wire = ;
let n = encode_with_sentinel;
wire = 0xAA;
// Reassemble it into a fixed scratch buffer — no allocation anywhere.
let mut scratch = ;
let mut decoder = new.sentinel; // .reduced(true) for COBS/R
let mut out = ;
let mut out_len = 0;
decoder.push;
assert_eq!;
Reading a delimited serial stream (needs alloc):
use ;
let mut rx = new.max_frame_len;
// `chunk` is any &[u8] read from the link; chunks need not align with frames.
# let chunk = frame_to_vec;
rx.push;
Framing Protocol Buffers over serial
COBS is the standard way to frame Protobuf on a UART/RS-485 link: protobuf
serializes a message but doesn't delimit it, and COBS supplies the missing
0x00-delimited framing — with instant resync after line noise, unlike
length-prefixing. See examples/protobuf_cobs.rs
for a runnable device→host demo (cargo run --example protobuf_cobs) that
survives a corrupted frame.
Overhead
COBS overhead is data-independent. Encoding an n-byte packet produces at most
$$ n + \left\lceil \frac{n}{254} \right\rceil $$
bytes (one extra byte per 254, rounded up), so the overhead is bounded by
$\left\lceil n/254 \right\rceil$ and is always at least one byte. By contrast,
escape-based schemes (PPP, SLIP, HDLC) can double the packet in the worst case.
cobsr (COBS/R) can reach zero overhead. These bounds are what max_encoded_len
and encoding_overhead return.
Benchmarks
Throughput on a 1 KiB payload (cargo bench, criterion), on an AMD Ryzen 7
3800XT under WSL2 — indicative, not a controlled benchmark:
| Operation | Throughput |
|---|---|
| COBS encode | ~835 MiB/s |
| COBS decode | ~1.40 GiB/s |
| COBS/R encode | ~832 MiB/s |
Background
Stuart Cheshire and Mary Baker, "Consistent Overhead Byte Stuffing", IEEE/ACM Transactions on Networking, Vol. 7, No. 2, April 1999. COBS/R is a variant by Craig McQueen.
License
MIT © 2026 Alexander Salas Bastidas (Firechip). See LICENSE.