Documentation
¶
Overview ¶
Package wire implements MoQT wire-format primitives per draft-ietf-moq-transport-19: variable-length integers (§1.4.1, RFC 9000 §16), reason phrases (§1.4.4), track namespaces (§2.4.1), key-value pairs used in SETUP options (§1.4.3, §10.3.1), and control-message framing (§10).
Encoding follows an append-style: builders accumulate bytes into a Writer. Decoding uses a stateful Reader bounded by an input buffer; running past the buffer yields ErrShortBuffer, which the message layer maps to a session-level PROTOCOL_VIOLATION (§3.5).
For streaming decoding (e.g. data uni-streams), use StreamReader which wraps an io.Reader and exposes the same Decoder interface as Reader.
Index ¶
- Constants
- Variables
- func AppendVarint(dst []byte, v uint64) []byte
- func NewByteReader(r io.Reader) io.ByteReader
- func ParseVarint(b []byte) (uint64, int, error)
- func ReadFrame(r io.Reader) (uint64, []byte, error)
- func ReadVarint(r io.ByteReader) (uint64, error)
- func VarintLen(v uint64) int
- func WriteFrame(w io.Writer, msgType uint64, payload []byte) error
- type Decoder
- type KVPair
- type Reader
- func (r *Reader) Empty() bool
- func (r *Reader) FixedBytes(n int) ([]byte, error)
- func (r *Reader) KVPair(prev uint64) (KVPair, uint64, error)
- func (r *Reader) KVPairsRemaining() ([]KVPair, error)
- func (r *Reader) ReasonPhrase() (string, error)
- func (r *Reader) Remaining() int
- func (r *Reader) RemainingBytes() []byte
- func (r *Reader) Scanner() *Scanner
- func (r *Reader) TrackNamespace() (TrackNamespace, error)
- func (r *Reader) UInt8() (uint8, error)
- func (r *Reader) Varint() (uint64, error)
- func (r *Reader) VarintBytes() ([]byte, error)
- type Scanner
- type StreamReader
- type TrackNamespace
- type Writer
- func (w *Writer) Bytes() []byte
- func (w *Writer) FixedBytes(p []byte)
- func (w *Writer) KVPair(p KVPair, prev uint64) uint64
- func (w *Writer) KVPairs(pairs []KVPair)
- func (w *Writer) ReasonPhrase(s string)
- func (w *Writer) Reset()
- func (w *Writer) TrackNamespace(ns TrackNamespace)
- func (w *Writer) UInt8(v uint8)
- func (w *Writer) Varint(v uint64)
- func (w *Writer) VarintBytes(p []byte)
Constants ¶
const MaxControlMessagePayload = 0xFFFF
MaxControlMessagePayload is the largest payload that fits in a control message's 16-bit Length field (§10).
const MaxFullTrackNameBytes = 4096
MaxFullTrackNameBytes is the upper bound on the sum of all namespace field lengths plus the track name length per §2.4.1.
const MaxKVPairValueBytes = 0xFFFF
MaxKVPairValueBytes is the per-pair byte-value cap from §1.4.3.
const MaxReasonPhraseBytes = 1024
MaxReasonPhraseBytes is the §1.4.4 cap on an encoded reason phrase. Reader rejects anything longer, and Writer.ReasonPhrase truncates to it.
const MaxTrackNamespaceFields = 32
MaxTrackNamespaceFields is the upper bound on tuple count per §2.4.1.
Variables ¶
var ErrFieldTooLarge = errors.New("moqt/wire: field exceeds maximum size")
ErrFieldTooLarge is returned by StreamReader when a length-prefixed field claims more bytes than MaxStreamFieldSize. Callers should treat it as a malformed message (PROTOCOL_VIOLATION, §3.5).
var ErrShortBuffer = errors.New("moqt/wire: short buffer")
ErrShortBuffer is returned when a read would consume bytes past the end of the input buffer. Callers should treat this as a malformed message.
var MaxStreamFieldSize = 16 << 20 // 16 MiB
MaxStreamFieldSize bounds a single length-prefixed field (object payload, properties blob, name, …) that StreamReader will allocate for. Because a StreamReader reads from an unbounded io.Reader, FixedBytes refuses to pre-allocate more than this for a peer-supplied length, so a malicious peer cannot trigger an unbounded allocation by claiming a huge length before sending the bytes. (The in-memory Reader is already bounded by its buffer and is not subject to this limit.) The default is generous enough for large media objects such as 4K keyframes; deployments carrying larger objects can raise it.
Functions ¶
func AppendVarint ¶
AppendVarint appends the minimal leading-ones encoding of v to dst and returns the extended slice.
func NewByteReader ¶
func NewByteReader(r io.Reader) io.ByteReader
NewByteReader adapts an io.Reader to io.ByteReader by reading a single byte per call, with no buffering or look-ahead, so a varint read leaves the underlying reader positioned exactly after the varint.
func ParseVarint ¶
ParseVarint decodes a leading-ones varint from the front of b, returning the value and the number of bytes consumed. It returns ErrShortBuffer if b is shorter than the encoding the first byte announces.
func ReadFrame ¶
ReadFrame reads a single MoQT control-message frame (Type + Length + Payload) from r, returning the message type and the payload bytes. The returned payload is freshly allocated; the caller owns it.
ReadFrame returns io.EOF only when r reports EOF before the type byte has been read; once any byte has been consumed, a truncated frame surfaces as io.ErrUnexpectedEOF.
func ReadVarint ¶
func ReadVarint(r io.ByteReader) (uint64, error)
ReadVarint decodes a leading-ones varint from r, reading exactly the bytes of one encoding (never any look-ahead), so it is safe to call repeatedly on the same underlying stream.
Types ¶
type Decoder ¶
type Decoder interface {
Varint() (uint64, error)
UInt8() (uint8, error)
FixedBytes(n int) ([]byte, error)
VarintBytes() ([]byte, error)
}
Decoder is the read-side interface shared by Reader (in-memory) and StreamReader (streaming io.Reader). Parse methods in the message package accept Decoder so they work in both contexts.
type KVPair ¶
KVPair is a MoQT Key-Value-Pair (§1.4.3). When Type is even, IntVal carries the value (encoded as a single varint). When Type is odd, ByteVal carries the value (length-prefixed bytes).
KVPairs are used for SETUP Options (§10.3.1); they appear delta-encoded by Type within a list, with the running "previous type" starting at zero.
type Reader ¶
type Reader struct {
// contains filtered or unexported fields
}
Reader consumes MoQT wire primitives from an in-memory buffer. It tracks the read offset; partial reads do not advance the offset.
func NewReader ¶
NewReader returns a Reader over buf. buf is not copied; the caller must not mutate it while the Reader is in use.
func (*Reader) FixedBytes ¶
FixedBytes reads exactly n bytes. The returned slice is a fresh copy that the caller owns; mutating it does not affect the Reader's buffer, and retaining it does not pin the buffer for GC. Zero-length reads return nil.
func (*Reader) KVPair ¶
KVPair reads a single KVPair using prev as the running previous Type, and returns the pair plus the new previous Type.
func (*Reader) KVPairsRemaining ¶
KVPairsRemaining reads KVPairs until the reader is empty. This is used for SETUP, where Setup Options span the entire control-message payload (§10.3).
func (*Reader) ReasonPhrase ¶
ReasonPhrase reads a varint-length-prefixed UTF-8 string per §1.4.4. The maximum allowed length is 1024 bytes; exceeding this yields an error that the caller should map to PROTOCOL_VIOLATION.
func (*Reader) RemainingBytes ¶
RemainingBytes consumes and returns a copy of all unconsumed bytes. Used when a message has a trailing variable-length field bounded only by the outer frame length (e.g. Track Properties in SUBSCRIBE_OK / PUBLISH). Zero-length returns nil.
func (*Reader) TrackNamespace ¶
func (r *Reader) TrackNamespace() (TrackNamespace, error)
TrackNamespace reads a TrackNamespace per §2.4.1. Each field must be at least one byte; the tuple count must not exceed MaxTrackNamespaceFields. The returned slices are owned by the caller (see Reader.FixedBytes).
func (*Reader) VarintBytes ¶
VarintBytes reads a varint length followed by that many bytes. The returned slice is owned by the caller (see FixedBytes).
type Scanner ¶
type Scanner struct {
// contains filtered or unexported fields
}
Scanner is a sticky-error decoding cursor over a Reader. Each accessor reads one field into the supplied pointer and records the first error it hits; once an error is recorded every later accessor is a no-op until Err is consulted. It removes the repetitive per-field error handling that otherwise dominates message Parse methods:
func (m *Subscribe) Parse(r *wire.Reader) error {
s := r.Scanner()
s.Varint(&m.RequestID)
s.TrackNamespace(&m.Namespace)
s.VarintBytes(&m.Name)
if err := s.Err(); err != nil {
return err
}
return m.Parameters.parse(r)
}
A Scanner delegates to its Reader and advances the same read offset, so the underlying Reader stays usable directly after the Scanner (e.g. for Parameters.parse or a RemainingBytes tail) once Err reports no error.
Scanner only wraps the in-memory Reader; the streaming StreamReader / Decoder path is unaffected.
func (*Scanner) KVPairsRemaining ¶
KVPairsRemaining reads delta-encoded KV pairs to end-of-buffer into dst.
func (*Scanner) ReasonPhrase ¶
ReasonPhrase reads a §1.4.4 reason phrase into dst.
func (*Scanner) TrackNamespace ¶
func (s *Scanner) TrackNamespace(dst *TrackNamespace)
TrackNamespace reads a §2.4.1 track namespace into dst.
func (*Scanner) VarintBytes ¶
VarintBytes reads a varint-length-prefixed byte slice into dst.
type StreamReader ¶
type StreamReader struct {
// contains filtered or unexported fields
}
StreamReader wraps an io.Reader and exposes the same Decoder interface as Reader. It is intended for parsing self-delimiting wire objects directly from a QUIC uni-stream without buffering the entire object first.
func NewStreamReader ¶
func NewStreamReader(r io.Reader) *StreamReader
NewStreamReader returns a StreamReader over r. r should already be buffered (e.g. a *bufio.Reader) for efficiency; StreamReader does not add its own buffering layer.
func (*StreamReader) FixedBytes ¶
func (s *StreamReader) FixedBytes(n int) ([]byte, error)
FixedBytes reads exactly n bytes.
func (*StreamReader) UInt8 ¶
func (s *StreamReader) UInt8() (uint8, error)
UInt8 reads a single byte.
func (*StreamReader) Varint ¶
func (s *StreamReader) Varint() (uint64, error)
Varint reads a MoQT leading-ones varint (§1.4.1) from the underlying stream.
func (*StreamReader) VarintBytes ¶
func (s *StreamReader) VarintBytes() ([]byte, error)
VarintBytes reads a varint length then that many bytes.
type TrackNamespace ¶
type TrackNamespace [][]byte
TrackNamespace is an ordered set of 0..32 binary fields (§2.4.1).
func Namespace ¶
func Namespace(parts ...string) TrackNamespace
Namespace builds a TrackNamespace from string fields — the ergonomic form of the TrackNamespace{[]byte("a"), []byte("b")} literal. Each argument becomes one §2.4.1 field, in order. Namespace fields MAY contain arbitrary bytes; for non-UTF-8 fields use the [][]byte literal directly.
func (TrackNamespace) ByteLen ¶
func (ns TrackNamespace) ByteLen() int
ByteLen reports the sum of field lengths (used to enforce the 4096-byte Full Track Name limit alongside the Track Name's length).
func (TrackNamespace) HasPrefix ¶
func (ns TrackNamespace) HasPrefix(prefix TrackNamespace) bool
HasPrefix reports whether prefix is a (non-strict) prefix of ns in the field-by-field sense of §2.4.1. A zero-length prefix matches every ns, matching the §6.1 "Either message with zero Track Namespace fields indicates the sender is interested in all namespaces" rule used by SUBSCRIBE_NAMESPACE / SUBSCRIBE_TRACKS matching.
Fields are compared as opaque binary; namespace components MAY contain any bytes per §2.4.1.
func (TrackNamespace) String ¶
func (ns TrackNamespace) String() string
String renders the namespace as "/comp1/comp2/..." with each component shown verbatim. Intended for log and error messages; callers that need a strict serialization should use Writer.TrackNamespace.
type Writer ¶
type Writer struct {
// contains filtered or unexported fields
}
Writer accumulates encoded MoQT bytes. The zero value is ready to use.
func NewWriter ¶
NewWriter returns a Writer that appends to buf (which may be nil). Use Bytes to retrieve the accumulated output.
func (*Writer) Bytes ¶
Bytes returns the accumulated output. The returned slice aliases the Writer's internal buffer.
func (*Writer) FixedBytes ¶
FixedBytes appends raw bytes without any length prefix.
func (*Writer) KVPair ¶
KVPair appends a single KVPair using prev as the running previous Type, and returns the new previous Type. The first pair in a list passes prev=0.
func (*Writer) KVPairs ¶
KVPairs appends a list of KVPairs with delta encoding starting from prev=0. Pairs are sorted by Type before encoding so callers do not need to order them.
func (*Writer) ReasonPhrase ¶
ReasonPhrase appends a reason phrase per §1.4.4, truncating to MaxReasonPhraseBytes.
Truncating rather than encoding as-is because a Writer method has no way to report an error, so the alternative is emitting a frame that every conforming peer must treat as a PROTOCOL_VIOLATION — losing the tail of a diagnostic string is strictly better than losing the session that was trying to report it. The reason phrase is not always ours to bound: REQUEST_ERROR and PUBLISH_ERROR carry one built from a token verifier's error text, and a third-party [TokenVerifier] can return a string of any length.
The cut lands on a rune boundary, since §1.4.4 specifies UTF-8 and slicing mid-rune would produce a phrase the peer decodes as replacement characters.
func (*Writer) Reset ¶
func (w *Writer) Reset()
Reset clears the writer's buffer, allowing it to be reused.
func (*Writer) TrackNamespace ¶
func (w *Writer) TrackNamespace(ns TrackNamespace)
TrackNamespace appends a TrackNamespace per §2.4.1.
func (*Writer) VarintBytes ¶
VarintBytes appends a varint length followed by the bytes themselves.