A Golang module for the Consistent Overhead Byte Stuffing (COBS) algorithm.
This module provides both simple helper functions to Encode/Decode []byte arrays and
Encoder/Decoder structs to stream bytes to an io.Writer instance.
The helper functions will allocate buffers to hold the encoded/decoded data and return a []byte
slice or an error.
The following example encodes a string with embedded zeroes:
package main
import (
"os"
"github.com/pdgendt/cobs"
)
func main() {
enc, _ := cobs.Encode([]byte{'H', 'e', 'l', 'l', 'o', 0x00, 'w', 'o', 'r', 'l', 'd', '!'})
os.Stdout.write(enc)
}The structs require an io.Writer instance on creation. As soon as data is available it is written,
for the Encoder this is done for each group, with a maximum of 255 bytes, for the Decoder every
byte is written individually.
The structs implement the io.Writer interface to allow chaining.
The following example encodes bytes read from os.Stdin and writes them to os.Stdout:
package main
import (
"io"
"os"
"github.com/pdgendt/cobs"
)
func main() {
enc := cobs.NewEncoder(os.Stdout)
if _, err := io.Copy(enc, os.Stdin); err != nil {
panic(err)
}
// Close needs to be called to flush the last group
if err := enc.Close(); err != nil {
panic(err)
}
}By default, COBS uses 0x00 (zero) as the sentinel value - the special byte that never
appears in encoded data and is used for packet framing. You can configure a custom sentinel
value using the WithSentinel() option. This is useful when your protocol already uses
zero bytes, or when you need a different delimiter for packet framing.
When a custom sentinel value is used, the implementation applies an XOR operation with the sentinel value on the encoded data after encoding and before decoding. This transforms the standard COBS delimiters (0x00) to the custom sentinel value, allowing any byte to be used as the packet delimiter.
// Use 0xFF as the sentinel instead of 0x00
enc := cobs.NewEncoder(w, cobs.WithSentinel(0xFF))
dec := cobs.NewDecoder(w, cobs.WithSentinel(0xFF))
// Or with the helper functions
encoded, _ := cobs.Encode(data, cobs.WithSentinel(0xFF))
decoded, _ := cobs.Decode(encoded, cobs.WithSentinel(0xFF))COBS/R is a variant of the COBS encoding that provides slightly better encoding efficiency by saving one byte in certain cases. In standard COBS, the overhead byte at the start of each group indicates where the next zero byte would have been. In COBS/R, if the overhead byte value is less than the last data byte in a group, the overhead byte is replaced with that last data byte, and the last data byte is omitted. This can save one byte when encoding data that ends with a byte value larger than the group size.
You can enable COBS/R using the WithReduced() option:
// Use COBS/R encoding
enc := cobs.NewEncoder(w, cobs.WithReduced(true))
dec := cobs.NewDecoder(w, cobs.WithReduced(true))
// Or with the helper functions
encoded, _ := cobs.Encode(data, cobs.WithReduced(true))
decoded, _ := cobs.Decode(encoded, cobs.WithReduced(true))COBS/R can be combined with custom sentinel values:
// Use both COBS/R and a custom sentinel
encoded, _ := cobs.Encode(data, cobs.WithReduced(true), cobs.WithSentinel(0xFF))
decoded, _ := cobs.Decode(encoded, cobs.WithReduced(true), cobs.WithSentinel(0xFF))Note: When using COBS/R decoding, you must call Close() on the decoder to flush the final
byte, as the reduced encoding may hold back the last byte until it knows encoding is complete.
For more information about COBS/R, see the Python COBS documentation.
The cmd/ directory contains simple encode/decode command line tools that take in data
from stdin and writes it to stdout.
This can be used to pipe encoded/decoded data to other processes.
$ echo "Hello world" | go run cmd/encode/main.go | go run cmd/decode/main.go
Hello worldBoth encode and decode commands support the -s (or -sentinel) flag to specify a custom
sentinel value:
$ echo "Hello world" | go run cmd/encode/main.go -s 0xFF | go run cmd/decode/main.go -s 0xFF
Hello worldThe encode command also supports the -d (or -del) flag to append the sentinel delimiter
after the encoded data.
Both encode and decode commands support the -r (or -reduced) flag to enable COBS/R encoding:
$ echo "Hello world" | go run cmd/encode/main.go -r | go run cmd/decode/main.go -r
Hello worldThe -r flag can be combined with custom sentinel values:
$ echo "Hello world" | go run cmd/encode/main.go -r -s 0xFF | go run cmd/decode/main.go -r -s 0xFF
Hello world