cobs

package module
v0.0.0-...-b135d9c Latest Latest
Warning

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

Go to latest
Published: Aug 6, 2026 License: BSD-3-Clause Imports: 6 Imported by: 0

README

cobs

GoDoc CI

This repository implements the COBS encoding format in Go. It was written as an exercise to understand the format, and should be considered to be of experimental quality. The API should not be considered stable.

Documentation

Overview

Package cobs implements COBS, or Consistent Overload Byte Stuffing, an encoding method for byte-oriented data such as packets.

Overview

COBS is an encoding algorithm that transforms a record (or packet) of arbitrary binary data into a representation that excludes NUL (0) bytes. The encoded representation will be somewhat longer than the original (the "consistent overhead").

Informally, the algorithm works by splitting the input record into "blocks" of up to 254 bytes, delimited by size or a zero (NUL) byte. Each block is then encoded by writing a single-byte length prefix followed by the non-zero bytes of the block, and the encodings are concatenated. The resulting encoded output does not contain the NUL delimiter.

Encoding

A Writer writes arbitrary binary records into the COBS encoding. Only the baseline encoding is supported, extensions like paired-zero encoding are not currently implemented. To write a slice of bytes as a single record, use Writer.WriteData:

w := cobs.NewWriter(f)
err := w.WriteData(input)

Alternatively, use Writer.WriteRecord, which accepts a callback:

err := w.WriteRecord(func(rw io.Writer) error {
   _, err := io.Copy(rw, src)
   return err
})

Or use Writer.ReadFrom to write the complete contents of an io.Reader as a single record:

_, err := rw.ReadFrom(r)

To append the encoding of a single record to a slice, use Encode:

enc := cobs.Encode(nil, input)

Decoding

A Reader decodes the encoded format, allowing the caller to read back the decoded data transparently (provided the input is valid). It implements the io.Reader interface:

r := cobs.NewReader(f)
record, err := io.ReadAll(r)

See the Reader.Read documentation for specific details on how Reader handles delimiters in its input. Use Reader.Records to iterate:

for next, err := range r.Records() {
   if err != nil {
      log.Fatal(err)
   }
   doSomethingWith(next)
}

To append the decoding of a single record to a slice, use Decode:

dec, rest, err := cobs.Decode(nil, input)

This returns the decoded record and the remaining unconsumed suffix of src, if any. In case of error, Decode returns as much of the input as it was able to successfully decode, along with the error.

Implementation Notes

The Reader and Decode understand the permitted optimization of not encoding the logical trailing zero at the end of input, when the previous block was full. It will accept input in either representation. The Writer and Encode always omit the trailer after a full-size block at EOF.

Index

Constants

This section is empty.

Variables

View Source
var ErrEndOfRecord = errors.New("end of record")

ErrEndOfRecord is a sentinel error reported by Reader.Read or Decode when it encounters a NUL (0) byte at the end of a record.

View Source
var ErrUnexpectedNUL = errors.New("unexpected zero byte")

ErrUnexpectedNUL is a sentinel error reported by Reader.Read or Decode when it encounters a NUL (0) byte in then encoded input.

Functions

func Decode

func Decode(dst, src []byte) (dec, rest []byte, _ error)

Decode decodes a prefix of src as a single COBS record and appends the result to dst, returning the resulting decoded slice and the remaining unconsumed suffix of src, or nil.

Decode reports ErrUnexpectedNUL if it observes a NUL (0) byte within the encoded input. If it encounters a NUL (0) byte at the end of a complete (or empty) record, it reports ErrEndOfRecord. If a record is truncated at the end of src, it reports io.ErrUnexpectedEOF. In case of error, Decode reports any data successfully decoded along with the error.

If dst has sufficient capacity for the decoded result, Decode does not allocate memory. A destination buffer as big as the input is always sufficient. The src and dst slices must not overlap. The rest resultis either a slice into src, or nil.

func Encode

func Encode(dst, src []byte) []byte

