• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #define PY_SSIZE_T_CLEAN 1
2 #include <Python.h>
3 #include <bytesobject.h>
4 #include <structmember.h>
5 
6 #include <brotli/decode.h>
7 #include <brotli/encode.h>
8 
9 #if PY_MAJOR_VERSION >= 3
10 #define PyInt_Check PyLong_Check
11 #define PyInt_AsLong PyLong_AsLong
12 #else
13 #define Py_ARRAY_LENGTH(array)  (sizeof(array) / sizeof((array)[0]))
14 #endif
15 
16 static PyObject *BrotliError;
17 
18 /* -----------------------------------
19      BlocksOutputBuffer code
20    ----------------------------------- */
21 typedef struct {
22     /* List of blocks */
23     PyObject *list;
24     /* Number of whole allocated size. */
25     Py_ssize_t allocated;
26 } BlocksOutputBuffer;
27 
28 static const char unable_allocate_msg[] = "Unable to allocate output buffer.";
29 
30 /* Block size sequence */
31 #define KB (1024)
32 #define MB (1024*1024)
33 static const Py_ssize_t BUFFER_BLOCK_SIZE[] =
34     { 32*KB, 64*KB, 256*KB, 1*MB, 4*MB, 8*MB, 16*MB, 16*MB,
35       32*MB, 32*MB, 32*MB, 32*MB, 64*MB, 64*MB, 128*MB, 128*MB,
36       256*MB };
37 #undef KB
38 #undef MB
39 
40 /* According to the block sizes defined by BUFFER_BLOCK_SIZE, the whole
41    allocated size growth step is:
42     1   32 KB       +32 KB
43     2   96 KB       +64 KB
44     3   352 KB      +256 KB
45     4   1.34 MB     +1 MB
46     5   5.34 MB     +4 MB
47     6   13.34 MB    +8 MB
48     7   29.34 MB    +16 MB
49     8   45.34 MB    +16 MB
50     9   77.34 MB    +32 MB
51     10  109.34 MB   +32 MB
52     11  141.34 MB   +32 MB
53     12  173.34 MB   +32 MB
54     13  237.34 MB   +64 MB
55     14  301.34 MB   +64 MB
56     15  429.34 MB   +128 MB
57     16  557.34 MB   +128 MB
58     17  813.34 MB   +256 MB
59     18  1069.34 MB  +256 MB
60     19  1325.34 MB  +256 MB
61     20  1581.34 MB  +256 MB
62     21  1837.34 MB  +256 MB
63     22  2093.34 MB  +256 MB
64     ...
65 */
66 
67 /* Initialize the buffer, and grow the buffer.
68    Return 0 on success
69    Return -1 on failure
70 */
71 static inline int
BlocksOutputBuffer_InitAndGrow(BlocksOutputBuffer * buffer,size_t * avail_out,uint8_t ** next_out)72 BlocksOutputBuffer_InitAndGrow(BlocksOutputBuffer *buffer,
73                                size_t *avail_out, uint8_t **next_out)
74 {
75     PyObject *b;
76     const Py_ssize_t block_size = BUFFER_BLOCK_SIZE[0];
77 
78     // Ensure .list was set to NULL, for BlocksOutputBuffer_OnError().
79     assert(buffer->list == NULL);
80 
81     // The first block
82     b = PyBytes_FromStringAndSize(NULL, block_size);
83     if (b == NULL) {
84         return -1;
85     }
86 
87     // Create list
88     buffer->list = PyList_New(1);
89     if (buffer->list == NULL) {
90         Py_DECREF(b);
91         return -1;
92     }
93     PyList_SET_ITEM(buffer->list, 0, b);
94 
95     // Set variables
96     buffer->allocated = block_size;
97 
98     *avail_out = (size_t) block_size;
99     *next_out = (uint8_t*) PyBytes_AS_STRING(b);
100     return 0;
101 }
102 
103 /* Grow the buffer. The avail_out must be 0, please check it before calling.
104    Return 0 on success
105    Return -1 on failure
106 */
107 static inline int
BlocksOutputBuffer_Grow(BlocksOutputBuffer * buffer,size_t * avail_out,uint8_t ** next_out)108 BlocksOutputBuffer_Grow(BlocksOutputBuffer *buffer,
109                         size_t *avail_out, uint8_t **next_out)
110 {
111     PyObject *b;
112     const Py_ssize_t list_len = Py_SIZE(buffer->list);
113     Py_ssize_t block_size;
114 
115     // Ensure no gaps in the data
116     assert(*avail_out == 0);
117 
118     // Get block size
119     if (list_len < (Py_ssize_t) Py_ARRAY_LENGTH(BUFFER_BLOCK_SIZE)) {
120         block_size = BUFFER_BLOCK_SIZE[list_len];
121     } else {
122         block_size = BUFFER_BLOCK_SIZE[Py_ARRAY_LENGTH(BUFFER_BLOCK_SIZE) - 1];
123     }
124 
125     // Check buffer->allocated overflow
126     if (block_size > PY_SSIZE_T_MAX - buffer->allocated) {
127         PyErr_SetString(PyExc_MemoryError, unable_allocate_msg);
128         return -1;
129     }
130 
131     // Create the block
132     b = PyBytes_FromStringAndSize(NULL, block_size);
133     if (b == NULL) {
134         PyErr_SetString(PyExc_MemoryError, unable_allocate_msg);
135         return -1;
136     }
137     if (PyList_Append(buffer->list, b) < 0) {
138         Py_DECREF(b);
139         return -1;
140     }
141     Py_DECREF(b);
142 
143     // Set variables
144     buffer->allocated += block_size;
145 
146     *avail_out = (size_t) block_size;
147     *next_out = (uint8_t*) PyBytes_AS_STRING(b);
148     return 0;
149 }
150 
151 /* Finish the buffer.
152    Return a bytes object on success
153    Return NULL on failure
154 */
155 static inline PyObject *
BlocksOutputBuffer_Finish(BlocksOutputBuffer * buffer,size_t avail_out)156 BlocksOutputBuffer_Finish(BlocksOutputBuffer *buffer, size_t avail_out)
157 {
158     PyObject *result, *block;
159     const Py_ssize_t list_len = Py_SIZE(buffer->list);
160 
161     // Fast path for single block
162     if ((list_len == 1 && avail_out == 0) ||
163         (list_len == 2 && Py_SIZE(PyList_GET_ITEM(buffer->list, 1)) == (Py_ssize_t) avail_out))
164     {
165         block = PyList_GET_ITEM(buffer->list, 0);
166         Py_INCREF(block);
167 
168         Py_CLEAR(buffer->list);
169         return block;
170     }
171 
172     // Final bytes object
173     result = PyBytes_FromStringAndSize(NULL, buffer->allocated - avail_out);
174     if (result == NULL) {
175         PyErr_SetString(PyExc_MemoryError, unable_allocate_msg);
176         return NULL;
177     }
178 
179     // Memory copy
180     if (list_len > 0) {
181         char *posi = PyBytes_AS_STRING(result);
182 
183         // Blocks except the last one
184         Py_ssize_t i = 0;
185         for (; i < list_len-1; i++) {
186             block = PyList_GET_ITEM(buffer->list, i);
187             memcpy(posi, PyBytes_AS_STRING(block), Py_SIZE(block));
188             posi += Py_SIZE(block);
189         }
190         // The last block
191         block = PyList_GET_ITEM(buffer->list, i);
192         memcpy(posi, PyBytes_AS_STRING(block), Py_SIZE(block) - avail_out);
193     } else {
194         assert(Py_SIZE(result) == 0);
195     }
196 
197     Py_CLEAR(buffer->list);
198     return result;
199 }
200 
201 /* Clean up the buffer */
202 static inline void
BlocksOutputBuffer_OnError(BlocksOutputBuffer * buffer)203 BlocksOutputBuffer_OnError(BlocksOutputBuffer *buffer)
204 {
205     Py_CLEAR(buffer->list);
206 }
207 
208 
as_bounded_int(PyObject * o,int * result,int lower_bound,int upper_bound)209 static int as_bounded_int(PyObject *o, int* result, int lower_bound, int upper_bound) {
210   long value = PyInt_AsLong(o);
211   if ((value < (long) lower_bound) || (value > (long) upper_bound)) {
212     return 0;
213   }
214   *result = (int) value;
215   return 1;
216 }
217 
mode_convertor(PyObject * o,BrotliEncoderMode * mode)218 static int mode_convertor(PyObject *o, BrotliEncoderMode *mode) {
219   if (!PyInt_Check(o)) {
220     PyErr_SetString(BrotliError, "Invalid mode");
221     return 0;
222   }
223 
224   int mode_value = -1;
225   if (!as_bounded_int(o, &mode_value, 0, 255)) {
226     PyErr_SetString(BrotliError, "Invalid mode");
227     return 0;
228   }
229   *mode = (BrotliEncoderMode) mode_value;
230   if (*mode != BROTLI_MODE_GENERIC &&
231       *mode != BROTLI_MODE_TEXT &&
232       *mode != BROTLI_MODE_FONT) {
233     PyErr_SetString(BrotliError, "Invalid mode");
234     return 0;
235   }
236 
237   return 1;
238 }
239 
quality_convertor(PyObject * o,int * quality)240 static int quality_convertor(PyObject *o, int *quality) {
241   if (!PyInt_Check(o)) {
242     PyErr_SetString(BrotliError, "Invalid quality");
243     return 0;
244   }
245 
246   if (!as_bounded_int(o, quality, 0, 11)) {
247     PyErr_SetString(BrotliError, "Invalid quality. Range is 0 to 11.");
248     return 0;
249   }
250 
251   return 1;
252 }
253 
lgwin_convertor(PyObject * o,int * lgwin)254 static int lgwin_convertor(PyObject *o, int *lgwin) {
255   if (!PyInt_Check(o)) {
256     PyErr_SetString(BrotliError, "Invalid lgwin");
257     return 0;
258   }
259 
260   if (!as_bounded_int(o, lgwin, 10, 24)) {
261     PyErr_SetString(BrotliError, "Invalid lgwin. Range is 10 to 24.");
262     return 0;
263   }
264 
265   return 1;
266 }
267 
lgblock_convertor(PyObject * o,int * lgblock)268 static int lgblock_convertor(PyObject *o, int *lgblock) {
269   if (!PyInt_Check(o)) {
270     PyErr_SetString(BrotliError, "Invalid lgblock");
271     return 0;
272   }
273 
274   if (!as_bounded_int(o, lgblock, 0, 24) || (*lgblock != 0 && *lgblock < 16)) {
275     PyErr_SetString(BrotliError, "Invalid lgblock. Can be 0 or in range 16 to 24.");
276     return 0;
277   }
278 
279   return 1;
280 }
281 
compress_stream(BrotliEncoderState * enc,BrotliEncoderOperation op,uint8_t * input,size_t input_length)282 static PyObject* compress_stream(BrotliEncoderState* enc, BrotliEncoderOperation op,
283                                  uint8_t* input, size_t input_length) {
284   BROTLI_BOOL ok;
285 
286   size_t available_in = input_length;
287   const uint8_t* next_in = input;
288 
289   size_t available_out;
290   uint8_t* next_out;
291   BlocksOutputBuffer buffer = {.list=NULL};
292   PyObject *ret;
293 
294   if (BlocksOutputBuffer_InitAndGrow(&buffer, &available_out, &next_out) < 0) {
295     goto error;
296   }
297 
298   while (1) {
299     Py_BEGIN_ALLOW_THREADS
300     ok = BrotliEncoderCompressStream(enc, op,
301                                      &available_in, &next_in,
302                                      &available_out, &next_out, NULL);
303     Py_END_ALLOW_THREADS
304     if (!ok) {
305       goto error;
306     }
307 
308     if (available_in || BrotliEncoderHasMoreOutput(enc)) {
309       if (available_out == 0) {
310         if (BlocksOutputBuffer_Grow(&buffer, &available_out, &next_out) < 0) {
311           goto error;
312         }
313       }
314       continue;
315     }
316 
317     break;
318   }
319 
320   ret = BlocksOutputBuffer_Finish(&buffer, available_out);
321   if (ret != NULL) {
322     return ret;
323   }
324 
325 error:
326   BlocksOutputBuffer_OnError(&buffer);
327   return NULL;
328 }
329 
330 PyDoc_STRVAR(brotli_Compressor_doc,
331 "An object to compress a byte string.\n"
332 "\n"
333 "Signature:\n"
334 "  Compressor(mode=MODE_GENERIC, quality=11, lgwin=22, lgblock=0)\n"
335 "\n"
336 "Args:\n"
337 "  mode (int, optional): The compression mode can be MODE_GENERIC (default),\n"
338 "    MODE_TEXT (for UTF-8 format text input) or MODE_FONT (for WOFF 2.0). \n"
339 "  quality (int, optional): Controls the compression-speed vs compression-\n"
340 "    density tradeoff. The higher the quality, the slower the compression.\n"
341 "    Range is 0 to 11. Defaults to 11.\n"
342 "  lgwin (int, optional): Base 2 logarithm of the sliding window size. Range\n"
343 "    is 10 to 24. Defaults to 22.\n"
344 "  lgblock (int, optional): Base 2 logarithm of the maximum input block size.\n"
345 "    Range is 16 to 24. If set to 0, the value will be set based on the\n"
346 "    quality. Defaults to 0.\n"
347 "\n"
348 "Raises:\n"
349 "  brotli.error: If arguments are invalid.\n");
350 
351 typedef struct {
352   PyObject_HEAD
353   BrotliEncoderState* enc;
354 } brotli_Compressor;
355 
brotli_Compressor_dealloc(brotli_Compressor * self)356 static void brotli_Compressor_dealloc(brotli_Compressor* self) {
357   BrotliEncoderDestroyInstance(self->enc);
358   #if PY_MAJOR_VERSION >= 3
359   Py_TYPE(self)->tp_free((PyObject*)self);
360   #else
361   self->ob_type->tp_free((PyObject*)self);
362   #endif
363 }
364 
brotli_Compressor_new(PyTypeObject * type,PyObject * args,PyObject * keywds)365 static PyObject* brotli_Compressor_new(PyTypeObject *type, PyObject *args, PyObject *keywds) {
366   brotli_Compressor *self;
367   self = (brotli_Compressor *)type->tp_alloc(type, 0);
368 
369   if (self != NULL) {
370     self->enc = BrotliEncoderCreateInstance(0, 0, 0);
371   }
372 
373   return (PyObject *)self;
374 }
375 
brotli_Compressor_init(brotli_Compressor * self,PyObject * args,PyObject * keywds)376 static int brotli_Compressor_init(brotli_Compressor *self, PyObject *args, PyObject *keywds) {
377   BrotliEncoderMode mode = (BrotliEncoderMode) -1;
378   int quality = -1;
379   int lgwin = -1;
380   int lgblock = -1;
381   int ok;
382 
383   static const char *kwlist[] = {"mode", "quality", "lgwin", "lgblock", NULL};
384 
385   ok = PyArg_ParseTupleAndKeywords(args, keywds, "|O&O&O&O&:Compressor",
386                     (char **) kwlist,
387                     &mode_convertor, &mode,
388                     &quality_convertor, &quality,
389                     &lgwin_convertor, &lgwin,
390                     &lgblock_convertor, &lgblock);
391   if (!ok)
392     return -1;
393   if (!self->enc)
394     return -1;
395 
396   if ((int) mode != -1)
397     BrotliEncoderSetParameter(self->enc, BROTLI_PARAM_MODE, (uint32_t)mode);
398   if (quality != -1)
399     BrotliEncoderSetParameter(self->enc, BROTLI_PARAM_QUALITY, (uint32_t)quality);
400   if (lgwin != -1)
401     BrotliEncoderSetParameter(self->enc, BROTLI_PARAM_LGWIN, (uint32_t)lgwin);
402   if (lgblock != -1)
403     BrotliEncoderSetParameter(self->enc, BROTLI_PARAM_LGBLOCK, (uint32_t)lgblock);
404 
405   return 0;
406 }
407 
408 PyDoc_STRVAR(brotli_Compressor_process_doc,
409 "Process \"string\" for compression, returning a string that contains \n"
410 "compressed output data.  This data should be concatenated to the output \n"
411 "produced by any preceding calls to the \"process()\" or flush()\" methods. \n"
412 "Some or all of the input may be kept in internal buffers for later \n"
413 "processing, and the compressed output data may be empty until enough input \n"
414 "has been accumulated.\n"
415 "\n"
416 "Signature:\n"
417 "  compress(string)\n"
418 "\n"
419 "Args:\n"
420 "  string (bytes): The input data\n"
421 "\n"
422 "Returns:\n"
423 "  The compressed output data (bytes)\n"
424 "\n"
425 "Raises:\n"
426 "  brotli.error: If compression fails\n");
427 
brotli_Compressor_process(brotli_Compressor * self,PyObject * args)428 static PyObject* brotli_Compressor_process(brotli_Compressor *self, PyObject *args) {
429   PyObject* ret;
430   Py_buffer input;
431   int ok;
432 
433 #if PY_MAJOR_VERSION >= 3
434   ok = PyArg_ParseTuple(args, "y*:process", &input);
435 #else
436   ok = PyArg_ParseTuple(args, "s*:process", &input);
437 #endif
438 
439   if (!ok) {
440     return NULL;
441   }
442 
443   if (!self->enc) {
444     goto error;
445   }
446 
447   ret = compress_stream(self->enc, BROTLI_OPERATION_PROCESS,
448                         (uint8_t*) input.buf, input.len);
449   if (ret != NULL) {
450     goto finally;
451   }
452 
453 error:
454   PyErr_SetString(BrotliError,
455                   "BrotliEncoderCompressStream failed while processing the stream");
456   ret = NULL;
457 
458 finally:
459   PyBuffer_Release(&input);
460   return ret;
461 }
462 
463 PyDoc_STRVAR(brotli_Compressor_flush_doc,
464 "Process all pending input, returning a string containing the remaining\n"
465 "compressed data. This data should be concatenated to the output produced by\n"
466 "any preceding calls to the \"process()\" or \"flush()\" methods.\n"
467 "\n"
468 "Signature:\n"
469 "  flush()\n"
470 "\n"
471 "Returns:\n"
472 "  The compressed output data (bytes)\n"
473 "\n"
474 "Raises:\n"
475 "  brotli.error: If compression fails\n");
476 
brotli_Compressor_flush(brotli_Compressor * self)477 static PyObject* brotli_Compressor_flush(brotli_Compressor *self) {
478   PyObject *ret;
479 
480   if (!self->enc) {
481     goto error;
482   }
483 
484   ret = compress_stream(self->enc, BROTLI_OPERATION_FLUSH,
485                         NULL, 0);
486   if (ret != NULL) {
487     goto finally;
488   }
489 
490 error:
491   PyErr_SetString(BrotliError,
492                   "BrotliEncoderCompressStream failed while flushing the stream");
493   ret = NULL;
494 finally:
495   return ret;
496 }
497 
498 PyDoc_STRVAR(brotli_Compressor_finish_doc,
499 "Process all pending input and complete all compression, returning a string\n"
500 "containing the remaining compressed data. This data should be concatenated\n"
501 "to the output produced by any preceding calls to the \"process()\" or\n"
502 "\"flush()\" methods.\n"
503 "After calling \"finish()\", the \"process()\" and \"flush()\" methods\n"
504 "cannot be called again, and a new \"Compressor\" object should be created.\n"
505 "\n"
506 "Signature:\n"
507 "  finish(string)\n"
508 "\n"
509 "Returns:\n"
510 "  The compressed output data (bytes)\n"
511 "\n"
512 "Raises:\n"
513 "  brotli.error: If compression fails\n");
514 
brotli_Compressor_finish(brotli_Compressor * self)515 static PyObject* brotli_Compressor_finish(brotli_Compressor *self) {
516   PyObject *ret;
517 
518   if (!self->enc) {
519     goto error;
520   }
521 
522   ret = compress_stream(self->enc, BROTLI_OPERATION_FINISH,
523                         NULL, 0);
524 
525   if (ret == NULL || !BrotliEncoderIsFinished(self->enc)) {
526     goto error;
527   }
528   goto finally;
529 
530 error:
531   PyErr_SetString(BrotliError,
532                   "BrotliEncoderCompressStream failed while finishing the stream");
533   ret = NULL;
534 finally:
535   return ret;
536 }
537 
538 static PyMemberDef brotli_Compressor_members[] = {
539   {NULL}  /* Sentinel */
540 };
541 
542 static PyMethodDef brotli_Compressor_methods[] = {
543   {"process", (PyCFunction)brotli_Compressor_process, METH_VARARGS, brotli_Compressor_process_doc},
544   {"flush", (PyCFunction)brotli_Compressor_flush, METH_NOARGS, brotli_Compressor_flush_doc},
545   {"finish", (PyCFunction)brotli_Compressor_finish, METH_NOARGS, brotli_Compressor_finish_doc},
546   {NULL}  /* Sentinel */
547 };
548 
549 static PyTypeObject brotli_CompressorType = {
550   #if PY_MAJOR_VERSION >= 3
551   PyVarObject_HEAD_INIT(NULL, 0)
552   #else
553   PyObject_HEAD_INIT(NULL)
554   0,                                     /* ob_size*/
555   #endif
556   "brotli.Compressor",                   /* tp_name */
557   sizeof(brotli_Compressor),             /* tp_basicsize */
558   0,                                     /* tp_itemsize */
559   (destructor)brotli_Compressor_dealloc, /* tp_dealloc */
560   0,                                     /* tp_print */
561   0,                                     /* tp_getattr */
562   0,                                     /* tp_setattr */
563   0,                                     /* tp_compare */
564   0,                                     /* tp_repr */
565   0,                                     /* tp_as_number */
566   0,                                     /* tp_as_sequence */
567   0,                                     /* tp_as_mapping */
568   0,                                     /* tp_hash  */
569   0,                                     /* tp_call */
570   0,                                     /* tp_str */
571   0,                                     /* tp_getattro */
572   0,                                     /* tp_setattro */
573   0,                                     /* tp_as_buffer */
574   Py_TPFLAGS_DEFAULT,                    /* tp_flags */
575   brotli_Compressor_doc,                 /* tp_doc */
576   0,                                     /* tp_traverse */
577   0,                                     /* tp_clear */
578   0,                                     /* tp_richcompare */
579   0,                                     /* tp_weaklistoffset */
580   0,                                     /* tp_iter */
581   0,                                     /* tp_iternext */
582   brotli_Compressor_methods,             /* tp_methods */
583   brotli_Compressor_members,             /* tp_members */
584   0,                                     /* tp_getset */
585   0,                                     /* tp_base */
586   0,                                     /* tp_dict */
587   0,                                     /* tp_descr_get */
588   0,                                     /* tp_descr_set */
589   0,                                     /* tp_dictoffset */
590   (initproc)brotli_Compressor_init,      /* tp_init */
591   0,                                     /* tp_alloc */
592   brotli_Compressor_new,                 /* tp_new */
593 };
594 
decompress_stream(BrotliDecoderState * dec,uint8_t * input,size_t input_length)595 static PyObject* decompress_stream(BrotliDecoderState* dec,
596                                    uint8_t* input, size_t input_length) {
597   BrotliDecoderResult result;
598 
599   size_t available_in = input_length;
600   const uint8_t* next_in = input;
601 
602   size_t available_out;
603   uint8_t* next_out;
604   BlocksOutputBuffer buffer = {.list=NULL};
605   PyObject *ret;
606 
607   if (BlocksOutputBuffer_InitAndGrow(&buffer, &available_out, &next_out) < 0) {
608     goto error;
609   }
610 
611   while (1) {
612     Py_BEGIN_ALLOW_THREADS
613     result = BrotliDecoderDecompressStream(dec,
614                                            &available_in, &next_in,
615                                            &available_out, &next_out, NULL);
616     Py_END_ALLOW_THREADS
617 
618     if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
619       if (available_out == 0) {
620         if (BlocksOutputBuffer_Grow(&buffer, &available_out, &next_out) < 0) {
621           goto error;
622         }
623       }
624       continue;
625     }
626 
627     break;
628   }
629 
630   if (result == BROTLI_DECODER_RESULT_ERROR || available_in != 0) {
631     goto error;
632   }
633 
634   ret = BlocksOutputBuffer_Finish(&buffer, available_out);
635   if (ret != NULL) {
636     goto finally;
637   }
638 
639 error:
640   BlocksOutputBuffer_OnError(&buffer);
641   ret = NULL;
642 finally:
643   return ret;
644 }
645 
646 PyDoc_STRVAR(brotli_Decompressor_doc,
647 "An object to decompress a byte string.\n"
648 "\n"
649 "Signature:\n"
650 "  Decompressor()\n"
651 "\n"
652 "Raises:\n"
653 "  brotli.error: If arguments are invalid.\n");
654 
655 typedef struct {
656   PyObject_HEAD
657   BrotliDecoderState* dec;
658 } brotli_Decompressor;
659 
brotli_Decompressor_dealloc(brotli_Decompressor * self)660 static void brotli_Decompressor_dealloc(brotli_Decompressor* self) {
661   BrotliDecoderDestroyInstance(self->dec);
662   #if PY_MAJOR_VERSION >= 3
663   Py_TYPE(self)->tp_free((PyObject*)self);
664   #else
665   self->ob_type->tp_free((PyObject*)self);
666   #endif
667 }
668 
brotli_Decompressor_new(PyTypeObject * type,PyObject * args,PyObject * keywds)669 static PyObject* brotli_Decompressor_new(PyTypeObject *type, PyObject *args, PyObject *keywds) {
670   brotli_Decompressor *self;
671   self = (brotli_Decompressor *)type->tp_alloc(type, 0);
672 
673   if (self != NULL) {
674     self->dec = BrotliDecoderCreateInstance(0, 0, 0);
675   }
676 
677   return (PyObject *)self;
678 }
679 
brotli_Decompressor_init(brotli_Decompressor * self,PyObject * args,PyObject * keywds)680 static int brotli_Decompressor_init(brotli_Decompressor *self, PyObject *args, PyObject *keywds) {
681   int ok;
682 
683   static const char *kwlist[] = {NULL};
684 
685   ok = PyArg_ParseTupleAndKeywords(args, keywds, "|:Decompressor",
686                                    (char **) kwlist);
687   if (!ok)
688     return -1;
689   if (!self->dec)
690     return -1;
691 
692   return 0;
693 }
694 
695 PyDoc_STRVAR(brotli_Decompressor_process_doc,
696 "Process \"string\" for decompression, returning a string that contains \n"
697 "decompressed output data.  This data should be concatenated to the output \n"
698 "produced by any preceding calls to the \"process()\" method. \n"
699 "Some or all of the input may be kept in internal buffers for later \n"
700 "processing, and the decompressed output data may be empty until enough input \n"
701 "has been accumulated.\n"
702 "\n"
703 "Signature:\n"
704 "  decompress(string)\n"
705 "\n"
706 "Args:\n"
707 "  string (bytes): The input data\n"
708 "\n"
709 "Returns:\n"
710 "  The decompressed output data (bytes)\n"
711 "\n"
712 "Raises:\n"
713 "  brotli.error: If decompression fails\n");
714 
brotli_Decompressor_process(brotli_Decompressor * self,PyObject * args)715 static PyObject* brotli_Decompressor_process(brotli_Decompressor *self, PyObject *args) {
716   PyObject* ret;
717   Py_buffer input;
718   int ok;
719 
720 #if PY_MAJOR_VERSION >= 3
721   ok = PyArg_ParseTuple(args, "y*:process", &input);
722 #else
723   ok = PyArg_ParseTuple(args, "s*:process", &input);
724 #endif
725 
726   if (!ok) {
727     return NULL;
728   }
729 
730   if (!self->dec) {
731     goto error;
732   }
733 
734   ret = decompress_stream(self->dec, (uint8_t*) input.buf, input.len);
735   if (ret != NULL) {
736     goto finally;
737   }
738 
739 error:
740   PyErr_SetString(BrotliError,
741                   "BrotliDecoderDecompressStream failed while processing the stream");
742   ret = NULL;
743 
744 finally:
745   PyBuffer_Release(&input);
746   return ret;
747 }
748 
749 PyDoc_STRVAR(brotli_Decompressor_is_finished_doc,
750 "Checks if decoder instance reached the final state.\n"
751 "\n"
752 "Signature:\n"
753 "  is_finished()\n"
754 "\n"
755 "Returns:\n"
756 "  True  if the decoder is in a state where it reached the end of the input\n"
757 "        and produced all of the output\n"
758 "  False otherwise\n"
759 "\n"
760 "Raises:\n"
761 "  brotli.error: If decompression fails\n");
762 
brotli_Decompressor_is_finished(brotli_Decompressor * self)763 static PyObject* brotli_Decompressor_is_finished(brotli_Decompressor *self) {
764   if (!self->dec) {
765     PyErr_SetString(BrotliError, "BrotliDecoderState is NULL while checking is_finished");
766     return NULL;
767   }
768 
769   if (BrotliDecoderIsFinished(self->dec)) {
770     Py_RETURN_TRUE;
771   } else {
772     Py_RETURN_FALSE;
773   }
774 }
775 
776 static PyMemberDef brotli_Decompressor_members[] = {
777   {NULL}  /* Sentinel */
778 };
779 
780 static PyMethodDef brotli_Decompressor_methods[] = {
781   {"process", (PyCFunction)brotli_Decompressor_process, METH_VARARGS, brotli_Decompressor_process_doc},
782   {"is_finished", (PyCFunction)brotli_Decompressor_is_finished, METH_NOARGS, brotli_Decompressor_is_finished_doc},
783   {NULL}  /* Sentinel */
784 };
785 
786 static PyTypeObject brotli_DecompressorType = {
787   #if PY_MAJOR_VERSION >= 3
788   PyVarObject_HEAD_INIT(NULL, 0)
789   #else
790   PyObject_HEAD_INIT(NULL)
791   0,                                     /* ob_size*/
792   #endif
793   "brotli.Decompressor",                   /* tp_name */
794   sizeof(brotli_Decompressor),             /* tp_basicsize */
795   0,                                       /* tp_itemsize */
796   (destructor)brotli_Decompressor_dealloc, /* tp_dealloc */
797   0,                                       /* tp_print */
798   0,                                       /* tp_getattr */
799   0,                                       /* tp_setattr */
800   0,                                       /* tp_compare */
801   0,                                       /* tp_repr */
802   0,                                       /* tp_as_number */
803   0,                                       /* tp_as_sequence */
804   0,                                       /* tp_as_mapping */
805   0,                                       /* tp_hash  */
806   0,                                       /* tp_call */
807   0,                                       /* tp_str */
808   0,                                       /* tp_getattro */
809   0,                                       /* tp_setattro */
810   0,                                       /* tp_as_buffer */
811   Py_TPFLAGS_DEFAULT,                      /* tp_flags */
812   brotli_Decompressor_doc,                 /* tp_doc */
813   0,                                       /* tp_traverse */
814   0,                                       /* tp_clear */
815   0,                                       /* tp_richcompare */
816   0,                                       /* tp_weaklistoffset */
817   0,                                       /* tp_iter */
818   0,                                       /* tp_iternext */
819   brotli_Decompressor_methods,             /* tp_methods */
820   brotli_Decompressor_members,             /* tp_members */
821   0,                                       /* tp_getset */
822   0,                                       /* tp_base */
823   0,                                       /* tp_dict */
824   0,                                       /* tp_descr_get */
825   0,                                       /* tp_descr_set */
826   0,                                       /* tp_dictoffset */
827   (initproc)brotli_Decompressor_init,      /* tp_init */
828   0,                                       /* tp_alloc */
829   brotli_Decompressor_new,                 /* tp_new */
830 };
831 
832 PyDoc_STRVAR(brotli_decompress__doc__,
833 "Decompress a compressed byte string.\n"
834 "\n"
835 "Signature:\n"
836 "  decompress(string)\n"
837 "\n"
838 "Args:\n"
839 "  string (bytes): The compressed input data.\n"
840 "\n"
841 "Returns:\n"
842 "  The decompressed byte string.\n"
843 "\n"
844 "Raises:\n"
845 "  brotli.error: If decompressor fails.\n");
846 
brotli_decompress(PyObject * self,PyObject * args,PyObject * keywds)847 static PyObject* brotli_decompress(PyObject *self, PyObject *args, PyObject *keywds) {
848   BrotliDecoderState* state;
849   BrotliDecoderResult result;
850 
851   const uint8_t* next_in;
852   size_t available_in;
853 
854   uint8_t* next_out;
855   size_t available_out;
856   BlocksOutputBuffer buffer = {.list=NULL};
857   PyObject *ret;
858 
859   static const char *kwlist[] = {"string", NULL};
860   Py_buffer input;
861   int ok;
862 
863 #if PY_MAJOR_VERSION >= 3
864   ok = PyArg_ParseTupleAndKeywords(args, keywds, "y*|:decompress",
865                                    (char**) kwlist, &input);
866 #else
867   ok = PyArg_ParseTupleAndKeywords(args, keywds, "s*|:decompress",
868                                    (char**) kwlist, &input);
869 #endif
870 
871   if (!ok) {
872     return NULL;
873   }
874 
875   state = BrotliDecoderCreateInstance(0, 0, 0);
876 
877   next_in = (uint8_t*) input.buf;
878   available_in = input.len;
879 
880   if (BlocksOutputBuffer_InitAndGrow(&buffer, &available_out, &next_out) < 0) {
881     goto error;
882   }
883 
884   while (1) {
885     Py_BEGIN_ALLOW_THREADS
886     result = BrotliDecoderDecompressStream(state, &available_in, &next_in,
887                                            &available_out, &next_out, 0);
888     Py_END_ALLOW_THREADS
889 
890     if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
891       if (available_out == 0) {
892         if (BlocksOutputBuffer_Grow(&buffer, &available_out, &next_out) < 0) {
893           goto error;
894         }
895       }
896       continue;
897     }
898 
899     break;
900   }
901 
902   if (result != BROTLI_DECODER_RESULT_SUCCESS || available_in != 0) {
903     goto error;
904   }
905 
906   ret = BlocksOutputBuffer_Finish(&buffer, available_out);
907   if (ret != NULL) {
908     goto finally;
909   }
910 
911 error:
912   BlocksOutputBuffer_OnError(&buffer);
913   PyErr_SetString(BrotliError, "BrotliDecompress failed");
914   ret = NULL;
915 
916 finally:
917   BrotliDecoderDestroyInstance(state);
918   PyBuffer_Release(&input);
919   return ret;
920 }
921 
922 static PyMethodDef brotli_methods[] = {
923   {"decompress", (PyCFunction)brotli_decompress, METH_VARARGS | METH_KEYWORDS, brotli_decompress__doc__},
924   {NULL, NULL, 0, NULL}
925 };
926 
927 PyDoc_STRVAR(brotli_doc, "Implementation module for the Brotli library.");
928 
929 #if PY_MAJOR_VERSION >= 3
930 #define INIT_BROTLI   PyInit__brotli
931 #define CREATE_BROTLI PyModule_Create(&brotli_module)
932 #define RETURN_BROTLI return m
933 #define RETURN_NULL return NULL
934 
935 static struct PyModuleDef brotli_module = {
936   PyModuleDef_HEAD_INIT,
937   "_brotli",      /* m_name */
938   brotli_doc,     /* m_doc */
939   0,              /* m_size */
940   brotli_methods, /* m_methods */
941   NULL,           /* m_reload */
942   NULL,           /* m_traverse */
943   NULL,           /* m_clear */
944   NULL            /* m_free */
945 };
946 #else
947 #define INIT_BROTLI   init_brotli
948 #define CREATE_BROTLI Py_InitModule3("_brotli", brotli_methods, brotli_doc)
949 #define RETURN_BROTLI return
950 #define RETURN_NULL return
951 #endif
952 
INIT_BROTLI(void)953 PyMODINIT_FUNC INIT_BROTLI(void) {
954   PyObject *m = CREATE_BROTLI;
955 
956   BrotliError = PyErr_NewException((char*) "brotli.error", NULL, NULL);
957   if (BrotliError != NULL) {
958     Py_INCREF(BrotliError);
959     PyModule_AddObject(m, "error", BrotliError);
960   }
961 
962   if (PyType_Ready(&brotli_CompressorType) < 0) {
963     RETURN_NULL;
964   }
965   Py_INCREF(&brotli_CompressorType);
966   PyModule_AddObject(m, "Compressor", (PyObject *)&brotli_CompressorType);
967 
968   if (PyType_Ready(&brotli_DecompressorType) < 0) {
969     RETURN_NULL;
970   }
971   Py_INCREF(&brotli_DecompressorType);
972   PyModule_AddObject(m, "Decompressor", (PyObject *)&brotli_DecompressorType);
973 
974   PyModule_AddIntConstant(m, "MODE_GENERIC", (int) BROTLI_MODE_GENERIC);
975   PyModule_AddIntConstant(m, "MODE_TEXT", (int) BROTLI_MODE_TEXT);
976   PyModule_AddIntConstant(m, "MODE_FONT", (int) BROTLI_MODE_FONT);
977 
978   char version[16];
979   uint32_t decoderVersion = BrotliDecoderVersion();
980   snprintf(version, sizeof(version), "%d.%d.%d",
981       decoderVersion >> 24, (decoderVersion >> 12) & 0xFFF, decoderVersion & 0xFFF);
982   PyModule_AddStringConstant(m, "__version__", version);
983 
984   RETURN_BROTLI;
985 }
986