blob: d4075bd45848dc86e0fc708425a488c6ce7b854b [file] [log] [blame]
Khaled Hosny11306232014-11-20 14:16:26 +02001#define PY_SSIZE_T_CLEAN 1
2#include <Python.h>
3#include <bytesobject.h>
Alex Nicksay595a5242016-09-29 15:14:16 -04004#include <structmember.h>
Eugene Kliuchnikovdb3a1162016-06-13 15:22:13 +02005#include <vector>
Eugene Kliuchnikov2c2d5572016-08-22 15:44:12 +02006#include "../common/version.h"
Eugene Kliuchnikov81480012016-08-23 14:40:33 +02007#include <brotli/decode.h>
8#include <brotli/encode.h>
Khaled Hosny11306232014-11-20 14:16:26 +02009
10#if PY_MAJOR_VERSION >= 3
11#define PyInt_Check PyLong_Check
12#define PyInt_AsLong PyLong_AsLong
13#endif
14
Khaled Hosny11306232014-11-20 14:16:26 +020015static PyObject *BrotliError;
16
eustasc5c80a92016-01-25 15:13:17 +010017static int as_bounded_int(PyObject *o, int* result, int lower_bound, int upper_bound) {
eustasa4f40c12016-01-25 11:39:05 +010018 long value = PyInt_AsLong(o);
eustasc5c80a92016-01-25 15:13:17 +010019 if ((value < (long) lower_bound) || (value > (long) upper_bound)) {
eustasa4f40c12016-01-25 11:39:05 +010020 return 0;
21 }
22 *result = (int) value;
eustasca0ae4c2016-01-25 11:40:20 +010023 return 1;
eustasa4f40c12016-01-25 11:39:05 +010024}
25
Eugene Kliuchnikovdb3a1162016-06-13 15:22:13 +020026static int mode_convertor(PyObject *o, BrotliEncoderMode *mode) {
Khaled Hosny11306232014-11-20 14:16:26 +020027 if (!PyInt_Check(o)) {
28 PyErr_SetString(BrotliError, "Invalid mode");
29 return 0;
30 }
31
eustasc49918e2016-01-25 12:07:39 +010032 int mode_value = -1;
eustasc5c80a92016-01-25 15:13:17 +010033 if (!as_bounded_int(o, &mode_value, 0, 255)) {
eustasc49918e2016-01-25 12:07:39 +010034 PyErr_SetString(BrotliError, "Invalid mode");
35 return 0;
36 }
Eugene Kliuchnikovdb3a1162016-06-13 15:22:13 +020037 *mode = (BrotliEncoderMode) mode_value;
38 if (*mode != BROTLI_MODE_GENERIC &&
39 *mode != BROTLI_MODE_TEXT &&
40 *mode != BROTLI_MODE_FONT) {
Khaled Hosny11306232014-11-20 14:16:26 +020041 PyErr_SetString(BrotliError, "Invalid mode");
42 return 0;
43 }
44
45 return 1;
46}
47
Cosimo Lupo6d935db2015-05-08 12:38:22 +010048static int quality_convertor(PyObject *o, int *quality) {
49 if (!PyInt_Check(o)) {
50 PyErr_SetString(BrotliError, "Invalid quality");
51 return 0;
52 }
53
eustasc5c80a92016-01-25 15:13:17 +010054 if (!as_bounded_int(o, quality, 0, 11)) {
Cosimo Lupo6d935db2015-05-08 12:38:22 +010055 PyErr_SetString(BrotliError, "Invalid quality. Range is 0 to 11.");
56 return 0;
57 }
58
59 return 1;
60}
61
62static int lgwin_convertor(PyObject *o, int *lgwin) {
63 if (!PyInt_Check(o)) {
64 PyErr_SetString(BrotliError, "Invalid lgwin");
65 return 0;
66 }
67
eustasc5c80a92016-01-25 15:13:17 +010068 if (!as_bounded_int(o, lgwin, 10, 24)) {
Cosimo Lupo8195a5c2015-10-06 19:49:11 +010069 PyErr_SetString(BrotliError, "Invalid lgwin. Range is 10 to 24.");
Cosimo Lupo6d935db2015-05-08 12:38:22 +010070 return 0;
71 }
72
73 return 1;
74}
75
76static int lgblock_convertor(PyObject *o, int *lgblock) {
77 if (!PyInt_Check(o)) {
78 PyErr_SetString(BrotliError, "Invalid lgblock");
79 return 0;
80 }
81
eustasc5c80a92016-01-25 15:13:17 +010082 if (!as_bounded_int(o, lgblock, 0, 24) || (*lgblock != 0 && *lgblock < 16)) {
Cosimo Lupo6d935db2015-05-08 12:38:22 +010083 PyErr_SetString(BrotliError, "Invalid lgblock. Can be 0 or in range 16 to 24.");
84 return 0;
85 }
86
87 return 1;
88}
89
Alex Nicksay56323152016-10-24 07:28:56 -040090static BROTLI_BOOL compress_stream(BrotliEncoderState* enc, BrotliEncoderOperation op,
Eugene Kliuchnikova6292892017-08-28 11:31:29 +020091 std::vector<uint8_t>* output,
92 uint8_t* input, size_t input_length) {
Alex Nicksay56323152016-10-24 07:28:56 -040093 BROTLI_BOOL ok = BROTLI_TRUE;
Eugene Kliuchnikov0ee41612016-12-12 10:27:13 +010094 Py_BEGIN_ALLOW_THREADS
Alex Nicksay56323152016-10-24 07:28:56 -040095
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
Eugene Kliuchnikov0ee41612016-12-12 10:27:13 +0100121 Py_END_ALLOW_THREADS
Alex Nicksay56323152016-10-24 07:28:56 -0400122 return ok;
123}
124
Alex Nicksay595a5242016-09-29 15:14:16 -0400125PyDoc_STRVAR(brotli_Compressor_doc,
126"An object to compress a byte string.\n"
Cosimo Lupo3351bb02015-05-08 13:02:05 +0100127"\n"
128"Signature:\n"
Eugene Kliuchnikovd63e8f72017-08-04 10:02:56 +0200129" Compressor(mode=MODE_GENERIC, quality=11, lgwin=22, lgblock=0)\n"
Khaled Hosny11306232014-11-20 14:16:26 +0200130"\n"
Cosimo Lupo89c74d62015-05-08 11:23:08 +0100131"Args:\n"
Cosimo Lupoaa6f7d82015-05-11 11:09:36 +0100132" 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"
Cosimo Lupo4c1d0692015-05-08 12:02:08 +0100134" 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"
Cosimo Lupo8195a5c2015-10-06 19:49:11 +0100138" is 10 to 24. Defaults to 22.\n"
Cosimo Lupo4c1d0692015-05-08 12:02:08 +0100139" 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"
Cosimo Lupo89c74d62015-05-08 11:23:08 +0100142"\n"
Cosimo Lupo89c74d62015-05-08 11:23:08 +0100143"Raises:\n"
Alex Nicksay595a5242016-09-29 15:14:16 -0400144" brotli.error: If arguments are invalid.\n");
Khaled Hosny11306232014-11-20 14:16:26 +0200145
Alex Nicksay595a5242016-09-29 15:14:16 -0400146typedef struct {
147 PyObject_HEAD
148 BrotliEncoderState* enc;
149} brotli_Compressor;
150
151static 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
160static 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
171static int brotli_Compressor_init(brotli_Compressor *self, PyObject *args, PyObject *keywds) {
Eugene Kliuchnikovdb3a1162016-06-13 15:22:13 +0200172 BrotliEncoderMode mode = (BrotliEncoderMode) -1;
Cosimo Lupo6d935db2015-05-08 12:38:22 +0100173 int quality = -1;
174 int lgwin = -1;
175 int lgblock = -1;
Khaled Hosny11306232014-11-20 14:16:26 +0200176 int ok;
177
Eugene Kliuchnikovd63e8f72017-08-04 10:02:56 +0200178 static const char *kwlist[] = {"mode", "quality", "lgwin", "lgblock", NULL};
Cosimo Lupo89c74d62015-05-08 11:23:08 +0100179
Eugene Kliuchnikovd63e8f72017-08-04 10:02:56 +0200180 ok = PyArg_ParseTupleAndKeywords(args, keywds, "|O&O&O&O&:Compressor",
Alex Nicksay595a5242016-09-29 15:14:16 -0400181 const_cast<char **>(kwlist),
182 &mode_convertor, &mode,
183 &quality_convertor, &quality,
184 &lgwin_convertor, &lgwin,
Eugene Kliuchnikovd63e8f72017-08-04 10:02:56 +0200185 &lgblock_convertor, &lgblock);
Alex Nicksay595a5242016-09-29 15:14:16 -0400186 if (!ok)
187 return -1;
188 if (!self->enc)
189 return -1;
Eugene Kliuchnikov30612e32016-02-23 17:42:55 +0100190
Alex Nicksay595a5242016-09-29 15:14:16 -0400191 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
Alex Nicksay595a5242016-09-29 15:14:16 -0400200 return 0;
201}
202
Alex Nicksay56323152016-10-24 07:28:56 -0400203PyDoc_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"
Alex Nicksay595a5242016-09-29 15:14:16 -0400210"\n"
211"Signature:\n"
212" compress(string)\n"
213"\n"
214"Args:\n"
Alex Nicksay56323152016-10-24 07:28:56 -0400215" string (bytes): The input data\n"
Alex Nicksay595a5242016-09-29 15:14:16 -0400216"\n"
217"Returns:\n"
Alex Nicksay56323152016-10-24 07:28:56 -0400218" The compressed output data (bytes)\n"
Alex Nicksay595a5242016-09-29 15:14:16 -0400219"\n"
220"Raises:\n"
Alex Nicksay56323152016-10-24 07:28:56 -0400221" brotli.error: If compression fails\n");
Alex Nicksay595a5242016-09-29 15:14:16 -0400222
Alex Nicksay56323152016-10-24 07:28:56 -0400223static PyObject* brotli_Compressor_process(brotli_Compressor *self, PyObject *args) {
Alex Nicksay595a5242016-09-29 15:14:16 -0400224 PyObject* ret = NULL;
Alex Nicksay56323152016-10-24 07:28:56 -0400225 std::vector<uint8_t> output;
Eugene Kliuchnikova6292892017-08-28 11:31:29 +0200226 Py_buffer input;
Alex Nicksay56323152016-10-24 07:28:56 -0400227 BROTLI_BOOL ok = BROTLI_TRUE;
Alex Nicksay595a5242016-09-29 15:14:16 -0400228
Eugene Kliuchnikova6292892017-08-28 11:31:29 +0200229#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
Khaled Hosny11306232014-11-20 14:16:26 +0200235 if (!ok)
236 return NULL;
237
Alex Nicksay595a5242016-09-29 15:14:16 -0400238 if (!self->enc) {
Alex Nicksay56323152016-10-24 07:28:56 -0400239 ok = BROTLI_FALSE;
Eugene Kliuchnikovdb3a1162016-06-13 15:22:13 +0200240 goto end;
241 }
Alex Nicksay595a5242016-09-29 15:14:16 -0400242
Alex Nicksay56323152016-10-24 07:28:56 -0400243 ok = compress_stream(self->enc, BROTLI_OPERATION_PROCESS,
Eugene Kliuchnikova6292892017-08-28 11:31:29 +0200244 &output, static_cast<uint8_t*>(input.buf), input.len);
Eugene Kliuchnikov30612e32016-02-23 17:42:55 +0100245
Eugene Kliuchnikovdb3a1162016-06-13 15:22:13 +0200246end:
Eugene Kliuchnikova6292892017-08-28 11:31:29 +0200247 PyBuffer_Release(&input);
Khaled Hosny11306232014-11-20 14:16:26 +0200248 if (ok) {
Alex Nicksay56323152016-10-24 07:28:56 -0400249 ret = PyBytes_FromStringAndSize((char*)(output.size() ? &output[0] : NULL), output.size());
Khaled Hosny11306232014-11-20 14:16:26 +0200250 } else {
Alex Nicksay56323152016-10-24 07:28:56 -0400251 PyErr_SetString(BrotliError, "BrotliEncoderCompressStream failed while processing the stream");
Khaled Hosny11306232014-11-20 14:16:26 +0200252 }
253
Alex Nicksay56323152016-10-24 07:28:56 -0400254 return ret;
255}
256
257PyDoc_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
271static 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
284end:
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
294PyDoc_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
311static 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
328end:
329 if (ok) {
Eugene Kliuchnikov1becbbf2017-06-30 13:09:50 +0200330 ret = PyBytes_FromStringAndSize((char*)(output.empty() ? NULL : &output[0]), output.size());
Alex Nicksay56323152016-10-24 07:28:56 -0400331 } else {
332 PyErr_SetString(BrotliError, "BrotliEncoderCompressStream failed while finishing the stream");
333 }
Khaled Hosny11306232014-11-20 14:16:26 +0200334
335 return ret;
336}
337
Alex Nicksay595a5242016-09-29 15:14:16 -0400338static PyMemberDef brotli_Compressor_members[] = {
339 {NULL} /* Sentinel */
340};
341
342static PyMethodDef brotli_Compressor_methods[] = {
Alex Nicksay56323152016-10-24 07:28:56 -0400343 {"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},
Alex Nicksay595a5242016-09-29 15:14:16 -0400346 {NULL} /* Sentinel */
347};
348
349static 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
Janek58f5c372017-06-28 16:32:28 +0200395static BROTLI_BOOL decompress_stream(BrotliDecoderState* dec,
Eugene Kliuchnikova6292892017-08-28 11:31:29 +0200396 std::vector<uint8_t>* output,
397 uint8_t* input, size_t input_length) {
Janek58f5c372017-06-28 16:32:28 +0200398 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 }
Justin Ridgewell5805f992018-11-12 04:36:00 -0500417 ok = result != BROTLI_DECODER_RESULT_ERROR && !available_in;
Janek58f5c372017-06-28 16:32:28 +0200418
419 Py_END_ALLOW_THREADS
420 return ok;
421}
422
423PyDoc_STRVAR(brotli_Decompressor_doc,
424"An object to decompress a byte string.\n"
425"\n"
426"Signature:\n"
Eugene Kliuchnikovd63e8f72017-08-04 10:02:56 +0200427" Decompressor()\n"
Janek58f5c372017-06-28 16:32:28 +0200428"\n"
429"Raises:\n"
430" brotli.error: If arguments are invalid.\n");
431
432typedef struct {
433 PyObject_HEAD
434 BrotliDecoderState* dec;
435} brotli_Decompressor;
436
437static 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
446static 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
457static int brotli_Decompressor_init(brotli_Decompressor *self, PyObject *args, PyObject *keywds) {
Janek58f5c372017-06-28 16:32:28 +0200458 int ok;
459
Eugene Kliuchnikovd63e8f72017-08-04 10:02:56 +0200460 static const char *kwlist[] = {NULL};
Janek58f5c372017-06-28 16:32:28 +0200461
Eugene Kliuchnikovd63e8f72017-08-04 10:02:56 +0200462 ok = PyArg_ParseTupleAndKeywords(args, keywds, "|:Decompressor",
463 const_cast<char **>(kwlist));
Janek58f5c372017-06-28 16:32:28 +0200464 if (!ok)
465 return -1;
466 if (!self->dec)
467 return -1;
468
Janek58f5c372017-06-28 16:32:28 +0200469 return 0;
470}
471
472PyDoc_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
492static PyObject* brotli_Decompressor_process(brotli_Decompressor *self, PyObject *args) {
493 PyObject* ret = NULL;
494 std::vector<uint8_t> output;
Eugene Kliuchnikova6292892017-08-28 11:31:29 +0200495 Py_buffer input;
Janek58f5c372017-06-28 16:32:28 +0200496 BROTLI_BOOL ok = BROTLI_TRUE;
497
Eugene Kliuchnikova6292892017-08-28 11:31:29 +0200498#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
Janek58f5c372017-06-28 16:32:28 +0200504 if (!ok)
505 return NULL;
506
507 if (!self->dec) {
508 ok = BROTLI_FALSE;
509 goto end;
510 }
511
Eugene Kliuchnikova6292892017-08-28 11:31:29 +0200512 ok = decompress_stream(self->dec, &output, static_cast<uint8_t*>(input.buf), input.len);
Janek58f5c372017-06-28 16:32:28 +0200513
514end:
Eugene Kliuchnikova6292892017-08-28 11:31:29 +0200515 PyBuffer_Release(&input);
Janek58f5c372017-06-28 16:32:28 +0200516 if (ok) {
Eugene Kliuchnikov1becbbf2017-06-30 13:09:50 +0200517 ret = PyBytes_FromStringAndSize((char*)(output.empty() ? NULL : &output[0]), output.size());
Janek58f5c372017-06-28 16:32:28 +0200518 } else {
519 PyErr_SetString(BrotliError, "BrotliDecoderDecompressStream failed while processing the stream");
520 }
521
522 return ret;
523}
524
525PyDoc_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
539static 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
556end:
557 if (ok) {
Eugene Kliuchnikov1becbbf2017-06-30 13:09:50 +0200558 ret = PyBytes_FromStringAndSize((char*)(output.empty() ? NULL : &output[0]), output.size());
Janek58f5c372017-06-28 16:32:28 +0200559 } else {
560 PyErr_SetString(BrotliError, "BrotliDecoderDecompressStream failed while finishing the stream");
561 }
562
563 return ret;
564}
565
566static PyMemberDef brotli_Decompressor_members[] = {
567 {NULL} /* Sentinel */
568};
569
570static 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
576static 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
Alex Nicksay595a5242016-09-29 15:14:16 -0400622PyDoc_STRVAR(brotli_decompress__doc__,
Cosimo Lupo3351bb02015-05-08 13:02:05 +0100623"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");
Khaled Hosny11306232014-11-20 14:16:26 +0200636
Eugene Kliuchnikov30612e32016-02-23 17:42:55 +0100637static PyObject* brotli_decompress(PyObject *self, PyObject *args, PyObject *keywds) {
Khaled Hosny11306232014-11-20 14:16:26 +0200638 PyObject *ret = NULL;
Eugene Kliuchnikova6292892017-08-28 11:31:29 +0200639 Py_buffer input;
640 const uint8_t* next_in;
641 size_t available_in;
Khaled Hosny11306232014-11-20 14:16:26 +0200642 int ok;
643
Eugene Kliuchnikovd63e8f72017-08-04 10:02:56 +0200644 static const char *kwlist[] = {"string", NULL};
Eugene Kliuchnikov30612e32016-02-23 17:42:55 +0100645
Eugene Kliuchnikova6292892017-08-28 11:31:29 +0200646#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
Khaled Hosny11306232014-11-20 14:16:26 +0200654 if (!ok)
655 return NULL;
656
Khaled Hosnye0c5df82015-03-13 23:55:03 +0200657 std::vector<uint8_t> output;
Eugene Kliuchnikov0ee41612016-12-12 10:27:13 +0100658
659 /* >>> Pure C block; release python GIL. */
660 Py_BEGIN_ALLOW_THREADS
661
Eugene Kliuchnikovb754f602016-09-21 15:37:45 +0200662 BrotliDecoderState* state = BrotliDecoderCreateInstance(0, 0, 0);
Eugene Kliuchnikov30612e32016-02-23 17:42:55 +0100663
Eugene Kliuchnikovb754f602016-09-21 15:37:45 +0200664 BrotliDecoderResult result = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
Eugene Kliuchnikova6292892017-08-28 11:31:29 +0200665 next_in = static_cast<uint8_t*>(input.buf);
666 available_in = input.len;
Eugene Kliuchnikovb754f602016-09-21 15:37:45 +0200667 while (result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
Eugene Kliuchnikov0ee41612016-12-12 10:27:13 +0100668 size_t available_out = 0;
Eugene Kliuchnikova6292892017-08-28 11:31:29 +0200669 result = BrotliDecoderDecompressStream(state, &available_in, &next_in,
Eugene Kliuchnikov0ee41612016-12-12 10:27:13 +0100670 &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);
eustas1f910642016-01-22 14:01:46 +0100674 }
Justin Ridgewell5805f992018-11-12 04:36:00 -0500675 ok = result == BROTLI_DECODER_RESULT_SUCCESS && !available_in;
Eugene Kliuchnikov0ee41612016-12-12 10:27:13 +0100676 BrotliDecoderDestroyInstance(state);
677
678 Py_END_ALLOW_THREADS
679 /* <<< Pure C block end. Python GIL reacquired. */
680
Eugene Kliuchnikova6292892017-08-28 11:31:29 +0200681 PyBuffer_Release(&input);
Khaled Hosny11306232014-11-20 14:16:26 +0200682 if (ok) {
Cosimo Lupo3e67d482015-10-05 11:09:11 +0100683 ret = PyBytes_FromStringAndSize((char*)(output.size() ? &output[0] : NULL), output.size());
Khaled Hosny11306232014-11-20 14:16:26 +0200684 } else {
Khaled Hosnye0c5df82015-03-13 23:55:03 +0200685 PyErr_SetString(BrotliError, "BrotliDecompress failed");
Khaled Hosny11306232014-11-20 14:16:26 +0200686 }
Alex Nicksayf7b5b3d2016-09-28 17:26:00 -0400687
Khaled Hosny11306232014-11-20 14:16:26 +0200688 return ret;
689}
690
691static PyMethodDef brotli_methods[] = {
Alex Nicksay595a5242016-09-29 15:14:16 -0400692 {"decompress", (PyCFunction)brotli_decompress, METH_VARARGS | METH_KEYWORDS, brotli_decompress__doc__},
Khaled Hosny11306232014-11-20 14:16:26 +0200693 {NULL, NULL, 0, NULL}
694};
695
Alex Nicksay595a5242016-09-29 15:14:16 -0400696PyDoc_STRVAR(brotli_doc, "Implementation module for the Brotli library.");
Khaled Hosny11306232014-11-20 14:16:26 +0200697
698#if PY_MAJOR_VERSION >= 3
Alex Nicksayf7b5b3d2016-09-28 17:26:00 -0400699#define INIT_BROTLI PyInit__brotli
Khaled Hosny11306232014-11-20 14:16:26 +0200700#define CREATE_BROTLI PyModule_Create(&brotli_module)
701#define RETURN_BROTLI return m
Alex Nicksay595a5242016-09-29 15:14:16 -0400702#define RETURN_NULL return NULL
Khaled Hosny11306232014-11-20 14:16:26 +0200703
704static struct PyModuleDef brotli_module = {
705 PyModuleDef_HEAD_INIT,
Eugene Kliuchnikovc8b37e82019-07-17 14:39:56 +0200706 "_brotli", /* m_name */
707 brotli_doc, /* m_doc */
708 0, /* m_size */
709 brotli_methods, /* m_methods */
710 NULL, /* m_reload */
711 NULL, /* m_traverse */
712 NULL, /* m_clear */
713 NULL /* m_free */
Khaled Hosny11306232014-11-20 14:16:26 +0200714};
715#else
Alex Nicksayf7b5b3d2016-09-28 17:26:00 -0400716#define INIT_BROTLI init_brotli
Alex Nicksay595a5242016-09-29 15:14:16 -0400717#define CREATE_BROTLI Py_InitModule3("_brotli", brotli_methods, brotli_doc)
Khaled Hosny11306232014-11-20 14:16:26 +0200718#define RETURN_BROTLI return
Alex Nicksay595a5242016-09-29 15:14:16 -0400719#define RETURN_NULL return
Khaled Hosny11306232014-11-20 14:16:26 +0200720#endif
721
722PyMODINIT_FUNC INIT_BROTLI(void) {
723 PyObject *m = CREATE_BROTLI;
724
725 BrotliError = PyErr_NewException((char*) "brotli.error", NULL, NULL);
Khaled Hosny11306232014-11-20 14:16:26 +0200726 if (BrotliError != NULL) {
727 Py_INCREF(BrotliError);
728 PyModule_AddObject(m, "error", BrotliError);
729 }
730
Alex Nicksay595a5242016-09-29 15:14:16 -0400731 if (PyType_Ready(&brotli_CompressorType) < 0) {
732 RETURN_NULL;
733 }
734 Py_INCREF(&brotli_CompressorType);
735 PyModule_AddObject(m, "Compressor", (PyObject *)&brotli_CompressorType);
736
Janek58f5c372017-06-28 16:32:28 +0200737 if (PyType_Ready(&brotli_DecompressorType) < 0) {
738 RETURN_NULL;
739 }
740 Py_INCREF(&brotli_DecompressorType);
741 PyModule_AddObject(m, "Decompressor", (PyObject *)&brotli_DecompressorType);
742
Eugene Kliuchnikovdb3a1162016-06-13 15:22:13 +0200743 PyModule_AddIntConstant(m, "MODE_GENERIC", (int) BROTLI_MODE_GENERIC);
744 PyModule_AddIntConstant(m, "MODE_TEXT", (int) BROTLI_MODE_TEXT);
745 PyModule_AddIntConstant(m, "MODE_FONT", (int) BROTLI_MODE_FONT);
Khaled Hosny11306232014-11-20 14:16:26 +0200746
Eugene Kliuchnikov2c2d5572016-08-22 15:44:12 +0200747 char version[16];
748 snprintf(version, sizeof(version), "%d.%d.%d",
749 BROTLI_VERSION >> 24, (BROTLI_VERSION >> 12) & 0xFFF, BROTLI_VERSION & 0xFFF);
750 PyModule_AddStringConstant(m, "__version__", version);
Cosimo Lupoac33d352015-08-11 10:42:22 +0100751
Khaled Hosny11306232014-11-20 14:16:26 +0200752 RETURN_BROTLI;
753}