]> git.rkrishnan.org Git - tahoe-lafs/zfec.git/blob - zfec/_fecmodule.c
1243a62ae471621ed49b74478b9917d649624e44
[tahoe-lafs/zfec.git] / zfec / _fecmodule.c
1 /**
2  * zfec -- fast forward error correction library with Python interface
3  */
4
5 #include <Python.h>
6 #include <structmember.h>
7 #include <stddef.h>
8
9
10 #if (PY_VERSION_HEX < 0x02050000)
11 typedef int Py_ssize_t;
12 #endif
13
14 #include "fec.h"
15
16 #include "stdarg.h"
17
18 static PyObject *py_fec_error;
19
20 static char fec__doc__[] = "\
21 FEC - Forward Error Correction \n\
22 ";
23
24 static char Encoder__doc__[] = "\
25 Hold static encoder state (an in-memory table for matrix multiplication), and k and m parameters, and provide {encode()} method.\n\n\
26 @param k: the number of packets required for reconstruction \n\
27 @param m: the number of packets generated \n\
28 ";
29
30 typedef struct {
31     PyObject_HEAD
32
33     /* expose these */
34     unsigned short kk;
35     unsigned short mm;
36
37     /* internal */
38     fec_t* fec_matrix;
39 } Encoder;
40
41 static PyObject *
42 Encoder_new(PyTypeObject *type, PyObject *args, PyObject *kwdict) {
43     Encoder *self;
44
45     self = (Encoder*)type->tp_alloc(type, 0);
46     if (self != NULL) {
47         self->kk = 0;
48         self->mm = 0;
49         self->fec_matrix = NULL;
50     }
51
52     return (PyObject *)self;
53 }
54
55 static int
56 Encoder_init(Encoder *self, PyObject *args, PyObject *kwdict) {
57     static char *kwlist[] = {
58         "k",
59         "m",
60         NULL
61     };
62     int ink, inm;
63     if (!PyArg_ParseTupleAndKeywords(args, kwdict, "ii:Encoder.__init__", kwlist, &ink, &inm))
64         return -1;
65
66     if (ink < 1) {
67         PyErr_Format(py_fec_error, "Precondition violation: first argument is required to be greater than or equal to 1, but it was %d", ink);
68         return -1;
69     }
70     if (inm < 1) {
71         PyErr_Format(py_fec_error, "Precondition violation: second argument is required to be greater than or equal to 1, but it was %d", inm);
72         return -1;
73     }
74     if (inm > 256) {
75         PyErr_Format(py_fec_error, "Precondition violation: second argument is required to be less than or equal to 256, but it was %d", inm);
76         return -1;
77     }
78     if (ink > inm) {
79         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);
80         return -1;
81     }
82     self->kk = (unsigned short)ink;
83     self->mm = (unsigned short)inm;
84     self->fec_matrix = fec_new(self->kk, self->mm);
85
86     return 0;
87 }
88
89 static char Encoder_encode__doc__[] = "\
90 Encode data into m packets.\n\
91 \n\
92 @param inblocks: a sequence of k buffers of data to encode -- these are the k primary blocks, i.e. the input data split into k pieces (for best performance, make it a tuple instead of a list);  All blocks are required to be the same length.\n\
93 @param desired_blocks_nums optional sequence of blocknums indicating which blocks to produce and return;  If None, all m blocks will be returned (in order).  (For best performance, make it a tuple instead of a list.)\n\
94 @returns: a list of buffers containing the requested blocks; Note that if any of the input blocks were 'primary blocks', i.e. their blocknum was < k, then the result sequence will contain a Python reference to the same Python object as was passed in.  As long as the Python object in question is immutable (i.e. a string) then you don't have to think about this detail, but if it is mutable (i.e. an array), then you have to be aware that if you subsequently mutate the contents of that object then that will also change the contents of the sequence that was returned from this call to encode().\n\
95 ";
96
97 static PyObject *
98 Encoder_encode(Encoder *self, PyObject *args) {
99     PyObject* inblocks;
100     PyObject* desired_blocks_nums = NULL; /* The blocknums of the blocks that should be returned. */
101     PyObject* result = NULL;
102
103     gf** check_blocks_produced = (gf**)alloca((self->mm - self->kk) * sizeof(PyObject*)); /* This is an upper bound -- we will actually use only num_check_blocks_produced of these elements (see below). */
104     PyObject** pystrs_produced = (PyObject**)alloca((self->mm - self->kk) * sizeof(PyObject*)); /* This is an upper bound -- we will actually use only num_check_blocks_produced of these elements (see below). */
105     unsigned num_check_blocks_produced = 0; /* The first num_check_blocks_produced elements of the check_blocks_produced array and of the pystrs_produced array will be used. */
106     const gf** incblocks = (const gf**)alloca(self->kk * sizeof(const gf*));
107     unsigned num_desired_blocks;
108     PyObject* fast_desired_blocks_nums = NULL;
109     PyObject** fast_desired_blocks_nums_items;
110     unsigned* c_desired_blocks_nums = (unsigned*)alloca(self->mm * sizeof(unsigned));
111     unsigned* c_desired_checkblocks_ids = (unsigned*)alloca((self->mm - self->kk) * sizeof(unsigned));
112     unsigned i;
113     PyObject* fastinblocks = NULL;
114     PyObject** fastinblocksitems;
115     Py_ssize_t sz, oldsz = 0;
116     unsigned char check_block_index = 0; /* index into the check_blocks_produced and (parallel) pystrs_produced arrays */
117
118     if (!PyArg_ParseTuple(args, "O|O:Encoder.encode", &inblocks, &desired_blocks_nums))
119         return NULL;
120
121     for (i=0; i<self->mm - self->kk; i++)
122         pystrs_produced[i] = NULL;
123     if (desired_blocks_nums) {
124         fast_desired_blocks_nums = PySequence_Fast(desired_blocks_nums, "Second argument (optional) was not a sequence.");
125         if (!fast_desired_blocks_nums)
126             goto err;
127         num_desired_blocks = PySequence_Fast_GET_SIZE(fast_desired_blocks_nums);
128         fast_desired_blocks_nums_items = PySequence_Fast_ITEMS(fast_desired_blocks_nums);
129         for (i=0; i<num_desired_blocks; i++) {
130             if (!PyInt_Check(fast_desired_blocks_nums_items[i])) {
131                 PyErr_Format(py_fec_error, "Precondition violation: second argument is required to contain int.");
132                 goto err;
133             }
134             c_desired_blocks_nums[i] = PyInt_AsLong(fast_desired_blocks_nums_items[i]);
135             if (c_desired_blocks_nums[i] >= self->kk)
136                 num_check_blocks_produced++;
137         }
138     } else {
139         num_desired_blocks = self->mm;
140         for (i=0; i<num_desired_blocks; i++)
141             c_desired_blocks_nums[i] = i;
142         num_check_blocks_produced = self->mm - self->kk;
143     }
144
145     fastinblocks = PySequence_Fast(inblocks, "First argument was not a sequence.");
146     if (!fastinblocks)
147         goto err;
148
149     if (PySequence_Fast_GET_SIZE(fastinblocks) != self->kk) {
150         PyErr_Format(py_fec_error, "Precondition violation: Wrong length -- first argument (the sequence of input blocks) is required to contain exactly k blocks.  len(first): %zu, k: %d", PySequence_Fast_GET_SIZE(fastinblocks), self->kk);
151         goto err;
152     }
153
154     /* Construct a C array of gf*'s of the input data. */
155     fastinblocksitems = PySequence_Fast_ITEMS(fastinblocks);
156     if (!fastinblocksitems)
157         goto err;
158
159     for (i=0; i<self->kk; i++) {
160         if (!PyObject_CheckReadBuffer(fastinblocksitems[i])) {
161             PyErr_Format(py_fec_error, "Precondition violation: %u'th item is required to offer the single-segment read character buffer protocol, but it does not.", i);
162             goto err;
163         }
164         if (PyObject_AsReadBuffer(fastinblocksitems[i], (const void**)&(incblocks[i]), &sz))
165             goto err;
166         if (oldsz != 0 && oldsz != sz) {
167             PyErr_Format(py_fec_error, "Precondition violation: Input blocks are required to be all the same length.  length of one block was: %zu, length of another block was: %zu", oldsz, sz);
168             goto err;
169         }
170         oldsz = sz;
171     }
172
173     /* Allocate space for all of the check blocks. */
174
175     for (i=0; i<num_desired_blocks; i++) {
176         if (c_desired_blocks_nums[i] >= self->kk) {
177             c_desired_checkblocks_ids[check_block_index] = c_desired_blocks_nums[i];
178             pystrs_produced[check_block_index] = PyString_FromStringAndSize(NULL, sz);
179             if (pystrs_produced[check_block_index] == NULL)
180                 goto err;
181             check_blocks_produced[check_block_index] = (gf*)PyString_AsString(pystrs_produced[check_block_index]);
182             if (check_blocks_produced[check_block_index] == NULL)
183                 goto err;
184             check_block_index++;
185         }
186     }
187     assert (check_block_index == num_check_blocks_produced);
188
189     /* Encode any check blocks that are needed. */
190     fec_encode(self->fec_matrix, incblocks, check_blocks_produced, c_desired_checkblocks_ids, num_check_blocks_produced, sz);
191
192     /* Wrap all requested blocks up into a Python list of Python strings. */
193     result = PyList_New(num_desired_blocks);
194     if (result == NULL)
195         goto err;
196     check_block_index = 0;
197     for (i=0; i<num_desired_blocks; i++) {
198         if (c_desired_blocks_nums[i] < self->kk) {
199             Py_INCREF(fastinblocksitems[c_desired_blocks_nums[i]]);
200             if (PyList_SetItem(result, i, fastinblocksitems[c_desired_blocks_nums[i]]) == -1) {
201                 Py_DECREF(fastinblocksitems[c_desired_blocks_nums[i]]);
202                 goto err;
203             }
204         } else {
205             if (PyList_SetItem(result, i, pystrs_produced[check_block_index]) == -1)
206                 goto err;
207             pystrs_produced[check_block_index] = NULL;
208             check_block_index++;
209         }
210     }
211
212     goto cleanup;
213   err:
214     for (i=0; i<num_check_blocks_produced; i++)
215         Py_XDECREF(pystrs_produced[i]);
216     Py_XDECREF(result); result = NULL;
217   cleanup:
218     Py_XDECREF(fastinblocks); fastinblocks=NULL;
219     Py_XDECREF(fast_desired_blocks_nums); fast_desired_blocks_nums=NULL;
220     return result;
221 }
222
223 static void
224 Encoder_dealloc(Encoder * self) {
225     if (self->fec_matrix)
226         fec_free(self->fec_matrix);
227     self->ob_type->tp_free((PyObject*)self);
228 }
229
230 static PyMethodDef Encoder_methods[] = {
231     {"encode", (PyCFunction)Encoder_encode, METH_VARARGS, Encoder_encode__doc__},
232     {NULL},
233 };
234
235 static PyMemberDef Encoder_members[] = {
236     {"k", T_SHORT, offsetof(Encoder, kk), READONLY, "k"},
237     {"m", T_SHORT, offsetof(Encoder, mm), READONLY, "m"},
238     {NULL} /* Sentinel */
239 };
240
241 static PyTypeObject Encoder_type = {
242     PyObject_HEAD_INIT(NULL)
243     0,                         /*ob_size*/
244     "_fec.Encoder", /*tp_name*/
245     sizeof(Encoder),             /*tp_basicsize*/
246     0,                         /*tp_itemsize*/
247     (destructor)Encoder_dealloc, /*tp_dealloc*/
248     0,                         /*tp_print*/
249     0,                         /*tp_getattr*/
250     0,                         /*tp_setattr*/
251     0,                         /*tp_compare*/
252     0,                         /*tp_repr*/
253     0,                         /*tp_as_number*/
254     0,                         /*tp_as_sequence*/
255     0,                         /*tp_as_mapping*/
256     0,                         /*tp_hash */
257     0,                         /*tp_call*/
258     0,                         /*tp_str*/
259     0,                         /*tp_getattro*/
260     0,                         /*tp_setattro*/
261     0,                         /*tp_as_buffer*/
262     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
263     Encoder__doc__,           /* tp_doc */
264     0,                         /* tp_traverse */
265     0,                         /* tp_clear */
266     0,                         /* tp_richcompare */
267     0,                         /* tp_weaklistoffset */
268     0,                         /* tp_iter */
269     0,                         /* tp_iternext */
270     Encoder_methods,             /* tp_methods */
271     Encoder_members,             /* tp_members */
272     0,                         /* tp_getset */
273     0,                         /* tp_base */
274     0,                         /* tp_dict */
275     0,                         /* tp_descr_get */
276     0,                         /* tp_descr_set */
277     0,                         /* tp_dictoffset */
278     (initproc)Encoder_init,      /* tp_init */
279     0,                         /* tp_alloc */
280     Encoder_new,                 /* tp_new */
281 };
282
283 static char Decoder__doc__[] = "\
284 Hold static decoder state (an in-memory table for matrix multiplication), and k and m parameters, and provide {decode()} method.\n\n\
285 @param k: the number of packets required for reconstruction \n\
286 @param m: the number of packets generated \n\
287 ";
288
289 typedef struct {
290     PyObject_HEAD
291
292     /* expose these */
293     unsigned short kk;
294     unsigned short mm;
295
296     /* internal */
297     fec_t* fec_matrix;
298 } Decoder;
299
300 static PyObject *
301 Decoder_new(PyTypeObject *type, PyObject *args, PyObject *kwdict) {
302     Decoder *self;
303
304     self = (Decoder*)type->tp_alloc(type, 0);
305     if (self != NULL) {
306         self->kk = 0;
307         self->mm = 0;
308         self->fec_matrix = NULL;
309     }
310
311     return (PyObject *)self;
312 }
313
314 static int
315 Decoder_init(Encoder *self, PyObject *args, PyObject *kwdict) {
316     static char *kwlist[] = {
317         "k",
318         "m",
319         NULL
320     };
321
322     int ink, inm;
323     if (!PyArg_ParseTupleAndKeywords(args, kwdict, "ii:Decoder.__init__", kwlist, &ink, &inm))
324         return -1;
325
326     if (ink < 1) {
327         PyErr_Format(py_fec_error, "Precondition violation: first argument is required to be greater than or equal to 1, but it was %d", ink);
328         return -1;
329     }
330     if (inm < 1) {
331         PyErr_Format(py_fec_error, "Precondition violation: second argument is required to be greater than or equal to 1, but it was %d", inm);
332         return -1;
333     }
334     if (inm > 256) {
335         PyErr_Format(py_fec_error, "Precondition violation: second argument is required to be less than or equal to 256, but it was %d", inm);
336         return -1;
337     }
338     if (ink > inm) {
339         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);
340         return -1;
341     }
342     self->kk = (unsigned short)ink;
343     self->mm = (unsigned short)inm;
344     self->fec_matrix = fec_new(self->kk, self->mm);
345
346     return 0;
347 }
348
349 #define SWAP(a,b,t) {t tmp; tmp=a; a=b; b=tmp;}
350
351 static char Decoder_decode__doc__[] = "\
352 Decode a list blocks into a list of segments.\n\
353 @param blocks a sequence of buffers containing block data (for best performance, make it a tuple instead of a list)\n\
354 @param blocknums a sequence of integers of the blocknum for each block in blocks (for best performance, make it a tuple instead of a list)\n\
355 \n\
356 @return a list of strings containing the segment data (i.e. ''.join(retval) yields a string containing the decoded data)\n\
357 ";
358
359 static PyObject *
360 Decoder_decode(Decoder *self, PyObject *args) {
361     PyObject*restrict blocks;
362     PyObject*restrict blocknums;
363     PyObject* result = NULL;
364
365     const gf**restrict cblocks = (const gf**restrict)alloca(self->kk * sizeof(const gf*));
366     unsigned* cblocknums = (unsigned*)alloca(self->kk * sizeof(unsigned));
367     gf**restrict recoveredcstrs = (gf**)alloca(self->kk * sizeof(gf*)); /* self->kk is actually an upper bound -- we probably won't need all of this space. */
368     PyObject**restrict recoveredpystrs = (PyObject**restrict)alloca(self->kk * sizeof(PyObject*)); /* self->kk is actually an upper bound -- we probably won't need all of this space. */
369     unsigned i;
370     PyObject*restrict fastblocknums = NULL;
371     PyObject*restrict fastblocks;
372     unsigned needtorecover=0;
373     PyObject** fastblocksitems;
374     PyObject** fastblocknumsitems;
375     Py_ssize_t sz, oldsz = 0;
376     long tmpl;
377     unsigned nextrecoveredix=0;
378
379     if (!PyArg_ParseTuple(args, "OO:Decoder.decode", &blocks, &blocknums))
380         return NULL;
381
382     for (i=0; i<self->kk; i++)
383         recoveredpystrs[i] = NULL;
384     fastblocks = PySequence_Fast(blocks, "First argument was not a sequence.");
385     if (!fastblocks)
386         goto err;
387     fastblocknums = PySequence_Fast(blocknums, "Second argument was not a sequence.");
388     if (!fastblocknums)
389         goto err;
390
391     if (PySequence_Fast_GET_SIZE(fastblocks) != self->kk) {
392         PyErr_Format(py_fec_error, "Precondition violation: Wrong length -- first argument is required to contain exactly k blocks.  len(first): %zu, k: %d", PySequence_Fast_GET_SIZE(fastblocks), self->kk);
393         goto err;
394     }
395     if (PySequence_Fast_GET_SIZE(fastblocknums) != self->kk) {
396         PyErr_Format(py_fec_error, "Precondition violation: Wrong length -- blocknums is required to contain exactly k blocks.  len(blocknums): %zu, k: %d", PySequence_Fast_GET_SIZE(fastblocknums), self->kk);
397         goto err;
398     }
399
400     /* Construct a C array of gf*'s of the data and another of C ints of the blocknums. */
401     fastblocknumsitems = PySequence_Fast_ITEMS(fastblocknums);
402     if (!fastblocknumsitems)
403         goto err;
404     fastblocksitems = PySequence_Fast_ITEMS(fastblocks);
405     if (!fastblocksitems)
406         goto err;
407
408     for (i=0; i<self->kk; i++) {
409         if (!PyInt_Check(fastblocknumsitems[i])) {
410             PyErr_Format(py_fec_error, "Precondition violation: second argument is required to contain int.");
411             goto err;
412         }
413         tmpl = PyInt_AsLong(fastblocknumsitems[i]);
414         if (tmpl < 0 || tmpl > 255) {
415             PyErr_Format(py_fec_error, "Precondition violation: block nums can't be less than zero or greater than 255.  %ld\n", tmpl);
416             goto err;
417         }
418         cblocknums[i] = (unsigned)tmpl;
419         if (cblocknums[i] >= self->kk)
420             needtorecover+=1;
421
422         if (!PyObject_CheckReadBuffer(fastblocksitems[i])) {
423             PyErr_Format(py_fec_error, "Precondition violation: %u'th item is required to offer the single-segment read character buffer protocol, but it does not.\n", i);
424             goto err;
425         }
426         if (PyObject_AsReadBuffer(fastblocksitems[i], (const void**)&(cblocks[i]), &sz))
427             goto err;
428         if (oldsz != 0 && oldsz != sz) {
429             PyErr_Format(py_fec_error, "Precondition violation: Input blocks are required to be all the same length.  length of one block was: %zu, length of another block was: %zu\n", oldsz, sz);
430             goto err;
431         }
432         oldsz = sz;
433     }
434
435     /* Move src packets into position.  At the end of this loop we want the i'th
436        element of the arrays to be the block with block number i, if that block
437        is among our inputs. */
438     for (i=0; i<self->kk;) {
439         if (cblocknums[i] >= self->kk || cblocknums[i] == i)
440             i++;
441         else {
442             /* put pkt in the right position. */
443             unsigned c = cblocknums[i];
444
445             SWAP (cblocknums[i], cblocknums[c], int);
446             SWAP (cblocks[i], cblocks[c], const gf*);
447             SWAP (fastblocksitems[i], fastblocksitems[c], PyObject*);
448         }
449     }
450
451     /* Allocate space for all of the recovered blocks. */
452     for (i=0; i<needtorecover; i++) {
453         recoveredpystrs[i] = PyString_FromStringAndSize(NULL, sz);
454         if (recoveredpystrs[i] == NULL)
455             goto err;
456         recoveredcstrs[i] = (gf*)PyString_AsString(recoveredpystrs[i]);
457         if (recoveredcstrs[i] == NULL)
458             goto err;
459     }
460
461     /* Decode any recovered blocks that are needed. */
462     fec_decode(self->fec_matrix, cblocks, recoveredcstrs, cblocknums, sz);
463
464     /* Wrap up both original primary blocks and decoded blocks into a Python list of Python strings. */
465     result = PyList_New(self->kk);
466     if (result == NULL)
467         goto err;
468     for (i=0; i<self->kk; i++) {
469         if (cblocknums[i] == i) {
470             /* Original primary block. */
471             Py_INCREF(fastblocksitems[i]);
472             if (PyList_SetItem(result, i, fastblocksitems[i]) == -1) {
473                 Py_DECREF(fastblocksitems[i]);
474                 goto err;
475             }
476         } else {
477             /* Recovered block. */
478             if (PyList_SetItem(result, i, recoveredpystrs[nextrecoveredix]) == -1)
479                 goto err;
480             recoveredpystrs[nextrecoveredix] = NULL;
481             nextrecoveredix++;
482         }
483     }
484
485     goto cleanup;
486   err:
487     for (i=0; i<self->kk; i++)
488         Py_XDECREF(recoveredpystrs[i]);
489     Py_XDECREF(result); result = NULL;
490   cleanup:
491     Py_XDECREF(fastblocks); fastblocks=NULL;
492     Py_XDECREF(fastblocknums); fastblocknums=NULL;
493     return result;
494 }
495
496 static void
497 Decoder_dealloc(Decoder * self) {
498     if (self->fec_matrix)
499         fec_free(self->fec_matrix);
500     self->ob_type->tp_free((PyObject*)self);
501 }
502
503 static PyMethodDef Decoder_methods[] = {
504     {"decode", (PyCFunction)Decoder_decode, METH_VARARGS, Decoder_decode__doc__},
505     {NULL},
506 };
507
508 static PyMemberDef Decoder_members[] = {
509     {"k", T_SHORT, offsetof(Encoder, kk), READONLY, "k"},
510     {"m", T_SHORT, offsetof(Encoder, mm), READONLY, "m"},
511     {NULL} /* Sentinel */
512 };
513
514 static PyTypeObject Decoder_type = {
515     PyObject_HEAD_INIT(NULL)
516     0,                         /*ob_size*/
517     "_fec.Decoder", /*tp_name*/
518     sizeof(Decoder),             /*tp_basicsize*/
519     0,                         /*tp_itemsize*/
520     (destructor)Decoder_dealloc, /*tp_dealloc*/
521     0,                         /*tp_print*/
522     0,                         /*tp_getattr*/
523     0,                         /*tp_setattr*/
524     0,                         /*tp_compare*/
525     0,                         /*tp_repr*/
526     0,                         /*tp_as_number*/
527     0,                         /*tp_as_sequence*/
528     0,                         /*tp_as_mapping*/
529     0,                         /*tp_hash */
530     0,                         /*tp_call*/
531     0,                         /*tp_str*/
532     0,                         /*tp_getattro*/
533     0,                         /*tp_setattro*/
534     0,                         /*tp_as_buffer*/
535     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
536     Decoder__doc__,           /* tp_doc */
537     0,                         /* tp_traverse */
538     0,                         /* tp_clear */
539     0,                         /* tp_richcompare */
540     0,                         /* tp_weaklistoffset */
541     0,                         /* tp_iter */
542     0,                         /* tp_iternext */
543     Decoder_methods,             /* tp_methods */
544     Decoder_members,             /* tp_members */
545     0,                         /* tp_getset */
546     0,                         /* tp_base */
547     0,                         /* tp_dict */
548     0,                         /* tp_descr_get */
549     0,                         /* tp_descr_set */
550     0,                         /* tp_dictoffset */
551     (initproc)Decoder_init,      /* tp_init */
552     0,                         /* tp_alloc */
553     Decoder_new,                 /* tp_new */
554 };
555
556
557 void
558 _hexwrite(unsigned char*s, size_t l) {
559   size_t i;
560   for (i = 0; i < l; i++)
561     printf("%.2x", s[i]);
562 }
563
564
565 PyObject*
566 test_from_agl(PyObject* self, PyObject* args) {
567   unsigned char b0c[8], b1c[8];
568   unsigned char b0[8], b1[8], b2[8], b3[8], b4[8];
569
570   const unsigned char *blocks[3] = {b0, b1, b2};
571   unsigned char *outblocks[2] = {b3, b4};
572   unsigned block_nums[] = {3, 4};
573
574   fec_t *const fec = fec_new(3, 5);
575
576   const unsigned char *inpkts[] = {b3, b4, b2};
577   unsigned char *outpkts[] = {b0, b1};
578   unsigned indexes[] = {3, 4, 2};
579
580   memset(b0, 1, 8);
581   memset(b1, 2, 8);
582   memset(b2, 3, 8);
583
584   /*printf("_from_c before encoding:\n");
585   printf("b0: "); _hexwrite(b0, 8); printf(", ");
586   printf("b1: "); _hexwrite(b1, 8); printf(", ");
587   printf("b2: "); _hexwrite(b2, 8); printf(", ");
588   printf("\n");*/
589
590   fec_encode(fec, blocks, outblocks, block_nums, 2, 8);
591
592   /*printf("after encoding:\n");
593   printf("b3: "); _hexwrite(b3, 8); printf(", ");
594   printf("b4: "); _hexwrite(b4, 8); printf(", ");
595   printf("\n");*/
596
597   memcpy(b0c, b0, 8); memcpy(b1c, b1, 8);
598
599   fec_decode(fec, inpkts, outpkts, indexes, 8);
600
601   /*printf("after decoding:\n");
602   printf("b0: "); _hexwrite(b0, 8); printf(", ");
603   printf("b1: "); _hexwrite(b1, 8);
604   printf("\n");*/
605
606   if ((memcmp(b0, b0c,8) == 0) && (memcmp(b1, b1c,8) == 0))
607     Py_RETURN_TRUE;
608   else
609     Py_RETURN_FALSE;
610 }
611
612 static PyMethodDef fec_functions[] = {
613     {"test_from_agl", test_from_agl, METH_NOARGS, NULL},
614     {NULL}
615 };
616
617 #ifndef PyMODINIT_FUNC  /* declarations for DLL import/export */
618 #define PyMODINIT_FUNC void
619 #endif
620 PyMODINIT_FUNC
621 init_fec(void) {
622     PyObject *module;
623     PyObject *module_dict;
624
625     if (PyType_Ready(&Encoder_type) < 0)
626         return;
627     if (PyType_Ready(&Decoder_type) < 0)
628         return;
629
630     module = Py_InitModule3("_fec", fec_functions, fec__doc__);
631     if (module == NULL)
632       return;
633
634     Py_INCREF(&Encoder_type);
635     Py_INCREF(&Decoder_type);
636
637     PyModule_AddObject(module, "Encoder", (PyObject *)&Encoder_type);
638     PyModule_AddObject(module, "Decoder", (PyObject *)&Decoder_type);
639
640     module_dict = PyModule_GetDict(module);
641     py_fec_error = PyErr_NewException("_fec.Error", NULL, NULL);
642     PyDict_SetItemString(module_dict, "Error", py_fec_error);
643 }
644
645 /**
646  * originally inspired by fecmodule.c by the Mnet Project, especially Myers
647  * Carpenter and Hauke Johannknecht
648  */