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