libaaruformat 1.0
Aaru Data Preservation Suite - Format Library
Loading...
Searching...
No Matches
reed_solomon.c
Go to the documentation of this file.
1/*
2 * This file is part of the Aaru Data Preservation Suite.
3 * Copyright (c) 2019-2026 Natalia Portillo.
4 *
5 * This library is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU Lesser General Public License as
7 * published by the Free Software Foundation; either version 2.1 of the
8 * License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 */
18
36
37#include <stdlib.h>
38#include <string.h>
39
40#include "reed_solomon.h"
41#include "gf256.h"
42
44{
45 uint16_t K;
46 uint16_t M;
47 uint8_t *gen;
48 uint8_t *coding;
49};
50
58static uint8_t *build_vandermonde(uint16_t K, uint16_t M)
59{
60 const uint16_t N = K + M;
61 uint8_t *V = calloc((size_t)N * K, sizeof(uint8_t));
62 if(!V) return NULL;
63
64 for(uint16_t i = 0; i < N; i++)
65 {
66 uint8_t val = 1; /* i^0 = 1 */
67 for(uint16_t j = 0; j < K; j++)
68 {
69 V[(size_t)i * K + j] = val;
70 val = gf256_mul(val, (uint8_t)i);
71 }
72 }
73 return V;
74}
75
84static int invert_matrix(const uint8_t *mat, uint8_t *inv, uint16_t K)
85{
86 /* Work on a copy to avoid modifying input */
87 uint8_t *work = malloc((size_t)K * K);
88 if(!work) return -1;
89 memcpy(work, mat, (size_t)K * K);
90
91 /* Initialize inv to identity */
92 memset(inv, 0, (size_t)K * K);
93 for(uint16_t i = 0; i < K; i++)
94 inv[(size_t)i * K + i] = 1;
95
96 /* Forward elimination */
97 for(uint16_t col = 0; col < K; col++)
98 {
99 /* Find pivot */
100 uint16_t pivot = col;
101 while(pivot < K && work[(size_t)pivot * K + col] == 0)
102 pivot++;
103 if(pivot == K) { free(work); return -1; } /* Singular */
104
105 /* Swap rows if needed */
106 if(pivot != col)
107 {
108 for(uint16_t j = 0; j < K; j++)
109 {
110 uint8_t tmp = work[(size_t)col * K + j];
111 work[(size_t)col * K + j] = work[(size_t)pivot * K + j];
112 work[(size_t)pivot * K + j] = tmp;
113
114 tmp = inv[(size_t)col * K + j];
115 inv[(size_t)col * K + j] = inv[(size_t)pivot * K + j];
116 inv[(size_t)pivot * K + j] = tmp;
117 }
118 }
119
120 /* Scale pivot row to make diagonal element 1 */
121 uint8_t diag = work[(size_t)col * K + col];
122 if(diag != 1)
123 {
124 uint8_t inv_diag = gf256_inv(diag);
125 for(uint16_t j = 0; j < K; j++)
126 {
127 work[(size_t)col * K + j] = gf256_mul(work[(size_t)col * K + j], inv_diag);
128 inv[(size_t)col * K + j] = gf256_mul(inv[(size_t)col * K + j], inv_diag);
129 }
130 }
131
132 /* Eliminate column in all other rows */
133 for(uint16_t row = 0; row < K; row++)
134 {
135 if(row == col) continue;
136 uint8_t factor = work[(size_t)row * K + col];
137 if(factor == 0) continue;
138 for(uint16_t j = 0; j < K; j++)
139 {
140 work[(size_t)row * K + j] ^= gf256_mul(factor, work[(size_t)col * K + j]);
141 inv[(size_t)row * K + j] ^= gf256_mul(factor, inv[(size_t)col * K + j]);
142 }
143 }
144 }
145
146 free(work);
147 return 0;
148}
149
150rs_context *rs_create(uint16_t K, uint16_t M)
151{
152 if(K == 0 || M == 0 || (uint32_t)K + M > 255) return NULL;
153
154 rs_context *ctx = calloc(1, sizeof(rs_context));
155 if(!ctx) return NULL;
156 ctx->K = K;
157 ctx->M = M;
158
159 const uint16_t N = K + M;
160
161 /* Build Vandermonde matrix */
162 uint8_t *V = build_vandermonde(K, M);
163 if(!V) { free(ctx); return NULL; }
164
165 /* Invert top K x K submatrix */
166 uint8_t *top_inv = malloc((size_t)K * K);
167 if(!top_inv) { free(V); free(ctx); return NULL; }
168
169 if(invert_matrix(V, top_inv, K) != 0)
170 {
171 free(top_inv);
172 free(V);
173 free(ctx);
174 return NULL;
175 }
176
177 /* Compute coding matrix = V * top_inv^(-1) so top K rows become identity */
178 ctx->coding = calloc((size_t)N * K, sizeof(uint8_t));
179 if(!ctx->coding) { free(top_inv); free(V); free(ctx); return NULL; }
180
181 for(uint16_t i = 0; i < N; i++)
182 {
183 for(uint16_t j = 0; j < K; j++)
184 {
185 uint8_t val = 0;
186 for(uint16_t m = 0; m < K; m++)
187 val ^= gf256_mul(V[(size_t)i * K + m], top_inv[(size_t)m * K + j]);
188 ctx->coding[(size_t)i * K + j] = val;
189 }
190 }
191
192 free(top_inv);
193 free(V);
194
195 /* Generator matrix = bottom M rows of the coding matrix */
196 ctx->gen = ctx->coding + (size_t)K * K;
197
198 return ctx;
199}
200
202{
203 if(!ctx) return;
204 free(ctx->coding); /* gen points inside coding, don't free separately */
205 free(ctx);
206}
207
208uint8_t rs_get_coefficient(const rs_context *ctx, uint16_t m, uint16_t k)
209{
210 return ctx->gen[(size_t)m * ctx->K + k];
211}
212
213void rs_encode_incremental(uint8_t coeff, const uint8_t *data, uint8_t *parity, size_t shard_size)
214{
215 gf256_mul_region(parity, data, coeff, shard_size);
216}
217
218int rs_decode(const rs_context *ctx, uint8_t **shards, const uint8_t *present, size_t shard_size)
219{
220 const uint16_t K = ctx->K;
221 const uint16_t M = ctx->M;
222 const uint16_t N = K + M;
223
224 /* Count erasures */
225 uint16_t num_erased = 0;
226 for(uint16_t i = 0; i < N; i++)
227 if(!present[i]) num_erased++;
228
229 if(num_erased == 0) return 0; /* Nothing to do */
230 if(num_erased > M) return -1; /* Too many erasures */
231
232 /* Build the submatrix from rows of the coding matrix corresponding to
233 * the K surviving shards. We need exactly K surviving shards to form
234 * a K x K system. */
235
236 /* Collect indices of surviving shards (pick first K) */
237 uint16_t *surviving = malloc((size_t)K * sizeof(uint16_t));
238 if(!surviving) return -2;
239
240 uint16_t s = 0;
241 for(uint16_t i = 0; i < N && s < K; i++)
242 {
243 if(present[i]) surviving[s++] = i;
244 }
245
246 if(s < K) { free(surviving); return -1; } /* Not enough surviving shards */
247
248 /* Build K x K submatrix from surviving rows of the coding matrix */
249 uint8_t *submat = malloc((size_t)K * K);
250 if(!submat) { free(surviving); return -2; }
251
252 for(uint16_t i = 0; i < K; i++)
253 memcpy(submat + (size_t)i * K, ctx->coding + (size_t)surviving[i] * K, K);
254
255 /* Invert the submatrix */
256 uint8_t *submat_inv = malloc((size_t)K * K);
257 if(!submat_inv) { free(submat); free(surviving); return -2; }
258
259 if(invert_matrix(submat, submat_inv, K) != 0)
260 {
261 free(submat_inv);
262 free(submat);
263 free(surviving);
264 return -1; /* Should not happen if coding matrix is MDS */
265 }
266
267 /* Reconstruct erased shards:
268 * For each erased shard e, compute:
269 * shard[e] = sum over j=0..K-1 of (coding[e][j] * decoded_data[j])
270 *
271 * But decoded_data = submat_inv * surviving_shards
272 * So: shard[e] = sum_j coding[e][j] * (sum_k submat_inv[j][k] * surviving_shards[k])
273 *
274 * Reorder: shard[e] = sum_k (sum_j coding[e][j] * submat_inv[j][k]) * surviving_shards[k]
275 * Let repair_row[e][k] = sum_j coding[e][j] * submat_inv[j][k]
276 */
277 for(uint16_t e = 0; e < N; e++)
278 {
279 if(present[e]) continue;
280
281 /* Compute repair coefficients for this erased shard */
282 memset(shards[e], 0, shard_size);
283
284 for(uint16_t k = 0; k < K; k++)
285 {
286 /* Compute combined coefficient: sum_j coding[e][j] * submat_inv[j][k] */
287 uint8_t coeff = 0;
288 for(uint16_t j = 0; j < K; j++)
289 coeff ^= gf256_mul(ctx->coding[(size_t)e * K + j], submat_inv[(size_t)j * K + k]);
290
291 if(coeff != 0)
292 gf256_mul_region(shards[e], shards[surviving[k]], coeff, shard_size);
293 }
294 }
295
296 free(submat_inv);
297 free(submat);
298 free(surviving);
299 return 0;
300}
uint8_t gf256_inv(uint8_t a)
Compute multiplicative inverse in GF(2^8).
Definition gf256.c:92
void gf256_mul_region(uint8_t *dst, const uint8_t *src, uint8_t coeff, size_t len)
Multiply-accumulate a region: dst[i] ^= GF_mul(src[i], coeff) for all i.
Definition gf256.c:324
uint8_t gf256_mul(uint8_t a, uint8_t b)
Multiply two elements in GF(2^8) with polynomial 0x11D.
Definition gf256.c:77
static int invert_matrix(const uint8_t *mat, uint8_t *inv, uint16_t K)
Invert a K x K matrix in-place using Gaussian elimination over GF(2^8).
void rs_free(rs_context *ctx)
Free a Reed-Solomon codec context.
rs_context * rs_create(uint16_t K, uint16_t M)
Create a Reed-Solomon codec for RS(K, M) over GF(2^8).
void rs_encode_incremental(uint8_t coeff, const uint8_t *data, uint8_t *parity, size_t shard_size)
Incrementally accumulate one data shard's contribution to one parity shard.
static uint8_t * build_vandermonde(uint16_t K, uint16_t M)
Build a Vandermonde matrix (K+M) x K in GF(2^8).
uint8_t rs_get_coefficient(const rs_context *ctx, uint16_t m, uint16_t k)
Get the generator matrix coefficient for parity shard m, data shard k.
int rs_decode(const rs_context *ctx, uint8_t **shards, const uint8_t *present, size_t shard_size)
Decode (reconstruct) erased shards.
static const uint32_t K[64]
Definition sha256.c:31
uint16_t K
Number of data shards.
uint8_t * gen
Generator matrix: M rows x K columns (row-major).
uint16_t M
Number of parity shards.
uint8_t * coding
Full coding matrix: (K+M) rows x K columns.