mirror of
https://github.com/slackhq/nebula.git
synced 2026-11-15 15:56:52 +01:00
116 lines
2.4 KiB
Go
116 lines
2.4 KiB
Go
package fec
|
|
|
|
// Arithmetic over GF(2^8) with the reducing polynomial x^8+x^4+x^3+x^2+1 (0x11d).
|
|
// Addition is XOR. Multiplication uses log/exp tables; the exp table is doubled so
|
|
// the sum of two logs never needs reducing mod 255.
|
|
|
|
const gfPoly = 0x11d
|
|
|
|
var (
|
|
gfExp [510]byte
|
|
gfLog [256]byte
|
|
)
|
|
|
|
func init() {
|
|
x := 1
|
|
for i := range 255 {
|
|
gfExp[i] = byte(x)
|
|
gfExp[i+255] = byte(x)
|
|
gfLog[x] = byte(i)
|
|
x <<= 1
|
|
if x&0x100 != 0 {
|
|
x ^= gfPoly
|
|
}
|
|
}
|
|
}
|
|
|
|
func gfMul(a, b byte) byte {
|
|
if a == 0 || b == 0 {
|
|
return 0
|
|
}
|
|
return gfExp[int(gfLog[a])+int(gfLog[b])]
|
|
}
|
|
|
|
// gfInv returns the multiplicative inverse of a. a must be non-zero.
|
|
func gfInv(a byte) byte {
|
|
return gfExp[255-int(gfLog[a])]
|
|
}
|
|
|
|
// mulAdd computes dst ^= c*src element-wise. dst must be at least as long as src.
|
|
func mulAdd(dst, src []byte, c byte) {
|
|
switch c {
|
|
case 0:
|
|
return
|
|
case 1:
|
|
for i, s := range src {
|
|
dst[i] ^= s
|
|
}
|
|
return
|
|
}
|
|
|
|
var t [256]byte
|
|
lc := int(gfLog[c])
|
|
for s := 1; s < 256; s++ {
|
|
t[s] = gfExp[int(gfLog[s])+lc]
|
|
}
|
|
for i, s := range src {
|
|
dst[i] ^= t[s]
|
|
}
|
|
}
|
|
|
|
// coefficient returns the generator matrix entry for repair symbol index row and
|
|
// data symbol index col. Repair rows are a Cauchy matrix 1/(x_row + y_col) with
|
|
// x_row = row and y_col = col: rows are always >= k and columns < k so the two
|
|
// sets are disjoint, which makes every square submatrix invertible and the
|
|
// systematic code [I; C] MDS. A row depends only on its index, never on how many
|
|
// repair symbols exist, so new repair symbols can be generated at any time.
|
|
func coefficient(row, col int) byte {
|
|
return gfInv(byte(row) ^ byte(col))
|
|
}
|
|
|
|
// invert inverts the n x n matrix m in place with Gauss-Jordan elimination.
|
|
// It returns false if m is singular.
|
|
func invert(m [][]byte) bool {
|
|
n := len(m)
|
|
inv := make([][]byte, n)
|
|
for i := range inv {
|
|
inv[i] = make([]byte, n)
|
|
inv[i][i] = 1
|
|
}
|
|
|
|
for col := range n {
|
|
pivot := -1
|
|
for r := col; r < n; r++ {
|
|
if m[r][col] != 0 {
|
|
pivot = r
|
|
break
|
|
}
|
|
}
|
|
if pivot < 0 {
|
|
return false
|
|
}
|
|
m[col], m[pivot] = m[pivot], m[col]
|
|
inv[col], inv[pivot] = inv[pivot], inv[col]
|
|
|
|
scale := gfInv(m[col][col])
|
|
for c := range n {
|
|
m[col][c] = gfMul(m[col][c], scale)
|
|
inv[col][c] = gfMul(inv[col][c], scale)
|
|
}
|
|
|
|
for r := 0; r < n; r++ {
|
|
if r == col || m[r][col] == 0 {
|
|
continue
|
|
}
|
|
f := m[r][col]
|
|
for c := range n {
|
|
m[r][c] ^= gfMul(f, m[col][c])
|
|
inv[r][c] ^= gfMul(f, inv[col][c])
|
|
}
|
|
}
|
|
}
|
|
|
|
copy(m, inv)
|
|
return true
|
|
}
|