diff --git a/zfec/__init__.py b/zfec/__init__.py index b218b92..35fc709 100644 --- a/zfec/__init__.py +++ b/zfec/__init__.py @@ -9,6 +9,9 @@ from . import _version __version__ = _version.get_versions()['version'] +OPTION_POWER_SEQUENCE = 0 +OPTION_SEQUENTIAL_INTEGERS = 1 + from ._fec import Encoder, Decoder, Error from . import easyfec, filefec, cmdline_zfec, cmdline_zunfec diff --git a/zfec/_fecmodule.c b/zfec/_fecmodule.c index 846101f..5dc3dc9 100644 --- a/zfec/_fecmodule.c +++ b/zfec/_fecmodule.c @@ -73,10 +73,11 @@ Encoder_init(Encoder *self, PyObject *args, PyObject *kwdict) { static char *kwlist[] = { "k", "m", + "option", NULL }; - int ink, inm; - if (!PyArg_ParseTupleAndKeywords(args, kwdict, "ii:Encoder.__init__", kwlist, &ink, &inm)) + int ink, inm, option = FEC_OPTION_POWER_SEQUENCE; + if (!PyArg_ParseTupleAndKeywords(args, kwdict, "ii|i:Encoder.__init__", kwlist, &ink, &inm, &option)) return -1; if (ink < 1) { @@ -95,11 +96,15 @@ Encoder_init(Encoder *self, PyObject *args, PyObject *kwdict) { PyErr_Format(py_fec_error, "Precondition violation: first argument is required to be less than or equal to the second argument, but they were %d and %d respectively", ink, inm); return -1; } + if (option != FEC_OPTION_POWER_SEQUENCE && option != FEC_OPTION_SEQUENTIAL_INTEGERS) { + PyErr_Format(py_fec_error, "Precondition violation: option third argument must be one of (%d,%d)", FEC_OPTION_POWER_SEQUENCE, FEC_OPTION_SEQUENTIAL_INTEGERS); + return -1; + } self->kk = (unsigned short)ink; self->mm = (unsigned short)inm; Py_BEGIN_ALLOW_THREADS - self->fec_matrix = fec_new(self->kk, self->mm); + self->fec_matrix = fec_new2(self->kk, self->mm, option); Py_END_ALLOW_THREADS return 0; @@ -340,11 +345,12 @@ Decoder_init(Encoder *self, PyObject *args, PyObject *kwdict) { static char *kwlist[] = { "k", "m", + "option", NULL }; - int ink, inm; - if (!PyArg_ParseTupleAndKeywords(args, kwdict, "ii:Decoder.__init__", kwlist, &ink, &inm)) + int ink, inm, option = FEC_OPTION_POWER_SEQUENCE; + if (!PyArg_ParseTupleAndKeywords(args, kwdict, "ii|i:Decoder.__init__", kwlist, &ink, &inm, &option)) return -1; if (ink < 1) { @@ -367,7 +373,7 @@ Decoder_init(Encoder *self, PyObject *args, PyObject *kwdict) { self->mm = (unsigned short)inm; Py_BEGIN_ALLOW_THREADS - self->fec_matrix = fec_new(self->kk, self->mm); + self->fec_matrix = fec_new2(self->kk, self->mm, option); Py_END_ALLOW_THREADS return 0; diff --git a/zfec/easyfec.py b/zfec/easyfec.py index c97bf34..b334e21 100644 --- a/zfec/easyfec.py +++ b/zfec/easyfec.py @@ -22,8 +22,8 @@ def ab(x): # debuggery return "%s:%s" % (len(x), "--empty--",) class Encoder(object): - def __init__(self, k, m): - self.fec = zfec.Encoder(k, m) + def __init__(self, k, m, option=0): + self.fec = zfec.Encoder(k, m, option=option) def encode(self, data): """ @@ -39,8 +39,8 @@ def encode(self, data): return self.fec.encode(l) class Decoder(object): - def __init__(self, k, m): - self.fec = zfec.Decoder(k, m) + def __init__(self, k, m, option=0): + self.fec = zfec.Decoder(k, m, option=option) def decode(self, blocks, sharenums, padlen): """ diff --git a/zfec/fec.c b/zfec/fec.c index 993f22a..24032b1 100644 --- a/zfec/fec.c +++ b/zfec/fec.c @@ -429,6 +429,11 @@ fec_free (fec_t *p) { fec_t * fec_new(unsigned short k, unsigned short n) { + return fec_new2(k, n, FEC_OPTION_POWER_SEQUENCE); +} + +fec_t * +fec_new2(unsigned short k, unsigned short n, fec_option_t option) { unsigned row, col; gf *p, *tmp_m; @@ -456,16 +461,24 @@ fec_new(unsigned short k, unsigned short n) { tmp_m[0] = 1; for (col = 1; col < k; col++) tmp_m[col] = 0; - for (p = tmp_m + k, row = 0; row + 1 < n; row++, p += k) - for (col = 0; col < k; col++) - p[col] = gf_exp[modnn (row * col)]; + if (option == FEC_OPTION_POWER_SEQUENCE) { + for (p = tmp_m + k, row = 0; row + 1 < n; row++, p += k) + for (col = 0; col < k; col++) + p[col] = gf_exp[modnn (row * col)]; + } else if (option == FEC_OPTION_SEQUENTIAL_INTEGERS) { + for (p = tmp_m + k, row = 1; row < n; row++, p += k) + for (col = 0; col < k; col++) + p[col] = gf_exp[modnn (gf_log[row] * col)]; + } else { + assert(false); + } /* * quick code to build systematic matrix: invert the top * k*k vandermonde matrix, multiply right the bottom n-k rows * by the inverse, and construct the identity matrix at the top. */ - _invert_vdm (tmp_m, k); /* much faster than _invert_mat */ + _invert_mat (tmp_m, k); /* much faster than _invert_mat */ _matmul(tmp_m + k * k, tmp_m, retval->enc_matrix + k * k, n - k, k, k); /* * the upper matrix is I so do not bother with a slow multiply diff --git a/zfec/fec.h b/zfec/fec.h index aaa4257..fc823bd 100644 --- a/zfec/fec.h +++ b/zfec/fec.h @@ -8,6 +8,25 @@ typedef unsigned char gf; +typedef enum { + /** + * This is the default option if you do not specify one. + * This instanciates the vandermonde matrix over a vector of + * powers of the primitive element. It ensures maximum + * separation between elements in the field, which enhances + * error correction performance. + */ + FEC_OPTION_POWER_SEQUENCE = 0, + /** + * This instanciates the vandermonde matrix over a vector of + * sequential integers. This choice is common among libraries + * that implement reed solomon erasure coding. + * Known libraries that do this: + * - klauspost/reedsolomon + */ + FEC_OPTION_SEQUENTIAL_INTEGERS = 1, +} fec_option_t; + typedef struct { unsigned long magic; unsigned short k, n; /* parameters of the code */ @@ -37,6 +56,12 @@ void fec_init(void); * param m the total number of blocks created */ fec_t* fec_new(unsigned short k, unsigned short m); +/** + * param k the number of blocks required to reconstruct + * param m the total number of blocks created + * param option options that control how the encoding/decoding matrix is built. + */ +fec_t* fec_new2(unsigned short k, unsigned short m, fec_option_t option); void fec_free(fec_t* p); /**