nx_fec_gf256.nx source
↩ module page · 173 lines · 5871 B
1// nx_fec_gf256.nx -- the FEC CEILING: Reed-Solomon erasure coding over
2// GF(256). MDS (maximum-distance-separable): an RS(n=K+R, K) code
3// recovers ANY R lost symbols, in ANY positions -- not just the specific
4// patterns XOR parity handles. This is the DSN/CCSDS/RaptorQ-class
5// rateless recovery ceiling. Sovereign, integer-only: GF(256) tables are
6// BUILT AT RUNTIME from the primitive polynomial 0x11D (sidesteps the
7// const-array-literal gap, same trick as nx_crc32c), and a CAUCHY parity
8// matrix guarantees every K x K submatrix is invertible (true MDS).
9//
10// Encoding matrix = [ I_K ; C ] where C[j][i] = 1 / ((K+j) ^ i) in GF(256)
11// (disjoint Cauchy point sets -> non-singular). Decode: take the K rows
12// for any K surviving symbols -> K x K matrix M -> data = M^-1 * survivors
13// (Gauss-Jordan over GF(256)). Reconstructs every erased symbol.
14//
15// license_tier: ORIGINAL
16
17import "nx_syscalls.nx"
18
19const GF_POLY: i64 = 0x11D // x^8+x^4+x^3+x^2+1, generator 2
20
21// ---- GF(256) tables (exp sized 512 so multiply needs no mod) ----
22func gf_build(exp: *i64, log: *i64) -> i64 {
23 var x: i64 = 1
24 var i: i64 = 0
25 while i < 255 {
26 exp[i] = x
27 log[x] = i
28 x = x << 1
29 if (x & 0x100) != 0 { x = x ^ GF_POLY }
30 i = i + 1
31 }
32 var j: i64 = 255
33 while j < 512 { exp[j] = exp[j - 255]; j = j + 1 }
34 return 0
35}
36func gf_mul(exp: *i64, log: *i64, a: i64, b: i64) -> i64 {
37 if a == 0 { return 0 }
38 if b == 0 { return 0 }
39 return exp[log[a] + log[b]]
40}
41func gf_inv(exp: *i64, log: *i64, a: i64) -> i64 { return exp[255 - log[a]] }
42
43func rs_cauchy(exp: *i64, log: *i64, j: i64, i: i64, K: i64) -> i64 {
44 return gf_inv(exp, log, (K + j) ^ i)
45}
46
47// ---- encode: K data packets (S bytes each) -> R parity packets ----
48func rs_encode(exp: *i64, log: *i64, data: *u8, K: i64, R: i64, S: i64, parity: *u8) -> i64 {
49 var jj: i64 = 0
50 while jj < R {
51 var b: i64 = 0
52 while b < S {
53 var acc: i64 = 0
54 var ii: i64 = 0
55 while ii < K {
56 let c: i64 = rs_cauchy(exp, log, jj, ii, K)
57 let dv: i64 = (data[ii * S + b] as i64) & 0xff
58 acc = acc ^ gf_mul(exp, log, c, dv)
59 ii = ii + 1
60 }
61 parity[jj * S + b] = (acc & 0xff) as u8
62 b = b + 1
63 }
64 jj = jj + 1
65 }
66 return 0
67}
68
69// ---- Gauss-Jordan invert a K x K GF(256) matrix. 0 ok, -1 singular ----
70func gf_mat_invert(exp: *i64, log: *i64, M: *i64, K: i64, Minv: *i64) -> i64 {
71 let W: i64 = 2 * K
72 let A: *i64 = sys_mmap(K * W * 8 + 16) as *i64
73 var r: i64 = 0
74 while r < K {
75 var c: i64 = 0
76 while c < K { A[r * W + c] = M[r * K + c]; c = c + 1 }
77 c = 0
78 while c < K { if c == r { A[r * W + K + c] = 1 } else { A[r * W + K + c] = 0 } c = c + 1 }
79 r = r + 1
80 }
81 var p: i64 = 0
82 while p < K {
83 if A[p * W + p] == 0 {
84 var sr: i64 = p + 1
85 var found: i64 = 0 - 1
86 while sr < K { if A[sr * W + p] != 0 { found = sr; sr = K } else { sr = sr + 1 } }
87 if found == (0 - 1) { return 0 - 1 }
88 var c2: i64 = 0
89 while c2 < W {
90 let tmp: i64 = A[p * W + c2]
91 A[p * W + c2] = A[found * W + c2]
92 A[found * W + c2] = tmp
93 c2 = c2 + 1
94 }
95 }
96 let pivinv: i64 = gf_inv(exp, log, A[p * W + p])
97 var c3: i64 = 0
98 while c3 < W { A[p * W + c3] = gf_mul(exp, log, A[p * W + c3], pivinv); c3 = c3 + 1 }
99 var rr: i64 = 0
100 while rr < K {
101 if rr != p {
102 let factor: i64 = A[rr * W + p]
103 if factor != 0 {
104 var c4: i64 = 0
105 while c4 < W {
106 A[rr * W + c4] = A[rr * W + c4] ^ gf_mul(exp, log, factor, A[p * W + c4])
107 c4 = c4 + 1
108 }
109 }
110 }
111 rr = rr + 1
112 }
113 p = p + 1
114 }
115 r = 0
116 while r < K {
117 var c: i64 = 0
118 while c < K { Minv[r * K + c] = A[r * W + K + c]; c = c + 1 }
119 r = r + 1
120 }
121 return 0
122}
123
124// ---- decode from erasures. recv = n=K+R symbols (data 0..K-1, parity
125// K..K+R-1) each S bytes; present[] flags survivors. Reconstructs all K
126// data packets into out. Returns 0, or -1 if < K survived. ----
127func rs_decode_erasures(exp: *i64, log: *i64, recv: *u8, present: *i64,
128 K: i64, R: i64, S: i64, out: *u8) -> i64 {
129 let n: i64 = K + R
130 let surv: *i64 = sys_mmap(K * 8 + 16) as *i64
131 var cnt: i64 = 0
132 var s: i64 = 0
133 while s < n {
134 if present[s] == 1 { if cnt < K { surv[cnt] = s; cnt = cnt + 1 } }
135 s = s + 1
136 }
137 if cnt < K { return 0 - 1 }
138 let M: *i64 = sys_mmap(K * K * 8 + 16) as *i64
139 var r: i64 = 0
140 while r < K {
141 let sym: i64 = surv[r]
142 var i: i64 = 0
143 while i < K {
144 if sym < K {
145 if i == sym { M[r * K + i] = 1 } else { M[r * K + i] = 0 }
146 } else {
147 M[r * K + i] = rs_cauchy(exp, log, sym - K, i, K)
148 }
149 i = i + 1
150 }
151 r = r + 1
152 }
153 let Minv: *i64 = sys_mmap(K * K * 8 + 16) as *i64
154 if gf_mat_invert(exp, log, M, K, Minv) != 0 { return 0 - 1 }
155 var di: i64 = 0
156 while di < K {
157 var b: i64 = 0
158 while b < S {
159 var acc: i64 = 0
160 var rr: i64 = 0
161 while rr < K {
162 let mv: i64 = Minv[di * K + rr]
163 let val: i64 = (recv[surv[rr] * S + b] as i64) & 0xff
164 acc = acc ^ gf_mul(exp, log, mv, val)
165 rr = rr + 1
166 }
167 out[di * S + b] = (acc & 0xff) as u8
168 b = b + 1
169 }
170 di = di + 1
171 }
172 return 0
173}