Help us shape the future of Go! Take the 2026 Go Developer Survey

mac

package
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Oct 14, 2026 License: MIT Imports: 4 Imported by: 0

Documentation

Overview

Package mac owns the bytes of a burst: the frame header, its encoding, and the integrity check that decides whether a burst is used or discarded.

The division of labour with pkg/modulation is worth stating plainly, because it is easy to blur and expensive to get wrong:

  • pkg/mac decides WHAT the bytes are: header fields, payload, CRC-32C.
  • pkg/modulation decides HOW they are transmitted: preamble, sync, tones, subcarriers, sample counts.

Consequently mac has no idea what a frequency is, and modulation has no idea what a sequence number is. That seam is what makes the PHY swappable - MFSK to OFDM - without touching the reliability machinery, and it is what lets the frame codec be tested by round-tripping bytes rather than by listening.

Index

Constants

View Source
const (
	// Magic prefixes every header. Its only job is to make a random byte
	// sequence from a wrong-length window fail fast, before the CRC gets a vote.
	Magic uint16 = 0x4D55 // "MU"

	// Version is the wire protocol version. A mismatch aborts the handshake
	// rather than attempting compatibility.
	Version uint8 = 1

	// HeaderSize is the encoded header length in bytes.
	HeaderSize = 28

	// CRCSize is the trailing integrity check.
	CRCSize = 4

	// MaxPayload is the ceiling on a single frame's payload, chosen so that a
	// frame fits comfortably inside one OFDM burst. The effective limit for any
	// given PHY is min(MaxPayload, scheme.Capacity(burstSamples)); the link layer
	// applies that, not this constant.
	MaxPayload = 512
)

Variables

View Source
var (
	// ErrShortFrame means the buffer is smaller than a header plus CRC.
	ErrShortFrame = errors.New("mac: frame too short")
	// ErrBadMagic means the buffer is not a murmur frame.
	ErrBadMagic = errors.New("mac: bad magic")
	// ErrBadVersion means a protocol version mismatch.
	ErrBadVersion = errors.New("mac: unsupported protocol version")
	// ErrBadType means an unknown frame type, which is a forward-compatibility
	// signal and must never be guessed at.
	ErrBadType = errors.New("mac: unknown frame type")
	// ErrPayloadTooLarge means a payload exceeds MaxPayload.
	ErrPayloadTooLarge = errors.New("mac: payload too large")
	// ErrCorrupt means the CRC did not match. The frame must be discarded
	// entirely.
	ErrCorrupt = errors.New("mac: frame corrupt (CRC mismatch)")
	// ErrBadLength means the declared payload length contradicts the buffer.
	ErrBadLength = errors.New("mac: declared length inconsistent with buffer")
)

Functions

func BuildAck

func BuildAck(cumulative uint32, received []uint32) (uint32, uint32)

BuildAck produces the (Ack, AckBits) pair for a receiver that has received everything up to and including cumulative, plus the listed higher sequences.

func EncodeFrame

func EncodeFrame(dst []byte, f *Frame) ([]byte, error)

EncodeFrame renders the frame into dst, which must be at least FramedLen(len(f.Payload)) bytes, and returns the used prefix. The CRC covers the header and the payload but not itself, and it is computed over the buffer exactly as transmitted. Any post-processing that alters those bytes - scrambling, whitening - therefore has to happen before this call, not after, or the receiver will compute a different CRC over the same frame.

func FramedLen

func FramedLen(n int) int

FramedLen is the on-air byte length of a frame with a payload of n bytes.

func SetAckBit

func SetAckBit(bits, ack, seq uint32) uint32

SetAckBit sets the selective-ack bit for seq, returning the updated bitmap. Sequences at or below the cumulative point are already covered, and sequences more than 32 ahead are not representable; both are no-ops rather than errors, because a receiver filling a bitmap has nothing better to do with a sequence it cannot describe.

Types

type Flags

type Flags uint8

Flags are per-frame modifiers.

