2058 lines
65 KiB
Diff
2058 lines
65 KiB
Diff
diff -urN brotli-1.0.6/python/bro.py brotli-1.0.6-modified/python/bro.py
|
|
--- brotli-1.0.6/python/bro.py 2018-09-13 23:01:23.000000000 +0530
|
|
+++ brotli-1.0.6-modified/python/bro.py 2023-08-29 16:30:29.000000000 +0530
|
|
@@ -1,16 +1,21 @@
|
|
#! /usr/bin/env python
|
|
"""Compression/decompression utility using the Brotli algorithm."""
|
|
|
|
+# Note: Python2 has been deprecated long ago, but some projects out in
|
|
+# the wide world may still use it nevertheless. This should not
|
|
+# deprive them from being able to run Brotli.
|
|
from __future__ import print_function
|
|
+
|
|
import argparse
|
|
-import sys
|
|
import os
|
|
import platform
|
|
+import sys
|
|
|
|
import brotli
|
|
|
|
+
|
|
# default values of encoder parameters
|
|
-DEFAULT_PARAMS = {
|
|
+_DEFAULT_PARAMS = {
|
|
'mode': brotli.MODE_GENERIC,
|
|
'quality': 11,
|
|
'lgwin': 22,
|
|
@@ -19,17 +24,32 @@
|
|
|
|
|
|
def get_binary_stdio(stream):
|
|
- """ Return the specified standard input, output or errors stream as a
|
|
- 'raw' buffer object suitable for reading/writing binary data from/to it.
|
|
+ """Return the specified stdin/stdout/stderr stream.
|
|
+
|
|
+ If the stdio stream requested (i.e. sys.(stdin|stdout|stderr))
|
|
+ has been replaced with a stream object that does not have a `.buffer`
|
|
+ attribute, this will return the original stdio stream's buffer, i.e.
|
|
+ `sys.__(stdin|stdout|stderr)__.buffer`.
|
|
+
|
|
+ Args:
|
|
+ stream: One of 'stdin', 'stdout', 'stderr'.
|
|
+
|
|
+ Returns:
|
|
+ The stream, as a 'raw' buffer object (i.e. io.BufferedIOBase subclass
|
|
+ instance such as io.Bufferedreader/io.BufferedWriter), suitable for
|
|
+ reading/writing binary data from/to it.
|
|
"""
|
|
- assert stream in ['stdin', 'stdout', 'stderr'], 'invalid stream name'
|
|
- stdio = getattr(sys, stream)
|
|
+ if stream == 'stdin': stdio = sys.stdin
|
|
+ elif stream == 'stdout': stdio = sys.stdout
|
|
+ elif stream == 'stderr': stdio = sys.stderr
|
|
+ else:
|
|
+ raise ValueError('invalid stream name: %s' % (stream,))
|
|
if sys.version_info[0] < 3:
|
|
if sys.platform == 'win32':
|
|
# set I/O stream binary flag on python2.x (Windows)
|
|
runtime = platform.python_implementation()
|
|
if runtime == 'PyPy':
|
|
- # the msvcrt trick doesn't work in pypy, so I use fdopen
|
|
+ # the msvcrt trick doesn't work in pypy, so use fdopen().
|
|
mode = 'rb' if stream == 'stdin' else 'wb'
|
|
stdio = os.fdopen(stdio.fileno(), mode, 0)
|
|
else:
|
|
@@ -38,12 +58,20 @@
|
|
msvcrt.setmode(stdio.fileno(), os.O_BINARY)
|
|
return stdio
|
|
else:
|
|
- # get 'buffer' attribute to read/write binary data on python3.x
|
|
- if hasattr(stdio, 'buffer'):
|
|
+ try:
|
|
return stdio.buffer
|
|
- else:
|
|
- orig_stdio = getattr(sys, '__%s__' % stream)
|
|
- return orig_stdio.buffer
|
|
+ except AttributeError:
|
|
+ # The Python reference explains
|
|
+ # (-> https://docs.python.org/3/library/sys.html#sys.stdin)
|
|
+ # that the `.buffer` attribute might not exist, since
|
|
+ # the standard streams might have been replaced by something else
|
|
+ # (such as an `io.StringIO()` - perhaps via
|
|
+ # `contextlib.redirect_stdout()`).
|
|
+ # We fall back to the original stdio in these cases.
|
|
+ if stream == 'stdin': return sys.__stdin__.buffer
|
|
+ if stream == 'stdout': return sys.__stdout__.buffer
|
|
+ if stream == 'stderr': return sys.__stderr__.buffer
|
|
+ assert False, 'Impossible Situation.'
|
|
|
|
|
|
def main(args=None):
|
|
@@ -51,7 +79,7 @@
|
|
parser = argparse.ArgumentParser(
|
|
prog=os.path.basename(__file__), description=__doc__)
|
|
parser.add_argument(
|
|
- '--version', action='version', version=brotli.__version__)
|
|
+ '--version', action='version', version=brotli.version)
|
|
parser.add_argument(
|
|
'-i',
|
|
'--input',
|
|
@@ -114,46 +142,52 @@
|
|
help='Base 2 logarithm of the maximum input block size. '
|
|
'Range is 16 to 24. If set to 0, the value will be set based '
|
|
'on the quality. Defaults to 0.')
|
|
- # set default values using global DEFAULT_PARAMS dictionary
|
|
- parser.set_defaults(**DEFAULT_PARAMS)
|
|
+ # set default values using global _DEFAULT_PARAMS dictionary
|
|
+ parser.set_defaults(**_DEFAULT_PARAMS)
|
|
|
|
options = parser.parse_args(args=args)
|
|
|
|
if options.infile:
|
|
- if not os.path.isfile(options.infile):
|
|
- parser.error('file "%s" not found' % options.infile)
|
|
- with open(options.infile, 'rb') as infile:
|
|
- data = infile.read()
|
|
+ try:
|
|
+ with open(options.infile, 'rb') as infile:
|
|
+ data = infile.read()
|
|
+ except OSError:
|
|
+ parser.error('Could not read --infile: %s' % (infile,))
|
|
else:
|
|
if sys.stdin.isatty():
|
|
# interactive console, just quit
|
|
- parser.error('no input')
|
|
+ parser.error('No input (called from interactive terminal).')
|
|
infile = get_binary_stdio('stdin')
|
|
data = infile.read()
|
|
|
|
if options.outfile:
|
|
- if os.path.isfile(options.outfile) and not options.force:
|
|
- parser.error('output file exists')
|
|
+ # Caution! If `options.outfile` is a broken symlink, will try to
|
|
+ # redirect the write according to symlink.
|
|
+ if os.path.exists(options.outfile) and not options.force:
|
|
+ parser.error(('Target --outfile=%s already exists, '
|
|
+ 'but --force was not requested.') % (outfile,))
|
|
outfile = open(options.outfile, 'wb')
|
|
+ did_open_outfile = True
|
|
else:
|
|
outfile = get_binary_stdio('stdout')
|
|
-
|
|
+ did_open_outfile = False
|
|
try:
|
|
- if options.decompress:
|
|
- data = brotli.decompress(data)
|
|
- else:
|
|
- data = brotli.compress(
|
|
- data,
|
|
- mode=options.mode,
|
|
- quality=options.quality,
|
|
- lgwin=options.lgwin,
|
|
- lgblock=options.lgblock)
|
|
+ try:
|
|
+ if options.decompress:
|
|
+ data = brotli.decompress(data)
|
|
+ else:
|
|
+ data = brotli.compress(
|
|
+ data,
|
|
+ mode=options.mode,
|
|
+ quality=options.quality,
|
|
+ lgwin=options.lgwin,
|
|
+ lgblock=options.lgblock)
|
|
+ outfile.write(data)
|
|
+ finally:
|
|
+ if did_open_outfile: outfile.close()
|
|
except brotli.error as e:
|
|
parser.exit(1,
|
|
- 'bro: error: %s: %s' % (e, options.infile or 'sys.stdin'))
|
|
-
|
|
- outfile.write(data)
|
|
- outfile.close()
|
|
+ 'bro: error: %s: %s' % (e, options.infile or '{stdin}'))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
diff -urN brotli-1.0.6/python/_brotli.c brotli-1.0.6-modified/python/_brotli.c
|
|
--- brotli-1.0.6/python/_brotli.c 1970-01-01 05:30:00.000000000 +0530
|
|
+++ brotli-1.0.6-modified/python/_brotli.c 2026-01-27 14:34:57.771022855 +0530
|
|
@@ -0,0 +1,1073 @@
|
|
+#define PY_SSIZE_T_CLEAN 1
|
|
+#include <Python.h>
|
|
+#include <bytesobject.h>
|
|
+#include <structmember.h>
|
|
+
|
|
+#include <brotli/decode.h>
|
|
+#include <brotli/encode.h>
|
|
+
|
|
+#if PY_MAJOR_VERSION >= 3
|
|
+#define PyInt_Check PyLong_Check
|
|
+#define PyInt_AsLong PyLong_AsLong
|
|
+#else
|
|
+#define Py_ARRAY_LENGTH(array) (sizeof(array) / sizeof((array)[0]))
|
|
+#endif
|
|
+
|
|
+static PyObject *BrotliError;
|
|
+
|
|
+/* -----------------------------------
|
|
+ BlocksOutputBuffer code
|
|
+ ----------------------------------- */
|
|
+typedef struct {
|
|
+ /* List of blocks */
|
|
+ PyObject *list;
|
|
+ /* Number of whole allocated size. */
|
|
+ Py_ssize_t allocated;
|
|
+ Py_ssize_t size_limit;
|
|
+} BlocksOutputBuffer;
|
|
+
|
|
+static const char unable_allocate_msg[] = "Unable to allocate output buffer.";
|
|
+
|
|
+/* Block size sequence */
|
|
+#define KB (1024)
|
|
+#define MB (1024*1024)
|
|
+static const Py_ssize_t BUFFER_BLOCK_SIZE[] =
|
|
+ { 32*KB, 64*KB, 256*KB, 1*MB, 4*MB, 8*MB, 16*MB, 16*MB,
|
|
+ 32*MB, 32*MB, 32*MB, 32*MB, 64*MB, 64*MB, 128*MB, 128*MB,
|
|
+ 256*MB };
|
|
+#undef KB
|
|
+#undef MB
|
|
+
|
|
+/* According to the block sizes defined by BUFFER_BLOCK_SIZE, the whole
|
|
+ allocated size growth step is:
|
|
+ 1 32 KB +32 KB
|
|
+ 2 96 KB +64 KB
|
|
+ 3 352 KB +256 KB
|
|
+ 4 1.34 MB +1 MB
|
|
+ 5 5.34 MB +4 MB
|
|
+ 6 13.34 MB +8 MB
|
|
+ 7 29.34 MB +16 MB
|
|
+ 8 45.34 MB +16 MB
|
|
+ 9 77.34 MB +32 MB
|
|
+ 10 109.34 MB +32 MB
|
|
+ 11 141.34 MB +32 MB
|
|
+ 12 173.34 MB +32 MB
|
|
+ 13 237.34 MB +64 MB
|
|
+ 14 301.34 MB +64 MB
|
|
+ 15 429.34 MB +128 MB
|
|
+ 16 557.34 MB +128 MB
|
|
+ 17 813.34 MB +256 MB
|
|
+ 18 1069.34 MB +256 MB
|
|
+ 19 1325.34 MB +256 MB
|
|
+ 20 1581.34 MB +256 MB
|
|
+ 21 1837.34 MB +256 MB
|
|
+ 22 2093.34 MB +256 MB
|
|
+ ...
|
|
+*/
|
|
+
|
|
+/* Initialize the buffer, and grow the buffer.
|
|
+ Return 0 on success
|
|
+ Return -1 on failure
|
|
+*/
|
|
+static inline int
|
|
+BlocksOutputBuffer_InitAndGrow(BlocksOutputBuffer *buffer, Py_ssize_t size_limit,
|
|
+ size_t *avail_out, uint8_t **next_out)
|
|
+{
|
|
+ PyObject *b;
|
|
+ Py_ssize_t block_size = BUFFER_BLOCK_SIZE[0];
|
|
+
|
|
+ assert(size_limit > 0);
|
|
+
|
|
+ if (size_limit < block_size) {
|
|
+ block_size = size_limit;
|
|
+ }
|
|
+
|
|
+ // Ensure .list was set to NULL, for BlocksOutputBuffer_OnError().
|
|
+ assert(buffer->list == NULL);
|
|
+
|
|
+ // The first block
|
|
+ b = PyBytes_FromStringAndSize(NULL, block_size);
|
|
+ if (b == NULL) {
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ // Create list
|
|
+ buffer->list = PyList_New(1);
|
|
+ if (buffer->list == NULL) {
|
|
+ Py_DECREF(b);
|
|
+ return -1;
|
|
+ }
|
|
+ PyList_SET_ITEM(buffer->list, 0, b);
|
|
+
|
|
+ // Set variables
|
|
+ buffer->allocated = block_size;
|
|
+ buffer->size_limit = size_limit;
|
|
+
|
|
+ *avail_out = (size_t) block_size;
|
|
+ *next_out = (uint8_t*) PyBytes_AS_STRING(b);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Grow the buffer. The avail_out must be 0, please check it before calling.
|
|
+ Return 0 on success
|
|
+ Return -1 on failure
|
|
+*/
|
|
+static inline int
|
|
+BlocksOutputBuffer_Grow(BlocksOutputBuffer *buffer,
|
|
+ size_t *avail_out, uint8_t **next_out)
|
|
+{
|
|
+ PyObject *b;
|
|
+ const Py_ssize_t list_len = Py_SIZE(buffer->list);
|
|
+ Py_ssize_t block_size;
|
|
+
|
|
+ // Ensure no gaps in the data
|
|
+ assert(*avail_out == 0);
|
|
+
|
|
+ // Get block size
|
|
+ if (list_len < (Py_ssize_t) Py_ARRAY_LENGTH(BUFFER_BLOCK_SIZE)) {
|
|
+ block_size = BUFFER_BLOCK_SIZE[list_len];
|
|
+ } else {
|
|
+ block_size = BUFFER_BLOCK_SIZE[Py_ARRAY_LENGTH(BUFFER_BLOCK_SIZE) - 1];
|
|
+ }
|
|
+
|
|
+ if (block_size > buffer->size_limit - buffer->allocated) {
|
|
+ block_size = buffer->size_limit - buffer->allocated;
|
|
+ }
|
|
+
|
|
+ if (block_size == 0) {
|
|
+ // We are at the size_limit (either the provided one, in which case we
|
|
+ // shouldn't have been called, or the implicit PY_SSIZE_T_MAX one, in
|
|
+ // which case we wouldn't be able to concatenate the blocks at the end).
|
|
+ PyErr_SetString(PyExc_MemoryError, "too long");
|
|
+ return -1;
|
|
+ }
|
|
+
|
|
+ // Create the block
|
|
+ b = PyBytes_FromStringAndSize(NULL, block_size);
|
|
+ if (b == NULL) {
|
|
+ PyErr_SetString(PyExc_MemoryError, unable_allocate_msg);
|
|
+ return -1;
|
|
+ }
|
|
+ if (PyList_Append(buffer->list, b) < 0) {
|
|
+ Py_DECREF(b);
|
|
+ return -1;
|
|
+ }
|
|
+ Py_DECREF(b);
|
|
+
|
|
+ // Set variables
|
|
+ buffer->allocated += block_size;
|
|
+
|
|
+ *avail_out = (size_t) block_size;
|
|
+ *next_out = (uint8_t*) PyBytes_AS_STRING(b);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+/* Finish the buffer.
|
|
+ Return a bytes object on success
|
|
+ Return NULL on failure
|
|
+*/
|
|
+static inline PyObject *
|
|
+BlocksOutputBuffer_Finish(BlocksOutputBuffer *buffer, size_t avail_out)
|
|
+{
|
|
+ PyObject *result, *block;
|
|
+ const Py_ssize_t list_len = Py_SIZE(buffer->list);
|
|
+
|
|
+ // Fast path for single block
|
|
+ if ((list_len == 1 && avail_out == 0) ||
|
|
+ (list_len == 2 && Py_SIZE(PyList_GET_ITEM(buffer->list, 1)) == (Py_ssize_t) avail_out))
|
|
+ {
|
|
+ block = PyList_GET_ITEM(buffer->list, 0);
|
|
+ Py_INCREF(block);
|
|
+
|
|
+ Py_CLEAR(buffer->list);
|
|
+ return block;
|
|
+ }
|
|
+
|
|
+ // Final bytes object
|
|
+ result = PyBytes_FromStringAndSize(NULL, buffer->allocated - avail_out);
|
|
+ if (result == NULL) {
|
|
+ PyErr_SetString(PyExc_MemoryError, unable_allocate_msg);
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ // Memory copy
|
|
+ if (list_len > 0) {
|
|
+ char *posi = PyBytes_AS_STRING(result);
|
|
+
|
|
+ // Blocks except the last one
|
|
+ Py_ssize_t i = 0;
|
|
+ for (; i < list_len-1; i++) {
|
|
+ block = PyList_GET_ITEM(buffer->list, i);
|
|
+ memcpy(posi, PyBytes_AS_STRING(block), Py_SIZE(block));
|
|
+ posi += Py_SIZE(block);
|
|
+ }
|
|
+ // The last block
|
|
+ block = PyList_GET_ITEM(buffer->list, i);
|
|
+ memcpy(posi, PyBytes_AS_STRING(block), Py_SIZE(block) - avail_out);
|
|
+ } else {
|
|
+ assert(Py_SIZE(result) == 0);
|
|
+ }
|
|
+
|
|
+ Py_CLEAR(buffer->list);
|
|
+ return result;
|
|
+}
|
|
+
|
|
+/* Clean up the buffer */
|
|
+static inline void
|
|
+BlocksOutputBuffer_OnError(BlocksOutputBuffer *buffer)
|
|
+{
|
|
+ Py_CLEAR(buffer->list);
|
|
+}
|
|
+
|
|
+
|
|
+static int as_bounded_int(PyObject *o, int* result, int lower_bound, int upper_bound) {
|
|
+ long value = PyInt_AsLong(o);
|
|
+ if ((value < (long) lower_bound) || (value > (long) upper_bound)) {
|
|
+ return 0;
|
|
+ }
|
|
+ *result = (int) value;
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static int mode_convertor(PyObject *o, BrotliEncoderMode *mode) {
|
|
+ if (!PyInt_Check(o)) {
|
|
+ PyErr_SetString(BrotliError, "Invalid mode");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ int mode_value = -1;
|
|
+ if (!as_bounded_int(o, &mode_value, 0, 255)) {
|
|
+ PyErr_SetString(BrotliError, "Invalid mode");
|
|
+ return 0;
|
|
+ }
|
|
+ *mode = (BrotliEncoderMode) mode_value;
|
|
+ if (*mode != BROTLI_MODE_GENERIC &&
|
|
+ *mode != BROTLI_MODE_TEXT &&
|
|
+ *mode != BROTLI_MODE_FONT) {
|
|
+ PyErr_SetString(BrotliError, "Invalid mode");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static int quality_convertor(PyObject *o, int *quality) {
|
|
+ if (!PyInt_Check(o)) {
|
|
+ PyErr_SetString(BrotliError, "Invalid quality");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (!as_bounded_int(o, quality, 0, 11)) {
|
|
+ PyErr_SetString(BrotliError, "Invalid quality. Range is 0 to 11.");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static int lgwin_convertor(PyObject *o, int *lgwin) {
|
|
+ if (!PyInt_Check(o)) {
|
|
+ PyErr_SetString(BrotliError, "Invalid lgwin");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (!as_bounded_int(o, lgwin, 10, 24)) {
|
|
+ PyErr_SetString(BrotliError, "Invalid lgwin. Range is 10 to 24.");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static int lgblock_convertor(PyObject *o, int *lgblock) {
|
|
+ if (!PyInt_Check(o)) {
|
|
+ PyErr_SetString(BrotliError, "Invalid lgblock");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ if (!as_bounded_int(o, lgblock, 0, 24) || (*lgblock != 0 && *lgblock < 16)) {
|
|
+ PyErr_SetString(BrotliError, "Invalid lgblock. Can be 0 or in range 16 to 24.");
|
|
+ return 0;
|
|
+ }
|
|
+
|
|
+ return 1;
|
|
+}
|
|
+
|
|
+static PyObject* compress_stream(BrotliEncoderState* enc, BrotliEncoderOperation op,
|
|
+ uint8_t* input, size_t input_length) {
|
|
+ BROTLI_BOOL ok;
|
|
+
|
|
+ size_t available_in = input_length;
|
|
+ const uint8_t* next_in = input;
|
|
+
|
|
+ size_t available_out;
|
|
+ uint8_t* next_out;
|
|
+ BlocksOutputBuffer buffer = {.list=NULL};
|
|
+ PyObject *ret;
|
|
+
|
|
+ if (BlocksOutputBuffer_InitAndGrow(&buffer, PY_SSIZE_T_MAX, &available_out, &next_out) < 0) {
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ while (1) {
|
|
+ Py_BEGIN_ALLOW_THREADS
|
|
+ ok = BrotliEncoderCompressStream(enc, op,
|
|
+ &available_in, &next_in,
|
|
+ &available_out, &next_out, NULL);
|
|
+ Py_END_ALLOW_THREADS
|
|
+ if (!ok) {
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ if (available_in || BrotliEncoderHasMoreOutput(enc)) {
|
|
+ if (available_out == 0) {
|
|
+ if (BlocksOutputBuffer_Grow(&buffer, &available_out, &next_out) < 0) {
|
|
+ goto error;
|
|
+ }
|
|
+ }
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ ret = BlocksOutputBuffer_Finish(&buffer, available_out);
|
|
+ if (ret != NULL) {
|
|
+ return ret;
|
|
+ }
|
|
+
|
|
+error:
|
|
+ BlocksOutputBuffer_OnError(&buffer);
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+PyDoc_STRVAR(brotli_Compressor_doc,
|
|
+"An object to compress a byte string.\n"
|
|
+"\n"
|
|
+"Signature:\n"
|
|
+" Compressor(mode=MODE_GENERIC, quality=11, lgwin=22, lgblock=0)\n"
|
|
+"\n"
|
|
+"Args:\n"
|
|
+" mode (int, optional): The compression mode can be MODE_GENERIC (default),\n"
|
|
+" MODE_TEXT (for UTF-8 format text input) or MODE_FONT (for WOFF 2.0). \n"
|
|
+" quality (int, optional): Controls the compression-speed vs compression-\n"
|
|
+" density tradeoff. The higher the quality, the slower the compression.\n"
|
|
+" Range is 0 to 11. Defaults to 11.\n"
|
|
+" lgwin (int, optional): Base 2 logarithm of the sliding window size. Range\n"
|
|
+" is 10 to 24. Defaults to 22.\n"
|
|
+" lgblock (int, optional): Base 2 logarithm of the maximum input block size.\n"
|
|
+" Range is 16 to 24. If set to 0, the value will be set based on the\n"
|
|
+" quality. Defaults to 0.\n"
|
|
+"\n"
|
|
+"Raises:\n"
|
|
+" brotli.error: If arguments are invalid.\n");
|
|
+
|
|
+typedef struct {
|
|
+ PyObject_HEAD
|
|
+ BrotliEncoderState* enc;
|
|
+} brotli_Compressor;
|
|
+
|
|
+static void brotli_Compressor_dealloc(brotli_Compressor* self) {
|
|
+ BrotliEncoderDestroyInstance(self->enc);
|
|
+ #if PY_MAJOR_VERSION >= 3
|
|
+ Py_TYPE(self)->tp_free((PyObject*)self);
|
|
+ #else
|
|
+ self->ob_type->tp_free((PyObject*)self);
|
|
+ #endif
|
|
+}
|
|
+
|
|
+static PyObject* brotli_Compressor_new(PyTypeObject *type, PyObject *args, PyObject *keywds) {
|
|
+ brotli_Compressor *self;
|
|
+ self = (brotli_Compressor *)type->tp_alloc(type, 0);
|
|
+
|
|
+ if (self != NULL) {
|
|
+ self->enc = BrotliEncoderCreateInstance(0, 0, 0);
|
|
+ }
|
|
+
|
|
+ return (PyObject *)self;
|
|
+}
|
|
+
|
|
+static int brotli_Compressor_init(brotli_Compressor *self, PyObject *args, PyObject *keywds) {
|
|
+ BrotliEncoderMode mode = (BrotliEncoderMode) -1;
|
|
+ int quality = -1;
|
|
+ int lgwin = -1;
|
|
+ int lgblock = -1;
|
|
+ int ok;
|
|
+
|
|
+ static const char *kwlist[] = {"mode", "quality", "lgwin", "lgblock", NULL};
|
|
+
|
|
+ ok = PyArg_ParseTupleAndKeywords(args, keywds, "|O&O&O&O&:Compressor",
|
|
+ (char **) kwlist,
|
|
+ &mode_convertor, &mode,
|
|
+ &quality_convertor, &quality,
|
|
+ &lgwin_convertor, &lgwin,
|
|
+ &lgblock_convertor, &lgblock);
|
|
+ if (!ok)
|
|
+ return -1;
|
|
+ if (!self->enc)
|
|
+ return -1;
|
|
+
|
|
+ if ((int) mode != -1)
|
|
+ BrotliEncoderSetParameter(self->enc, BROTLI_PARAM_MODE, (uint32_t)mode);
|
|
+ if (quality != -1)
|
|
+ BrotliEncoderSetParameter(self->enc, BROTLI_PARAM_QUALITY, (uint32_t)quality);
|
|
+ if (lgwin != -1)
|
|
+ BrotliEncoderSetParameter(self->enc, BROTLI_PARAM_LGWIN, (uint32_t)lgwin);
|
|
+ if (lgblock != -1)
|
|
+ BrotliEncoderSetParameter(self->enc, BROTLI_PARAM_LGBLOCK, (uint32_t)lgblock);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+PyDoc_STRVAR(brotli_Compressor_process_doc,
|
|
+"Process \"string\" for compression, returning a string that contains \n"
|
|
+"compressed output data. This data should be concatenated to the output \n"
|
|
+"produced by any preceding calls to the \"process()\" or flush()\" methods. \n"
|
|
+"Some or all of the input may be kept in internal buffers for later \n"
|
|
+"processing, and the compressed output data may be empty until enough input \n"
|
|
+"has been accumulated.\n"
|
|
+"\n"
|
|
+"Signature:\n"
|
|
+" compress(string)\n"
|
|
+"\n"
|
|
+"Args:\n"
|
|
+" string (bytes): The input data\n"
|
|
+"\n"
|
|
+"Returns:\n"
|
|
+" The compressed output data (bytes)\n"
|
|
+"\n"
|
|
+"Raises:\n"
|
|
+" brotli.error: If compression fails\n");
|
|
+
|
|
+static PyObject* brotli_Compressor_process(brotli_Compressor *self, PyObject *args) {
|
|
+ PyObject* ret;
|
|
+ Py_buffer input;
|
|
+ int ok;
|
|
+
|
|
+#if PY_MAJOR_VERSION >= 3
|
|
+ ok = PyArg_ParseTuple(args, "y*:process", &input);
|
|
+#else
|
|
+ ok = PyArg_ParseTuple(args, "s*:process", &input);
|
|
+#endif
|
|
+
|
|
+ if (!ok) {
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ if (!self->enc) {
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ ret = compress_stream(self->enc, BROTLI_OPERATION_PROCESS,
|
|
+ (uint8_t*) input.buf, input.len);
|
|
+ if (ret != NULL) {
|
|
+ goto finally;
|
|
+ }
|
|
+
|
|
+error:
|
|
+ PyErr_SetString(BrotliError,
|
|
+ "BrotliEncoderCompressStream failed while processing the stream");
|
|
+ ret = NULL;
|
|
+
|
|
+finally:
|
|
+ PyBuffer_Release(&input);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+PyDoc_STRVAR(brotli_Compressor_flush_doc,
|
|
+"Process all pending input, returning a string containing the remaining\n"
|
|
+"compressed data. This data should be concatenated to the output produced by\n"
|
|
+"any preceding calls to the \"process()\" or \"flush()\" methods.\n"
|
|
+"\n"
|
|
+"Signature:\n"
|
|
+" flush()\n"
|
|
+"\n"
|
|
+"Returns:\n"
|
|
+" The compressed output data (bytes)\n"
|
|
+"\n"
|
|
+"Raises:\n"
|
|
+" brotli.error: If compression fails\n");
|
|
+
|
|
+static PyObject* brotli_Compressor_flush(brotli_Compressor *self) {
|
|
+ PyObject *ret;
|
|
+
|
|
+ if (!self->enc) {
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ ret = compress_stream(self->enc, BROTLI_OPERATION_FLUSH,
|
|
+ NULL, 0);
|
|
+ if (ret != NULL) {
|
|
+ goto finally;
|
|
+ }
|
|
+
|
|
+error:
|
|
+ PyErr_SetString(BrotliError,
|
|
+ "BrotliEncoderCompressStream failed while flushing the stream");
|
|
+ ret = NULL;
|
|
+finally:
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+PyDoc_STRVAR(brotli_Compressor_finish_doc,
|
|
+"Process all pending input and complete all compression, returning a string\n"
|
|
+"containing the remaining compressed data. This data should be concatenated\n"
|
|
+"to the output produced by any preceding calls to the \"process()\" or\n"
|
|
+"\"flush()\" methods.\n"
|
|
+"After calling \"finish()\", the \"process()\" and \"flush()\" methods\n"
|
|
+"cannot be called again, and a new \"Compressor\" object should be created.\n"
|
|
+"\n"
|
|
+"Signature:\n"
|
|
+" finish(string)\n"
|
|
+"\n"
|
|
+"Returns:\n"
|
|
+" The compressed output data (bytes)\n"
|
|
+"\n"
|
|
+"Raises:\n"
|
|
+" brotli.error: If compression fails\n");
|
|
+
|
|
+static PyObject* brotli_Compressor_finish(brotli_Compressor *self) {
|
|
+ PyObject *ret;
|
|
+
|
|
+ if (!self->enc) {
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ ret = compress_stream(self->enc, BROTLI_OPERATION_FINISH,
|
|
+ NULL, 0);
|
|
+
|
|
+ if (ret == NULL || !BrotliEncoderIsFinished(self->enc)) {
|
|
+ goto error;
|
|
+ }
|
|
+ goto finally;
|
|
+
|
|
+error:
|
|
+ PyErr_SetString(BrotliError,
|
|
+ "BrotliEncoderCompressStream failed while finishing the stream");
|
|
+ ret = NULL;
|
|
+finally:
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static PyMemberDef brotli_Compressor_members[] = {
|
|
+ {NULL} /* Sentinel */
|
|
+};
|
|
+
|
|
+static PyMethodDef brotli_Compressor_methods[] = {
|
|
+ {"process", (PyCFunction)brotli_Compressor_process, METH_VARARGS, brotli_Compressor_process_doc},
|
|
+ {"flush", (PyCFunction)brotli_Compressor_flush, METH_NOARGS, brotli_Compressor_flush_doc},
|
|
+ {"finish", (PyCFunction)brotli_Compressor_finish, METH_NOARGS, brotli_Compressor_finish_doc},
|
|
+ {NULL} /* Sentinel */
|
|
+};
|
|
+
|
|
+static PyTypeObject brotli_CompressorType = {
|
|
+ #if PY_MAJOR_VERSION >= 3
|
|
+ PyVarObject_HEAD_INIT(NULL, 0)
|
|
+ #else
|
|
+ PyObject_HEAD_INIT(NULL)
|
|
+ 0, /* ob_size*/
|
|
+ #endif
|
|
+ "brotli.Compressor", /* tp_name */
|
|
+ sizeof(brotli_Compressor), /* tp_basicsize */
|
|
+ 0, /* tp_itemsize */
|
|
+ (destructor)brotli_Compressor_dealloc, /* tp_dealloc */
|
|
+ 0, /* tp_print */
|
|
+ 0, /* tp_getattr */
|
|
+ 0, /* tp_setattr */
|
|
+ 0, /* tp_compare */
|
|
+ 0, /* tp_repr */
|
|
+ 0, /* tp_as_number */
|
|
+ 0, /* tp_as_sequence */
|
|
+ 0, /* tp_as_mapping */
|
|
+ 0, /* tp_hash */
|
|
+ 0, /* tp_call */
|
|
+ 0, /* tp_str */
|
|
+ 0, /* tp_getattro */
|
|
+ 0, /* tp_setattro */
|
|
+ 0, /* tp_as_buffer */
|
|
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
|
|
+ brotli_Compressor_doc, /* tp_doc */
|
|
+ 0, /* tp_traverse */
|
|
+ 0, /* tp_clear */
|
|
+ 0, /* tp_richcompare */
|
|
+ 0, /* tp_weaklistoffset */
|
|
+ 0, /* tp_iter */
|
|
+ 0, /* tp_iternext */
|
|
+ brotli_Compressor_methods, /* tp_methods */
|
|
+ brotli_Compressor_members, /* tp_members */
|
|
+ 0, /* tp_getset */
|
|
+ 0, /* tp_base */
|
|
+ 0, /* tp_dict */
|
|
+ 0, /* tp_descr_get */
|
|
+ 0, /* tp_descr_set */
|
|
+ 0, /* tp_dictoffset */
|
|
+ (initproc)brotli_Compressor_init, /* tp_init */
|
|
+ 0, /* tp_alloc */
|
|
+ brotli_Compressor_new, /* tp_new */
|
|
+};
|
|
+
|
|
+PyDoc_STRVAR(brotli_Decompressor_doc,
|
|
+"An object to decompress a byte string.\n"
|
|
+"\n"
|
|
+"Signature:\n"
|
|
+" Decompressor()\n"
|
|
+"\n"
|
|
+"Raises:\n"
|
|
+" brotli.error: If arguments are invalid.\n");
|
|
+
|
|
+typedef struct {
|
|
+ PyObject_HEAD
|
|
+ BrotliDecoderState* dec;
|
|
+ uint8_t* unconsumed_data;
|
|
+ size_t unconsumed_data_length;
|
|
+} brotli_Decompressor;
|
|
+
|
|
+static void brotli_Decompressor_dealloc(brotli_Decompressor* self) {
|
|
+ BrotliDecoderDestroyInstance(self->dec);
|
|
+ if (self->unconsumed_data)
|
|
+ free(self->unconsumed_data);
|
|
+ #if PY_MAJOR_VERSION >= 3
|
|
+ Py_TYPE(self)->tp_free((PyObject*)self);
|
|
+ #else
|
|
+ self->ob_type->tp_free((PyObject*)self);
|
|
+ #endif
|
|
+}
|
|
+
|
|
+static PyObject* brotli_Decompressor_new(PyTypeObject *type, PyObject *args, PyObject *keywds) {
|
|
+ brotli_Decompressor *self;
|
|
+ self = (brotli_Decompressor *)type->tp_alloc(type, 0);
|
|
+
|
|
+ if (self != NULL) {
|
|
+ self->dec = BrotliDecoderCreateInstance(0, 0, 0);
|
|
+ }
|
|
+
|
|
+ self->unconsumed_data = NULL;
|
|
+ self->unconsumed_data_length = 0;
|
|
+
|
|
+ return (PyObject *)self;
|
|
+}
|
|
+
|
|
+static int brotli_Decompressor_init(brotli_Decompressor *self, PyObject *args, PyObject *keywds) {
|
|
+ int ok;
|
|
+
|
|
+ static const char *kwlist[] = {NULL};
|
|
+
|
|
+ ok = PyArg_ParseTupleAndKeywords(args, keywds, "|:Decompressor",
|
|
+ (char **) kwlist);
|
|
+ if (!ok)
|
|
+ return -1;
|
|
+ if (!self->dec)
|
|
+ return -1;
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static PyObject* decompress_stream(brotli_Decompressor* self,
|
|
+ uint8_t* input, size_t input_length, Py_ssize_t max_output_length) {
|
|
+ BrotliDecoderResult result;
|
|
+
|
|
+ size_t available_in = input_length;
|
|
+ const uint8_t* next_in = input;
|
|
+
|
|
+ size_t available_out;
|
|
+ uint8_t* next_out;
|
|
+ uint8_t* new_tail;
|
|
+ BlocksOutputBuffer buffer = {.list=NULL};
|
|
+ PyObject *ret;
|
|
+
|
|
+ if (BlocksOutputBuffer_InitAndGrow(&buffer, max_output_length, &available_out, &next_out) < 0) {
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ while (1) {
|
|
+ Py_BEGIN_ALLOW_THREADS
|
|
+ result = BrotliDecoderDecompressStream(self->dec,
|
|
+ &available_in, &next_in,
|
|
+ &available_out, &next_out, NULL);
|
|
+ Py_END_ALLOW_THREADS
|
|
+
|
|
+ if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
|
|
+ if (available_out == 0) {
|
|
+ if (buffer.allocated == PY_SSIZE_T_MAX) {
|
|
+ PyErr_SetString(PyExc_MemoryError, unable_allocate_msg);
|
|
+ goto error;
|
|
+ }
|
|
+ if (buffer.allocated == max_output_length) {
|
|
+ // We've reached the output length limit.
|
|
+ break;
|
|
+ }
|
|
+ if (BlocksOutputBuffer_Grow(&buffer, &available_out, &next_out) < 0) {
|
|
+ goto error;
|
|
+ }
|
|
+ }
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ if (result == BROTLI_DECODER_RESULT_ERROR || available_in != 0) {
|
|
+ available_in = 0;
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ ret = BlocksOutputBuffer_Finish(&buffer, available_out);
|
|
+ if (ret != NULL) {
|
|
+ goto finally;
|
|
+ }
|
|
+
|
|
+error:
|
|
+ BlocksOutputBuffer_OnError(&buffer);
|
|
+ ret = NULL;
|
|
+
|
|
+finally:
|
|
+ new_tail = available_in > 0 ? malloc(available_in) : NULL;
|
|
+ if (available_in > 0) {
|
|
+ memcpy(new_tail, next_in, available_in);
|
|
+ }
|
|
+ if (self->unconsumed_data) {
|
|
+ free(self->unconsumed_data);
|
|
+ }
|
|
+ self->unconsumed_data = new_tail;
|
|
+ self->unconsumed_data_length = available_in;
|
|
+
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+
|
|
+PyDoc_STRVAR(brotli_Decompressor_process_doc,
|
|
+"Process \"string\" for decompression, returning a string that contains \n"
|
|
+"decompressed output data. This data should be concatenated to the output \n"
|
|
+"produced by any preceding calls to the \"process()\" method. \n"
|
|
+"Some or all of the input may be kept in internal buffers for later \n"
|
|
+"processing, and the decompressed output data may be empty until enough input \n"
|
|
+"has been accumulated.\n"
|
|
+"If max_output_length is set, no more than max_output_length bytes will be\n"
|
|
+"returned. If the limit is reached, further calls to process (potentially with\n"
|
|
+"empty input) will continue to yield more data. If, after returning a string of\n"
|
|
+"the length equal to limit, can_accept_more_data() returns False, process()\n"
|
|
+"must only be called with empty input until can_accept_more_data() once again\n"
|
|
+"returns True.\n"
|
|
+"\n"
|
|
+"Signature:\n"
|
|
+" decompress(string, max_output_length=int)\n"
|
|
+"\n"
|
|
+"Args:\n"
|
|
+" string (bytes): The input data\n"
|
|
+"\n""Returns:\n"
|
|
+" The decompressed output data (bytes)\n"
|
|
+"\n"
|
|
+"Raises:\n"
|
|
+" brotli.error: If decompression fails\n");
|
|
+
|
|
+static PyObject* brotli_Decompressor_process(brotli_Decompressor *self, PyObject *args, PyObject* keywds) {
|
|
+ PyObject* ret;
|
|
+ Py_buffer input;
|
|
+ int ok;
|
|
+ Py_ssize_t max_output_length = PY_SSIZE_T_MAX;
|
|
+ uint8_t* data;
|
|
+ size_t data_length;
|
|
+
|
|
+ static char* kwlist[] = { "", "max_output_length", NULL };
|
|
+
|
|
+#if PY_MAJOR_VERSION >= 3
|
|
+ ok = PyArg_ParseTupleAndKeywords(args, keywds, "y*|n:process", kwlist, &input, &max_output_length);
|
|
+#else
|
|
+ ok = PyArg_ParseTupleAndKeywords(args, keywds, "s*|n:process", kwlist, &input, &max_output_length);
|
|
+#endif
|
|
+
|
|
+ if (!ok) {
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ if (!self->dec) {
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ if (self->unconsumed_data_length > 0) {
|
|
+ if (input.len > 0) {
|
|
+ PyErr_SetString(BrotliError, "process called with data when accept_more_data is False");
|
|
+ ret = NULL;
|
|
+ goto finally;
|
|
+ }
|
|
+ data = self->unconsumed_data;
|
|
+ data_length = self->unconsumed_data_length;
|
|
+ } else {
|
|
+ data = (uint8_t*)input.buf;
|
|
+ data_length = input.len;
|
|
+ }
|
|
+
|
|
+ ret = decompress_stream(self, data, data_length, max_output_length);
|
|
+ if (ret != NULL) {
|
|
+ goto finally;
|
|
+ }
|
|
+
|
|
+error:
|
|
+ PyErr_SetString(BrotliError,
|
|
+ "BrotliDecoderDecompressStream failed while processing the stream");
|
|
+ ret = NULL;
|
|
+
|
|
+finally:
|
|
+ PyBuffer_Release(&input);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+PyDoc_STRVAR(brotli_Decompressor_is_finished_doc,
|
|
+"Checks if decoder instance reached the final state.\n"
|
|
+"\n"
|
|
+"Signature:\n"
|
|
+" is_finished()\n"
|
|
+"\n"
|
|
+"Returns:\n"
|
|
+" True if the decoder is in a state where it reached the end of the input\n"
|
|
+" and produced all of the output\n"
|
|
+" False otherwise\n"
|
|
+"\n"
|
|
+"Raises:\n"
|
|
+" brotli.error: If decompression fails\n");
|
|
+
|
|
+static PyObject* brotli_Decompressor_is_finished(brotli_Decompressor *self) {
|
|
+ if (!self->dec) {
|
|
+ PyErr_SetString(BrotliError, "BrotliDecoderState is NULL while checking is_finished");
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ if (BrotliDecoderIsFinished(self->dec)) {
|
|
+ Py_RETURN_TRUE;
|
|
+ } else {
|
|
+ Py_RETURN_FALSE;
|
|
+ }
|
|
+}
|
|
+
|
|
+PyDoc_STRVAR(brotli_Decompressor_can_accept_more_data_doc,
|
|
+"Checks if the decoder instance can accept more compressed data. If the decompress()\n"
|
|
+"method on this instance of decompressor was never called with max_length,\n"
|
|
+"this method will always return True.\n"
|
|
+"\n"
|
|
+"Signature:"
|
|
+" can_accept_more_data()\n"
|
|
+"\n"
|
|
+"Returns:\n"
|
|
+" True if the decoder is ready to accept more compressed data via decompress()\n"
|
|
+" False if the decoder needs to output some data via decompress(b'') before\n"
|
|
+" being provided any more compressed data\n");
|
|
+
|
|
+static PyObject* brotli_Decompressor_can_accept_more_data(brotli_Decompressor* self) {
|
|
+ if (self->unconsumed_data_length > 0) {
|
|
+ Py_RETURN_FALSE;
|
|
+ } else {
|
|
+ Py_RETURN_TRUE;
|
|
+ }
|
|
+}
|
|
+
|
|
+static PyMemberDef brotli_Decompressor_members[] = {
|
|
+ {NULL} /* Sentinel */
|
|
+};
|
|
+
|
|
+static PyMethodDef brotli_Decompressor_methods[] = {
|
|
+ {"process", (PyCFunction)brotli_Decompressor_process, METH_VARARGS | METH_KEYWORDS, brotli_Decompressor_process_doc},
|
|
+ {"is_finished", (PyCFunction)brotli_Decompressor_is_finished, METH_NOARGS, brotli_Decompressor_is_finished_doc},
|
|
+ {"can_accept_more_data", (PyCFunction)brotli_Decompressor_can_accept_more_data, METH_NOARGS, brotli_Decompressor_can_accept_more_data_doc},
|
|
+ {NULL} /* Sentinel */
|
|
+};
|
|
+
|
|
+static PyTypeObject brotli_DecompressorType = {
|
|
+ #if PY_MAJOR_VERSION >= 3
|
|
+ PyVarObject_HEAD_INIT(NULL, 0)
|
|
+ #else
|
|
+ PyObject_HEAD_INIT(NULL)
|
|
+ 0, /* ob_size*/
|
|
+ #endif
|
|
+ "brotli.Decompressor", /* tp_name */
|
|
+ sizeof(brotli_Decompressor), /* tp_basicsize */
|
|
+ 0, /* tp_itemsize */
|
|
+ (destructor)brotli_Decompressor_dealloc, /* tp_dealloc */
|
|
+ 0, /* tp_print */
|
|
+ 0, /* tp_getattr */
|
|
+ 0, /* tp_setattr */
|
|
+ 0, /* tp_compare */
|
|
+ 0, /* tp_repr */
|
|
+ 0, /* tp_as_number */
|
|
+ 0, /* tp_as_sequence */
|
|
+ 0, /* tp_as_mapping */
|
|
+ 0, /* tp_hash */
|
|
+ 0, /* tp_call */
|
|
+ 0, /* tp_str */
|
|
+ 0, /* tp_getattro */
|
|
+ 0, /* tp_setattro */
|
|
+ 0, /* tp_as_buffer */
|
|
+ Py_TPFLAGS_DEFAULT, /* tp_flags */
|
|
+ brotli_Decompressor_doc, /* tp_doc */
|
|
+ 0, /* tp_traverse */
|
|
+ 0, /* tp_clear */
|
|
+ 0, /* tp_richcompare */
|
|
+ 0, /* tp_weaklistoffset */
|
|
+ 0, /* tp_iter */
|
|
+ 0, /* tp_iternext */
|
|
+ brotli_Decompressor_methods, /* tp_methods */
|
|
+ brotli_Decompressor_members, /* tp_members */
|
|
+ 0, /* tp_getset */
|
|
+ 0, /* tp_base */
|
|
+ 0, /* tp_dict */
|
|
+ 0, /* tp_descr_get */
|
|
+ 0, /* tp_descr_set */
|
|
+ 0, /* tp_dictoffset */
|
|
+ (initproc)brotli_Decompressor_init, /* tp_init */
|
|
+ 0, /* tp_alloc */
|
|
+ brotli_Decompressor_new, /* tp_new */
|
|
+};
|
|
+
|
|
+PyDoc_STRVAR(brotli_decompress__doc__,
|
|
+"Decompress a compressed byte string.\n"
|
|
+"\n"
|
|
+"Signature:\n"
|
|
+" decompress(string)\n"
|
|
+"\n"
|
|
+"Args:\n"
|
|
+" string (bytes): The compressed input data.\n"
|
|
+"\n"
|
|
+"Returns:\n"
|
|
+" The decompressed byte string.\n"
|
|
+"\n"
|
|
+"Raises:\n"
|
|
+" brotli.error: If decompressor fails.\n");
|
|
+
|
|
+static PyObject* brotli_decompress(PyObject *self, PyObject *args, PyObject *keywds) {
|
|
+ BrotliDecoderState* state;
|
|
+ BrotliDecoderResult result;
|
|
+
|
|
+ const uint8_t* next_in;
|
|
+ size_t available_in;
|
|
+
|
|
+ uint8_t* next_out;
|
|
+ size_t available_out;
|
|
+ BlocksOutputBuffer buffer = {.list=NULL};
|
|
+ PyObject *ret;
|
|
+
|
|
+ static const char *kwlist[] = {"string", NULL};
|
|
+ Py_buffer input;
|
|
+ int ok;
|
|
+
|
|
+#if PY_MAJOR_VERSION >= 3
|
|
+ ok = PyArg_ParseTupleAndKeywords(args, keywds, "y*|:decompress",
|
|
+ (char**) kwlist, &input);
|
|
+#else
|
|
+ ok = PyArg_ParseTupleAndKeywords(args, keywds, "s*|:decompress",
|
|
+ (char**) kwlist, &input);
|
|
+#endif
|
|
+
|
|
+ if (!ok) {
|
|
+ return NULL;
|
|
+ }
|
|
+
|
|
+ state = BrotliDecoderCreateInstance(0, 0, 0);
|
|
+
|
|
+ next_in = (uint8_t*) input.buf;
|
|
+ available_in = input.len;
|
|
+
|
|
+ if (BlocksOutputBuffer_InitAndGrow(&buffer, PY_SSIZE_T_MAX, &available_out, &next_out) < 0) {
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ while (1) {
|
|
+ Py_BEGIN_ALLOW_THREADS
|
|
+ result = BrotliDecoderDecompressStream(state, &available_in, &next_in,
|
|
+ &available_out, &next_out, 0);
|
|
+ Py_END_ALLOW_THREADS
|
|
+
|
|
+ if (result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
|
|
+ if (available_out == 0) {
|
|
+ if (BlocksOutputBuffer_Grow(&buffer, &available_out, &next_out) < 0) {
|
|
+ goto error;
|
|
+ }
|
|
+ }
|
|
+ continue;
|
|
+ }
|
|
+
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ if (result != BROTLI_DECODER_RESULT_SUCCESS || available_in != 0) {
|
|
+ goto error;
|
|
+ }
|
|
+
|
|
+ ret = BlocksOutputBuffer_Finish(&buffer, available_out);
|
|
+ if (ret != NULL) {
|
|
+ goto finally;
|
|
+ }
|
|
+
|
|
+error:
|
|
+ BlocksOutputBuffer_OnError(&buffer);
|
|
+ PyErr_SetString(BrotliError, "BrotliDecompress failed");
|
|
+ ret = NULL;
|
|
+
|
|
+finally:
|
|
+ BrotliDecoderDestroyInstance(state);
|
|
+ PyBuffer_Release(&input);
|
|
+ return ret;
|
|
+}
|
|
+
|
|
+static PyMethodDef brotli_methods[] = {
|
|
+ {"decompress", (PyCFunction)brotli_decompress, METH_VARARGS | METH_KEYWORDS, brotli_decompress__doc__},
|
|
+ {NULL, NULL, 0, NULL}
|
|
+};
|
|
+
|
|
+PyDoc_STRVAR(brotli_doc, "Implementation module for the Brotli library.");
|
|
+
|
|
+#if PY_MAJOR_VERSION >= 3
|
|
+#define INIT_BROTLI PyInit__brotli
|
|
+#define CREATE_BROTLI PyModule_Create(&brotli_module)
|
|
+#define RETURN_BROTLI return m
|
|
+#define RETURN_NULL return NULL
|
|
+
|
|
+static struct PyModuleDef brotli_module = {
|
|
+ PyModuleDef_HEAD_INIT,
|
|
+ "_brotli", /* m_name */
|
|
+ brotli_doc, /* m_doc */
|
|
+ 0, /* m_size */
|
|
+ brotli_methods, /* m_methods */
|
|
+ NULL, /* m_reload */
|
|
+ NULL, /* m_traverse */
|
|
+ NULL, /* m_clear */
|
|
+ NULL /* m_free */
|
|
+};
|
|
+#else
|
|
+#define INIT_BROTLI init_brotli
|
|
+#define CREATE_BROTLI Py_InitModule3("_brotli", brotli_methods, brotli_doc)
|
|
+#define RETURN_BROTLI return
|
|
+#define RETURN_NULL return
|
|
+#endif
|
|
+
|
|
+PyMODINIT_FUNC INIT_BROTLI(void) {
|
|
+ PyObject *m = CREATE_BROTLI;
|
|
+
|
|
+ BrotliError = PyErr_NewException((char*) "brotli.error", NULL, NULL);
|
|
+ if (BrotliError != NULL) {
|
|
+ Py_INCREF(BrotliError);
|
|
+ PyModule_AddObject(m, "error", BrotliError);
|
|
+ }
|
|
+
|
|
+ if (PyType_Ready(&brotli_CompressorType) < 0) {
|
|
+ RETURN_NULL;
|
|
+ }
|
|
+ Py_INCREF(&brotli_CompressorType);
|
|
+ PyModule_AddObject(m, "Compressor", (PyObject *)&brotli_CompressorType);
|
|
+
|
|
+ if (PyType_Ready(&brotli_DecompressorType) < 0) {
|
|
+ RETURN_NULL;
|
|
+ }
|
|
+ Py_INCREF(&brotli_DecompressorType);
|
|
+ PyModule_AddObject(m, "Decompressor", (PyObject *)&brotli_DecompressorType);
|
|
+
|
|
+ PyModule_AddIntConstant(m, "MODE_GENERIC", (int) BROTLI_MODE_GENERIC);
|
|
+ PyModule_AddIntConstant(m, "MODE_TEXT", (int) BROTLI_MODE_TEXT);
|
|
+ PyModule_AddIntConstant(m, "MODE_FONT", (int) BROTLI_MODE_FONT);
|
|
+
|
|
+ char version[16];
|
|
+ uint32_t decoderVersion = BrotliDecoderVersion();
|
|
+ snprintf(version, sizeof(version), "%d.%d.%d",
|
|
+ decoderVersion >> 24, (decoderVersion >> 12) & 0xFFF, decoderVersion & 0xFFF);
|
|
+ PyModule_AddStringConstant(m, "__version__", version);
|
|
+
|
|
+ RETURN_BROTLI;
|
|
+}
|
|
diff -urN brotli-1.0.6/python/_brotli.cc brotli-1.0.6-modified/python/_brotli.cc
|
|
--- brotli-1.0.6/python/_brotli.cc 2018-09-13 23:01:23.000000000 +0530
|
|
+++ brotli-1.0.6-modified/python/_brotli.cc 1970-01-01 05:30:00.000000000 +0530
|
|
@@ -1,752 +0,0 @@
|
|
-#define PY_SSIZE_T_CLEAN 1
|
|
-#include <Python.h>
|
|
-#include <bytesobject.h>
|
|
-#include <structmember.h>
|
|
-#include <vector>
|
|
-#include "../common/version.h"
|
|
-#include <brotli/decode.h>
|
|
-#include <brotli/encode.h>
|
|
-
|
|
-#if PY_MAJOR_VERSION >= 3
|
|
-#define PyInt_Check PyLong_Check
|
|
-#define PyInt_AsLong PyLong_AsLong
|
|
-#endif
|
|
-
|
|
-static PyObject *BrotliError;
|
|
-
|
|
-static int as_bounded_int(PyObject *o, int* result, int lower_bound, int upper_bound) {
|
|
- long value = PyInt_AsLong(o);
|
|
- if ((value < (long) lower_bound) || (value > (long) upper_bound)) {
|
|
- return 0;
|
|
- }
|
|
- *result = (int) value;
|
|
- return 1;
|
|
-}
|
|
-
|
|
-static int mode_convertor(PyObject *o, BrotliEncoderMode *mode) {
|
|
- if (!PyInt_Check(o)) {
|
|
- PyErr_SetString(BrotliError, "Invalid mode");
|
|
- return 0;
|
|
- }
|
|
-
|
|
- int mode_value = -1;
|
|
- if (!as_bounded_int(o, &mode_value, 0, 255)) {
|
|
- PyErr_SetString(BrotliError, "Invalid mode");
|
|
- return 0;
|
|
- }
|
|
- *mode = (BrotliEncoderMode) mode_value;
|
|
- if (*mode != BROTLI_MODE_GENERIC &&
|
|
- *mode != BROTLI_MODE_TEXT &&
|
|
- *mode != BROTLI_MODE_FONT) {
|
|
- PyErr_SetString(BrotliError, "Invalid mode");
|
|
- return 0;
|
|
- }
|
|
-
|
|
- return 1;
|
|
-}
|
|
-
|
|
-static int quality_convertor(PyObject *o, int *quality) {
|
|
- if (!PyInt_Check(o)) {
|
|
- PyErr_SetString(BrotliError, "Invalid quality");
|
|
- return 0;
|
|
- }
|
|
-
|
|
- if (!as_bounded_int(o, quality, 0, 11)) {
|
|
- PyErr_SetString(BrotliError, "Invalid quality. Range is 0 to 11.");
|
|
- return 0;
|
|
- }
|
|
-
|
|
- return 1;
|
|
-}
|
|
-
|
|
-static int lgwin_convertor(PyObject *o, int *lgwin) {
|
|
- if (!PyInt_Check(o)) {
|
|
- PyErr_SetString(BrotliError, "Invalid lgwin");
|
|
- return 0;
|
|
- }
|
|
-
|
|
- if (!as_bounded_int(o, lgwin, 10, 24)) {
|
|
- PyErr_SetString(BrotliError, "Invalid lgwin. Range is 10 to 24.");
|
|
- return 0;
|
|
- }
|
|
-
|
|
- return 1;
|
|
-}
|
|
-
|
|
-static int lgblock_convertor(PyObject *o, int *lgblock) {
|
|
- if (!PyInt_Check(o)) {
|
|
- PyErr_SetString(BrotliError, "Invalid lgblock");
|
|
- return 0;
|
|
- }
|
|
-
|
|
- if (!as_bounded_int(o, lgblock, 0, 24) || (*lgblock != 0 && *lgblock < 16)) {
|
|
- PyErr_SetString(BrotliError, "Invalid lgblock. Can be 0 or in range 16 to 24.");
|
|
- return 0;
|
|
- }
|
|
-
|
|
- return 1;
|
|
-}
|
|
-
|
|
-static BROTLI_BOOL compress_stream(BrotliEncoderState* enc, BrotliEncoderOperation op,
|
|
- std::vector<uint8_t>* output,
|
|
- uint8_t* input, size_t input_length) {
|
|
- BROTLI_BOOL ok = BROTLI_TRUE;
|
|
- Py_BEGIN_ALLOW_THREADS
|
|
-
|
|
- size_t available_in = input_length;
|
|
- const uint8_t* next_in = input;
|
|
- size_t available_out = 0;
|
|
- uint8_t* next_out = NULL;
|
|
-
|
|
- while (ok) {
|
|
- ok = BrotliEncoderCompressStream(enc, op,
|
|
- &available_in, &next_in,
|
|
- &available_out, &next_out, NULL);
|
|
- if (!ok)
|
|
- break;
|
|
-
|
|
- size_t buffer_length = 0; // Request all available output.
|
|
- const uint8_t* buffer = BrotliEncoderTakeOutput(enc, &buffer_length);
|
|
- if (buffer_length) {
|
|
- (*output).insert((*output).end(), buffer, buffer + buffer_length);
|
|
- }
|
|
-
|
|
- if (available_in || BrotliEncoderHasMoreOutput(enc)) {
|
|
- continue;
|
|
- }
|
|
-
|
|
- break;
|
|
- }
|
|
-
|
|
- Py_END_ALLOW_THREADS
|
|
- return ok;
|
|
-}
|
|
-
|
|
-PyDoc_STRVAR(brotli_Compressor_doc,
|
|
-"An object to compress a byte string.\n"
|
|
-"\n"
|
|
-"Signature:\n"
|
|
-" Compressor(mode=MODE_GENERIC, quality=11, lgwin=22, lgblock=0)\n"
|
|
-"\n"
|
|
-"Args:\n"
|
|
-" mode (int, optional): The compression mode can be MODE_GENERIC (default),\n"
|
|
-" MODE_TEXT (for UTF-8 format text input) or MODE_FONT (for WOFF 2.0). \n"
|
|
-" quality (int, optional): Controls the compression-speed vs compression-\n"
|
|
-" density tradeoff. The higher the quality, the slower the compression.\n"
|
|
-" Range is 0 to 11. Defaults to 11.\n"
|
|
-" lgwin (int, optional): Base 2 logarithm of the sliding window size. Range\n"
|
|
-" is 10 to 24. Defaults to 22.\n"
|
|
-" lgblock (int, optional): Base 2 logarithm of the maximum input block size.\n"
|
|
-" Range is 16 to 24. If set to 0, the value will be set based on the\n"
|
|
-" quality. Defaults to 0.\n"
|
|
-"\n"
|
|
-"Raises:\n"
|
|
-" brotli.error: If arguments are invalid.\n");
|
|
-
|
|
-typedef struct {
|
|
- PyObject_HEAD
|
|
- BrotliEncoderState* enc;
|
|
-} brotli_Compressor;
|
|
-
|
|
-static void brotli_Compressor_dealloc(brotli_Compressor* self) {
|
|
- BrotliEncoderDestroyInstance(self->enc);
|
|
- #if PY_MAJOR_VERSION >= 3
|
|
- Py_TYPE(self)->tp_free((PyObject*)self);
|
|
- #else
|
|
- self->ob_type->tp_free((PyObject*)self);
|
|
- #endif
|
|
-}
|
|
-
|
|
-static PyObject* brotli_Compressor_new(PyTypeObject *type, PyObject *args, PyObject *keywds) {
|
|
- brotli_Compressor *self;
|
|
- self = (brotli_Compressor *)type->tp_alloc(type, 0);
|
|
-
|
|
- if (self != NULL) {
|
|
- self->enc = BrotliEncoderCreateInstance(0, 0, 0);
|
|
- }
|
|
-
|
|
- return (PyObject *)self;
|
|
-}
|
|
-
|
|
-static int brotli_Compressor_init(brotli_Compressor *self, PyObject *args, PyObject *keywds) {
|
|
- BrotliEncoderMode mode = (BrotliEncoderMode) -1;
|
|
- int quality = -1;
|
|
- int lgwin = -1;
|
|
- int lgblock = -1;
|
|
- int ok;
|
|
-
|
|
- static const char *kwlist[] = {"mode", "quality", "lgwin", "lgblock", NULL};
|
|
-
|
|
- ok = PyArg_ParseTupleAndKeywords(args, keywds, "|O&O&O&O&:Compressor",
|
|
- const_cast<char **>(kwlist),
|
|
- &mode_convertor, &mode,
|
|
- &quality_convertor, &quality,
|
|
- &lgwin_convertor, &lgwin,
|
|
- &lgblock_convertor, &lgblock);
|
|
- if (!ok)
|
|
- return -1;
|
|
- if (!self->enc)
|
|
- return -1;
|
|
-
|
|
- if ((int) mode != -1)
|
|
- BrotliEncoderSetParameter(self->enc, BROTLI_PARAM_MODE, (uint32_t)mode);
|
|
- if (quality != -1)
|
|
- BrotliEncoderSetParameter(self->enc, BROTLI_PARAM_QUALITY, (uint32_t)quality);
|
|
- if (lgwin != -1)
|
|
- BrotliEncoderSetParameter(self->enc, BROTLI_PARAM_LGWIN, (uint32_t)lgwin);
|
|
- if (lgblock != -1)
|
|
- BrotliEncoderSetParameter(self->enc, BROTLI_PARAM_LGBLOCK, (uint32_t)lgblock);
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
-PyDoc_STRVAR(brotli_Compressor_process_doc,
|
|
-"Process \"string\" for compression, returning a string that contains \n"
|
|
-"compressed output data. This data should be concatenated to the output \n"
|
|
-"produced by any preceding calls to the \"process()\" or flush()\" methods. \n"
|
|
-"Some or all of the input may be kept in internal buffers for later \n"
|
|
-"processing, and the compressed output data may be empty until enough input \n"
|
|
-"has been accumulated.\n"
|
|
-"\n"
|
|
-"Signature:\n"
|
|
-" compress(string)\n"
|
|
-"\n"
|
|
-"Args:\n"
|
|
-" string (bytes): The input data\n"
|
|
-"\n"
|
|
-"Returns:\n"
|
|
-" The compressed output data (bytes)\n"
|
|
-"\n"
|
|
-"Raises:\n"
|
|
-" brotli.error: If compression fails\n");
|
|
-
|
|
-static PyObject* brotli_Compressor_process(brotli_Compressor *self, PyObject *args) {
|
|
- PyObject* ret = NULL;
|
|
- std::vector<uint8_t> output;
|
|
- Py_buffer input;
|
|
- BROTLI_BOOL ok = BROTLI_TRUE;
|
|
-
|
|
-#if PY_MAJOR_VERSION >= 3
|
|
- ok = (BROTLI_BOOL)PyArg_ParseTuple(args, "y*:process", &input);
|
|
-#else
|
|
- ok = (BROTLI_BOOL)PyArg_ParseTuple(args, "s*:process", &input);
|
|
-#endif
|
|
-
|
|
- if (!ok)
|
|
- return NULL;
|
|
-
|
|
- if (!self->enc) {
|
|
- ok = BROTLI_FALSE;
|
|
- goto end;
|
|
- }
|
|
-
|
|
- ok = compress_stream(self->enc, BROTLI_OPERATION_PROCESS,
|
|
- &output, static_cast<uint8_t*>(input.buf), input.len);
|
|
-
|
|
-end:
|
|
- PyBuffer_Release(&input);
|
|
- if (ok) {
|
|
- ret = PyBytes_FromStringAndSize((char*)(output.size() ? &output[0] : NULL), output.size());
|
|
- } else {
|
|
- PyErr_SetString(BrotliError, "BrotliEncoderCompressStream failed while processing the stream");
|
|
- }
|
|
-
|
|
- return ret;
|
|
-}
|
|
-
|
|
-PyDoc_STRVAR(brotli_Compressor_flush_doc,
|
|
-"Process all pending input, returning a string containing the remaining\n"
|
|
-"compressed data. This data should be concatenated to the output produced by\n"
|
|
-"any preceding calls to the \"process()\" or \"flush()\" methods.\n"
|
|
-"\n"
|
|
-"Signature:\n"
|
|
-" flush()\n"
|
|
-"\n"
|
|
-"Returns:\n"
|
|
-" The compressed output data (bytes)\n"
|
|
-"\n"
|
|
-"Raises:\n"
|
|
-" brotli.error: If compression fails\n");
|
|
-
|
|
-static PyObject* brotli_Compressor_flush(brotli_Compressor *self) {
|
|
- PyObject *ret = NULL;
|
|
- std::vector<uint8_t> output;
|
|
- BROTLI_BOOL ok = BROTLI_TRUE;
|
|
-
|
|
- if (!self->enc) {
|
|
- ok = BROTLI_FALSE;
|
|
- goto end;
|
|
- }
|
|
-
|
|
- ok = compress_stream(self->enc, BROTLI_OPERATION_FLUSH,
|
|
- &output, NULL, 0);
|
|
-
|
|
-end:
|
|
- if (ok) {
|
|
- ret = PyBytes_FromStringAndSize((char*)(output.size() ? &output[0] : NULL), output.size());
|
|
- } else {
|
|
- PyErr_SetString(BrotliError, "BrotliEncoderCompressStream failed while flushing the stream");
|
|
- }
|
|
-
|
|
- return ret;
|
|
-}
|
|
-
|
|
-PyDoc_STRVAR(brotli_Compressor_finish_doc,
|
|
-"Process all pending input and complete all compression, returning a string\n"
|
|
-"containing the remaining compressed data. This data should be concatenated\n"
|
|
-"to the output produced by any preceding calls to the \"process()\" or\n"
|
|
-"\"flush()\" methods.\n"
|
|
-"After calling \"finish()\", the \"process()\" and \"flush()\" methods\n"
|
|
-"cannot be called again, and a new \"Compressor\" object should be created.\n"
|
|
-"\n"
|
|
-"Signature:\n"
|
|
-" finish(string)\n"
|
|
-"\n"
|
|
-"Returns:\n"
|
|
-" The compressed output data (bytes)\n"
|
|
-"\n"
|
|
-"Raises:\n"
|
|
-" brotli.error: If compression fails\n");
|
|
-
|
|
-static PyObject* brotli_Compressor_finish(brotli_Compressor *self) {
|
|
- PyObject *ret = NULL;
|
|
- std::vector<uint8_t> output;
|
|
- BROTLI_BOOL ok = BROTLI_TRUE;
|
|
-
|
|
- if (!self->enc) {
|
|
- ok = BROTLI_FALSE;
|
|
- goto end;
|
|
- }
|
|
-
|
|
- ok = compress_stream(self->enc, BROTLI_OPERATION_FINISH,
|
|
- &output, NULL, 0);
|
|
-
|
|
- if (ok) {
|
|
- ok = BrotliEncoderIsFinished(self->enc);
|
|
- }
|
|
-
|
|
-end:
|
|
- if (ok) {
|
|
- ret = PyBytes_FromStringAndSize((char*)(output.empty() ? NULL : &output[0]), output.size());
|
|
- } else {
|
|
- PyErr_SetString(BrotliError, "BrotliEncoderCompressStream failed while finishing the stream");
|
|
- }
|
|
-
|
|
- return ret;
|
|
-}
|
|
-
|
|
-static PyMemberDef brotli_Compressor_members[] = {
|
|
- {NULL} /* Sentinel */
|
|
-};
|
|
-
|
|
-static PyMethodDef brotli_Compressor_methods[] = {
|
|
- {"process", (PyCFunction)brotli_Compressor_process, METH_VARARGS, brotli_Compressor_process_doc},
|
|
- {"flush", (PyCFunction)brotli_Compressor_flush, METH_NOARGS, brotli_Compressor_flush_doc},
|
|
- {"finish", (PyCFunction)brotli_Compressor_finish, METH_NOARGS, brotli_Compressor_finish_doc},
|
|
- {NULL} /* Sentinel */
|
|
-};
|
|
-
|
|
-static PyTypeObject brotli_CompressorType = {
|
|
- #if PY_MAJOR_VERSION >= 3
|
|
- PyVarObject_HEAD_INIT(NULL, 0)
|
|
- #else
|
|
- PyObject_HEAD_INIT(NULL)
|
|
- 0, /* ob_size*/
|
|
- #endif
|
|
- "brotli.Compressor", /* tp_name */
|
|
- sizeof(brotli_Compressor), /* tp_basicsize */
|
|
- 0, /* tp_itemsize */
|
|
- (destructor)brotli_Compressor_dealloc, /* tp_dealloc */
|
|
- 0, /* tp_print */
|
|
- 0, /* tp_getattr */
|
|
- 0, /* tp_setattr */
|
|
- 0, /* tp_compare */
|
|
- 0, /* tp_repr */
|
|
- 0, /* tp_as_number */
|
|
- 0, /* tp_as_sequence */
|
|
- 0, /* tp_as_mapping */
|
|
- 0, /* tp_hash */
|
|
- 0, /* tp_call */
|
|
- 0, /* tp_str */
|
|
- 0, /* tp_getattro */
|
|
- 0, /* tp_setattro */
|
|
- 0, /* tp_as_buffer */
|
|
- Py_TPFLAGS_DEFAULT, /* tp_flags */
|
|
- brotli_Compressor_doc, /* tp_doc */
|
|
- 0, /* tp_traverse */
|
|
- 0, /* tp_clear */
|
|
- 0, /* tp_richcompare */
|
|
- 0, /* tp_weaklistoffset */
|
|
- 0, /* tp_iter */
|
|
- 0, /* tp_iternext */
|
|
- brotli_Compressor_methods, /* tp_methods */
|
|
- brotli_Compressor_members, /* tp_members */
|
|
- 0, /* tp_getset */
|
|
- 0, /* tp_base */
|
|
- 0, /* tp_dict */
|
|
- 0, /* tp_descr_get */
|
|
- 0, /* tp_descr_set */
|
|
- 0, /* tp_dictoffset */
|
|
- (initproc)brotli_Compressor_init, /* tp_init */
|
|
- 0, /* tp_alloc */
|
|
- brotli_Compressor_new, /* tp_new */
|
|
-};
|
|
-
|
|
-static BROTLI_BOOL decompress_stream(BrotliDecoderState* dec,
|
|
- std::vector<uint8_t>* output,
|
|
- uint8_t* input, size_t input_length) {
|
|
- BROTLI_BOOL ok = BROTLI_TRUE;
|
|
- Py_BEGIN_ALLOW_THREADS
|
|
-
|
|
- size_t available_in = input_length;
|
|
- const uint8_t* next_in = input;
|
|
- size_t available_out = 0;
|
|
- uint8_t* next_out = NULL;
|
|
-
|
|
- BrotliDecoderResult result = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
|
|
- while (result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
|
|
- result = BrotliDecoderDecompressStream(dec,
|
|
- &available_in, &next_in,
|
|
- &available_out, &next_out, NULL);
|
|
- size_t buffer_length = 0; // Request all available output.
|
|
- const uint8_t* buffer = BrotliDecoderTakeOutput(dec, &buffer_length);
|
|
- if (buffer_length) {
|
|
- (*output).insert((*output).end(), buffer, buffer + buffer_length);
|
|
- }
|
|
- }
|
|
- ok = result != BROTLI_DECODER_RESULT_ERROR;
|
|
-
|
|
- Py_END_ALLOW_THREADS
|
|
- return ok;
|
|
-}
|
|
-
|
|
-PyDoc_STRVAR(brotli_Decompressor_doc,
|
|
-"An object to decompress a byte string.\n"
|
|
-"\n"
|
|
-"Signature:\n"
|
|
-" Decompressor()\n"
|
|
-"\n"
|
|
-"Raises:\n"
|
|
-" brotli.error: If arguments are invalid.\n");
|
|
-
|
|
-typedef struct {
|
|
- PyObject_HEAD
|
|
- BrotliDecoderState* dec;
|
|
-} brotli_Decompressor;
|
|
-
|
|
-static void brotli_Decompressor_dealloc(brotli_Decompressor* self) {
|
|
- BrotliDecoderDestroyInstance(self->dec);
|
|
- #if PY_MAJOR_VERSION >= 3
|
|
- Py_TYPE(self)->tp_free((PyObject*)self);
|
|
- #else
|
|
- self->ob_type->tp_free((PyObject*)self);
|
|
- #endif
|
|
-}
|
|
-
|
|
-static PyObject* brotli_Decompressor_new(PyTypeObject *type, PyObject *args, PyObject *keywds) {
|
|
- brotli_Decompressor *self;
|
|
- self = (brotli_Decompressor *)type->tp_alloc(type, 0);
|
|
-
|
|
- if (self != NULL) {
|
|
- self->dec = BrotliDecoderCreateInstance(0, 0, 0);
|
|
- }
|
|
-
|
|
- return (PyObject *)self;
|
|
-}
|
|
-
|
|
-static int brotli_Decompressor_init(brotli_Decompressor *self, PyObject *args, PyObject *keywds) {
|
|
- int ok;
|
|
-
|
|
- static const char *kwlist[] = {NULL};
|
|
-
|
|
- ok = PyArg_ParseTupleAndKeywords(args, keywds, "|:Decompressor",
|
|
- const_cast<char **>(kwlist));
|
|
- if (!ok)
|
|
- return -1;
|
|
- if (!self->dec)
|
|
- return -1;
|
|
-
|
|
- return 0;
|
|
-}
|
|
-
|
|
-PyDoc_STRVAR(brotli_Decompressor_process_doc,
|
|
-"Process \"string\" for decompression, returning a string that contains \n"
|
|
-"decompressed output data. This data should be concatenated to the output \n"
|
|
-"produced by any preceding calls to the \"process()\" method. \n"
|
|
-"Some or all of the input may be kept in internal buffers for later \n"
|
|
-"processing, and the decompressed output data may be empty until enough input \n"
|
|
-"has been accumulated.\n"
|
|
-"\n"
|
|
-"Signature:\n"
|
|
-" decompress(string)\n"
|
|
-"\n"
|
|
-"Args:\n"
|
|
-" string (bytes): The input data\n"
|
|
-"\n"
|
|
-"Returns:\n"
|
|
-" The decompressed output data (bytes)\n"
|
|
-"\n"
|
|
-"Raises:\n"
|
|
-" brotli.error: If decompression fails\n");
|
|
-
|
|
-static PyObject* brotli_Decompressor_process(brotli_Decompressor *self, PyObject *args) {
|
|
- PyObject* ret = NULL;
|
|
- std::vector<uint8_t> output;
|
|
- Py_buffer input;
|
|
- BROTLI_BOOL ok = BROTLI_TRUE;
|
|
-
|
|
-#if PY_MAJOR_VERSION >= 3
|
|
- ok = (BROTLI_BOOL)PyArg_ParseTuple(args, "y*:process", &input);
|
|
-#else
|
|
- ok = (BROTLI_BOOL)PyArg_ParseTuple(args, "s*:process", &input);
|
|
-#endif
|
|
-
|
|
- if (!ok)
|
|
- return NULL;
|
|
-
|
|
- if (!self->dec) {
|
|
- ok = BROTLI_FALSE;
|
|
- goto end;
|
|
- }
|
|
-
|
|
- ok = decompress_stream(self->dec, &output, static_cast<uint8_t*>(input.buf), input.len);
|
|
-
|
|
-end:
|
|
- PyBuffer_Release(&input);
|
|
- if (ok) {
|
|
- ret = PyBytes_FromStringAndSize((char*)(output.empty() ? NULL : &output[0]), output.size());
|
|
- } else {
|
|
- PyErr_SetString(BrotliError, "BrotliDecoderDecompressStream failed while processing the stream");
|
|
- }
|
|
-
|
|
- return ret;
|
|
-}
|
|
-
|
|
-PyDoc_STRVAR(brotli_Decompressor_is_finished_doc,
|
|
-"Checks if decoder instance reached the final state.\n"
|
|
-"\n"
|
|
-"Signature:\n"
|
|
-" is_finished()\n"
|
|
-"\n"
|
|
-"Returns:\n"
|
|
-" True if the decoder is in a state where it reached the end of the input\n"
|
|
-" and produced all of the output\n"
|
|
-" False otherwise\n"
|
|
-"\n"
|
|
-"Raises:\n"
|
|
-" brotli.error: If decompression fails\n");
|
|
-
|
|
-static PyObject* brotli_Decompressor_is_finished(brotli_Decompressor *self) {
|
|
- PyObject *ret = NULL;
|
|
- std::vector<uint8_t> output;
|
|
- BROTLI_BOOL ok = BROTLI_TRUE;
|
|
-
|
|
- if (!self->dec) {
|
|
- ok = BROTLI_FALSE;
|
|
- PyErr_SetString(BrotliError, "BrotliDecoderState is NULL while checking is_finished");
|
|
- goto end;
|
|
- }
|
|
-
|
|
- if (BrotliDecoderIsFinished(self->dec)) {
|
|
- Py_RETURN_TRUE;
|
|
- } else {
|
|
- Py_RETURN_FALSE;
|
|
- }
|
|
-
|
|
-end:
|
|
- if (ok) {
|
|
- ret = PyBytes_FromStringAndSize((char*)(output.empty() ? NULL : &output[0]), output.size());
|
|
- } else {
|
|
- PyErr_SetString(BrotliError, "BrotliDecoderDecompressStream failed while finishing the stream");
|
|
- }
|
|
-
|
|
- return ret;
|
|
-}
|
|
-
|
|
-static PyMemberDef brotli_Decompressor_members[] = {
|
|
- {NULL} /* Sentinel */
|
|
-};
|
|
-
|
|
-static PyMethodDef brotli_Decompressor_methods[] = {
|
|
- {"process", (PyCFunction)brotli_Decompressor_process, METH_VARARGS, brotli_Decompressor_process_doc},
|
|
- {"is_finished", (PyCFunction)brotli_Decompressor_is_finished, METH_NOARGS, brotli_Decompressor_is_finished_doc},
|
|
- {NULL} /* Sentinel */
|
|
-};
|
|
-
|
|
-static PyTypeObject brotli_DecompressorType = {
|
|
- #if PY_MAJOR_VERSION >= 3
|
|
- PyVarObject_HEAD_INIT(NULL, 0)
|
|
- #else
|
|
- PyObject_HEAD_INIT(NULL)
|
|
- 0, /* ob_size*/
|
|
- #endif
|
|
- "brotli.Decompressor", /* tp_name */
|
|
- sizeof(brotli_Decompressor), /* tp_basicsize */
|
|
- 0, /* tp_itemsize */
|
|
- (destructor)brotli_Decompressor_dealloc, /* tp_dealloc */
|
|
- 0, /* tp_print */
|
|
- 0, /* tp_getattr */
|
|
- 0, /* tp_setattr */
|
|
- 0, /* tp_compare */
|
|
- 0, /* tp_repr */
|
|
- 0, /* tp_as_number */
|
|
- 0, /* tp_as_sequence */
|
|
- 0, /* tp_as_mapping */
|
|
- 0, /* tp_hash */
|
|
- 0, /* tp_call */
|
|
- 0, /* tp_str */
|
|
- 0, /* tp_getattro */
|
|
- 0, /* tp_setattro */
|
|
- 0, /* tp_as_buffer */
|
|
- Py_TPFLAGS_DEFAULT, /* tp_flags */
|
|
- brotli_Decompressor_doc, /* tp_doc */
|
|
- 0, /* tp_traverse */
|
|
- 0, /* tp_clear */
|
|
- 0, /* tp_richcompare */
|
|
- 0, /* tp_weaklistoffset */
|
|
- 0, /* tp_iter */
|
|
- 0, /* tp_iternext */
|
|
- brotli_Decompressor_methods, /* tp_methods */
|
|
- brotli_Decompressor_members, /* tp_members */
|
|
- 0, /* tp_getset */
|
|
- 0, /* tp_base */
|
|
- 0, /* tp_dict */
|
|
- 0, /* tp_descr_get */
|
|
- 0, /* tp_descr_set */
|
|
- 0, /* tp_dictoffset */
|
|
- (initproc)brotli_Decompressor_init, /* tp_init */
|
|
- 0, /* tp_alloc */
|
|
- brotli_Decompressor_new, /* tp_new */
|
|
-};
|
|
-
|
|
-PyDoc_STRVAR(brotli_decompress__doc__,
|
|
-"Decompress a compressed byte string.\n"
|
|
-"\n"
|
|
-"Signature:\n"
|
|
-" decompress(string)\n"
|
|
-"\n"
|
|
-"Args:\n"
|
|
-" string (bytes): The compressed input data.\n"
|
|
-"\n"
|
|
-"Returns:\n"
|
|
-" The decompressed byte string.\n"
|
|
-"\n"
|
|
-"Raises:\n"
|
|
-" brotli.error: If decompressor fails.\n");
|
|
-
|
|
-static PyObject* brotli_decompress(PyObject *self, PyObject *args, PyObject *keywds) {
|
|
- PyObject *ret = NULL;
|
|
- Py_buffer input;
|
|
- const uint8_t* next_in;
|
|
- size_t available_in;
|
|
- int ok;
|
|
-
|
|
- static const char *kwlist[] = {"string", NULL};
|
|
-
|
|
-#if PY_MAJOR_VERSION >= 3
|
|
- ok = PyArg_ParseTupleAndKeywords(args, keywds, "y*|:decompress",
|
|
- const_cast<char **>(kwlist), &input);
|
|
-#else
|
|
- ok = PyArg_ParseTupleAndKeywords(args, keywds, "s*|:decompress",
|
|
- const_cast<char **>(kwlist), &input);
|
|
-#endif
|
|
-
|
|
- if (!ok)
|
|
- return NULL;
|
|
-
|
|
- std::vector<uint8_t> output;
|
|
-
|
|
- /* >>> Pure C block; release python GIL. */
|
|
- Py_BEGIN_ALLOW_THREADS
|
|
-
|
|
- BrotliDecoderState* state = BrotliDecoderCreateInstance(0, 0, 0);
|
|
-
|
|
- BrotliDecoderResult result = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
|
|
- next_in = static_cast<uint8_t*>(input.buf);
|
|
- available_in = input.len;
|
|
- while (result == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
|
|
- size_t available_out = 0;
|
|
- result = BrotliDecoderDecompressStream(state, &available_in, &next_in,
|
|
- &available_out, 0, 0);
|
|
- const uint8_t* next_out = BrotliDecoderTakeOutput(state, &available_out);
|
|
- if (available_out != 0)
|
|
- output.insert(output.end(), next_out, next_out + available_out);
|
|
- }
|
|
- ok = result == BROTLI_DECODER_RESULT_SUCCESS;
|
|
- BrotliDecoderDestroyInstance(state);
|
|
-
|
|
- Py_END_ALLOW_THREADS
|
|
- /* <<< Pure C block end. Python GIL reacquired. */
|
|
-
|
|
- PyBuffer_Release(&input);
|
|
- if (ok) {
|
|
- ret = PyBytes_FromStringAndSize((char*)(output.size() ? &output[0] : NULL), output.size());
|
|
- } else {
|
|
- PyErr_SetString(BrotliError, "BrotliDecompress failed");
|
|
- }
|
|
-
|
|
- return ret;
|
|
-}
|
|
-
|
|
-static PyMethodDef brotli_methods[] = {
|
|
- {"decompress", (PyCFunction)brotli_decompress, METH_VARARGS | METH_KEYWORDS, brotli_decompress__doc__},
|
|
- {NULL, NULL, 0, NULL}
|
|
-};
|
|
-
|
|
-PyDoc_STRVAR(brotli_doc, "Implementation module for the Brotli library.");
|
|
-
|
|
-#if PY_MAJOR_VERSION >= 3
|
|
-#define INIT_BROTLI PyInit__brotli
|
|
-#define CREATE_BROTLI PyModule_Create(&brotli_module)
|
|
-#define RETURN_BROTLI return m
|
|
-#define RETURN_NULL return NULL
|
|
-
|
|
-static struct PyModuleDef brotli_module = {
|
|
- PyModuleDef_HEAD_INIT,
|
|
- "_brotli",
|
|
- brotli_doc,
|
|
- 0,
|
|
- brotli_methods,
|
|
- NULL,
|
|
- NULL,
|
|
- NULL
|
|
-};
|
|
-#else
|
|
-#define INIT_BROTLI init_brotli
|
|
-#define CREATE_BROTLI Py_InitModule3("_brotli", brotli_methods, brotli_doc)
|
|
-#define RETURN_BROTLI return
|
|
-#define RETURN_NULL return
|
|
-#endif
|
|
-
|
|
-PyMODINIT_FUNC INIT_BROTLI(void) {
|
|
- PyObject *m = CREATE_BROTLI;
|
|
-
|
|
- BrotliError = PyErr_NewException((char*) "brotli.error", NULL, NULL);
|
|
- if (BrotliError != NULL) {
|
|
- Py_INCREF(BrotliError);
|
|
- PyModule_AddObject(m, "error", BrotliError);
|
|
- }
|
|
-
|
|
- if (PyType_Ready(&brotli_CompressorType) < 0) {
|
|
- RETURN_NULL;
|
|
- }
|
|
- Py_INCREF(&brotli_CompressorType);
|
|
- PyModule_AddObject(m, "Compressor", (PyObject *)&brotli_CompressorType);
|
|
-
|
|
- if (PyType_Ready(&brotli_DecompressorType) < 0) {
|
|
- RETURN_NULL;
|
|
- }
|
|
- Py_INCREF(&brotli_DecompressorType);
|
|
- PyModule_AddObject(m, "Decompressor", (PyObject *)&brotli_DecompressorType);
|
|
-
|
|
- PyModule_AddIntConstant(m, "MODE_GENERIC", (int) BROTLI_MODE_GENERIC);
|
|
- PyModule_AddIntConstant(m, "MODE_TEXT", (int) BROTLI_MODE_TEXT);
|
|
- PyModule_AddIntConstant(m, "MODE_FONT", (int) BROTLI_MODE_FONT);
|
|
-
|
|
- char version[16];
|
|
- snprintf(version, sizeof(version), "%d.%d.%d",
|
|
- BROTLI_VERSION >> 24, (BROTLI_VERSION >> 12) & 0xFFF, BROTLI_VERSION & 0xFFF);
|
|
- PyModule_AddStringConstant(m, "__version__", version);
|
|
-
|
|
- RETURN_BROTLI;
|
|
-}
|
|
diff -urN brotli-1.0.6/python/brotli.py brotli-1.0.6-modified/python/brotli.py
|
|
--- brotli-1.0.6/python/brotli.py 2018-09-13 23:01:23.000000000 +0530
|
|
+++ brotli-1.0.6-modified/python/brotli.py 2023-08-29 16:30:29.000000000 +0530
|
|
@@ -7,9 +7,8 @@
|
|
|
|
import _brotli
|
|
|
|
-
|
|
# The library version.
|
|
-__version__ = _brotli.__version__
|
|
+version = __version__ = _brotli.__version__
|
|
|
|
# The compression mode.
|
|
MODE_GENERIC = _brotli.MODE_GENERIC
|
|
diff -urN brotli-1.0.6/python/README.md brotli-1.0.6-modified/python/README.md
|
|
--- brotli-1.0.6/python/README.md 2018-09-13 23:01:23.000000000 +0530
|
|
+++ brotli-1.0.6-modified/python/README.md 2023-08-29 16:30:29.000000000 +0530
|
|
@@ -22,7 +22,7 @@
|
|
You may run the following commands from this directory:
|
|
|
|
$ make # Build the module in-place
|
|
-
|
|
+
|
|
$ make test # Test the module
|
|
|
|
$ make clean # Remove all temporary files and build output
|
|
diff -urN brotli-1.0.6/setup.py brotli-1.0.6-modified/setup.py
|
|
--- brotli-1.0.6/setup.py 2026-01-27 14:56:01.010289514 +0530
|
|
+++ brotli-1.0.6-modified/setup.py 2026-01-27 14:57:44.458496228 +0530
|
|
@@ -180,7 +180,7 @@
|
|
Extension(
|
|
'_brotli',
|
|
sources=[
|
|
- 'python/_brotli.cc',
|
|
+ 'python/_brotli.c',
|
|
'c/common/constants.c',
|
|
'c/common/dictionary.c',
|
|
'c/common/transform.c',
|
|
@@ -263,8 +263,7 @@
|
|
],
|
|
include_dirs=[
|
|
'c/include',
|
|
- ],
|
|
- language='c++'),
|
|
+ ]),
|
|
]
|
|
|
|
TEST_SUITE = 'setup.get_test_suite'
|