Encode appends the COBS encoding of src to dst, and returns the resulting slice. If dst has sufficient capacity for the encoding, Encode does not allocate memory. See EncodingLen and MaxEncodingLen. The dst may be nil, but src and dst must not overlap.

func EncodingLen

func EncodingLen(data []byte) int

EncodingLen reports the (exact) length of the COBS encoding of data without allocating memory or constructing the encoding. It reads all of data, but does not modify it.

func MaxEncodingLen

func MaxEncodingLen(n int) int

MaxEncodingLen reports the maximum possible length of a COBS encoding of an n-byte input. The actual encoded length depends on the content, so this may over-estimate by an amount.

Types

type Reader

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

A Reader reads and decodes COBS format data from an underlying io.Reader. It implements io.Reader over the decoded data.

func NewReader

func NewReader(r io.Reader) *Reader

NewReader constructs a new Reader that consumes encoded data from r. Encoded records are delimited by NUL (0) bytes.

func (*Reader) DiscardUntilNUL

func (r *Reader) DiscardUntilNUL() (int, error)

DiscardUntilNUL consumes and discards input from the underlying reader until a NUL (0) byte is observed or the input ends. It reports the number of bytes discarded, including the NUL. If it reaches the end of the input without finding a NUL, it reports io.EOF.

The purpose of this method is to allow a reader to skip past invalid records in a NUL-delimited input stream. If it is called while in the middle of reading a valid record, any remaining unread sections of that record are also discarded.

func (*Reader) Read

func (r *Reader) Read(data []byte) (int, error)

Read implements the io.Reader interface to decode the contents of r.

Read reports ErrUnexpectedNUL if it observes a NUL (0) byte within the encoded input. If it encounters a NUL (0) byte at the end of a complete record, it consumes and discards the byte, and reports ErrEndOfRecord. A caller may call Read again to attempt to read a successive record.

Read reports io.ErrUnexpectedEOF if it encounters a non-empty incomplete record at the end of the input. It only reports io.EOF when it discovers the end of input at the beginning of a record, in which case it will return exactly 0, io.EOF.

func (*Reader) Records

func (r *Reader) Records() iter.Seq2[[]byte, error]

Records returns an iterator over the records encoded in r. Each pair reported by the iterator is either a valid record and a nil error, or an empty or incomplete record with a non-nil error.

A record slice reported by the iterator is only valid for the duration of the loop iteration to which it is delivered. If a record needs to be retained across iterations or beyond the loop, the caller must make a copy. It is safe to modify the contents of the record slice, but any such modifications will not be retained beyond the current iteration.

type Writer

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

A Writer wraps an io.Writer to allow encoding and writing COBS format records.

Use Writer.WriteData to write a slice of bytes as a single record. Use Writer.WriteRecord to generate a record from a callback. Use Writer.ReadFrom to generate a record from an io.Reader.

func NewWriter

func NewWriter(w io.Writer) Writer

NewWriter constructs a new Writer that encodes data to w.

func (Writer) ReadFrom

func (w Writer) ReadFrom(r io.Reader) (int64, error)

ReadFrom writes the complete contents of r as a single record. It implements the io.ReaderFrom interface.

func (Writer) WriteData

func (w Writer) WriteData(data []byte) error

WriteData encodes data as a single COBS record. If WriteData succeeds, the complete record has been written to the underlying writer.

func (Writer) WriteNUL

func (w Writer) WriteNUL() error

WriteNUL writes a single NUL (0) byte to the underlying writer, without encoding.

func (Writer) WriteRecord

func (w Writer) WriteRecord(do func(io.Writer) error) error

WriteRecord calls do with an io.Writer. All data written to that writer are encoded into a single COBS record in w.

If do reports an error, WriteRecord reports that error. Any data it wrote prior to returning will still be encoded. Otherwise it reports the result of encoding the record. If WriteRecord succeeds, the complete record has been written to the underlying writer.

Jump to

Keyboard shortcuts

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