c0

package module
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Sep 22, 2026 License: MIT Imports: 7 Imported by: 0

README

c0

A Go implementation of C0DATA — structured data built on ASCII C0 control codes.

Pure Go, no dependencies. The read path is zero-copy: accessors return sub-slices of the input buffer (a Go slice is a view into the backing array). The hot loop is a single comparison, byte < 0x20.

Install

go get github.com/c0data/c0-go
import c0 "github.com/c0data/c0-go"

Usage

// Write
buf, _ := c0.Build(func(b *c0.Builder) {
    b.Group("users", []string{"name", "amount"})
    b.Record("Alice", "100")
    b.Record("Bob", "200")
})

// Read (zero-copy: fields are sub-slices of buf)
t := c0.NewTable(buf)
for _, rec := range t.Records() {
    name := rec.Field(0)   // []byte view into buf
    _ = rec.Value(1)       // []byte, DLE-escapes decoded
    _ = name
}

// Compact form is canonical — hashable for content addressing
c0.Canonical(buf) // => true

// Documents, streams, pretty
c0.NewDocument(buf)
c0.NewStreamReader(data) // .Torn(), .Committed(), .Block(i)
c0.Format(buf)           // Unicode Control Pictures

Status

Core: tokenizer, table/record and document/group readers (zero-copy), builder, canonical helpers, ETB stream mode, and pretty (compact format + parse). Passes the shared conformance vectors from c0-spec.

Converters (CSV / JSON / C0DIFF) are not yet ported — Go's stdlib encoding/csv and encoding/json make those straightforward follow-ups.

Docs

API docs: https://pkg.go.dev/github.com/c0data/c0-go

Test

git submodule update --init   # pulls in c0-spec (the shared vectors)
go test ./...

License

MIT

Documentation

Overview

Package c0 implements C0DATA — structured data using ASCII C0 control codes.

Values are plain UTF-8 text; structure is expressed through single-byte control codes. The read path is zero-copy: accessors return sub-slices of the input buffer (Go slices are views into the backing array). The hot loop is a single comparison, byte < 0x20.

Index

Constants

View Source
const (
	SOH byte = 0x01 // Header (field name declarations)
	STX byte = 0x02 // Open nested sub-structure / reference scope
	ETX byte = 0x03 // Close nested sub-structure / reference scope
	EOT byte = 0x04 // End of document / message
	ENQ byte = 0x05 // Reference (enquiry — look up named data)
	DLE byte = 0x10 // Escape (next byte is literal)
	ETB byte = 0x17 // Commit marker (stream mode block terminator)
	SUB byte = 0x1a // Substitution (old → new, C0-DIFF)
	FS  byte = 0x1c // File / Database separator
	GS  byte = 0x1d // Group / Table / Section separator
	RS  byte = 0x1e // Record / Row separator
	US  byte = 0x1f // Unit / Field separator
)

Assigned C0 control codes.

Variables

View Source
var ErrUnexpectedEnd = errors.New("c0: unexpected end of input after DLE escape")

ErrUnexpectedEnd is returned when input ends immediately after a DLE escape.

Functions

func Build

func Build(fn func(*Builder)) ([]byte, error)

Build drives a fresh Builder and returns its bytes and any error.

func Canonical

func Canonical(buf []byte) bool

Canonical reports whether bytes are a canonical document unit for content addressing: well-formed, minimally escaped (DLE appears only before bytes < 0x20), and free of framing bytes (ETB, EOT). Stream logs validate per block, not with this.

func Format

func Format(buf []byte) string

Format renders compact bytes as a human-readable Unicode string with two-space indentation (compact layout).

func FormatWith

func FormatWith(buf []byte, indent string) string

FormatWith renders with a custom indent string.

func Glyph

func Glyph(b byte) rune

Glyph returns the Unicode Control Picture (U+2400 block) for a control byte.

func IsAssigned

func IsAssigned(b byte) bool

IsAssigned reports whether b is an assigned C0 control code.

func Parse

