Browse Source

Allow RS batches to choose their block size from input length

master
evgeny 5 days ago
parent
commit
3477a8844c
  1. 66
      lib/reed_solomon.c
  2. 31
      lib/reed_solomon.h

66
lib/reed_solomon.c

@ -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;
}

31
lib/reed_solomon.h

@ -1,6 +1,6 @@
/**
* Reed–Solomon: кодирование и восстановление одной пачки по известным пропускам.
* rs_init задаёт n кодограмм, r проверочных и полный размер каждой кодограммы.
* rs_init задаёт n кодограмм, r проверочных и максимальный размер каждой кодограммы.
* Первые n-r кодограмм содержат исходные данные, остальные — проверочные.
* Формат: seq (8 байт, big-endian), subblock (1 байт), полезные данные.
* blocks[i] всегда соответствует subblock=i; при decode NULL обозначает пропуск.
@ -8,7 +8,8 @@
* Полученные данные и заголовки должны быть проверены транспортом на повреждения.
* Все входные/выходные буферы принадлежат вызывающему коду и не перекрываются.
* Кодек выделяет рабочую память только при init; один контекст используют последовательно.
* Неполную пачку вызывающий код дополняет нулями и отдельно сохраняет её настоящую длину.
* encode принимает действительную длину, выбирает минимальный одинаковый размер частей
* и дополняет хвост максимум n-r-1 нулями. Размер и исходную длину хранит вызывающий код.
*/
#ifndef REED_SOLOMON_H
#define REED_SOLOMON_H
@ -35,32 +36,36 @@ enum rs_result {
struct rs_codec;
/* Создать кодек: 1 <= n <= 255, 0 <= r < n, 10 <= block_size <= 4096.
/* Создать кодек: 1 <= n <= 255, 0 <= r < n, 10 <= max_block_size <= 4096.
* ctx — выходной указатель; при ошибке *ctx=NULL. Старый контекст сначала освобождают.
* Возвращает RS_OK или отрицательный rs_result.
*/
int rs_init(struct rs_codec **ctx, unsigned n, unsigned r, size_t block_size);
int rs_init(struct rs_codec **ctx, unsigned n, unsigned r, size_t max_block_size);
/* Освободить контекст и рабочую память; NULL допустим. */
void rs_free(struct rs_codec *ctx);
/* Размер входа encode и выхода decode: (n-r)*(block_size-RS_HEADER_SIZE).
/* Максимальная длина входа encode: (n-r)*(max_block_size-RS_HEADER_SIZE).
* Для NULL возвращает 0 и логирует ошибку.
*/
size_t rs_input_size(const struct rs_codec *ctx);
size_t rs_input_capacity(const struct rs_codec *ctx);
/* Читать rs_input_size(ctx) байт input, записать n кодограмм с заданным seq.
* blocks — n ненулевых указателей, каждый буфер содержит block_size байт.
* При ошибке аргументов выходные буферы не изменяются.
/* Читать 0 < length <= rs_input_capacity(ctx) байт input, записать n кодограмм.
* blocks — n ненулевых указателей на буферы ёмкостью max_block_size каждый.
* block_size получает RS_HEADER_SIZE+ceil(length/(n-r)); хвост за этим размером не меняется.
* При ошибке аргументов выходные буферы и block_size не изменяются.
*/
int rs_encode(struct rs_codec *ctx, uint64_t seq, const uint8_t *input, uint8_t *const 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);
/* Восстановить rs_input_size(ctx) байт в output из массива n указателей blocks.
* Ненулевые буферы содержат block_size байт; NULL обозначает пропуск.
/* Восстановить (n-r)*(block_size-RS_HEADER_SIZE) байт, включая нулевой хвост.
* Ненулевые буферы содержат block_size байт; NULL обозначает пропуск. Размер общий для пачки.
* output_capacity проверяется до записи; block_size не превышает максимум контекста.
* Проверяет одинаковый seq и subblock=i у всех присутствующих кодограмм.
* Входные буферы не изменяются. При ошибке output не изменяется.
*/
int rs_decode(struct rs_codec *ctx, const uint8_t *const blocks[], uint8_t *output);
int rs_decode(struct rs_codec *ctx, const uint8_t *const blocks[], size_t block_size,
uint8_t *output, size_t output_capacity);
#ifdef __cplusplus
}

Loading…
Cancel
Save