|
|
|
|
@ -7,8 +7,7 @@
|
|
|
|
|
struct rs_codec { |
|
|
|
|
unsigned n; |
|
|
|
|
unsigned k; |
|
|
|
|
size_t block_size; |
|
|
|
|
size_t payload_size; |
|
|
|
|
size_t max_block_size; |
|
|
|
|
uint8_t gf_log[256]; |
|
|
|
|
uint8_t gf_exp[510]; |
|
|
|
|
uint8_t *generator; |
|
|
|
|
@ -108,8 +107,7 @@ int rs_init(struct rs_codec **out, unsigned n, unsigned r, size_t block_size) {
|
|
|
|
|
} |
|
|
|
|
ctx->n = n; |
|
|
|
|
ctx->k = k; |
|
|
|
|
ctx->block_size = block_size; |
|
|
|
|
ctx->payload_size = block_size - RS_HEADER_SIZE; |
|
|
|
|
ctx->max_block_size = block_size; |
|
|
|
|
ctx->generator = ctx->storage; |
|
|
|
|
ctx->work = ctx->storage + n * k; |
|
|
|
|
unsigned value = 1; |
|
|
|
|
@ -127,32 +125,34 @@ int rs_init(struct rs_codec **out, unsigned n, unsigned r, size_t block_size) {
|
|
|
|
|
return rc; |
|
|
|
|
} |
|
|
|
|
*out = ctx; |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "reed_solomon: initialized n=%u r=%u block_size=%zu input_size=%zu memory=%zu", |
|
|
|
|
n, r, block_size, k * ctx->payload_size, bytes); |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "reed_solomon: initialized n=%u r=%u max_block_size=%zu input_capacity=%zu memory=%zu", |
|
|
|
|
n, r, block_size, k * (block_size - RS_HEADER_SIZE), bytes); |
|
|
|
|
return RS_OK; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void rs_free(struct rs_codec *ctx) { |
|
|
|
|
if (!ctx) return; |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "reed_solomon: free n=%u r=%u block_size=%zu", ctx->n, ctx->n - ctx->k, ctx->block_size); |
|
|
|
|
DEBUG_INFO(DEBUG_CATEGORY_GENERAL, "reed_solomon: free n=%u r=%u max_block_size=%zu", ctx->n, ctx->n - ctx->k, ctx->max_block_size); |
|
|
|
|
u_free(ctx); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
size_t rs_input_size(const struct rs_codec *ctx) { |
|
|
|
|
size_t rs_input_capacity(const struct rs_codec *ctx) { |
|
|
|
|
if (!ctx) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "reed_solomon: input_size requires a context"); |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "reed_solomon: input_capacity requires a context"); |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
return ctx->k * ctx->payload_size; |
|
|
|
|
return ctx->k * (ctx->max_block_size - RS_HEADER_SIZE); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Записать заголовки и исходные субблоки, затем вычислить r проверочных субблоков. */ |
|
|
|
|
int rs_encode(struct rs_codec *ctx, uint64_t seq, const uint8_t *input, uint8_t *const blocks[]) { |
|
|
|
|
if (!ctx || !input || !blocks) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "reed_solomon: invalid encode ctx=%p input=%p blocks=%p", |
|
|
|
|
(void *)ctx, (const void *)input, (const void *)blocks); |
|
|
|
|
int rs_encode(struct rs_codec *ctx, uint64_t seq, const uint8_t *input, size_t length, |
|
|
|
|
uint8_t *const blocks[], size_t *block_size) { |
|
|
|
|
if (!ctx || !input || !blocks || !block_size || !length || length > rs_input_capacity(ctx)) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "reed_solomon: invalid encode ctx=%p input=%p blocks=%p length=%zu size_out=%p", |
|
|
|
|
(void *)ctx, (const void *)input, (const void *)blocks, length, (void *)block_size); |
|
|
|
|
return RS_ERR_INVALID_ARG; |
|
|
|
|
} |
|
|
|
|
size_t payload_size = (length + ctx->k - 1) / ctx->k; |
|
|
|
|
for (unsigned i = 0; i < ctx->n; i++) { |
|
|
|
|
if (!blocks[i]) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "reed_solomon: missing encode buffer seq=%llu subblock=%u", (unsigned long long)seq, i); |
|
|
|
|
@ -163,30 +163,38 @@ int rs_encode(struct rs_codec *ctx, uint64_t seq, const uint8_t *input, uint8_t
|
|
|
|
|
for (unsigned j = 0; j < 8; j++) blocks[i][j] = (uint8_t)(seq >> (56 - 8 * j)); |
|
|
|
|
blocks[i][8] = (uint8_t)i; |
|
|
|
|
uint8_t *payload = blocks[i] + RS_HEADER_SIZE; |
|
|
|
|
memset(payload, 0, payload_size); |
|
|
|
|
if (i < ctx->k) { |
|
|
|
|
memcpy(payload, input + i * ctx->payload_size, ctx->payload_size); |
|
|
|
|
size_t offset = i * payload_size; |
|
|
|
|
size_t take = offset < length ? length - offset : 0; |
|
|
|
|
if (take > payload_size) take = payload_size; |
|
|
|
|
if (take) memcpy(payload, input + offset, take); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
memset(payload, 0, ctx->payload_size); |
|
|
|
|
for (unsigned j = 0; j < ctx->k; j++) { |
|
|
|
|
uint8_t factor = ctx->generator[i * ctx->k + j]; |
|
|
|
|
if (!factor) continue; |
|
|
|
|
const uint8_t *source = input + j * ctx->payload_size; |
|
|
|
|
for (size_t b = 0; b < ctx->payload_size; b++) payload[b] ^= rs_multiply(ctx, factor, source[b]); |
|
|
|
|
const uint8_t *source = blocks[j] + RS_HEADER_SIZE; |
|
|
|
|
for (size_t b = 0; b < payload_size; b++) payload[b] ^= rs_multiply(ctx, factor, source[b]); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_GENERAL, "reed_solomon: encoded seq=%llu n=%u r=%u input_size=%zu block_size=%zu", |
|
|
|
|
(unsigned long long)seq, ctx->n, ctx->n - ctx->k, ctx->k * ctx->payload_size, ctx->block_size); |
|
|
|
|
*block_size = RS_HEADER_SIZE + payload_size; |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_GENERAL, "reed_solomon: encoded seq=%llu n=%u r=%u length=%zu padding=%zu block_size=%zu max=%zu", |
|
|
|
|
(unsigned long long)seq, ctx->n, ctx->n - ctx->k, length, ctx->k * payload_size - length, |
|
|
|
|
*block_size, ctx->max_block_size); |
|
|
|
|
return RS_OK; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Проверить всю пачку и восстановить только отсутствующие исходные субблоки. */ |
|
|
|
|
int rs_decode(struct rs_codec *ctx, const uint8_t *const blocks[], uint8_t *output) { |
|
|
|
|
if (!ctx || !blocks || !output) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "reed_solomon: invalid decode ctx=%p blocks=%p output=%p", |
|
|
|
|
(void *)ctx, (const void *)blocks, (void *)output); |
|
|
|
|
int rs_decode(struct rs_codec *ctx, const uint8_t *const blocks[], size_t block_size, |
|
|
|
|
uint8_t *output, size_t output_capacity) { |
|
|
|
|
if (!ctx || !blocks || !output || block_size <= RS_HEADER_SIZE || block_size > ctx->max_block_size |
|
|
|
|
|| output_capacity < ctx->k * (block_size - RS_HEADER_SIZE)) { |
|
|
|
|
DEBUG_ERROR(DEBUG_CATEGORY_GENERAL, "reed_solomon: invalid decode ctx=%p blocks=%p output=%p block_size=%zu capacity=%zu", |
|
|
|
|
(void *)ctx, (const void *)blocks, (void *)output, block_size, output_capacity); |
|
|
|
|
return RS_ERR_INVALID_ARG; |
|
|
|
|
} |
|
|
|
|
size_t payload_size = block_size - RS_HEADER_SIZE; |
|
|
|
|
const uint8_t *rows[RS_MAX_BLOCKS]; |
|
|
|
|
unsigned selected[RS_MAX_BLOCKS]; |
|
|
|
|
unsigned count = 0; |
|
|
|
|
@ -221,21 +229,21 @@ int rs_decode(struct rs_codec *ctx, const uint8_t *const blocks[], uint8_t *outp
|
|
|
|
|
if (rc != RS_OK) return rc; |
|
|
|
|
} |
|
|
|
|
for (unsigned i = 0; i < ctx->k; i++) { |
|
|
|
|
uint8_t *target = output + i * ctx->payload_size; |
|
|
|
|
uint8_t *target = output + i * payload_size; |
|
|
|
|
if (blocks[i]) { |
|
|
|
|
memcpy(target, blocks[i] + RS_HEADER_SIZE, ctx->payload_size); |
|
|
|
|
memcpy(target, blocks[i] + RS_HEADER_SIZE, payload_size); |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
memset(target, 0, ctx->payload_size); |
|
|
|
|
memset(target, 0, payload_size); |
|
|
|
|
for (unsigned j = 0; j < ctx->k; j++) { |
|
|
|
|
uint8_t factor = ctx->work[i * 2 * ctx->k + ctx->k + j]; |
|
|
|
|
if (!factor) continue; |
|
|
|
|
const uint8_t *source = blocks[selected[j]] + RS_HEADER_SIZE; |
|
|
|
|
for (size_t b = 0; b < ctx->payload_size; b++) target[b] ^= rs_multiply(ctx, factor, source[b]); |
|
|
|
|
for (size_t b = 0; b < payload_size; b++) target[b] ^= rs_multiply(ctx, factor, source[b]); |
|
|
|
|
} |
|
|
|
|
DEBUG_TRACE(DEBUG_CATEGORY_GENERAL, "reed_solomon: recovered seq=%llu subblock=%u", (unsigned long long)seq, i); |
|
|
|
|
} |
|
|
|
|
DEBUG_DEBUG(DEBUG_CATEGORY_GENERAL, "reed_solomon: decoded seq=%llu received=%u recovered=%u output_size=%zu", |
|
|
|
|
(unsigned long long)seq, count, missing, ctx->k * ctx->payload_size); |
|
|
|
|
(unsigned long long)seq, count, missing, ctx->k * payload_size); |
|
|
|
|
return RS_OK; |
|
|
|
|
} |
|
|
|
|
|