corncobs 0.1.3

COBS encode/decode for Rust no_std targets
Documentation
# `corncobs`: Corny COBS encoding/decoding in Rust

This crate provides [Consistent Overhead Byte Stuffing][cobs] (COBS) support for
Rust programs, with a particular focus on resource-limited embedded `no_std`
targets:

- Provides both fast (buffer-to-buffer) and small (in-place or iterator-based)
  versions of both encode and decode routines.

- Provides a `const fn` for computing the maximum encoded size for a given
  input size, so you can define fixed-size buffers precisely without magic
  numbers.

- Has pretty good test coverage, [Criterion] benchmarks, and a [honggfuzz] fuzz
  testing suite to try to ensure code quality.

## Cargo `features`

No features are enabled by default. Embedded programmers do not need to specify
`default-features = false` when using `corncobs` because who said `std` should
be the default anyhow? People with lots of RAM, that's who.

Features:

- `std`: if you're on one of them "big computers" with "infinite memory" and can
  afford the inherent nondeterminism of dynamic memory allocation, this feature
  enables routines for encoding to-from `Vec`, and an `Error` impl for
  `CobsError`.

## When to use COBS

COBS lets us take an arbitrary blob of bytes and turn it into a slightly
longer blob that doesn't contain a certain byte, except as a terminator at
the very end. `corncobs` implements the version of this where the byte is zero.
That is, `corncobs` can take a sequence of arbitrary bytes, and turn it into a
slightly longer sequence that doesn't contain zero except at the end.

The main reason you'd want to do this is _framing._ If you're transmitting a
series of messages over a stream, you need some way to tell where the messages
begin and end. There are many ways to do this -- such as by transmitting a
length before every message -- but most of them don't support _sync recovery._
Sync recovery lets a receiver tune in anywhere in a stream and figure out
(correctly) where the next message boundary is. The easiest way to provide sync
recovery is to use a marker at the beginning/end of each message that you can
reliably tell apart from the data in the messages. To find message boundaries in
an arbitrary data stream, you only need to hunt for the end of the current
message and start parsing from there. COBS can do this by ensuring that the
message terminator character (0) only appears between messages.

Unlike a lot of framing methods (particularly [SLIP]), COBS guarantees an upper
bound to the size of the encoded output: the original length, plus two bytes,
plus one byte per 254 input bytes. `corncobs` provides the `max_encoded_len`
function for sizing buffers to allow for worst-case encoding overhead, at
compile time.

## When to use this implementation of COBS

I wrote `corncobs` for an art project that required streaming video over a 3-10
Mbit/s RS485 link on an 80MHz Cortex-M4. Its performance is more than sufficient
for this task.

At the time, I had identified two main alternatives: [`cobs-rs`] and
[`postcard-cobs`]. (Note: `postcard-cobs` says it's a fork of `cobs-rs`, but it
isn't, it's a fork of [`cobs`][cobs-crate]. This confused me too.) They didn't
quite work for my application:

- At about 8 CPU cycles per incoming bit, I needed decoding to be very, very
  fast. `corncobs` decoding is about 60x faster and met my needs. (Encode is
  about 3x faster. Both numbers are for non-pathological data, i.e. not all
  zeroes. See the benchmark suite for details.)

- I was receiving messages in a circular buffer via DMA, where they'd be
  concatenated but separated with zeroes. This meant I couldn't express the
  length of incoming messages at compile time, and I needed an exact number of
  bytes consumed for each message, both of which made using `cobs-rs` difficult.

- I was operating in an environment where data transmission was not perfectly
  reliable, and needed the firmware to recover gracefully from corruption or
  lost data, i.e. not panic. This made using `postcard-cobs` difficult. (They
  don't have a public bug tracker, so I was unable to report the panics.)

However, `corncobs` and `postcard-cobs` are compatible; the `tests/compat.rs`
test suite in `corncobs` proves this. (Note that you need to make sure to strip
trailing zeroes before handing data to `postcard-cobs` to avoid panics.)
`corncobs` is also mostly compatible with `cobs-rs` with the exception of the
encoding of empty messages, an area where I think `cobs-rs` has a bug. So, you
can mix and match -- if you would like the slower-but-more-predictable encoding
performance of `postcard-cobs` and the faster decode of `corncobs`, go for it.

The performance tests I used to reach these conclusions are checked in. I keep
an eye on them, in case I can stop maintaining my own crate some day. :-) You
can run them with:

```
$ cargo bench comparison
```

## Tips for using COBS

If you're designing a protocol or message format and considering using COBS, you
have some options.

**Optimizing for size:** COBS encoding has the least overhead when the data
being encoded contains `0x00` bytes, at least one for every 254 bytes sent. In
practice, most data formats achieve this. However...

**Optimizing for speed:** COBS encode/decode, and particularly the `corncobs`
implementation, goes fastest when data contains as _few_ `0x00` bytes as
possible -- ideally none. If you can adjust the data you're encoding to avoid
zero, you can achieve higher encode/decode rates. For instance, in one of my
projects that sends RGB video data, I just declared that red/green/blue value 1
is the same as 0, and made all the 0s into 1s, for a large performance
improvement.

## Running the tests and stuff

For my future self, when I have forgotten the incantations. Or for you!

Tests: `cargo test`

Benchmarks: `cargo bench` (easy enough so far)

Fuzzing:

```
cargo install honggfuzz
cargo hfuzz run encode  # or...
cargo hfuzz run decode
```

[cobs]: https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing
[Criterion]: https://docs.rs/criterion/latest/criterion/
[honggfuzz]: https://docs.rs/honggfuzz/latest/honggfuzz/
[SLIP]: https://en.wikipedia.org/wiki/Serial_Line_Internet_Protocol
[`cobs-rs`]: https://crates.io/crates/cobs-rs
[`postcard-cobs`]: https://crates.io/crates/postcard-cobs
[cobs-crate]: https://crates.io/crates/cobs