toytlv

package module
v0.2.1 Latest Latest
Warning

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

Go to latest
Published: Apr 18, 2024 License: MIT Imports: 9 Imported by: 4

README

ToyTLV

TLV or Type-Length-Value is the default way to implement binary protocols. You may ask, why not Protobuf? The answer is, Protobuf is a TLV protocol, as almost every other binary protocol out there. ToyTLV is a bare-bones TLV, nothing else.

This lib implements a really simple TLV where every record type is a letter (A-Z), while the length is either 8- or 32-bit. The value of a record has arbitrary structure, ToyTLV mandates nothing in this regard. Hint: nesting TLV records is trivial.

More formally, a ToyTLV record can go in 3 forms:

  1. long: the type letter is uppercase [A-Z], the length is a little-endian uint32,
  2. short: the type letter is lowercase [a-z], the length is uint8. That is merely an optimization to handle lots of small records (like short strings).
  3. tiny: the type is known in advance, the length is ASCII [0-9]. This is for tiny records (e.g. small ints), where one byte of overhead can make a difference (that happens).

The lib implements basic ToyTLV file and network I/O. That is:

  • reading/writing TLV files,
  • basic TLV over TCP fun: connecting, listening, reconnecting with exponential backoff, and otherwise managing the connections (see TCPDepot).

That is all it does.

Documentation

Index

Constants

View Source
const CaseBit uint8 = 'a' - 'A'
View Source
const DefaultPreBufLength = 4096
View Source
const MAX_RETRY_PERIOD = time.Minute
View Source
const MIN_RETRY_PERIOD = time.Second / 2
View Source
const MaxOutQueueLen = 1 << 20 // 16MB of pointers is a lot
View Source
const MinRecommendedRead = 512
View Source
const MinRecommendedWrite = 400
View Source
const TYPICAL_MTU = 1500

Variables

View Source
var ErrAddressUnknown = errors.New("address unknown")
View Source
var ErrBadRecord = errors.New("bad TLV record format")
View Source
var ErrDisconnected = errors.New("disconnected by user")
View Source
var ErrIncomplete = errors.New("incomplete data")

Functions

func Append

func Append(into []byte, lit byte, body ...[]byte) (res []byte)

Append appends a record to the buffer; note that uppercase type is always explicit, lowercase can be defaulted.

func AppendHeader

func AppendHeader(into []byte, lit byte, bodylen int) (ret []byte)

Feeds the header into the buffer. Subtle: lower-case lit allows for defaulting, uppercase must be explicit.

func AppendRead added in v0.1.8

func AppendRead(buf []byte, rdr io.Reader, lenHint int) ([]byte, error)

AppendRead reads data from io.Reader into the *spare space* of the provided buffer, i.e. those cap(buf)-len(buf) vacant bytes. If the spare space is smaller than lenHint, allocates (as reading less bytes might be unwise).

func AppendTiny added in v0.2.0

func AppendTiny(into []byte, lit byte, body []byte) (res []byte)

func CloseHeader

func CloseHeader(buf []byte, bookmark int)

CloseHeader closes a streamed TLV record

func Concat

func Concat(msg ...[]byte) []byte

func Incomplete added in v0.1.8

func Incomplete(data []byte) int

Incomplete returns the number of supposedly yet-unread bytes. 0 for complete, -1 for bad format, >0 for least-necessary read to complete either header or record.

func Join

func Join(records ...[]byte) (ret toyqueue.Records)

func Lit

func Lit(rec []byte) byte

func OpenHeader

func OpenHeader(buf []byte, lit byte) (bookmark int, res []byte)

OpenHeader opens a streamed TLV record; use append() to create the record body, then call CloseHeader(&buf, bookmark)

func ProbeHeader

func ProbeHeader(data []byte) (lit byte, hdrlen, bodylen int)

ProbeHeader probes a TLV record header. Return values:

  • 0 0 0 incomplete header
  • '-' 0 0 bad format
  • 'A' 2 123 success

func ProbeHeaders

func ProbeHeaders(lits string, data []byte) int

func ReadBuf

func ReadBuf(buf []byte, rdr io.Reader) ([]byte, error)

func Record

func Record(lit byte, body ...[]byte) []byte

Record composes a record of a given type

func Records

func Records(lit byte, bodies ...[]byte) (recs toyqueue.Records)

func RoundPage added in v0.1.8

func RoundPage(l int) int

func Split

func Split(data []byte) (recs toyqueue.Records, rest []byte, err error)

func Take

func Take(lit byte, data []byte) (body, rest []byte)

Take is used to read safe TLV inputs (e.g. from own storage) with record types known in advance.

func TakeAny

func TakeAny(data []byte) (lit byte, body, rest []byte)

TakeAny is used for safe TLV inputs when record types can vary.

func TakeAnyRecord

func TakeAnyRecord(data []byte) (lit byte, rec, rest []byte)

func TakeAnyWary

func TakeAnyWary(data []byte) (lit byte, body, rest []byte, err error)

TakeWary reads TLV records of arbitrary type from unsafe input.