func Parse(s string) []byte

Parse parses pretty-form text back to compact bytes. Control Pictures (U+2400–U+241F) become C0 bytes; LF/CR are ignored; whitespace adjacent to control codes is trimmed; inside STX/ETX everything is preserved verbatim.

func ReadLog

func ReadLog(path string) ([]byte, error)

ReadLog reads a log file into a byte buffer; wrap it with NewStreamReader.

func Unescape

func Unescape(buf []byte) []byte

Unescape decodes DLE escapes, returning the logical bytes of a value. When the input contains no escapes it returns the input slice unchanged (zero-copy). A trailing DLE with nothing to escape (only on malformed input) is dropped.

Types

type Builder

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

Builder builds C0DATA documents in compact form. Methods chain. Names (file/group/header) reject control bytes — the first such error is recorded and reported by Err; record field values are byte-transparent and DLE-escaped automatically.

func (*Builder) Block added in v0.2.0

func (b *Builder) Block(text string) *Builder

Block writes a document-mode content block (RS + escaped text).

func (*Builder) Bytes

func (b *Builder) Bytes() []byte

Bytes returns the built buffer.

func (*Builder) EOT

func (b *Builder) EOT() *Builder

EOT writes an end-of-document marker.

func (*Builder) ETB

func (b *Builder) ETB() *Builder

ETB writes a stream-mode commit marker.

func (*Builder) ETBPayload added in v0.2.0

func (b *Builder) ETBPayload(payload string) *Builder

ETBPayload writes a stream-mode commit marker followed by an integrity payload. The payload may not contain control bytes (it is terminated by the next control code on read); the first such error is recorded and reported by Err, and the payload is not written.

func (*Builder) Err

func (b *Builder) Err() error

Err returns the first error encountered (e.g. a control byte in a name).

func (*Builder) Field added in v0.2.0

func (b *Builder) Field(value string) *Builder

Field writes a single field value (US + escaped value), for building a record's fields individually.

func (*Builder) File

func (b *Builder) File(name string) *Builder

File writes a file/database scope (FS + name).

func (*Builder) Group

func (b *Builder) Group(name string, headers []string) *Builder

Group writes a group/table scope (GS + name) with optional SOH headers (pass nil for none).

func (*Builder) Header

func (b *Builder) Header(names []string) *Builder

Header writes a standalone SOH header.

func (*Builder) Item added in v0.2.0

func (b *Builder) Item(text string) *Builder

Item writes a document-mode list item (US + escaped text).

func (*Builder) ListField added in v0.2.0

func (b *Builder) ListField(items ...string) *Builder

ListField writes a field whose value is a flat list (spec: "arrays are US-separated values inside STX/ETX"): US, STX, the items separated by US (each DLE-escaped), ETX. Read back with Record.List.

func (*Builder) Nested added in v0.2.0

func (b *Builder) Nested(fn func(*Builder)) *Builder

Nested writes a nested sub-structure: STX, whatever fn writes, ETX.

func (*Builder) Record

func (b *Builder) Record(fields ...string) *Builder

Record writes a record with positional fields. A Go string may carry any bytes, so binary fields are fine.

func (*Builder) Ref added in v0.2.0

func (b *Builder) Ref(name string) *Builder

Ref writes a reference to a named group (ENQ + name). Reference targets are names, so a control byte is recorded via Err like any other name.

func (*Builder) RefPath added in v0.2.0

func (b *Builder) RefPath(path ...string) *Builder

RefPath writes a path reference (group, record id, optional field): ENQ, STX, the segments separated by US, ETX. Segments are names, so a control byte is recorded via Err.

func (*Builder) Section added in v0.2.0

func (b *Builder) Section(name string, depth int) *Builder

Section writes a document-mode section: GS repeated depth times, then the name.

type Document

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

Document is a zero-copy navigator for a full C0DATA document.

func NewDocument

func NewDocument(buf []byte) *Document

NewDocument indexes a document buffer (FS/GS/RS/US structure).

