A small C11 implementation of streaming Consistent Overhead Byte Stuffing (COBS). It incrementally encodes and decodes zero-delimited messages using bounded state, with no message-size or input-chunk alignment requirements.
Both operations use input/output size parameters. On entry, they contain the available input length and output capacity. On return, they contain bytes consumed and written. Callers must advance the buffers by the returned amounts.
ccobs_encode() accepts chunks belonging to one message. Set end_message
only for the final chunk and keep it set while retrying after
CCOBS_NEED_OUTPUT. CCOBS_PARTIAL means a non-final chunk was accepted and
the encoder is waiting for the rest of the message. Success with
end_message set means the trailing zero delimiter was written. Empty
messages are encoded as 01 00.
ccobs_decode() accepts arbitrary stream chunks and stops after one complete
message, leaving subsequent encoded bytes unconsumed. Accumulate its decoded
output until message_complete is true. CCOBS_PARTIAL means the input ended
inside a valid frame. CCOBS_NEED_OUTPUT requests another output buffer
without losing progress.
A malformed frame resets the decoder when its delimiter is encountered, so the next call can resume at the next frame. Output already returned for the malformed frame must be discarded by the caller.
For batches of independent messages, ccobs_encodev() encodes vectored frames
into a flat byte buffer. Its size vector is updated with the number of bytes
consumed from each frame, so output exhaustion can be resumed without hidden
caller-buffer ownership. It also reports how many frame delimiters were fully
written, covering the case where all source bytes were consumed but the
encoded frame is still being drained. ccobs_decodev() performs the inverse operation: it
decodes a flat stream into flat output and returns the decoded length of each
complete frame in a size vector. A returned frame length includes decoded
bytes produced for that frame by earlier partial calls.
Both vector APIs take a ccobs_state and can return CCOBS_PARTIAL or
CCOBS_NEED_OUTPUT. This makes them suitable for parsers receiving arbitrary
network or file chunks while preserving message boundaries.
ccobs_encode_max_size() returns a safe encoded capacity, including the zero
delimiter, for one input message.
There is no dynamic allocation after state creation. An encoder buffers at most one 254-byte nonzero run. Encoding and decoding use independent fields in the state, but simultaneous calls on the same state are not thread-safe.
Use ccobs_init() for stack allocation or ccobs_create() with
ccobs_destroy() for heap allocation. ccobs_reset() abandons any partial
messages in both directions.
cmake -S . -B build
cmake --build build
ctest --test-dir build --output-on-failureThe default build produces a static library. To build a shared library
instead, enable CMake's standard BUILD_SHARED_LIBS option:
cmake -S . -B build-shared -DBUILD_SHARED_LIBS=ON
cmake --build build-shared
ctest --test-dir build-shared --output-on-failureThis project is licensed under the Zero-Clause BSD license (0BSD).