mirror of
https://github.com/google/brotli.git
synced 2025-01-03 21:51:07 +00:00
Merge pull request #105 from anthrotype/newparams
[python] expose new encoder parameters as kwargs of brotli.compress
This commit is contained in:
commit
cc211b92f1
119
python/bro.py
119
python/bro.py
@ -2,22 +2,22 @@
|
||||
"""bro %s -- compression/decompression utility using the Brotli algorithm."""
|
||||
|
||||
from __future__ import print_function
|
||||
import getopt
|
||||
import argparse
|
||||
import sys
|
||||
import os
|
||||
import brotli
|
||||
import platform
|
||||
|
||||
__usage__ = """\
|
||||
Usage: bro [--force] [--decompress] [--input filename] [--output filename]
|
||||
[--mode 'text'|'font'] [--transform]"""
|
||||
|
||||
__version__ = '0.1'
|
||||
__version__ = '1.0'
|
||||
|
||||
|
||||
BROTLI_MODES = {
|
||||
'text': brotli.MODE_TEXT,
|
||||
'font': brotli.MODE_FONT
|
||||
# default values of encoder parameters
|
||||
DEFAULT_PARAMS = {
|
||||
'mode': brotli.MODE_GENERIC,
|
||||
'quality': 11,
|
||||
'lgwin': 22,
|
||||
'lgblock': 0,
|
||||
}
|
||||
|
||||
|
||||
@ -49,27 +49,59 @@ def get_binary_stdio(stream):
|
||||
return orig_stdio.buffer
|
||||
|
||||
|
||||
def main(args):
|
||||
def main():
|
||||
|
||||
options = parse_options(args)
|
||||
parser = argparse.ArgumentParser(
|
||||
prog='bro.py',
|
||||
description="Compression/decompression utility using the Brotli algorithm.")
|
||||
parser.add_argument('--version', action='version', version='%(prog)s 1.0')
|
||||
parser.add_argument('-i', '--input', metavar='FILE', type=str, dest='infile',
|
||||
help='Input file', default=None)
|
||||
parser.add_argument('-o', '--output', metavar='FILE', type=str, dest='outfile',
|
||||
help='Output file', default=None)
|
||||
parser.add_argument('-f', '--force', action='store_true',
|
||||
help='Overwrite existing output file', default=False)
|
||||
parser.add_argument('-d', '--decompress', action='store_true',
|
||||
help='Decompress input file', default=False)
|
||||
params = parser.add_argument_group('optional encoder parameters')
|
||||
params.add_argument('-m', '--mode', metavar="MODE", type=int, choices=[0, 1],
|
||||
help='The compression mode can be 0 for generic input, '
|
||||
'1 for UTF-8 encoded text, or 2 for WOFF 2.0 font data.'
|
||||
'Defaults to 0.')
|
||||
params.add_argument('-q', '--quality', metavar="QUALITY", type=int,
|
||||
choices=list(range(0, 12)),
|
||||
help='Controls the compression-speed vs compression-density '
|
||||
'tradeoff. The higher the quality, the slower the '
|
||||
'compression. Range is 0 to 11. Defaults to 11.')
|
||||
params.add_argument('--lgwin', metavar="LGWIN", type=int,
|
||||
choices=list(range(16, 25)),
|
||||
help='Base 2 logarithm of the sliding window size. Range is '
|
||||
'16 to 24. Defaults to 22.')
|
||||
params.add_argument('--lgblock', metavar="LGBLOCK", type=int,
|
||||
choices=[0] + list(range(16, 25)),
|
||||
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)
|
||||
|
||||
options = parser.parse_args()
|
||||
|
||||
if options.infile:
|
||||
if not os.path.isfile(options.infile):
|
||||
print('file "%s" not found' % options.infile, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
parser.error('file "%s" not found' % options.infile)
|
||||
with open(options.infile, "rb") as infile:
|
||||
data = infile.read()
|
||||
else:
|
||||
if sys.stdin.isatty():
|
||||
# interactive console, just quit
|
||||
usage()
|
||||
parser.error('no input')
|
||||
infile = get_binary_stdio('stdin')
|
||||
data = infile.read()
|
||||
|
||||
if options.outfile:
|
||||
if os.path.isfile(options.outfile) and not options.force:
|
||||
print('output file exists')
|
||||
sys.exit(1)
|
||||
parser.error('output file exists')
|
||||
outfile = open(options.outfile, "wb")
|
||||
else:
|
||||
outfile = get_binary_stdio('stdout')
|
||||
@ -78,62 +110,15 @@ def main(args):
|
||||
if options.decompress:
|
||||
data = brotli.decompress(data)
|
||||
else:
|
||||
data = brotli.compress(data, options.mode, options.transform)
|
||||
data = brotli.compress(
|
||||
data, mode=options.mode, quality=options.quality,
|
||||
lgwin=options.lgwin, lgblock=options.lgblock)
|
||||
except brotli.error as e:
|
||||
print('[ERROR] %s: %s' % (e, options.infile or 'sys.stdin'),
|
||||
file=sys.stderr)
|
||||
sys.exit(1)
|
||||
parser.exit(1,'bro: error: %s: %s' % (e, options.infile or 'sys.stdin'))
|
||||
|
||||
outfile.write(data)
|
||||
outfile.close()
|
||||
|
||||
|
||||
def parse_options(args):
|
||||
try:
|
||||
raw_options, dummy = getopt.gnu_getopt(
|
||||
args, "?hdi:o:fm:t",
|
||||
["help", "decompress", "input=", "output=", "force", "mode=",
|
||||
"transform"])
|
||||
except getopt.GetoptError as e:
|
||||
print(e, file=sys.stderr)
|
||||
usage()
|
||||
options = Options(raw_options)
|
||||
return options
|
||||
|
||||
|
||||
def usage():
|
||||
print(__usage__, file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
class Options(object):
|
||||
|
||||
def __init__(self, raw_options):
|
||||
self.decompress = self.force = self.transform = False
|
||||
self.infile = self.outfile = None
|
||||
self.mode = BROTLI_MODES['text']
|
||||
for option, value in raw_options:
|
||||
if option in ("-h", "--help"):
|
||||
print(__doc__ % (__version__))
|
||||
print("\n%s" % __usage__)
|
||||
sys.exit(0)
|
||||
elif option in ('-d', '--decompress'):
|
||||
self.decompress = True
|
||||
elif option in ('-i', '--input'):
|
||||
self.infile = value
|
||||
elif option in ('-o', '--output'):
|
||||
self.outfile = value
|
||||
elif option in ('-f', '--force'):
|
||||
self.force = True
|
||||
elif option in ('-m', '--mode'):
|
||||
value = value.lower()
|
||||
if value not in ('text', 'font'):
|
||||
print('mode "%s" not recognized' % value, file=sys.stderr)
|
||||
usage()
|
||||
self.mode = BROTLI_MODES[value]
|
||||
elif option in ('-t', '--transform'):
|
||||
self.transform = True
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv[1:])
|
||||
main()
|
||||
|
@ -20,7 +20,8 @@ static int mode_convertor(PyObject *o, BrotliParams::Mode *mode) {
|
||||
}
|
||||
|
||||
*mode = (BrotliParams::Mode) PyInt_AsLong(o);
|
||||
if (*mode != BrotliParams::Mode::MODE_TEXT &&
|
||||
if (*mode != BrotliParams::Mode::MODE_GENERIC &&
|
||||
*mode != BrotliParams::Mode::MODE_TEXT &&
|
||||
*mode != BrotliParams::Mode::MODE_FONT) {
|
||||
PyErr_SetString(BrotliError, "Invalid mode");
|
||||
return 0;
|
||||
@ -29,25 +30,114 @@ static int mode_convertor(PyObject *o, BrotliParams::Mode *mode) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(compress__doc__,
|
||||
"compress(string[, mode[, transform]]) -- Returned compressed string.\n"
|
||||
"\n"
|
||||
"Optional arg mode is the compression mode, either MODE_TEXT (default) or\n"
|
||||
"MODE_FONT. Optional boolean arg transform controls whether to enable\n"
|
||||
"encoder transforms or not, defaults to False.");
|
||||
static int quality_convertor(PyObject *o, int *quality) {
|
||||
if (!PyInt_Check(o)) {
|
||||
PyErr_SetString(BrotliError, "Invalid quality");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PyObject* brotli_compress(PyObject *self, PyObject *args) {
|
||||
*quality = PyInt_AsLong(o);
|
||||
if (*quality < 0 || *quality > 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;
|
||||
}
|
||||
|
||||
*lgwin = PyInt_AsLong(o);
|
||||
if (*lgwin < 16 || *lgwin > 24) {
|
||||
PyErr_SetString(BrotliError, "Invalid lgwin. Range is 16 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;
|
||||
}
|
||||
|
||||
*lgblock = PyInt_AsLong(o);
|
||||
if ((*lgblock != 0 && *lgblock < 16) || *lgblock > 24) {
|
||||
PyErr_SetString(BrotliError, "Invalid lgblock. Can be 0 or in range 16 to 24.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(compress__doc__,
|
||||
"Compress a byte string.\n"
|
||||
"\n"
|
||||
"Signature:\n"
|
||||
" compress(string, mode=MODE_GENERIC, quality=11, lgwin=22, lgblock=0,\n"
|
||||
" enable_dictionary=True, enable_transforms=False\n"
|
||||
" greedy_block_split=False, enable_context_modeling=True)\n"
|
||||
"\n"
|
||||
"Args:\n"
|
||||
" string (bytes): The input data.\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 16 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"
|
||||
" enable_dictionary (bool, optional): Enables encoder dictionary. Defaults to\n"
|
||||
" True. Respected only if quality > 9.\n"
|
||||
" enable_transforms (bool, optional): Enable encoder transforms. Defaults to\n"
|
||||
" False. Respected only if quality > 9.\n"
|
||||
" greedy_block_split (bool, optional): Enables a faster but less dense\n"
|
||||
" compression mode. Defaults to False. Respected only if quality > 9.\n"
|
||||
" enable_context_modeling (bool, optional): Controls whether to enable context\n"
|
||||
" modeling. Defaults to True. Respected only if quality > 9.\n"
|
||||
"\n"
|
||||
"Returns:\n"
|
||||
" The compressed byte string.\n"
|
||||
"\n"
|
||||
"Raises:\n"
|
||||
" brotli.error: If arguments are invalid, or compressor fails.\n");
|
||||
|
||||
static PyObject* brotli_compress(PyObject *self, PyObject *args, PyObject *keywds) {
|
||||
PyObject *ret = NULL;
|
||||
PyObject* transform = NULL;
|
||||
PyObject* enable_dictionary = NULL;
|
||||
PyObject* enable_transforms = NULL;
|
||||
PyObject* greedy_block_split = NULL;
|
||||
PyObject* enable_context_modeling = NULL;
|
||||
uint8_t *input, *output;
|
||||
size_t length, output_length;
|
||||
BrotliParams::Mode mode = (BrotliParams::Mode) -1;
|
||||
int quality = -1;
|
||||
int lgwin = -1;
|
||||
int lgblock = -1;
|
||||
int ok;
|
||||
|
||||
ok = PyArg_ParseTuple(args, "s#|O&O!:compress",
|
||||
static char *kwlist[] = {"string", "mode", "quality", "lgwin", "lgblock",
|
||||
"enable_dictionary", "enable_transforms",
|
||||
"greedy_block_split", "enable_context_modeling", NULL};
|
||||
|
||||
ok = PyArg_ParseTupleAndKeywords(args, keywds, "s#|O&O&O&O&O!O!O!O!:compress", kwlist,
|
||||
&input, &length,
|
||||
&mode_convertor, &mode,
|
||||
&PyBool_Type, &transform);
|
||||
&quality_convertor, &quality,
|
||||
&lgwin_convertor, &lgwin,
|
||||
&lgblock_convertor, &lgblock,
|
||||
&PyBool_Type, &enable_dictionary,
|
||||
&PyBool_Type, &enable_transforms,
|
||||
&PyBool_Type, &greedy_block_split,
|
||||
&PyBool_Type, &enable_context_modeling);
|
||||
|
||||
if (!ok)
|
||||
return NULL;
|
||||
@ -58,8 +148,20 @@ static PyObject* brotli_compress(PyObject *self, PyObject *args) {
|
||||
BrotliParams params;
|
||||
if (mode != -1)
|
||||
params.mode = mode;
|
||||
if (transform)
|
||||
params.enable_transforms = PyObject_IsTrue(transform);
|
||||
if (quality != -1)
|
||||
params.quality = quality;
|
||||
if (lgwin != -1)
|
||||
params.lgwin = lgwin;
|
||||
if (lgblock != -1)
|
||||
params.lgblock = lgblock;
|
||||
if (enable_dictionary)
|
||||
params.enable_dictionary = PyObject_IsTrue(enable_dictionary);
|
||||
if (enable_transforms)
|
||||
params.enable_transforms = PyObject_IsTrue(enable_transforms);
|
||||
if (greedy_block_split)
|
||||
params.greedy_block_split = PyObject_IsTrue(greedy_block_split);
|
||||
if (enable_context_modeling)
|
||||
params.enable_context_modeling = PyObject_IsTrue(enable_context_modeling);
|
||||
|
||||
ok = BrotliCompressBuffer(params, length, input,
|
||||
&output_length, output);
|
||||
@ -81,7 +183,19 @@ int output_callback(void* data, const uint8_t* buf, size_t count) {
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(decompress__doc__,
|
||||
"decompress(string) -- Return decompressed string.");
|
||||
"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 *ret = NULL;
|
||||
@ -112,17 +226,14 @@ static PyObject* brotli_decompress(PyObject *self, PyObject *args) {
|
||||
}
|
||||
|
||||
static PyMethodDef brotli_methods[] = {
|
||||
{"compress", brotli_compress, METH_VARARGS, compress__doc__},
|
||||
{"compress", (PyCFunction)brotli_compress, METH_VARARGS | METH_KEYWORDS, compress__doc__},
|
||||
{"decompress", brotli_decompress, METH_VARARGS, decompress__doc__},
|
||||
{NULL, NULL, 0, NULL}
|
||||
};
|
||||
|
||||
PyDoc_STRVAR(brotli__doc__,
|
||||
"The functions in this module allow compression and decompression using the\n"
|
||||
"Brotli library.\n"
|
||||
"\n"
|
||||
"compress(string[, mode, transform]) -- Compress string.\n"
|
||||
"decompress(string) -- Decompresses a compressed string.\n");
|
||||
"Brotli library.\n\n");
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
#define INIT_BROTLI PyInit_brotli
|
||||
@ -155,6 +266,7 @@ PyMODINIT_FUNC INIT_BROTLI(void) {
|
||||
PyModule_AddObject(m, "error", BrotliError);
|
||||
}
|
||||
|
||||
PyModule_AddIntConstant(m, "MODE_GENERIC", (int) BrotliParams::Mode::MODE_GENERIC);
|
||||
PyModule_AddIntConstant(m, "MODE_TEXT", (int) BrotliParams::Mode::MODE_TEXT);
|
||||
PyModule_AddIntConstant(m, "MODE_FONT", (int) BrotliParams::Mode::MODE_FONT);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user