func (*Document) Group

func (d *Document) Group(i int) *Group

Group returns top-level group i.

func (*Document) GroupByName

func (d *Document) GroupByName(name string) *Group

GroupByName returns the group with the given name, or nil if none.

func (*Document) GroupCount

func (d *Document) GroupCount() int

GroupCount returns the number of top-level groups.

func (*Document) GroupNames

func (d *Document) GroupNames() [][]byte

GroupNames returns all top-level group names.

func (*Document) Groups

func (d *Document) Groups() []*Group

Groups returns all top-level groups.

func (*Document) Name

func (d *Document) Name() []byte

Name returns the document/file name (text after FS); empty if no FS.

type FileLog

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

FileLog is an append-only log file with ETB commits. Each commit is flushed and, when sync is set, fsync'd.

func OpenLog

func OpenLog(path string) (*FileLog, error)

OpenLog opens an append-only log file, repairing any torn tail first (truncating to the last commit). Each commit is fsync'd.

func OpenLogSync

func OpenLogSync(path string, sync bool) (*FileLog, error)

OpenLogSync is OpenLog with explicit control over per-commit fsync.

func (*FileLog) Batch

func (l *FileLog) Batch(fn func(*Builder)) error

Batch appends several records under a single commit (an atomic batch).

func (*FileLog) Close

func (l *FileLog) Close() error

Close closes the log file.

func (*FileLog) Header

func (l *FileLog) Header(names []string) error

Header appends an SOH header as a committed block.

func (*FileLog) Record

func (l *FileLog) Record(fields ...string) error

Record appends one record as a committed block.

type Group

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

Group is a group within a document; read it as a Table.

func (*Group) HasHeader

func (g *Group) HasHeader() bool

HasHeader reports whether the group has an SOH header.

func (*Group) Name

func (g *Group) Name() []byte

Name returns the group name.

func (*Group) Raw

func (g *Group) Raw() []byte

Raw returns the group's bytes.

func (*Group) Record

func (g *Group) Record(i int) *Record

Record returns record i.

func (*Group) RecordCount

func (g *Group) RecordCount() int

RecordCount returns the number of records.

func (*Group) Table

func (g *Group) Table() *Table

Table reads the group as a Table.

type Record

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

Record is a zero-copy accessor for a single record within a table.

func (*Record) Field

func (r *Record) Field(n int) []byte

Field returns field n. Respects DLE escaping and STX/ETX nesting; the field is raw (use Value to decode escapes).

func (*Record) FieldCount

func (r *Record) FieldCount() int

FieldCount returns the number of fields (N separators yield N+1 fields).

func (*Record) Fields

func (r *Record) Fields() [][]byte

Fields returns all fields as sub-slices.

func (*Record) List added in v0.2.0

func (r *Record) List(n int) [][]byte

List returns field n as a flat list (see Builder.ListField): the items of its STX/ETX scope, split on top-level US, with escapes decoded. A field that is not a list comes back as a single item; an empty list scope yields an empty slice.

func (*Record) Raw

func (r *Record) Raw() []byte

Raw returns the entire record's bytes.

func (*Record) Value

func (r *Record) Value(n int) []byte

Value returns field n with DLE escapes decoded.

func (*Record) Values

func (r *Record) Values() [][]byte

Values returns all logical field values (escapes decoded).

type StreamReader

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

StreamReader scans an append-only log for ETB commit markers and exposes only the committed region. Zero-copy: accessors return sub-slices of the buffer.

func NewStreamReader

func NewStreamReader(buf []byte) *StreamReader

NewStreamReader scans buf for ETB commits.

func (*StreamReader) Block

func (s *StreamReader) Block(i int) []byte

Block returns committed block i (marker and payload excluded).

func (*StreamReader) BlockCount

func (s *StreamReader) BlockCount() int

BlockCount returns the number of committed blocks.

func (*StreamReader) Blocks

func (s *StreamReader) Blocks() [][]byte

Blocks returns all committed blocks.

