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