const (
	// FlagHasAck marks a frame whose Ack and AckBits fields are meaningful.
	//
	// This flag exists because the alternative - reserving sequence number zero as
	// a sentinel meaning "no acknowledgement" - collides with wraparound. A link
	// that has sent 2^32 frames legitimately has zero as its highest acknowledged
	// sequence, and a protocol in which zero means "nothing" would silently stop
	// acknowledging it, retransmitting that frame until the retry limit. Making
	// the presence of an acknowledgement explicit removes the sentinel and the
	// collision with it.
	FlagHasAck Flags = 1 << iota
	// FlagRetransmit marks a frame the sender has sent before. Receivers use it
	// for duplicate suppression diagnostics, and Karn's algorithm uses it to
	// refuse to sample RTT from an ambiguous acknowledgement.
	FlagRetransmit
	// FlagLast marks the final frame of a message.
	FlagLast
	// FlagEncrypted marks a payload protected by the session AEAD.
	FlagEncrypted
)

type Frame

type Frame struct {
	Header

	Payload []byte
}

Frame is a header plus its payload.

func DecodeFrame

func DecodeFrame(src []byte) (*Frame, error)

DecodeFrame parses and verifies a frame. It deliberately accepts a buffer longer than the frame declares: the PHY hands over whatever it demodulated, and the frame's own length field decides how much of that is real. This is safe because the CRC is computed over exactly the declared bytes, so trailing noise cannot be smuggled into the payload.

func DecodeFrameInto

func DecodeFrameInto(src, buf []byte) (*Frame, error)

DecodeFrameInto parses and verifies a frame, reusing buf for the payload to keep the receive path allocation-free. buf must be at least the declared payload length; a shorter buf yields ErrBadLength rather than a partial frame.

func (*Frame) Encode

func (f *Frame) Encode() ([]byte, error)

Encode renders the frame into a freshly allocated buffer.

type Header struct {
	Type    Type
	Flags   Flags
	Payload uint16 // payload length in bytes

	// SessionID is drawn at random per session and echoed in every frame, so a
	// burst from an unrelated pair of laptops in the same room is discarded
	// without ceremony.
	SessionID uint64

	// Seq numbers this frame within its direction. Data frames number from 1 and
	// wrap through the full 2^32 space; control frames leave it at zero, because
	// the frame type already says whether a sequence is meaningful. Nothing
	// depends on zero being unused, which is what keeps wraparound harmless.
	Seq uint32

	// Ack is the highest sequence that has been received cumulatively: every
	// sequence at or below it has arrived. AckBits is a bitmap of the 32
	// sequences above Ack that have also arrived, bit 0 marking Ack+1. The
	// selective bitmap is therefore never redundant with the cumulative point,
	// which is what lets it retire frames that sit behind a hole.
	//
	// Both fields are meaningful only when FlagHasAck is set.
	Ack     uint32
	AckBits uint32
}

Header is the fixed-format prefix of every burst. Field order is chosen so that the fields needed to decide whether to even look at the rest of the frame - session and sequence - sit together and are cheap to compare before any allocation happens.

func DecodeHeader

func DecodeHeader(src []byte) (Header, error)

DecodeHeader parses a header from src.

func (Header) AckBit

func (h Header) AckBit(seq uint32) bool

AckBit reports whether seq has been acknowledged, either cumulatively or selectively, by a frame carrying these fields.

func (Header) Encode

func (h Header) Encode(dst []byte) []byte

Encode writes the header into dst, which must be at least HeaderSize bytes. The reserved byte is written as zero and must be ignored on decode, so that a future field can claim it without a version bump for old peers that only validate magic and version.

func (Header) Has

func (h Header) Has(f Flags) bool

Has reports whether a flag is set.

type Type

type Type uint8

Type identifies a frame's role in the protocol.

const (
	// TypeHello opens a session. It is the only frame a receiver will act on
	// without knowing the peer (: nothing else is ever sent
	// unsolicited).
	TypeHello Type = iota
	// TypeHelloAck answers a Hello, carrying the responder's ephemeral key.
	TypeHelloAck
	// TypeCaps exchanges capabilities: bands, profiles, codecs, versions.
	TypeCaps
	// TypeData carries application bytes.
	TypeData
	// TypeAck acknowledges data, possibly with a selective-ack bitmap.
	TypeAck
	// TypeNack requests specific retransmissions.
	TypeNack
	// TypeManifest announces a payload before it is sent.
	TypeManifest
	// TypeFin closes a transfer in one direction.
	TypeFin
	// TypeFinAck confirms closure.
	TypeFinAck
	// TypeAbort terminates a session immediately, with a reason in the payload.
	TypeAbort
	// TypePing probes liveness and measures turnaround.
	TypePing
)

func (Type) String

func (t Type) String() string

func (Type) Valid

func (t Type) Valid() bool

Valid reports whether the type is known.

Jump to

Keyboard shortcuts

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