func (*StreamReader) Committed

func (s *StreamReader) Committed() []byte

Committed returns the committed region.

func (*StreamReader) CommittedEnd

func (s *StreamReader) CommittedEnd() int

CommittedEnd returns the offset just past the last commit marker and payload.

func (*StreamReader) Table

func (s *StreamReader) Table() *Table

Table reads the committed region as a Table.

func (*StreamReader) Tail

func (s *StreamReader) Tail() []byte

Tail returns the uncommitted trailing bytes.

func (*StreamReader) Torn

func (s *StreamReader) Torn() bool

Torn reports whether uncommitted bytes trail the last commit marker.

type StreamWriter

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

StreamWriter appends ETB-committed blocks to any io.Writer. Each block and its ETB are written as one unit. For files, prefer OpenLog (torn-tail repair + per-commit fsync).

func NewStreamWriter

func NewStreamWriter(w io.Writer) *StreamWriter

NewStreamWriter returns a StreamWriter over w.

func (*StreamWriter) Batch

func (sw *StreamWriter) Batch(fn func(*Builder)) error

Batch appends several records under a single commit (an atomic batch).

func (*StreamWriter) Header

func (sw *StreamWriter) Header(names []string) error

Header appends an SOH header as a committed block.

func (*StreamWriter) Record

func (sw *StreamWriter) Record(fields ...string) error

Record appends one record as a committed block.

type Table

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

Table is a zero-copy accessor for a tabular C0DATA group.

func NewTable

func NewTable(buf []byte) *Table

NewTable indexes a tabular group at the start of buf.

func NewTableAt

func NewTableAt(buf []byte, offset int) *Table

NewTableAt indexes a tabular group starting at offset (e.g. a group's GS).

func (*Table) Header

func (t *Table) Header(i int) []byte

Header returns header field i.

func (*Table) HeaderCount

func (t *Table) HeaderCount() int

HeaderCount returns the number of header fields.

func (*Table) Headers

func (t *Table) Headers() [][]byte

Headers returns all header names.

func (*Table) Name

func (t *Table) Name() []byte

Name returns the group/table name as a sub-slice of the buffer.

func (*Table) Record

func (t *Table) Record(i int) *Record

Record returns record i.

func (*Table) RecordCount

func (t *Table) RecordCount() int

RecordCount returns the number of records.

func (*Table) Records

func (t *Table) Records() []*Record

Records returns all records.

type Token

type Token struct {
	Type TokenType
	// Start and End are byte offsets into the buffer; End is exclusive.
	Start, End int
}

Token is a span of the source buffer.

func Tokenize

func Tokenize(buf []byte) ([]Token, error)

Tokenize returns all tokens, or the first error if the buffer is malformed.

func (Token) Value

func (t Token) Value(buf []byte) []byte

Value returns the token's bytes as a sub-slice of buf. Zero-copy.

type TokenType

type TokenType int

TokenType is the kind of a token emitted by the Tokenizer.

const (
	TokenData TokenType = iota // data content between control codes
	TokenSOH
	TokenSTX
	TokenETX
	TokenEOT
	TokenENQ
	TokenETB
	TokenSUB
	TokenFS
	TokenGS
	TokenRS
	TokenUS
)

type Tokenizer

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

Tokenizer scans a buffer for control codes, yielding tokens as offsets.

func NewTokenizer

func NewTokenizer(buf []byte) *Tokenizer

NewTokenizer returns a Tokenizer over buf.

func (*Tokenizer) Next

func (tz *Tokenizer) Next() (tok Token, ok bool, err error)

Next returns the next token. ok is false at end of input. A non-nil err (unassigned code or dangling DLE) ends iteration.

type UnassignedCodeError

type UnassignedCodeError struct {
	Byte byte
	Pos  int
}

UnassignedCodeError reports a control byte (< 0x20) that is not assigned.

func (*UnassignedCodeError) Error

func (e *UnassignedCodeError) Error() string

Jump to

Keyboard shortcuts

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