| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216 |
- // Package fec implements the systematic Reed-Solomon erasure code used to carry
- // handshake messages that do not fit in a single UDP packet.
- //
- // A message is split into K equally sized data symbols (the last one zero
- // padded). Symbol indexes 0..K-1 are the data symbols themselves; indexes
- // K..MaxSymbols-1 are repair symbols. Any K distinct symbols reconstruct the
- // message. Repair symbols are generated on demand, so a sender can emit fresh
- // ones on every retransmit and a receiver can merge everything it has seen.
- package fec
- import (
- "errors"
- "fmt"
- )
- // MaxSymbols is the number of distinct symbols, data and repair together, one
- // message can be encoded into. It is bounded by the size of GF(2^8).
- const MaxSymbols = 256
- var (
- ErrInvalidLayout = errors.New("invalid fec layout")
- ErrIndexRange = errors.New("symbol index out of range")
- ErrSymbolSize = errors.New("symbol has the wrong size")
- ErrNotEnough = errors.New("not enough symbols to decode")
- ErrCorrupt = errors.New("decoded message has non-zero padding")
- )
- // Layout describes how a message is split into symbols. Both ends derive it
- // from the message length alone, so it never has to be sent.
- type Layout struct {
- MsgLen int // length of the message in bytes
- K int // number of data symbols
- SymbolSize int // size of every symbol, data and repair
- }
- // NewLayout returns the layout for a message of msgLen bytes carried in symbols
- // of at most maxSymbolSize bytes. Symbols are balanced, so padding is always
- // less than K bytes.
- func NewLayout(msgLen, maxSymbolSize int) (Layout, error) {
- if msgLen <= 0 || maxSymbolSize <= 0 {
- return Layout{}, ErrInvalidLayout
- }
- k := (msgLen + maxSymbolSize - 1) / maxSymbolSize
- if k >= MaxSymbols {
- return Layout{}, fmt.Errorf("%w: %d data symbols", ErrInvalidLayout, k)
- }
- return Layout{
- MsgLen: msgLen,
- K: k,
- SymbolSize: (msgLen + k - 1) / k,
- }, nil
- }
- // Encoder produces symbols for one message.
- type Encoder struct {
- layout Layout
- data [][]byte
- }
- // NewEncoder copies msg into padded data symbols.
- func NewEncoder(msg []byte, maxSymbolSize int) (*Encoder, error) {
- l, err := NewLayout(len(msg), maxSymbolSize)
- if err != nil {
- return nil, err
- }
- buf := make([]byte, l.K*l.SymbolSize)
- copy(buf, msg)
- data := make([][]byte, l.K)
- for i := range data {
- data[i] = buf[i*l.SymbolSize : (i+1)*l.SymbolSize]
- }
- return &Encoder{layout: l, data: data}, nil
- }
- func (e *Encoder) Layout() Layout {
- return e.layout
- }
- // Symbol appends the symbol with the given index to dst and returns the result.
- func (e *Encoder) Symbol(dst []byte, index int) ([]byte, error) {
- if index < 0 || index >= MaxSymbols {
- return nil, ErrIndexRange
- }
- if index < e.layout.K {
- return append(dst, e.data[index]...), nil
- }
- start := len(dst)
- dst = append(dst, make([]byte, e.layout.SymbolSize)...)
- out := dst[start:]
- for col, d := range e.data {
- mulAdd(out, d, coefficient(index, col))
- }
- return dst, nil
- }
- // Decoder collects symbols for one message until it can reconstruct it.
- // It stores at most K symbols, so its memory is bounded by the layout no matter
- // how many symbols arrive.
- type Decoder struct {
- layout Layout
- have [MaxSymbols]bool
- syms map[int][]byte
- }
- // NewDecoder returns a decoder for a message of msgLen bytes that was encoded
- // with the same maxSymbolSize.
- func NewDecoder(msgLen, maxSymbolSize int) (*Decoder, error) {
- l, err := NewLayout(msgLen, maxSymbolSize)
- if err != nil {
- return nil, err
- }
- return &Decoder{layout: l, syms: make(map[int][]byte, l.K)}, nil
- }
- func (d *Decoder) Layout() Layout {
- return d.layout
- }
- // Stored returns the number of symbols held.
- func (d *Decoder) Stored() int {
- return len(d.syms)
- }
- // Has reports whether the symbol with the given index is held.
- func (d *Decoder) Has(index int) bool {
- return index >= 0 && index < MaxSymbols && d.have[index]
- }
- // Ready reports whether enough symbols have arrived to decode.
- func (d *Decoder) Ready() bool {
- return len(d.syms) >= d.layout.K
- }
- // Add stores a copy of the symbol. Duplicates, and anything beyond the first K
- // distinct symbols, are ignored. It returns whether the decoder is ready.
- func (d *Decoder) Add(index int, sym []byte) (bool, error) {
- if index < 0 || index >= MaxSymbols {
- return d.Ready(), ErrIndexRange
- }
- if len(sym) != d.layout.SymbolSize {
- return d.Ready(), ErrSymbolSize
- }
- if d.have[index] || d.Ready() {
- return d.Ready(), nil
- }
- d.have[index] = true
- d.syms[index] = append([]byte(nil), sym...)
- return d.Ready(), nil
- }
- // Decode reconstructs the message from the stored symbols.
- func (d *Decoder) Decode() ([]byte, error) {
- if !d.Ready() {
- return nil, ErrNotEnough
- }
- l := d.layout
- out := make([]byte, l.K*l.SymbolSize)
- var missing, repair []int
- for i := range l.K {
- if s, ok := d.syms[i]; ok {
- copy(out[i*l.SymbolSize:], s)
- } else {
- missing = append(missing, i)
- }
- }
- if len(missing) > 0 {
- for i := l.K; i < MaxSymbols && len(repair) < len(missing); i++ {
- if d.have[i] {
- repair = append(repair, i)
- }
- }
- if len(repair) < len(missing) {
- return nil, ErrNotEnough
- }
- // Each repair symbol r satisfies r = sum_col coefficient(r, col) * data[col].
- // Move the known data columns to the left hand side, leaving a square
- // Cauchy system in the missing columns.
- rhs := make([][]byte, len(repair))
- m := make([][]byte, len(repair))
- for ri, row := range repair {
- rhs[ri] = append([]byte(nil), d.syms[row]...)
- m[ri] = make([]byte, len(missing))
- mi := 0
- for col := range l.K {
- if mi < len(missing) && missing[mi] == col {
- m[ri][mi] = coefficient(row, col)
- mi++
- continue
- }
- mulAdd(rhs[ri], out[col*l.SymbolSize:(col+1)*l.SymbolSize], coefficient(row, col))
- }
- }
- if !invert(m) {
- // Unreachable for a Cauchy matrix; kept so a broken invariant fails loudly.
- return nil, errors.New("fec: singular decode matrix")
- }
- for mi, col := range missing {
- dst := out[col*l.SymbolSize : (col+1)*l.SymbolSize]
- for ri := range repair {
- mulAdd(dst, rhs[ri], m[mi][ri])
- }
- }
- }
- for _, b := range out[l.MsgLen:] {
- if b != 0 {
- return nil, ErrCorrupt
- }
- }
- return out[:l.MsgLen], nil
- }
|