• 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 #include <vector>
6 #include "../common/version.h"
7 #include <brotli/decode.h>
8 #include <brotli/encode.h>
9 
10 #if PY_MAJOR_VERSION >= 3
11 #define PyInt_Check PyLong_Check
12 #define PyInt_AsLong PyLong_AsLong
13 #endif
14 
15 static PyObject *BrotliError;
16 
as_bounded_int(PyObject * o,int * result,int lower_bound,int upper_bound)17 static int as_bounded_int(PyObject *o, int* result, int lower_bound, int upper_bound) {
18   long value = PyInt_AsLong(o);
19   if ((value < (long) lower_bound) || (value > (long) upper_bound)) {
20     return 0;
21   }
22   *result = (int) value;
23   return 1;
24 }
25 
mode_convertor(PyObject * o,BrotliEncoderMode * mode)26 static int mode_convertor(PyObject *o, BrotliEncoderMode *mode) {
27   if (!PyInt_Check(o)) {
28     PyErr_SetString(BrotliError, "Invalid mode");
29     return 0;
30   }
31 
32   int mode_value = -1;
33   if (!as_bounded_int(o, &mode_value, 0, 255)) {
34     PyErr_SetString(BrotliError, "Invalid mode");
35     return 0;
36   }
37   *mode = (BrotliEncoderMode) mode_value;
38   if (*mode != BROTLI_MODE_GENERIC &&
39       *mode != BROTLI_MODE_TEXT &&
40       *mode != BROTLI_MODE_FONT) {
41     PyErr_SetString(BrotliError, "Invalid mode");
42     return 0;
43   }
44 
45   return 1;
46 }
47 
quality_convertor(PyObject * o,int * quality)48 static int quality_convertor(PyObject *o, int *quality) {
49   if (!PyInt_Check(o)) {
50     PyErr_SetString(BrotliError, "Invalid quality");
51     return 0;
52   }
53 
54   if (!as_bounded_int(o, quality, 0, 11)) {
55     PyErr_SetString(BrotliError, "Invalid quality. Range is 0 to 11.");
56     return 0;
57   }
58 
59   return 1;
60 }
61 
lgwin_convertor(PyObject * o,int * lgwin)62 static int lgwin_convertor(PyObject *o, int *lgwin) {
63   if (!PyInt_Check(o)) {
64     PyErr_SetString(BrotliError, "Invalid lgwin");
65     return 0;
66   }
67 
68   if (!as_bounded_int(o, lgwin, 10, 24)) {
69     PyErr_SetString(BrotliError, "Invalid lgwin. Range is 10 to 24.");
70     return 0;
71   }
72 
73   return 1;
74 }
75 
lgblock_convertor(PyObject * o,int * lgblock)76 static int lgblock_convertor(PyObject *o, int *lgblock) {
77   if (!PyInt_Check(o)) {
78     PyErr_SetString(BrotliError, "Invalid lgblock");
79     return 0;
80   }
81 
82   if (!as_bounded_int(o, lgblock, 0, 24) || (*lgblock != 0 && *lgblock < 16)) {
83     PyErr_SetString(BrotliError, "Invalid lgblock. Can be 0 or in range 16 to 24.");
84     return 0;
85   }
86 
87   return 1;
88 }
89 
compress_stream(BrotliEncoderState * enc,BrotliEncoderOperation op,std::vector<uint8_t> * output,uint8_t * input,size_t input_length)90 static BROTLI_BOOL compress_stream(BrotliEncoderState* enc, BrotliEncoderOperation op,
91                                    std::vector<uint8_t>* output,
92                                    uint8_t* input, size_t input_length) {
93   BROTLI_BOOL ok = BROTLI_TRUE;
94   Py_BEGIN_ALLOW_THREADS
95 
96   size_t available_in = input_length;
97   const uint8_t* next_in = input;
98   size_t available_out = 0;
99   uint8_t* next_out = NULL;
100 
101   while (ok) {
102     ok = BrotliEncoderCompressStream(enc, op,
103                                      &available_in, &next_in,
104                                      &available_out, &next_out, NULL);
105     if (!ok)
106       break;
107 
108     size_t buffer_length = 0; // Request all available output.
109     const uint8_t* buffer = BrotliEncoderTakeOutput(enc, &buffer_length);
110     if (buffer_length) {
111       (*output).insert((*output).end(), buffer, buffer + buffer_length);
112     }
113 
114     if (available_in || BrotliEncoderHasMoreOutput(enc)) {
115       continue;
116     }
117 
118     break;
119   }
120 
121   Py_END_ALLOW_THREADS
122   return ok;
123 }
124 
125 PyDoc_STRVAR(brotli_Compressor_doc,
126 "An object to compress a byte string.\n"
127 "\n"
128 "Signature:\n"
129 "  Compressor(mode=MODE_GENERIC, quality=11, lgwin=22, lgblock=0)\n"
130 "\n"
131 "Args:\n"
132 "  mode (int, optional): The compression mode can be MODE_GENERIC (default),\n"
133 "    MODE_TEXT (for UTF-8 format text input) or MODE_FONT (for WOFF 2.0). \n"
134 "  quality (int, optional): Controls the compression-speed vs compression-\n"
135 "    density tradeoff. The higher the quality, the slower the compression.\n"
136 "    Range is 0 to 11. Defaults to 11.\n"
137 "  lgwin (int, optional): Base 2 logarithm of the sliding window size. Range\n"
138 "    is 10 to 24. Defaults to 22.\n"
139 "  lgblock (int, optional): Base 2 logarithm of the maximum input block size.\n"
140 "    Range is 16 to 24. If set to 0, the value will be set based on the\n"
141 "    quality. Defaults to 0.\n"
142 "\n"
143 "Raises:\n"
144 "  brotli.error: If arguments are invalid.\n");
145 
146 typedef struct {
147   PyObject_HEAD
148   BrotliEncoderState* enc;
149 } brotli_Compressor;
150 
brotli_Compressor_dealloc(brotli_Compressor * self)151 static void brotli_Compressor_dealloc(brotli_Compressor* self) {
152   BrotliEncoderDestroyInstance(self->enc);
153   #if PY_MAJOR_VERSION >= 3
154   Py_TYPE(self)->tp_free((PyObject*)self);
155   #else
156   self->ob_type->tp_free((PyObject*)self);
157   #endif
158 }
159 
brotli_Compressor_new(PyTypeObject * type,PyObject * args,PyObject * keywds)160 static PyObject* brotli_Compressor_new(PyTypeObject *type, PyObject *args, PyObject *keywds) {
161   brotli_Compressor *self;
162   self = (brotli_Compressor *)type->tp_alloc(type, 0);
163 
164   if (self != NULL) {
165     self->enc = BrotliEncoderCreateInstance(0, 0, 0);
166   }
167 
168   return (PyObject *)self;
169 }
170 
brotli_Compressor_init(brotli_Compressor * self,PyObject * args,PyObject * keywds)171 static int brotli_Compressor_init(brotli_Compressor *self, PyObject *args, PyObject *keywds) {
172   BrotliEncoderMode mode = (BrotliEncoderMode) -1;
173   int quality = -1;
174   int lgwin = -1;
175   int lgblock = -1;
176   int ok;
177 
178   static const char *kwlist[] = {"mode", "quality", "lgwin", "lgblock", NULL};
179 
180   ok = PyArg_ParseTupleAndKeywords(args, keywds, "|O&O&O&O&:Compressor",
181                     const_cast<char **>(kwlist),
182                     &mode_convertor, &mode,
183                     &quality_convertor, &quality,
184                     &lgwin_convertor, &lgwin,
185                     &lgblock_convertor, &lgblock);
186   if (!ok)
187     return -1;
188   if (!self->enc)
189     return -1;
190 
191   if ((int) mode != -1)
192     BrotliEncoderSetParameter(self->enc, BROTLI_PARAM_MODE, (uint32_t)mode);
193   if (quality != -1)
194     BrotliEncoderSetParameter(self->enc, BROTLI_PARAM_QUALITY, (uint32_t)quality);
195   if (lgwin != -1)
196     BrotliEncoderSetParameter(self->enc, BROTLI_PARAM_LGWIN, (uint32_t)lgwin);
197   if (lgblock != -1)
198     BrotliEncoderSetParameter(self->enc, BROTLI_PARAM_LGBLOCK, (uint32_t)lgblock);
199 
200   return 0;
201 }
202 
203 PyDoc_STRVAR(brotli_Compressor_process_doc,
204 "Process \"string\" for compression, returning a string that contains \n"
205 "compressed output data.  This data should be concatenated to the output \n"
206 "produced by any preceding calls to the \"process()\" or flush()\" methods. \n"
207 "Some or all of the input may be kept in internal buffers for later \n"
208 "processing, and the compressed output data may be empty until enough input \n"
209 "has been accumulated.\n"
210 "\n"
211 "Signature:\n"
212 "  compress(string)\n"
213 "\n"
214 "Args:\n"
215 "  string (bytes): The input data\n"
216 "\n"
217 "Returns:\n"
218 "  The compressed output data (bytes)\n"
219 "\n"
220 "Raises:\n"
221 "  brotli.error: If compression fails\n");
222 
brotli_Compressor_process(brotli_Compressor * self,PyObject * args)223 static PyObject* brotli_Compressor_process(brotli_Compressor *self, PyObject *args) {
224   PyObject* ret = NULL;
225   std::vector<uint8_t> output;
226   Py_buffer input;
227   BROTLI_BOOL ok = BROTLI_TRUE;
228 
229 #if PY_MAJOR_VERSION >= 3
230   ok = (BROTLI_BOOL)PyArg_ParseTuple(args, "y*:process", &input);
231 #else
232   ok = (BROTLI_BOOL)PyArg_ParseTuple(args, "s*:process", &input);
233 #endif
234 
235   if (!ok)
236     return NULL;
237 
238   if (!self->enc) {
239     ok = BROTLI_FALSE;
240     goto end;
241   }
242 
243   ok = compress_stream(self->enc, BROTLI_OPERATION_PROCESS,
244                        &output, static_cast<uint8_t*>(input.buf), input.len);
245 
246 end:
247   PyBuffer_Release(&input);
248   if (ok) {
249     ret = PyBytes_FromStringAndSize((char*)(output.size() ? &output[0] : NULL), output.size());
250   } else {
251     PyErr_SetString(BrotliError, "BrotliEncoderCompressStream failed while processing the stream");
252   }
253 
254   return ret;
255 }
256 
257 PyDoc_STRVAR(brotli_Compressor_flush_doc,
258 "Process all pending input, returning a string containing the remaining\n"
259 "compressed data. This data should be concatenated to the output produced by\n"
260 "any preceding calls to the \"process()\" or \"flush()\" methods.\n"
261 "\n"
262 "Signature:\n"
263 "  flush()\n"
264 "\n"
265 "Returns:\n"
266 "  The compressed output data (bytes)\n"
267 "\n"
268 "Raises:\n"
269 "  brotli.error: If compression fails\n");
270 
brotli_Compressor_flush(brotli_Compressor * self)271 static PyObject* brotli_Compressor_flush(brotli_Compressor *self) {
272   PyObject *ret = NULL;
273   std::vector<uint8_t> output;
274   BROTLI_BOOL ok = BROTLI_TRUE;
275 
276   if (!self->enc) {
277     ok = BROTLI_FALSE;
278     goto end;
279   }
280 
281   ok = compress_stream(self->enc, BROTLI_OPERATION_FLUSH,
282                        &output, NULL, 0);
283 
284 end:
285   if (ok) {
286     ret = PyBytes_FromStringAndSize((char*)(output.size() ? &output[0] : NULL), output.size());
287   } else {
288     PyErr_SetString(BrotliError, "BrotliEncoderCompressStream failed while flushing the stream");
289   }
290 
291   return ret;
292 }
293 
294 PyDoc_STRVAR(brotli_Compressor_finish_doc,
295 "Process all pending input and complete all compression, returning a string\n"
296 "containing the remaining compressed data. This data should be concatenated\n"
297 "to the output produced by any preceding calls to the \"process()\" or\n"
298 "\"flush()\" methods.\n"
299 "After calling \"finish()\", the \"process()\" and \"flush()\" methods\n"
300 "cannot be called again, and a new \"Compressor\" object should be created.\n"
301 "\n"
302 "Signature:\n"
303 "  finish(string)\n"
304 "\n"
305 "Returns:\n"
306 "  The compressed output data (bytes)\n"
307 "\n"
308 "Raises:\n"
309 "  brotli.error: If compression fails\n");
310 
brotli_Compressor_finish(brotli_Compressor * self)311 static PyObject* brotli_Compressor_finish(brotli_Compressor *self) {
312   PyObject *ret = NULL;
313   std::vector<uint8_t> output;
314   BROTLI_BOOL ok = BROTLI_TRUE;
315 
316   if (!self->enc) {
317     ok = BROTLI_FALSE;
318     goto end;
319   }
320 
321   ok = compress_stream(self->enc, BROTLI_OPERATION_FINISH,
322                        &output, NULL, 0);
323 
324   if (ok) {
325     ok = BrotliEncoderIsFinished(self->enc);
326   }
327 
328 end:
329   if (ok) {
330     ret = PyBytes_FromStringAndSize((char*)(output.empty() ? NULL : &output[0]), output.size());
331   } else {
332     PyErr_SetString(BrotliError, "BrotliEncoderCompressStream failed while finishing the stream");
333   }
334 
335   return ret;
336 }
337 
338 static PyMemberDef brotli_Compressor_members[] = {
339   {NULL}  /* Sentinel */
340 };
341 
342 static PyMethodDef brotli_Compressor_methods[] = {
343   {"process", (PyCFunction)brotli_Compressor_process, METH_VARARGS, brotli_Compressor_process_doc},
344   {"flush", (PyCFunction)brotli_Compressor_flush, METH_NOARGS, brotli_Compressor_flush_doc},
345   {"finish", (PyCFunction)brotli_Compressor_finish, METH_NOARGS, brotli_Compressor_finish_doc},
346   {NULL}  /* Sentinel */
347 };
348 
349 static PyTypeObject brotli_CompressorType = {
350   #if PY_MAJOR_VERSION >= 3
351   PyVarObject_HEAD_INIT(NULL, 0)
352   #else
353   PyObject_HEAD_INIT(NULL)
354   0,                                     /* ob_size*/
355   #endif
356   "brotli.Compressor",                   /* tp_name */
357   sizeof(brotli_Compressor),             /* tp_basicsize */
358   0,                                     /* tp_itemsize */
359   (destructor)brotli_Compressor_dealloc, /* tp_dealloc */
360   0,                                     /* tp_print */
361   0,                                     /* tp_getattr */
362   0,                                     /* tp_setattr */
363   0,                                     /* tp_compare */
364   0,                                     /* tp_repr */
365   0,                                     /* tp_as_number */
366   0,                                     /* tp_as_sequence */
367   0,                                     /* tp_as_mapping */
368   0,                                     /* tp_hash  */
369   0,                                     /* tp_call */
370   0,                                     /* tp_str */
371   0,                                     /* tp_getattro */
372   0,                                     /* tp_setattro */
373   0,                                     /* tp_as_buffer */
374   Py_TPFLAGS_DEFAULT,                    /* tp_flags */
375   brotli_Compressor_doc,                 /* tp_doc */
376   0,                                     /* tp_traverse */
377   0,                                     /* tp_clear */
378   0,                                     /* tp_richcompare */
379   0,                                     /* tp_weaklistoffset */
380   0,                                     /* tp_iter */
381   0,                                     /* tp_iternext */
382   brotli_Compressor_methods,             /* tp_methods */
383   brotli_Compressor_members,             /* tp_members */
384   0,                                     /* tp_getset */
385   0,                                     /* tp_base */
386   0,                                     /* tp_dict */
387   0,                                     /* tp_descr_get */
388   0,                                     /* tp_descr_set */
389   0,                                     /* tp_dictoffset */
390   (initproc)brotli_Compressor_init,      /* tp_init */
391   0,                                     /* tp_alloc */
392   brotli_Compressor_new,                 /* tp_new */
393 };
394 
decompress_stream(BrotliDecoderState * dec,std::vector<uint8_t> * output,uint8_t * input,size_t input_length)395 static BROTLI_BOOL decompress_stream(BrotliDecoderState* dec,
396                                      std::vector<uint8_t>* output,
397                                      uint8_t* input, size_t input_length) {
398   BROTLI_BOOL ok = BROTLI_TRUE;
399   Py_BEGIN_ALLOW_THREADS
400 
401   size_t available_in = input_length;
402   const uint8_t* next_in = input;
403   size_t available_out = 0;
404   uint8_t* next_out = NULL;
405 
406   BrotliDecoderResult result = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
407   while (result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
408     result = BrotliDecoderDecompressStream(dec,
409                                            &available_in, &next_in,
410                                            &available_out, &next_out, NULL);
411     size_t buffer_length = 0; // Request all available output.
412     const uint8_t* buffer = BrotliDecoderTakeOutput(dec, &buffer_length);
413     if (buffer_length) {
414       (*output).insert((*output).end(), buffer, buffer + buffer_length);
415     }
416   }
417   ok = result != BROTLI_DECODER_RESULT_ERROR;
418 
419   Py_END_ALLOW_THREADS
420   return ok;
421 }
422 
423 PyDoc_STRVAR(brotli_Decompressor_doc,
424 "An object to decompress a byte string.\n"
425 "\n"
426 "Signature:\n"
427 "  Decompressor()\n"
428 "\n"
429 "Raises:\n"
430 "  brotli.error: If arguments are invalid.\n");
431 
432 typedef struct {
433   PyObject_HEAD
434   BrotliDecoderState* dec;
435 } brotli_Decompressor;
436 
brotli_Decompressor_dealloc(brotli_Decompressor * self)437 static void brotli_Decompressor_dealloc(brotli_Decompressor* self) {
438   BrotliDecoderDestroyInstance(self->dec);
439   #if PY_MAJOR_VERSION >= 3
440   Py_TYPE(self)->tp_free((PyObject*)self);
441   #else
442   self->ob_type->tp_free((PyObject*)self);
443   #endif
444 }
445 
brotli_Decompressor_new(PyTypeObject * type,PyObject * args,PyObject * keywds)446 static PyObject* brotli_Decompressor_new(PyTypeObject *type, PyObject *args, PyObject *keywds) {
447   brotli_Decompressor *self;
448   self = (brotli_Decompressor *)type->tp_alloc(type, 0);
449 
450   if (self != NULL) {
451     self->dec = BrotliDecoderCreateInstance(0, 0, 0);
452   }
453 
454   return (PyObject *)self;
455 }
456 
brotli_Decompressor_init(brotli_Decompressor * self,PyObject * args,PyObject * keywds)457 static int brotli_Decompressor_init(brotli_Decompressor *self, PyObject *args, PyObject *keywds) {
458   int ok;
459 
460   static const char *kwlist[] = {NULL};
461 
462   ok = PyArg_ParseTupleAndKeywords(args, keywds, "|:Decompressor",
463                     const_cast<char **>(kwlist));
464   if (!ok)
465     return -1;
466   if (!self->dec)
467     return -1;
468 
469   return 0;
470 }
471 
472 PyDoc_STRVAR(brotli_Decompressor_process_doc,
473 "Process \"string\" for decompression, returning a string that contains \n"
474 "decompressed output data.  This data should be concatenated to the output \n"
475 "produced by any preceding calls to the \"process()\" method. \n"
476 "Some or all of the input may be kept in internal buffers for later \n"
477 "processing, and the decompressed output data may be empty until enough input \n"
478 "has been accumulated.\n"
479 "\n"
480 "Signature:\n"
481 "  decompress(string)\n"
482 "\n"
483 "Args:\n"
484 "  string (bytes): The input data\n"
485 "\n"
486 "Returns:\n"
487 "  The decompressed output data (bytes)\n"
488 "\n"
489 "Raises:\n"
490 "  brotli.error: If decompression fails\n");
491 
brotli_Decompressor_process(brotli_Decompressor * self,PyObject * args)492 static PyObject* brotli_Decompressor_process(brotli_Decompressor *self, PyObject *args) {
493   PyObject* ret = NULL;
494   std::vector<uint8_t> output;
495   Py_buffer input;
496   BROTLI_BOOL ok = BROTLI_TRUE;
497 
498 #if PY_MAJOR_VERSION >= 3
499   ok = (BROTLI_BOOL)PyArg_ParseTuple(args, "y*:process", &input);
500 #else
501   ok = (BROTLI_BOOL)PyArg_ParseTuple(args, "s*:process", &input);
502 #endif
503 
504   if (!ok)
505     return NULL;
506 
507   if (!self->dec) {
508     ok = BROTLI_FALSE;
509     goto end;
510   }
511 
512   ok = decompress_stream(self->dec, &output, static_cast<uint8_t*>(input.buf), input.len);
513 
514 end:
515   PyBuffer_Release(&input);
516   if (ok) {
517     ret = PyBytes_FromStringAndSize((char*)(output.empty() ? NULL : &output[0]), output.size());
518   } else {
519     PyErr_SetString(BrotliError, "BrotliDecoderDecompressStream failed while processing the stream");
520   }
521 
522   return ret;
523 }
524 
525 PyDoc_STRVAR(brotli_Decompressor_is_finished_doc,
526 "Checks if decoder instance reached the final state.\n"
527 "\n"
528 "Signature:\n"
529 "  is_finished()\n"
530 "\n"
531 "Returns:\n"
532 "  True  if the decoder is in a state where it reached the end of the input\n"
533 "        and produced all of the output\n"
534 "  False otherwise\n"
535 "\n"
536 "Raises:\n"
537 "  brotli.error: If decompression fails\n");
538 
brotli_Decompressor_is_finished(brotli_Decompressor * self)539 static PyObject* brotli_Decompressor_is_finished(brotli_Decompressor *self) {
540   PyObject *ret = NULL;
541   std::vector<uint8_t> output;
542   BROTLI_BOOL ok = BROTLI_TRUE;
543 
544   if (!self->dec) {
545     ok = BROTLI_FALSE;
546     PyErr_SetString(BrotliError, "BrotliDecoderState is NULL while checking is_finished");
547     goto end;
548   }
549 
550   if (BrotliDecoderIsFinished(self->dec)) {
551     Py_RETURN_TRUE;
552   } else {
553     Py_RETURN_FALSE;
554   }
555 
556 end:
557   if (ok) {
558     ret = PyBytes_FromStringAndSize((char*)(output.empty() ? NULL : &output[0]), output.size());
559   } else {
560     PyErr_SetString(BrotliError, "BrotliDecoderDecompressStream failed while finishing the stream");
561   }
562 
563   return ret;
564 }
565 
566 static PyMemberDef brotli_Decompressor_members[] = {
567   {NULL}  /* Sentinel */
568 };
569 
570 static PyMethodDef brotli_Decompressor_methods[] = {
571   {"process", (PyCFunction)brotli_Decompressor_process, METH_VARARGS, brotli_Decompressor_process_doc},
572   {"is_finished", (PyCFunction)brotli_Decompressor_is_finished, METH_NOARGS, brotli_Decompressor_is_finished_doc},
573   {NULL}  /* Sentinel */
574 };
575 
576 static PyTypeObject brotli_DecompressorType = {
577   #if PY_MAJOR_VERSION >= 3
578   PyVarObject_HEAD_INIT(NULL, 0)
579   #else
580   PyObject_HEAD_INIT(NULL)
581   0,                                     /* ob_size*/
582   #endif
583   "brotli.Decompressor",                   /* tp_name */
584   sizeof(brotli_Decompressor),             /* tp_basicsize */
585   0,                                       /* tp_itemsize */
586   (destructor)brotli_Decompressor_dealloc, /* tp_dealloc */
587   0,                                       /* tp_print */
588   0,                                       /* tp_getattr */
589   0,                                       /* tp_setattr */
590   0,                                       /* tp_compare */
591   0,                                       /* tp_repr */
592   0,                                       /* tp_as_number */
593   0,                                       /* tp_as_sequence */
594   0,                                       /* tp_as_mapping */
595   0,                                       /* tp_hash  */
596   0,                                       /* tp_call */
597   0,                                       /* tp_str */
598   0,                                       /* tp_getattro */
599   0,                                       /* tp_setattro */
600   0,                                       /* tp_as_buffer */
601   Py_TPFLAGS_DEFAULT,                      /* tp_flags */
602   brotli_Decompressor_doc,                 /* tp_doc */
603   0,                                       /* tp_traverse */
604   0,                                       /* tp_clear */
605   0,                                       /* tp_richcompare */
606   0,                                       /* tp_weaklistoffset */
607   0,                                       /* tp_iter */
608   0,                                       /* tp_iternext */
609   brotli_Decompressor_methods,             /* tp_methods */
610   brotli_Decompressor_members,             /* tp_members */
611   0,                                       /* tp_getset */
612   0,                                       /* tp_base */
613   0,                                       /* tp_dict */
614   0,                                       /* tp_descr_get */
615   0,                                       /* tp_descr_set */
616   0,                                       /* tp_dictoffset */
617   (initproc)brotli_Decompressor_init,      /* tp_init */
618   0,                                       /* tp_alloc */
619   brotli_Decompressor_new,                 /* tp_new */
620 };
621 
622 PyDoc_STRVAR(brotli_decompress__doc__,
623 "Decompress a compressed byte string.\n"
624 "\n"
625 "Signature:\n"
626 "  decompress(string)\n"
627 "\n"
628 "Args:\n"
629 "  string (bytes): The compressed input data.\n"
630 "\n"
631 "Returns:\n"
632 "  The decompressed byte string.\n"
633 "\n"
634 "Raises:\n"
635 "  brotli.error: If decompressor fails.\n");
636 
brotli_decompress(PyObject * self,PyObject * args,PyObject * keywds)637 static PyObject* brotli_decompress(PyObject *self, PyObject *args, PyObject *keywds) {
638   PyObject *ret = NULL;
639   Py_buffer input;
640   const uint8_t* next_in;
641   size_t available_in;
642   int ok;
643 
644   static const char *kwlist[] = {"string", NULL};
645 
646 #if PY_MAJOR_VERSION >= 3
647   ok = PyArg_ParseTupleAndKeywords(args, keywds, "y*|:decompress",
648                                    const_cast<char **>(kwlist), &input);
649 #else
650   ok = PyArg_ParseTupleAndKeywords(args, keywds, "s*|:decompress",
651                                    const_cast<char **>(kwlist), &input);
652 #endif
653 
654   if (!ok)
655     return NULL;
656 
657   std::vector<uint8_t> output;
658 
659   /* >>> Pure C block; release python GIL. */
660   Py_BEGIN_ALLOW_THREADS
661 
662   BrotliDecoderState* state = BrotliDecoderCreateInstance(0, 0, 0);
663 
664   BrotliDecoderResult result = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
665   next_in = static_cast<uint8_t*>(input.buf);
666   available_in = input.len;
667   while (result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
668     size_t available_out = 0;
669     result = BrotliDecoderDecompressStream(state, &available_in, &next_in,
670                                            &available_out, 0, 0);
671     const uint8_t* next_out = BrotliDecoderTakeOutput(state, &available_out);
672     if (available_out != 0)
673       output.insert(output.end(), next_out, next_out + available_out);
674   }
675   ok = result == BROTLI_DECODER_RESULT_SUCCESS;
676   BrotliDecoderDestroyInstance(state);
677 
678   Py_END_ALLOW_THREADS
679   /* <<< Pure C block end. Python GIL reacquired. */
680 
681   PyBuffer_Release(&input);
682   if (ok) {
683     ret = PyBytes_FromStringAndSize((char*)(output.size() ? &output[0] : NULL), output.size());
684   } else {
685     PyErr_SetString(BrotliError, "BrotliDecompress failed");
686   }
687 
688   return ret;
689 }
690 
691 static PyMethodDef brotli_methods[] = {
692   {"decompress", (PyCFunction)brotli_decompress, METH_VARARGS | METH_KEYWORDS, brotli_decompress__doc__},
693   {NULL, NULL, 0, NULL}
694 };
695 
696 PyDoc_STRVAR(brotli_doc, "Implementation module for the Brotli library.");
697 
698 #if PY_MAJOR_VERSION >= 3
699 #define INIT_BROTLI   PyInit__brotli
700 #define CREATE_BROTLI PyModule_Create(&brotli_module)
701 #define RETURN_BROTLI return m
702 #define RETURN_NULL return NULL
703 
704 static struct PyModuleDef brotli_module = {
705   PyModuleDef_HEAD_INIT,
706   "_brotli",
707   brotli_doc,
708   0,
709   brotli_methods,
710   NULL,
711   NULL,
712   NULL
713 };
714 #else
715 #define INIT_BROTLI   init_brotli
716 #define CREATE_BROTLI Py_InitModule3("_brotli", brotli_methods, brotli_doc)
717 #define RETURN_BROTLI return
718 #define RETURN_NULL return
719 #endif
720 
INIT_BROTLI(void)721 PyMODINIT_FUNC INIT_BROTLI(void) {
722   PyObject *m = CREATE_BROTLI;
723 
724   BrotliError = PyErr_NewException((char*) "brotli.error", NULL, NULL);
725   if (BrotliError != NULL) {
726     Py_INCREF(BrotliError);
727     PyModule_AddObject(m, "error", BrotliError);
728   }
729 
730   if (PyType_Ready(&brotli_CompressorType) < 0) {
731     RETURN_NULL;
732   }
733   Py_INCREF(&brotli_CompressorType);
734   PyModule_AddObject(m, "Compressor", (PyObject *)&brotli_CompressorType);
735 
736   if (PyType_Ready(&brotli_DecompressorType) < 0) {
737     RETURN_NULL;
738   }
739   Py_INCREF(&brotli_DecompressorType);
740   PyModule_AddObject(m, "Decompressor", (PyObject *)&brotli_DecompressorType);
741 
742   PyModule_AddIntConstant(m, "MODE_GENERIC", (int) BROTLI_MODE_GENERIC);
743   PyModule_AddIntConstant(m, "MODE_TEXT", (int) BROTLI_MODE_TEXT);
744   PyModule_AddIntConstant(m, "MODE_FONT", (int) BROTLI_MODE_FONT);
745 
746   char version[16];
747   snprintf(version, sizeof(version), "%d.%d.%d",
748       BROTLI_VERSION >> 24, (BROTLI_VERSION >> 12) & 0xFFF, BROTLI_VERSION & 0xFFF);
749   PyModule_AddStringConstant(m, "__version__", version);
750 
751   RETURN_BROTLI;
752 }
753