cobs

package module
v1.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 21, 2025 License: MIT Imports: 3 Imported by: 0

README

COBS

Go version Code coverage

A Golang module for the Consistent Overhead Byte Stuffing (COBS) algorithm.

Usage

This module provides both simple helper functions to Encode/Decode []byte arrays and Encoder/Decoder structs to stream bytes to an io.Writer instance.

Encode/Decode functions

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)
}
Encoder/Decoder structs

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)
	}
}
Sentinel Value

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 (COBS Reduced)

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.

CLI tools

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 world
Custom Sentinel in CLI

Both 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 world

The encode command also supports the -d (or -del) flag to append the sentinel delimiter after the encoded data.

COBS/R in CLI

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 world

The -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

Documentation

Overview

Package cobs implements Consistent Overhead Byte Stuffing (COBS) encoding and decoding algorithms for efficient, reliable and unambiguous packet framing.

Index

Constants

View Source
const (
	Delimiter = byte(0x00) // default packet framing delimiter.
)

Variables

View Source
var EOD = errors.New("EOD")

EOD is the error returned when decoding and a delimiter was written. Functions return EOD to signal a graceful end of a frame.

View Source
var ErrIncompleteFrame = errors.New("frame incomplete")

ErrIncompleteData means a decoder was closed with an incomplete frame.

View Source
var ErrUnexpectedEOD = errors.New("unexpected EOD")

ErrUnexpectedEOD means that a delimiter was encountered in a malformed frame.

Functions

func Decode

func Decode(data []byte, opts ...option) ([]byte, error)

Decode decodes and returns a byte slice.

func Encode

func Encode(data []byte, opts ...option) ([]byte, error)

Encode encodes and returns a byte slice.

func WithDelimiterOnClose added in v1.2.0

func WithDelimiterOnClose(enabled bool) option

WithDelimiterOnClose configures the encoder to append a sentinel delimiter on close.

func WithReduced added in v1.2.0

func WithReduced(enabled bool) option

WithReduced configures the encoder/decoder to run COBS/R.

func WithSentinel added in v1.2.0

func WithSentinel(sentinel byte) option

WithSentinel configures the encoder/decoder to use a custom sentinel value.

Types

type Decoder

type Decoder struct {
	// contains filtered or unexported fields
}

A Decoder implements the io.Writer and io.ByteWriter interfaces. Data written will we be decoded and forwarded byte per byte.

func NewDecoder

func NewDecoder(w io.Writer, opts ...option) *Decoder

NewDecoder returns a Decoder that writes decoded data to w.

func (*Decoder) Close added in v1.2.0

func (d *Decoder) Close() error

Close flushes the last byte in case of COBS reduced (COBS/R) and will return an error if the decoder expects more data.

func (*Decoder) NeedsMoreData added in v1.1.0

func (d *Decoder) NeedsMoreData() bool

NeedsMoreData returns true if the decoder needs more data for a full frame.

func (*Decoder) Write

func (d *Decoder) Write(p []byte) (int, error)

Write will call WriteByte for each byte in p.

func (*Decoder) WriteByte

func (d *Decoder) WriteByte(c byte) error

WriteByte decodes a single byte c. If c is the sentinel value the decoder state is validated and either EOD or ErrUnexpectedEOD is returned.

type Encoder

type Encoder struct {
	// contains filtered or unexported fields
}

An Encoder implements the io.Writer and io.ByteWriter interfaces. Data written will we be encoded into groups and forwarded.

func NewEncoder

func NewEncoder(w io.Writer, opts ...option) *Encoder

NewEncoder returns an Encoder that writes encoded data to w.

func (*Encoder) Close

func (e *Encoder) Close() error

Close has to be called after writing a full frame and will write the last group.

func (*Encoder) Write

func (e *Encoder) Write(p []byte) (int, error)

Write will call WriteByte for each byte in p.

func (*Encoder) WriteByte

func (e *Encoder) WriteByte(c byte) error

WriteByte encodes a single byte c. If a group is finished it is written to w.

Directories

Path Synopsis
cmd
decode command
Decode reads from standard input, and writes the decoded data to standard output.
Decode reads from standard input, and writes the decoded data to standard output.
encode command
Encode reads from standard input, and writes the encoded data to standard output.
Encode reads from standard input, and writes the encoded data to standard output.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL