]> git.rkrishnan.org Git - tahoe-lafs/zfec.git/blob - zfec/zfec/_fecmodule.c
trivial: try to silence a gcc warning about prototypes
[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", self->kk);
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", self->mm);
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", self->mm);
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     fec_free(self->fec_matrix);
224     self->ob_type->tp_free((PyObject*)self);
225 }
226
227 static PyMethodDef Encoder_methods[] = {
228     {"encode", (PyCFunction)Encoder_encode, METH_VARARGS, Encoder_encode__doc__},
229     {NULL},
230 };
231
232 static PyMemberDef Encoder_members[] = {
233     {"k", T_SHORT, offsetof(Encoder, kk), READONLY, "k"},
234     {"m", T_SHORT, offsetof(Encoder, mm), READONLY, "m"},
235     {NULL} /* Sentinel */
236 };
237
238 static PyTypeObject Encoder_type = {
239     PyObject_HEAD_INIT(NULL)
240     0,                         /*ob_size*/
241     "_fec.Encoder", /*tp_name*/
242     sizeof(Encoder),             /*tp_basicsize*/
243     0,                         /*tp_itemsize*/
244     (destructor)Encoder_dealloc, /*tp_dealloc*/
245     0,                         /*tp_print*/
246     0,                         /*tp_getattr*/
247     0,                         /*tp_setattr*/
248     0,                         /*tp_compare*/
249     0,                         /*tp_repr*/
250     0,                         /*tp_as_number*/
251     0,                         /*tp_as_sequence*/
252     0,                         /*tp_as_mapping*/
253     0,                         /*tp_hash */
254     0,                         /*tp_call*/
255     0,                         /*tp_str*/
256     0,                         /*tp_getattro*/
257     0,                         /*tp_setattro*/
258     0,                         /*tp_as_buffer*/
259     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
260     Encoder__doc__,           /* tp_doc */
261     0,                         /* tp_traverse */
262     0,                         /* tp_clear */
263     0,                         /* tp_richcompare */
264     0,                         /* tp_weaklistoffset */
265     0,                         /* tp_iter */
266     0,                         /* tp_iternext */
267     Encoder_methods,             /* tp_methods */
268     Encoder_members,             /* tp_members */
269     0,                         /* tp_getset */
270     0,                         /* tp_base */
271     0,                         /* tp_dict */
272     0,                         /* tp_descr_get */
273     0,                         /* tp_descr_set */
274     0,                         /* tp_dictoffset */
275     (initproc)Encoder_init,      /* tp_init */
276     0,                         /* tp_alloc */
277     Encoder_new,                 /* tp_new */
278 };
279
280 static char Decoder__doc__[] = "\
281 Hold static decoder state (an in-memory table for matrix multiplication), and k and m parameters, and provide {decode()} method.\n\n\
282 @param k: the number of packets required for reconstruction \n\
283 @param m: the number of packets generated \n\
284 ";
285
286 typedef struct {
287     PyObject_HEAD
288
289     /* expose these */
290     short kk;
291     short mm;
292
293     /* internal */
294     fec_t* fec_matrix;
295 } Decoder;
296
297 static PyObject *
298 Decoder_new(PyTypeObject *type, PyObject *args, PyObject *kwdict) {
299     Decoder *self;
300
301     self = (Decoder*)type->tp_alloc(type, 0);
302     if (self != NULL) {
303         self->kk = 0;
304         self->mm = 0;
305         self->fec_matrix = NULL;
306     }
307
308     return (PyObject *)self;
309 }
310
311 static int
312 Decoder_init(Encoder *self, PyObject *args, PyObject *kwdict) {
313     static char *kwlist[] = {
314         "k",
315         "m",
316         NULL
317     };
318
319     int ink, inm;
320     if (!PyArg_ParseTupleAndKeywords(args, kwdict, "ii:Decoder.__init__", kwlist, &ink, &inm))
321         return -1;
322
323     if (ink < 1) {
324         PyErr_Format(py_fec_error, "Precondition violation: first argument is required to be greater than or equal to 1, but it was %d", self->kk);
325         return -1;
326     }
327     if (inm < 1) {
328         PyErr_Format(py_fec_error, "Precondition violation: second argument is required to be greater than or equal to 1, but it was %d", self->mm);
329         return -1;
330     }
331     if (inm > 256) {
332         PyErr_Format(py_fec_error, "Precondition violation: second argument is required to be less than or equal to 256, but it was %d", self->mm);
333         return -1;
334     }
335     if (ink > inm) {
336         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);
337         return -1;
338     }
339     self->kk = (short)ink;
340     self->mm = (short)inm;
341     self->fec_matrix = fec_new(self->kk, self->mm);
342
343     return 0;
344 }
345
346 #define SWAP(a,b,t) {t tmp; tmp=a; a=b; b=tmp;}
347
348 static char Decoder_decode__doc__[] = "\
349 Decode a list blocks into a list of segments.\n\
350 @param blocks a sequence of buffers containing block data (for best performance, make it a tuple instead of a list)\n\
351 @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\
352 \n\
353 @return a list of strings containing the segment data (i.e. ''.join(retval) yields a string containing the decoded data)\n\
354 ";
355
356 static PyObject *
357 Decoder_decode(Decoder *self, PyObject *args) {
358     PyObject*restrict blocks;
359     PyObject*restrict blocknums;
360     PyObject* result = NULL;
361
362     const gf**restrict cblocks = (const gf**restrict)alloca(self->kk * sizeof(const gf*));
363     unsigned* cblocknums = (unsigned*)alloca(self->kk * sizeof(unsigned));
364     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. */
365     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. */
366     unsigned i;
367     PyObject*restrict fastblocknums = NULL;
368     PyObject*restrict fastblocks;
369     unsigned needtorecover=0;
370     PyObject** fastblocksitems;
371     PyObject** fastblocknumsitems;
372     Py_ssize_t sz, oldsz = 0;
373     long tmpl;
374     unsigned nextrecoveredix=0;
375
376     if (!PyArg_ParseTuple(args, "OO:Decoder.decode", &blocks, &blocknums))
377         return NULL;
378
379     for (i=0; i<self->kk; i++)
380         recoveredpystrs[i] = NULL;
381     fastblocks = PySequence_Fast(blocks, "First argument was not a sequence.");
382     if (!fastblocks)
383         goto err;
384     fastblocknums = PySequence_Fast(blocknums, "Second argument was not a sequence.");
385     if (!fastblocknums)
386         goto err;
387
388     if (PySequence_Fast_GET_SIZE(fastblocks) != self->kk) {
389         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); 
390         goto err;
391     }
392     if (PySequence_Fast_GET_SIZE(fastblocknums) != self->kk) {
393         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); 
394         goto err;
395     }
396
397     /* Construct a C array of gf*'s of the data and another of C ints of the blocknums. */
398     fastblocknumsitems = PySequence_Fast_ITEMS(fastblocknums);
399     if (!fastblocknumsitems)
400         goto err;
401     fastblocksitems = PySequence_Fast_ITEMS(fastblocks);
402     if (!fastblocksitems)
403         goto err;
404
405     for (i=0; i<self->kk; i++) {
406         if (!PyInt_Check(fastblocknumsitems[i])) {
407             PyErr_Format(py_fec_error, "Precondition violation: second argument is required to contain int.");
408             goto err;
409         }
410         tmpl = PyInt_AsLong(fastblocknumsitems[i]);
411         if (tmpl < 0 || tmpl > 255) {
412             PyErr_Format(py_fec_error, "Precondition violation: block nums can't be less than zero or greater than 255.  %ld\n", tmpl);
413             goto err;
414         }
415         cblocknums[i] = (unsigned)tmpl;
416         if (cblocknums[i] >= self->kk)
417             needtorecover+=1;
418
419         if (!PyObject_CheckReadBuffer(fastblocksitems[i])) {
420             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);
421             goto err;
422         }
423         if (PyObject_AsReadBuffer(fastblocksitems[i], (const void**)&(cblocks[i]), &sz))
424             goto err;
425         if (oldsz != 0 && oldsz != sz) {
426             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);
427             goto err;
428         }
429         oldsz = sz;
430     }
431
432     /* Move src packets into position.  At the end of this loop we want the i'th
433        element of the arrays to be the block with block number i, if that block
434        is among our inputs. */
435     for (i=0; i<self->kk;) {
436         if (cblocknums[i] >= self->kk || cblocknums[i] == i)
437             i++;
438         else {
439             /* put pkt in the right position. */
440             unsigned c = cblocknums[i];
441
442             SWAP (cblocknums[i], cblocknums[c], int);
443             SWAP (cblocks[i], cblocks[c], const gf*);
444             SWAP (fastblocksitems[i], fastblocksitems[c], PyObject*);
445         }
446     }
447
448     /* Allocate space for all of the recovered blocks. */
449     for (i=0; i<needtorecover; i++) {
450         recoveredpystrs[i] = PyString_FromStringAndSize(NULL, sz);
451         if (recoveredpystrs[i] == NULL)
452             goto err;
453         recoveredcstrs[i] = (gf*)PyString_AsString(recoveredpystrs[i]);
454         if (recoveredcstrs[i] == NULL)
455             goto err;
456     }
457
458     /* Decode any recovered blocks that are needed. */
459     fec_decode(self->fec_matrix, cblocks, recoveredcstrs, cblocknums, sz);
460
461     /* Wrap up both original primary blocks and decoded blocks into a Python list of Python strings. */
462     result = PyList_New(self->kk);
463     if (result == NULL)
464         goto err;
465     for (i=0; i<self->kk; i++) {
466         if (cblocknums[i] == i) {
467             /* Original primary block. */
468             Py_INCREF(fastblocksitems[i]);
469             if (PyList_SetItem(result, i, fastblocksitems[i]) == -1) {
470                 Py_DECREF(fastblocksitems[i]);
471                 goto err;
472             }
473         } else {
474             /* Recovered block. */
475             if (PyList_SetItem(result, i, recoveredpystrs[nextrecoveredix]) == -1)
476                 goto err;
477             recoveredpystrs[nextrecoveredix] = NULL;
478             nextrecoveredix++;
479         }
480     }
481
482     goto cleanup;
483   err:
484     for (i=0; i<self->kk; i++)
485         Py_XDECREF(recoveredpystrs[i]);
486     Py_XDECREF(result); result = NULL;
487   cleanup:
488     Py_XDECREF(fastblocks); fastblocks=NULL;
489     Py_XDECREF(fastblocknums); fastblocknums=NULL;
490     return result;
491 }
492
493 static void
494 Decoder_dealloc(Decoder * self) {
495     fec_free(self->fec_matrix);
496     self->ob_type->tp_free((PyObject*)self);
497 }
498
499 static PyMethodDef Decoder_methods[] = {
500     {"decode", (PyCFunction)Decoder_decode, METH_VARARGS, Decoder_decode__doc__},
501     {NULL},
502 };
503
504 static PyMemberDef Decoder_members[] = {
505     {"k", T_SHORT, offsetof(Encoder, kk), READONLY, "k"},
506     {"m", T_SHORT, offsetof(Encoder, mm), READONLY, "m"},
507     {NULL} /* Sentinel */
508 };
509
510 static PyTypeObject Decoder_type = {
511     PyObject_HEAD_INIT(NULL)
512     0,                         /*ob_size*/
513     "_fec.Decoder", /*tp_name*/
514     sizeof(Decoder),             /*tp_basicsize*/
515     0,                         /*tp_itemsize*/
516     (destructor)Decoder_dealloc, /*tp_dealloc*/
517     0,                         /*tp_print*/
518     0,                         /*tp_getattr*/
519     0,                         /*tp_setattr*/
520     0,                         /*tp_compare*/
521     0,                         /*tp_repr*/
522     0,                         /*tp_as_number*/
523     0,                         /*tp_as_sequence*/
524     0,                         /*tp_as_mapping*/
525     0,                         /*tp_hash */
526     0,                         /*tp_call*/
527     0,                         /*tp_str*/
528     0,                         /*tp_getattro*/
529     0,                         /*tp_setattro*/
530     0,                         /*tp_as_buffer*/
531     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
532     Decoder__doc__,           /* tp_doc */
533     0,                         /* tp_traverse */
534     0,                         /* tp_clear */
535     0,                         /* tp_richcompare */
536     0,                         /* tp_weaklistoffset */
537     0,                         /* tp_iter */
538     0,                         /* tp_iternext */
539     Decoder_methods,             /* tp_methods */
540     Decoder_members,             /* tp_members */
541     0,                         /* tp_getset */
542     0,                         /* tp_base */
543     0,                         /* tp_dict */
544     0,                         /* tp_descr_get */
545     0,                         /* tp_descr_set */
546     0,                         /* tp_dictoffset */
547     (initproc)Decoder_init,      /* tp_init */
548     0,                         /* tp_alloc */
549     Decoder_new,                 /* tp_new */
550 };
551
552 void
553 _hexwrite(unsigned char*s, size_t l) {
554   for (size_t i = 0; i < l; i++)
555     printf("%.2x", s[i]);
556 }
557
558
559 PyObject*
560 test_from_agl(); /* <- prototype */
561
562 PyObject*
563 test_from_agl() {
564   unsigned char b0c[8], b1c[8];
565   unsigned char b0[8], b1[8], b2[8], b3[8], b4[8];
566   memset(b0, 1, 8);
567   memset(b1, 2, 8);
568   memset(b2, 3, 8);
569   const unsigned char *blocks[3] = {b0, b1, b2};
570   unsigned char *outblocks[2] = {b3, b4};
571   unsigned block_nums[] = {3, 4};
572
573   /*printf("_from_c before encoding:\n");
574   printf("b0: "); _hexwrite(b0, 8); printf(", ");
575   printf("b1: "); _hexwrite(b1, 8); printf(", ");
576   printf("b2: "); _hexwrite(b2, 8); printf(", ");
577   printf("\n");*/
578
579   fec_t *const fec = fec_new(3, 5);
580   fec_encode(fec, blocks, outblocks, block_nums, 2, 8);
581
582   /*printf("after encoding:\n");
583   printf("b3: "); _hexwrite(b3, 8); printf(", ");
584   printf("b4: "); _hexwrite(b4, 8); printf(", ");
585   printf("\n");*/
586
587   memcpy(b0c, b0, 8); memcpy(b1c, b1, 8);
588
589   const unsigned char *inpkts[] = {b3, b4, b2};
590   unsigned char *outpkts[] = {b0, b1};
591   unsigned indexes[] = {3, 4, 2};
592
593   fec_decode(fec, inpkts, outpkts, indexes, 8);
594
595   /*printf("after decoding:\n");
596   printf("b0: "); _hexwrite(b0, 8); printf(", ");
597   printf("b1: "); _hexwrite(b1, 8);
598   printf("\n");*/
599
600   if ((memcmp(b0, b0c,8) == 0) && (memcmp(b1, b1c,8) == 0))
601     Py_RETURN_TRUE;
602   else
603     Py_RETURN_FALSE;
604 }
605
606 static PyMethodDef fec_functions[] = { 
607   {"test_from_agl", test_from_agl, METH_NOARGS, NULL},
608     {NULL} 
609 };
610
611 #ifndef PyMODINIT_FUNC  /* declarations for DLL import/export */
612 #define PyMODINIT_FUNC void
613 #endif
614 PyMODINIT_FUNC
615 init_fec(void) {
616     PyObject *module;
617     PyObject *module_dict;
618
619     if (PyType_Ready(&Encoder_type) < 0)
620         return;
621     if (PyType_Ready(&Decoder_type) < 0)
622         return;
623
624     module = Py_InitModule3("_fec", fec_functions, fec__doc__);
625     if (module == NULL)
626       return;
627
628     Py_INCREF(&Encoder_type);
629     Py_INCREF(&Decoder_type);
630
631     PyModule_AddObject(module, "Encoder", (PyObject *)&Encoder_type);
632     PyModule_AddObject(module, "Decoder", (PyObject *)&Decoder_type);
633
634     module_dict = PyModule_GetDict(module);
635     py_fec_error = PyErr_NewException("_fec.Error", NULL, NULL);
636     PyDict_SetItemString(module_dict, "Error", py_fec_error);
637 }
638
639 /**
640  * originally inspired by fecmodule.c by the Mnet Project, especially Myers
641  * Carpenter and Hauke Johannknecht
642  */