func TakeRecord

func TakeRecord(lit byte, data []byte) (rec, rest []byte)

func TakeWary

func TakeWary(lit byte, data []byte) (body, rest []byte, err error)

TakeWary reads TLV records of known type from unsafe input.

func TinyRecord added in v0.2.0

func TinyRecord(lit byte, body []byte) (tiny []byte)

func TotalLen

func TotalLen(inputs [][]byte) (sum int)

Types

type Jack

type Jack func(conn net.Conn) toyqueue.FeedDrainCloser

type ReadCloser2FeedCloser

type ReadCloser2FeedCloser struct {
	Reader io.ReadCloser
	// contains filtered or unexported fields
}

func (*ReadCloser2FeedCloser) Close

func (fs *ReadCloser2FeedCloser) Close() error

func (*ReadCloser2FeedCloser) Feed

func (fs *ReadCloser2FeedCloser) Feed() (recs toyqueue.Records, err error)

type ReadSeekCloser2FeedSeekCloser

type ReadSeekCloser2FeedSeekCloser struct {
	Reader io.ReadSeekCloser
	// contains filtered or unexported fields
}

func (*ReadSeekCloser2FeedSeekCloser) Close

func (*ReadSeekCloser2FeedSeekCloser) Feed

func (fs *ReadSeekCloser2FeedSeekCloser) Feed() (recs toyqueue.Records, err error)

func (*ReadSeekCloser2FeedSeekCloser) Seek

func (fs *ReadSeekCloser2FeedSeekCloser) Seek(offset int64, whence int) (int64, error)

type ReadSeeker2FeedSeeker

type ReadSeeker2FeedSeeker struct {
	Reader io.ReadSeeker
	// contains filtered or unexported fields
}

func (*ReadSeeker2FeedSeeker) Feed

func (fs *ReadSeeker2FeedSeeker) Feed() (recs toyqueue.Records, err error)

func (*ReadSeeker2FeedSeeker) Seek

func (fs *ReadSeeker2FeedSeeker) Seek(offset int64, whence int) (int64, error)

type Reader2Feeder

type Reader2Feeder struct {
	Reader io.Reader
	// contains filtered or unexported fields
}

Feeder reads TLV records from an io.Reader stream. Note that Feeder is buffered, i.e. it reads ahead. When doing Seek() on a file, recreate Feeder, that is cheap.

func (*Reader2Feeder) Feed

func (fs *Reader2Feeder) Feed() (recs toyqueue.Records, err error)

type TCPConn

type TCPConn struct {
	Reconnect bool
	KeepAlive bool
	// contains filtered or unexported fields
}

func (*TCPConn) Close

func (tcp *TCPConn) Close()

func (*TCPConn) Drain

func (tcp *TCPConn) Drain(recs toyqueue.Records) (err error)

func (*TCPConn) Feed

func (tcp *TCPConn) Feed() (recs toyqueue.Records, err error)

func (*TCPConn) KeepTalking

func (tcp *TCPConn) KeepTalking()

func (*TCPConn) Read

func (tcp *TCPConn) Read() (err error)

func (*TCPConn) Write

func (tcp *TCPConn) Write(data []byte) (n int, err error)

Write what we believe is a valid ToyTLV frame. Provided for io.Writer compatibility

type TCPDepot

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

A TCP server/client for the use case of real-time async communication. Differently from the case of request-response (like HTTP), we do not wait for a request, then dedicating a thread to processing, then sending back the resulting response. Instead, we constantly fan sendQueue tons of tiny messages. That dictates different work patterns than your typical HTTP/RPC server as, for example, we cannot let one slow receiver delay event transmission to all the other receivers.

func (*TCPDepot) Close

func (de *TCPDepot) Close()

func (*TCPDepot) Connect

func (de *TCPDepot) Connect(addr string) (err error)

attrib?!

func (*TCPDepot) Disconnect

func (de *TCPDepot) Disconnect(addr string) (err error)

func (*TCPDepot) DrainTo

func (de *TCPDepot) DrainTo(recs toyqueue.Records, addr string) error

func (*TCPDepot) KeepListening

func (de *TCPDepot) KeepListening(addr string)

func (*TCPDepot) Listen

func (de *TCPDepot) Listen(addr string) (err error)

func (*TCPDepot) Open

func (de *TCPDepot) Open(jack Jack)

func (*TCPDepot) StopListening

func (de *TCPDepot) StopListening(addr string) error

type WritCloser2DrainCloser

type WritCloser2DrainCloser struct {
	Writer io.WriteCloser
}

func (*WritCloser2DrainCloser) Close

func (dc *WritCloser2DrainCloser) Close() error

func (*WritCloser2DrainCloser) Drain

Having no writev() we do the next best thing: bundle writes

type Writer2Drainer

type Writer2Drainer struct {
	Writer io.Writer
}

func (*Writer2Drainer) Drain

func (d *Writer2Drainer) Drain(recs toyqueue.Records) error

Having no writev() we do the next best thing: bundle writes

Jump to

Keyboard shortcuts

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