code wiki / (root) / nx_fec_gf256.nx

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}