fec.go 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. // Package fec implements the systematic Reed-Solomon erasure code used to carry
  2. // handshake messages that do not fit in a single UDP packet.
  3. //
  4. // A message is split into K equally sized data symbols (the last one zero
  5. // padded). Symbol indexes 0..K-1 are the data symbols themselves; indexes
  6. // K..MaxSymbols-1 are repair symbols. Any K distinct symbols reconstruct the
  7. // message. Repair symbols are generated on demand, so a sender can emit fresh
  8. // ones on every retransmit and a receiver can merge everything it has seen.
  9. package fec
  10. import (
  11. "errors"
  12. "fmt"
  13. "slices"
  14. )
  15. // MaxSymbols is the number of distinct symbols, data and repair together, one
  16. // message can be encoded into. It is bounded by the size of GF(2^8).
  17. const MaxSymbols = 256
  18. var (
  19. ErrInvalidLayout = errors.New("invalid fec layout")
  20. ErrIndexRange = errors.New("symbol index out of range")
  21. ErrSymbolSize = errors.New("symbol has the wrong size")
  22. ErrNotEnough = errors.New("not enough symbols to decode")
  23. ErrCorrupt = errors.New("decoded message has non-zero padding")
  24. )
  25. // Layout describes how a message is split into symbols. Both ends derive it
  26. // from the message length alone, so it never has to be sent.
  27. type Layout struct {
  28. MsgLen int // length of the message in bytes
  29. K int // number of data symbols
  30. SymbolSize int // size of every symbol, data and repair
  31. }
  32. // NewLayout returns the layout for a message of msgLen bytes carried in symbols
  33. // of at most maxSymbolSize bytes. Symbols are balanced, so padding is always
  34. // less than K bytes.
  35. func NewLayout(msgLen, maxSymbolSize int) (Layout, error) {
  36. if msgLen <= 0 || maxSymbolSize <= 0 {
  37. return Layout{}, ErrInvalidLayout
  38. }
  39. k := (msgLen + maxSymbolSize - 1) / maxSymbolSize
  40. if k >= MaxSymbols {
  41. return Layout{}, fmt.Errorf("%w: %d data symbols", ErrInvalidLayout, k)
  42. }
  43. return Layout{
  44. MsgLen: msgLen,
  45. K: k,
  46. SymbolSize: (msgLen + k - 1) / k,
  47. }, nil
  48. }
  49. // Encoder produces symbols for one message.
  50. type Encoder struct {
  51. layout Layout
  52. data [][]byte
  53. }
  54. // NewEncoder splits msg into data symbols. They alias msg, all but a padded
  55. // last symbol, so msg must not change while the Encoder is in use.
  56. func NewEncoder(msg []byte, maxSymbolSize int) (*Encoder, error) {
  57. l, err := NewLayout(len(msg), maxSymbolSize)
  58. if err != nil {
  59. return nil, err
  60. }
  61. data := make([][]byte, l.K)
  62. for i := range data {
  63. data[i] = msg[i*l.SymbolSize : min((i+1)*l.SymbolSize, len(msg))]
  64. }
  65. if last := data[l.K-1]; len(last) < l.SymbolSize {
  66. data[l.K-1] = append(make([]byte, 0, l.SymbolSize), last...)[:l.SymbolSize]
  67. }
  68. return &Encoder{layout: l, data: data}, nil
  69. }
  70. func (e *Encoder) Layout() Layout {
  71. return e.layout
  72. }
  73. // Symbol appends the symbol with the given index to dst and returns the result.
  74. func (e *Encoder) Symbol(dst []byte, index int) ([]byte, error) {
  75. if index < 0 || index >= MaxSymbols {
  76. return nil, ErrIndexRange
  77. }
  78. if index < e.layout.K {
  79. return append(dst, e.data[index]...), nil
  80. }
  81. start := len(dst)
  82. dst = append(dst, make([]byte, e.layout.SymbolSize)...)
  83. out := dst[start:]
  84. for col, d := range e.data {
  85. mulAdd(out, d, coefficient(index, col))
  86. }
  87. return dst, nil
  88. }
  89. // Decoder collects symbols for one message until it can reconstruct it.
  90. // It stores at most K symbols, so its memory is bounded by the layout no matter
  91. // how many symbols arrive.
  92. //
  93. // TODO(in-place decode): for receivers that already hold state for the message,
  94. // the initiator's msg2 slots and a pending responder's msg3, store into one
  95. // buffer of K*SymbolSize (plus room for the Nebula header) allocated from the
  96. // layout: data symbol i at offset i*SymbolSize, repair symbols parked in the
  97. // slots of data symbols not seen yet (moved to another empty slot if that data
  98. // symbol arrives; one always exists while fewer than K are held). Decode then
  99. // folds the known columns into each parked repair symbol in place and runs
  100. // Gauss-Jordan across those rows in place; a Cauchy submatrix needs no
  101. // pivoting, so each solution lands in the slot of its own column and the
  102. // buffer is the packet. That removes the per-symbol copies, about 10KB and a
  103. // dozen allocations per msg2, at the
  104. // cost of holding K*SymbolSize from the first fragment and destroying the
  105. // input on a failed decode. It is not for the responder's msg1 table, which
  106. // must never allocate from a claimed size.
  107. type Decoder struct {
  108. layout Layout
  109. have [MaxSymbols]bool
  110. syms map[int][]byte
  111. }
  112. // NewDecoder returns a decoder for a message of msgLen bytes that was encoded
  113. // with the same maxSymbolSize.
  114. func NewDecoder(msgLen, maxSymbolSize int) (*Decoder, error) {
  115. l, err := NewLayout(msgLen, maxSymbolSize)
  116. if err != nil {
  117. return nil, err
  118. }
  119. return &Decoder{layout: l, syms: make(map[int][]byte, l.K)}, nil
  120. }
  121. func (d *Decoder) Layout() Layout {
  122. return d.layout
  123. }
  124. // Stored returns the number of symbols held.
  125. func (d *Decoder) Stored() int {
  126. return len(d.syms)
  127. }
  128. // Has reports whether the symbol with the given index is held.
  129. func (d *Decoder) Has(index int) bool {
  130. return index >= 0 && index < MaxSymbols && d.have[index]
  131. }
  132. // Seen returns which symbol indexes are held.
  133. func (d *Decoder) Seen() [MaxSymbols]bool {
  134. return d.have
  135. }
  136. // Ready reports whether enough symbols have arrived to decode.
  137. func (d *Decoder) Ready() bool {
  138. return len(d.syms) >= d.layout.K
  139. }
  140. // Add stores a copy of the symbol. Duplicates, and anything beyond the first K
  141. // distinct symbols, are ignored. It returns whether the decoder is ready.
  142. func (d *Decoder) Add(index int, sym []byte) (bool, error) {
  143. if index < 0 || index >= MaxSymbols {
  144. return d.Ready(), ErrIndexRange
  145. }
  146. if len(sym) != d.layout.SymbolSize {
  147. return d.Ready(), ErrSymbolSize
  148. }
  149. if d.have[index] || d.Ready() {
  150. return d.Ready(), nil
  151. }
  152. d.have[index] = true
  153. d.syms[index] = append([]byte(nil), sym...)
  154. return d.Ready(), nil
  155. }
  156. // Decode reconstructs the message from the stored symbols and appends it to
  157. // dst. Decoding needs the padding of the last symbol too, so dst has to have
  158. // room for K*SymbolSize bytes or it is grown to.
  159. func (d *Decoder) Decode(dst []byte) ([]byte, error) {
  160. if !d.Ready() {
  161. return nil, ErrNotEnough
  162. }
  163. l := d.layout
  164. start := len(dst)
  165. dst = slices.Grow(dst, l.K*l.SymbolSize)[:start+l.K*l.SymbolSize]
  166. out := dst[start:]
  167. var missing, repair []int
  168. for i := range l.K {
  169. if s, ok := d.syms[i]; ok {
  170. copy(out[i*l.SymbolSize:], s)
  171. } else {
  172. clear(out[i*l.SymbolSize : (i+1)*l.SymbolSize])
  173. missing = append(missing, i)
  174. }
  175. }
  176. if len(missing) > 0 {
  177. for i := l.K; i < MaxSymbols && len(repair) < len(missing); i++ {
  178. if d.have[i] {
  179. repair = append(repair, i)
  180. }
  181. }
  182. if len(repair) < len(missing) {
  183. return nil, ErrNotEnough
  184. }
  185. // Each repair symbol r satisfies r = sum_col coefficient(r, col) * data[col].
  186. // Move the known data columns to the left hand side, leaving a square
  187. // Cauchy system in the missing columns.
  188. rhs := make([][]byte, len(repair))
  189. m := make([][]byte, len(repair))
  190. for ri, row := range repair {
  191. rhs[ri] = append([]byte(nil), d.syms[row]...)
  192. m[ri] = make([]byte, len(missing))
  193. mi := 0
  194. for col := range l.K {
  195. if mi < len(missing) && missing[mi] == col {
  196. m[ri][mi] = coefficient(row, col)
  197. mi++
  198. continue
  199. }
  200. mulAdd(rhs[ri], out[col*l.SymbolSize:(col+1)*l.SymbolSize], coefficient(row, col))
  201. }
  202. }
  203. if !invert(m) {
  204. // Unreachable for a Cauchy matrix; kept so a broken invariant fails loudly.
  205. return nil, errors.New("fec: singular decode matrix")
  206. }
  207. for mi, col := range missing {
  208. dst := out[col*l.SymbolSize : (col+1)*l.SymbolSize]
  209. for ri := range repair {
  210. mulAdd(dst, rhs[ri], m[mi][ri])
  211. }
  212. }
  213. }
  214. for _, b := range out[l.MsgLen:] {
  215. if b != 0 {
  216. return nil, ErrCorrupt
  217. }
  218. }
  219. return dst[:start+l.MsgLen], nil
  220. }