diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 524bbbaf..473daa88 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -31,7 +31,6 @@ that PyPy is no longer supported. faster. + igzip.decompress has 30% less overhead when called. + ``isal_zlib`` functions now raise ``isal_zlib.error`` on error. - ``isal_zlib.IsalError`` has been removed. + The base class for ``isal_zlib.error`` and ``igzip_lib.IsalError`` is now ``Exception`` instead of ``OSError``. + GzipReader now uses larger input and output buffers (128k) by default and diff --git a/src/isal/igzip_lib.c b/src/isal/igzip_lib.c index 18d44192..5fd0ae1e 100644 --- a/src/isal/igzip_lib.c +++ b/src/isal/igzip_lib.c @@ -16,29 +16,7 @@ // - Constants were added that are particular to igzip_lib. -#define PY_SSIZE_T_CLEAN -#include -#include "structmember.h" // PyMemberDef #include "igzip_lib_impl.h" -#ifndef _PyArg_UnpackKeywords -#include "python_args.h" -#endif - -typedef struct { - PyTypeObject *Decomptype; - PyObject *IsalError; -} _igzip_lib_state; - -static inline _igzip_lib_state* -get_igzip_lib_state(PyObject *module) -{ - void *state = PyModule_GetState(module); - assert(state != NULL); - return (_igzip_lib_state*)state; -} -static PyModuleDef igzip_lib_module; -#define _igzip_lib_state_global ((_igzip_lib_state *)PyModule_GetState(PyState_FindModule(&igzip_lib_module))) - typedef struct { PyObject_HEAD @@ -98,7 +76,7 @@ igzip_lib_IgzipDecompressor___init___impl(IgzipDecompressor *self, (uint32_t)zdict_buf.len); PyBuffer_Release(&zdict_buf); if (err != ISAL_DECOMP_OK) { - isal_inflate_error(err, _igzip_lib_state_global->IsalError); + isal_inflate_error(err); goto error; } } @@ -164,7 +142,7 @@ decompress_buf(IgzipDecompressor *self, Py_ssize_t max_length) err = isal_inflate(&(self->state)); if (err != ISAL_DECOMP_OK){ - isal_inflate_error(err, _igzip_lib_state_global->IsalError); + isal_inflate_error(err); goto error; } } while (self->state.avail_out == 0 && self->state.block_state != ISAL_BLOCK_FINISH); @@ -334,78 +312,27 @@ PyDoc_STRVAR(igzip_lib_compress__doc__, " the header and trailer are controlled by the flag parameter."); #define IGZIP_LIB_COMPRESS_METHODDEF \ - {"compress", (PyCFunction)(void(*)(void))igzip_lib_compress, METH_FASTCALL|METH_KEYWORDS, igzip_lib_compress__doc__} + {"compress", (PyCFunction)(void(*)(void))igzip_lib_compress, METH_VARARGS|METH_KEYWORDS, igzip_lib_compress__doc__} static PyObject * -igzip_lib_compress(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +igzip_lib_compress(PyObject *module, PyObject *args, PyObject *kwargs) { - PyObject *return_value = NULL; - static const char * const _keywords[] = {"", "level", "flag", "mem_level", "hist_bits", NULL}; - static _PyArg_Parser _parser = {NULL, _keywords, "compress", 0}; - PyObject *argsbuf[5]; - Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; - PyObject *ErrorClass = get_igzip_lib_state(module)->IsalError; + char *keywords[] = {"", "level", "flag", "mem_level", "hist_bits", NULL}; + char *format ="y*|iiii:compress"; Py_buffer data = {NULL, NULL}; int level = ISAL_DEFAULT_COMPRESSION; int flag = COMP_DEFLATE; int mem_level = MEM_LEVEL_DEFAULT; int hist_bits = ISAL_DEF_MAX_HIST_BITS; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 5, 0, argsbuf); - if (!args) { - goto exit; - } - if (PyObject_GetBuffer(args[0], &data, PyBUF_SIMPLE) != 0) { - goto exit; - } - if (!PyBuffer_IsContiguous(&data, 'C')) { - _PyArg_BadArgument("compress", "argument 1", "contiguous buffer", args[0]); - goto exit; - } - if (!noptargs) { - goto skip_optional_pos; - } - if (args[1]) { - level = _PyLong_AsInt(args[1]); - if (level == -1 && PyErr_Occurred()) { - goto exit; - } - if (!--noptargs) { - goto skip_optional_pos; - } - } - if (args[2]) { - flag = _PyLong_AsInt(args[2]); - if (flag == -1 && PyErr_Occurred()) { - goto exit; - } - if (!--noptargs) { - goto skip_optional_pos; - } - } - if (args[3]) { - mem_level = _PyLong_AsInt(args[3]); - if (mem_level == -1 && PyErr_Occurred()) { - goto exit; - } - if (!--noptargs) { - goto skip_optional_pos; - } - } - hist_bits = _PyLong_AsInt(args[4]); - if (hist_bits == -1 && PyErr_Occurred()) { - goto exit; - } -skip_optional_pos: - return_value = igzip_lib_compress_impl( - ErrorClass, &data, level, flag, mem_level, hist_bits); - -exit: - /* Cleanup for data */ - if (data.obj) { - PyBuffer_Release(&data); + if (!PyArg_ParseTupleAndKeywords( + args, kwargs, format, keywords, + &data, &level, &flag, &mem_level, &hist_bits)) { + return NULL; } - + PyObject *return_value = igzip_lib_compress_impl( + &data, level, flag, mem_level, hist_bits); + PyBuffer_Release(&data); return return_value; } @@ -426,84 +353,25 @@ PyDoc_STRVAR(igzip_lib_decompress__doc__, " The initial output buffer size."); #define IGZIP_LIB_DECOMPRESS_METHODDEF \ - {"decompress", (PyCFunction)(void(*)(void))igzip_lib_decompress, METH_FASTCALL|METH_KEYWORDS, igzip_lib_decompress__doc__} + {"decompress", (PyCFunction)(void(*)(void))igzip_lib_decompress, METH_VARARGS|METH_KEYWORDS, igzip_lib_decompress__doc__} static PyObject * -igzip_lib_decompress(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +igzip_lib_decompress(PyObject *module, PyObject *args, PyObject *kwargs) { - PyObject *return_value = NULL; - static const char * const _keywords[] = {"", "flag", "hist_bits", "bufsize", NULL}; - static _PyArg_Parser _parser = {NULL, _keywords, "decompress", 0}; - PyObject *argsbuf[4]; - Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; - PyObject *ErrorClass = get_igzip_lib_state(module)->IsalError; + static const char *keywords[] = {"", "flag", "hist_bits", "bufsize", NULL}; + char *format ="y*|iin:decompress"; Py_buffer data = {NULL, NULL}; int flag = DECOMP_DEFLATE; int hist_bits = ISAL_DEF_MAX_HIST_BITS; Py_ssize_t bufsize = DEF_BUF_SIZE; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 4, 0, argsbuf); - if (!args) { - goto exit; - } - if (PyObject_GetBuffer(args[0], &data, PyBUF_SIMPLE) != 0) { - goto exit; - } - if (!PyBuffer_IsContiguous(&data, 'C')) { - _PyArg_BadArgument("decompress", "argument 1", "contiguous buffer", args[0]); - goto exit; - } - if (!noptargs) { - goto skip_optional_pos; - } - if (args[1]) { - flag = _PyLong_AsInt(args[1]); - if (flag == -1 && PyErr_Occurred()) { - goto exit; - } - if (!--noptargs) { - goto skip_optional_pos; - } - } - if (args[2]) { - hist_bits = _PyLong_AsInt(args[2]); - if (hist_bits == -1 && PyErr_Occurred()) { - goto exit; - } - if (!--noptargs) { - goto skip_optional_pos; - } - } - if (args[2]) { - flag = _PyLong_AsInt(args[2]); - if (flag == -1 && PyErr_Occurred()) { - goto exit; - } - if (!--noptargs) { - goto skip_optional_pos; - } - } - { - Py_ssize_t ival = -1; - PyObject *iobj = PyNumber_Index(args[3]); - if (iobj != NULL) { - ival = PyLong_AsSsize_t(iobj); - Py_DECREF(iobj); - } - if (ival == -1 && PyErr_Occurred()) { - goto exit; - } - bufsize = ival; - } -skip_optional_pos: - return_value = igzip_lib_decompress_impl(ErrorClass, &data, flag, hist_bits, bufsize); - -exit: - /* Cleanup for data */ - if (data.obj) { - PyBuffer_Release(&data); + if (!PyArg_ParseTupleAndKeywords( + args, kwargs, format, keywords, + &data, &flag, &hist_bits, &bufsize)) { + return NULL; } - + PyObject * return_value = igzip_lib_decompress_impl(&data, flag, hist_bits, bufsize); + PyBuffer_Release(&data); return return_value; } @@ -527,61 +395,26 @@ PyDoc_STRVAR(igzip_lib_IgzipDecompressor_decompress__doc__, "the unused_data attribute."); #define IGZIP_LIB_IGZIPDECOMPRESSOR_DECOMPRESS_METHODDEF \ - {"decompress", (PyCFunction)(void(*)(void))igzip_lib_IgzipDecompressor_decompress, METH_FASTCALL|METH_KEYWORDS, igzip_lib_IgzipDecompressor_decompress__doc__} + {"decompress", (PyCFunction)(void(*)(void))igzip_lib_IgzipDecompressor_decompress, METH_VARARGS|METH_KEYWORDS, igzip_lib_IgzipDecompressor_decompress__doc__} static PyObject * -igzip_lib_IgzipDecompressor_decompress(IgzipDecompressor *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +igzip_lib_IgzipDecompressor_decompress(IgzipDecompressor *self, PyObject *args, PyObject *kwargs) { - PyObject *return_value = NULL; - static const char * const _keywords[] = {"data", "max_length", NULL}; - static _PyArg_Parser _parser = {NULL, _keywords, "decompress", 0}; - PyObject *argsbuf[2]; - Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; + char *keywords[] = {"", "max_length", NULL}; + char *format = "y*|n:decompress"; Py_buffer data = {NULL, NULL}; Py_ssize_t max_length = -1; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf); - if (!args) { - goto exit; - } - if (PyObject_GetBuffer(args[0], &data, PyBUF_SIMPLE) != 0) { - goto exit; - } - if (!PyBuffer_IsContiguous(&data, 'C')) { - _PyArg_BadArgument("decompress", "argument 'data'", "contiguous buffer", args[0]); - goto exit; - } - if (!noptargs) { - goto skip_optional_pos; - } - if (PyFloat_Check(args[1])) { - PyErr_SetString(PyExc_TypeError, - "integer argument expected, got float" ); - goto exit; - } - { - Py_ssize_t ival = -1; - PyObject *iobj = PyNumber_Index(args[1]); - if (iobj != NULL) { - ival = PyLong_AsSsize_t(iobj); - Py_DECREF(iobj); - } - if (ival == -1 && PyErr_Occurred()) { - goto exit; - } - max_length = ival; - } -skip_optional_pos: - return_value = igzip_lib_IgzipDecompressor_decompress_impl(self, &data, max_length); - -exit: - /* Cleanup for data */ - if (data.obj) { - PyBuffer_Release(&data); + if (!PyArg_ParseTupleAndKeywords( + args, kwargs, format, keywords, &data, &max_length)) { + return NULL; } + PyObject *return_value = igzip_lib_IgzipDecompressor_decompress_impl(self, &data, max_length); + PyBuffer_Release(&data); return return_value; } + PyDoc_STRVAR(igzip_lib_IgzipDecompressor___init____doc__, "IgzipDecompressor(flag=0, hist_bits=15, zdict=b\'\')\n" "--\n" @@ -601,57 +434,17 @@ static int igzip_lib_IgzipDecompressor___init__(PyObject *self, PyObject *args, PyObject *kwargs) { int return_value = -1; - static const char * const _keywords[] = {"flag", "hist_bits", "zdict", NULL}; - static _PyArg_Parser _parser = {NULL, _keywords, "IgzipDecompressor", 0}; - PyObject *argsbuf[3]; - PyObject * const *fastargs; - Py_ssize_t nargs = PyTuple_GET_SIZE(args); - Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 0; + char *keywords[] = {"flag", "hist_bits", "zdict", NULL}; + char *format = "|iiO:IgzipDecompressor"; int flag = ISAL_DEFLATE; int hist_bits = ISAL_DEF_MAX_HIST_BITS; PyObject *zdict = NULL; - fastargs = _PyArg_UnpackKeywords(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, 0, 3, 0, argsbuf); - if (!fastargs) { - goto exit; - } - if (!noptargs) { - goto skip_optional_pos; - } - if (fastargs[0]) { - if (PyFloat_Check(fastargs[0])) { - PyErr_SetString(PyExc_TypeError, - "integer argument expected, got float" ); - goto exit; - } - flag = _PyLong_AsInt(fastargs[0]); - if (flag == -1 && PyErr_Occurred()) { - goto exit; - } - if (!--noptargs) { - goto skip_optional_pos; - } - } - if (fastargs[1]) { - if (PyFloat_Check(fastargs[1])) { - PyErr_SetString(PyExc_TypeError, - "integer argument expected, got float" ); - goto exit; - } - hist_bits = _PyLong_AsInt(fastargs[1]); - if (hist_bits == -1 && PyErr_Occurred()) { - goto exit; - } - if (!--noptargs) { - goto skip_optional_pos; - } + if (!PyArg_ParseTupleAndKeywords( + args, kwargs, format, keywords, &flag, &hist_bits, &zdict)) { + return NULL; } - zdict = fastargs[2]; -skip_optional_pos: - return_value = igzip_lib_IgzipDecompressor___init___impl((IgzipDecompressor *)self, flag, hist_bits, zdict); - -exit: - return return_value; + return igzip_lib_IgzipDecompressor___init___impl((IgzipDecompressor *)self, flag, hist_bits, zdict); } static PyMethodDef IgzipDecompressor_methods[] = { @@ -685,43 +478,15 @@ static PyMemberDef IgzipDecompressor_members[] = { static PyTypeObject IgzipDecompressor_Type = { PyVarObject_HEAD_INIT(NULL, 0) - "igzip_lib.IgzipDecompressor", /* tp_name */ - sizeof(IgzipDecompressor), /* tp_basicsize */ - 0, /* tp_itemsize */ - (destructor)IgzipDecompressor_dealloc,/* tp_dealloc */ - 0, /* tp_vectorcall_offset */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_as_async */ - 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 */ - igzip_lib_IgzipDecompressor___init____doc__, /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - 0, /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - IgzipDecompressor_methods, /* tp_methods */ - IgzipDecompressor_members, /* tp_members */ - 0, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - igzip_lib_IgzipDecompressor___init__, /* tp_init */ - 0, /* tp_alloc */ - PyType_GenericNew, /* tp_new */ + .tp_name = "igzip_lib.IgzipDecompressor", + .tp_basicsize = sizeof(IgzipDecompressor), + .tp_dealloc = (destructor)IgzipDecompressor_dealloc, + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_doc = igzip_lib_IgzipDecompressor___init____doc__, + .tp_methods = IgzipDecompressor_methods, + .tp_members = IgzipDecompressor_members, + .tp_init = igzip_lib_IgzipDecompressor___init__, + .tp_new = PyType_GenericNew, }; static PyMethodDef IgzipLibMethods[] = { @@ -779,7 +544,7 @@ static struct PyModuleDef igzip_lib_module = { PyModuleDef_HEAD_INIT, "igzip_lib", /* name of module */ igizp_lib_module_documentation, /* module documentation, may be NULL */ - sizeof(_igzip_lib_state), + 0, IgzipLibMethods }; @@ -788,21 +553,23 @@ PyMODINIT_FUNC PyInit_igzip_lib(void) { PyObject *m; - PyObject *IsalError; m = PyModule_Create(&igzip_lib_module); if (m == NULL) return NULL; IsalError = PyErr_NewException("igzip_lib.IsalError", NULL, NULL); - Py_XINCREF(IsalError); + if (IsalError == NULL) { + return NULL; + } + Py_INCREF(IsalError); if (PyModule_AddObject(m, "error", IsalError) < 0) { - Py_XDECREF(IsalError); - Py_CLEAR(IsalError); - Py_DECREF(m); return NULL; } - get_igzip_lib_state(m)->IsalError = IsalError; + Py_INCREF(IsalError); + if (PyModule_AddObject(m, "IsalError", IsalError) < 0) { + return NULL; + } if (PyType_Ready(&IgzipDecompressor_Type) != 0) return NULL; diff --git a/src/isal/igzip_lib_impl.h b/src/isal/igzip_lib_impl.h index e64bfac9..c7bb3aa0 100644 --- a/src/isal/igzip_lib_impl.h +++ b/src/isal/igzip_lib_impl.h @@ -20,10 +20,13 @@ #define PY_SSIZE_T_CLEAN #include +#include "structmember.h" // PyMemberDef #include #include +static PyObject *IsalError; + /* Initial buffer size. */ #define DEF_BUF_SIZE (16*1024) #define DEF_MAX_INITIAL_BUF_SIZE (16 * 1024 * 1024) @@ -89,7 +92,7 @@ static int mem_level_to_bufsize(int compression_level, int mem_level, return 0; } -static void isal_deflate_error(int err, PyObject *ErrorClass) +static void isal_deflate_error(int err) { const char * msg = NULL; if (err == COMP_OK) return; @@ -102,10 +105,10 @@ static void isal_deflate_error(int err, PyObject *ErrorClass) else if (err == ISAL_INVALID_LEVEL_BUF) msg = "Level buffer too small."; else msg = "Unknown Error"; - PyErr_Format(ErrorClass, "Error %d %s", err, msg); + PyErr_Format(IsalError, "Error %d %s", err, msg); } -static void isal_inflate_error(int err, PyObject *ErrorClass){ +static void isal_inflate_error(int err){ const char * msg = NULL; if (err == ISAL_DECOMP_OK) return; else if (err == ISAL_END_INPUT) msg = "End of input reached"; @@ -122,7 +125,7 @@ static void isal_inflate_error(int err, PyObject *ErrorClass){ else if (err == ISAL_INCORRECT_CHECKSUM) msg = "Incorrect checksum found"; else msg = "Unknown error"; - PyErr_Format(ErrorClass, "Error %d %s", err, msg); + PyErr_Format(IsalError, "Error %d %s", err, msg); } /** @@ -222,7 +225,7 @@ arrange_output_buffer(uint32_t *avail_out, } static PyObject * -igzip_lib_compress_impl(PyObject *ErrorClass, Py_buffer *data, +igzip_lib_compress_impl(Py_buffer *data, int level, int flag, int mem_level, @@ -233,7 +236,7 @@ igzip_lib_compress_impl(PyObject *ErrorClass, Py_buffer *data, uint8_t *level_buf = NULL; uint32_t level_buf_size; if (mem_level_to_bufsize(level, mem_level, &level_buf_size) != 0){ - PyErr_SetString(ErrorClass, "Invalid memory level or compression level"); + PyErr_SetString(IsalError, "Invalid memory level or compression level"); goto error; } level_buf = (uint8_t *)PyMem_Malloc(level_buf_size); @@ -274,7 +277,7 @@ igzip_lib_compress_impl(PyObject *ErrorClass, Py_buffer *data, err = isal_deflate(&zst); if (err != COMP_OK) { - isal_deflate_error(err, ErrorClass); + isal_deflate_error(err); goto error; } @@ -295,7 +298,7 @@ igzip_lib_compress_impl(PyObject *ErrorClass, Py_buffer *data, } static PyObject * -igzip_lib_decompress_impl(PyObject *ErrorClass, Py_buffer *data, int flag, +igzip_lib_decompress_impl(Py_buffer *data, int flag, int hist_bits, Py_ssize_t bufsize) { PyObject *RetVal = NULL; @@ -332,7 +335,7 @@ igzip_lib_decompress_impl(PyObject *ErrorClass, Py_buffer *data, int flag, err = isal_inflate(&zst); if (err != ISAL_DECOMP_OK) { - isal_inflate_error(err, ErrorClass); + isal_inflate_error(err); goto error; } } while (zst.avail_out == 0); @@ -340,7 +343,7 @@ igzip_lib_decompress_impl(PyObject *ErrorClass, Py_buffer *data, int flag, } while (zst.block_state != ISAL_BLOCK_FINISH && ibuflen != 0); if (zst.block_state != ISAL_BLOCK_FINISH) { - PyErr_SetString(ErrorClass, + PyErr_SetString(IsalError, "incomplete or truncated stream"); goto error; } diff --git a/src/isal/isal_zlib.c b/src/isal/isal_zlib.c index 8bc3132d..fb133d3f 100644 --- a/src/isal/isal_zlib.c +++ b/src/isal/isal_zlib.c @@ -11,16 +11,125 @@ // - All zlib naming changed to isal_zlib // - Including a few constants that are more specific to the ISA-L library // (ISAL_DEFAULT_COMPRESSION etc). +// - Zlib to ISA-L conversion functions were included. +// - All compression and checksum functions from zlib replaced with ISA-L +// compatible functions. +// - No locks in Compress and Decompress objects. These were deemed unnecessary +// as the ISA-L functions do not allocate memory, unlike the zlib +// counterparts. +// - zlib.compress also has a 'wbits' argument. This change was included in +// Python 3.11. It allows for faster gzip compression by using +// isal_zlib.compress(data, wbits=31). -#define PY_SSIZE_T_CLEAN -#include +#include "igzip_lib_impl.h" + +#include + +#define Z_DEFAULT_STRATEGY 0 +#define Z_FILTERED 1 +#define Z_HUFFMAN_ONLY 2 +#define Z_RLE 3 +#define Z_FIXED 4 + +#define Z_DEFLATED 8 + +// Flush modes copied from zlib.h +#define Z_NO_FLUSH 0 +#define Z_PARTIAL_FLUSH 1 +#define Z_SYNC_FLUSH 2 +#define Z_FULL_FLUSH 3 +#define Z_FINISH 4 +#define Z_BLOCK 5 +#define Z_TREES 6 + +#define DEF_MEM_LEVEL 8 + +static PyTypeObject IsalZlibCompType; +static PyTypeObject IsalZlibDecompType; + +static int +wbits_to_flag_and_hist_bits_deflate(int wbits, int *hist_bits, int *flag) +{ + if (wbits >= 9 && wbits <= 15){ + *hist_bits = wbits; + *flag = IGZIP_ZLIB; + } + else if (wbits >= 25 && wbits <= 31) { + *hist_bits = wbits - 16; + *flag = IGZIP_GZIP; + } + else if (wbits >=-15 && wbits <= -9) { + *hist_bits = -wbits; + *flag = IGZIP_DEFLATE; + } + else { + PyErr_Format(IsalError, "Invalid wbits value: %d", wbits); + return -1; + } + return 0; +} + +static int +wbits_to_flag_and_hist_bits_inflate(int wbits, int *hist_bits, int *flag) +{ + if (wbits == 0) { + *hist_bits = 0; + *flag = ISAL_ZLIB; + } + else if (wbits >= 8 && wbits <= 15){ + *hist_bits = wbits; + *flag = ISAL_ZLIB; + } + else if (wbits >= 24 && wbits <= 31) { + *hist_bits = wbits - 16; + *flag = ISAL_GZIP; + } + else if (wbits >=-15 && wbits <= -8) { + *hist_bits = -wbits; + *flag = ISAL_DEFLATE; + } + else if (wbits >=40 && wbits <= 47) { + *hist_bits = wbits - 32; + return 1; + } + else { + PyErr_Format(IsalError, "Invalid wbits value: %d", wbits); + return -1; + } + return 0; +} + +static const int ZLIB_MEM_LEVEL_TO_ISAL[10] = { + 0, // 0 Is an invalid mem_level in zlib, + MEM_LEVEL_MIN, // 1 -> min + MEM_LEVEL_SMALL, // 2-3 -> SMALL + MEM_LEVEL_SMALL, + MEM_LEVEL_MEDIUM, // 4-6 -> MEDIUM + MEM_LEVEL_MEDIUM, + MEM_LEVEL_MEDIUM, + MEM_LEVEL_LARGE, // 7-8 LARGE. The zlib module default = 8. Large is the ISA-L default value. + MEM_LEVEL_LARGE, + MEM_LEVEL_EXTRA_LARGE, // 9 -> EXTRA_LARGE. +}; + + +static int zlib_mem_level_to_isal(int mem_level) { + if (mem_level < 1 || mem_level > 9) { + PyErr_Format(PyExc_ValueError, + "Invalid mem level: %d. Mem level should be between 1 and 9"); + return -1;} + return ZLIB_MEM_LEVEL_TO_ISAL[mem_level]; +} + +static int +data_is_gzip(Py_buffer *data){ + if (data->len < 2) + return 0; + uint8_t *buf = (uint8_t *)data->buf; + return (buf[0] == 31 && buf[1] == 139); +} -#include "isal_zlib_impl.h" -#include "structmember.h" -#ifndef _PyArg_CheckPositional -#include "python_args.h" -#endif PyDoc_STRVAR(isal_zlib_adler32__doc__, "adler32($module, data, value=1, /)\n" @@ -43,31 +152,26 @@ isal_zlib_adler32(PyObject *module, PyObject *const *args, Py_ssize_t nargs) Py_buffer data = {NULL, NULL}; uint32_t value = 1; - if (!_PyArg_CheckPositional("adler32", nargs, 1, 2)) { - goto exit; + if (nargs < 1 || nargs > 2) { + PyErr_Format( + PyExc_TypeError, + "adler32 takes exactly 1 or 2 arguments, got %d", + nargs); + return NULL; } if (PyObject_GetBuffer(args[0], &data, PyBUF_SIMPLE) != 0) { - goto exit; - } - if (!PyBuffer_IsContiguous(&data, 'C')) { - _PyArg_BadArgument("adler32", "argument 1", "contiguous buffer", args[0]); - goto exit; - } - if (nargs < 2) { - goto skip_optional; - } - value = (uint32_t)PyLong_AsUnsignedLongMask(args[1]); - if (value == (uint32_t)-1 && PyErr_Occurred()) { - goto exit; + return NULL; } -skip_optional: - return_value = isal_zlib_adler32_impl(module, &data, value); - -exit: - /* Cleanup for data */ - if (data.obj) { - PyBuffer_Release(&data); + if (nargs > 1) { + value = (uint32_t)PyLong_AsUnsignedLongMask(args[1]); + if (value == (uint32_t)-1 && PyErr_Occurred()) { + PyBuffer_Release(&data); + return NULL; + } } + value = isal_adler32(value, data.buf, (uint64_t)data.len); + return_value = PyLong_FromUnsignedLong(value & 0xffffffffU); + PyBuffer_Release(&data); return return_value; } @@ -92,31 +196,26 @@ isal_zlib_crc32(PyObject *module, PyObject *const *args, Py_ssize_t nargs) Py_buffer data = {NULL, NULL}; uint32_t value = 0; - if (!_PyArg_CheckPositional("crc32", nargs, 1, 2)) { - goto exit; + if (nargs < 1 || nargs > 2) { + PyErr_Format( + PyExc_TypeError, + "crc32 takes exactly 1 or 2 arguments, got %d", + nargs); + return NULL; } if (PyObject_GetBuffer(args[0], &data, PyBUF_SIMPLE) != 0) { - goto exit; - } - if (!PyBuffer_IsContiguous(&data, 'C')) { - _PyArg_BadArgument("crc32", "argument 1", "contiguous buffer", args[0]); - goto exit; - } - if (nargs < 2) { - goto skip_optional; - } - value = (uint32_t)PyLong_AsUnsignedLongMask(args[1]); - if (value == (uint32_t)-1 && PyErr_Occurred()) { - goto exit; + return NULL; } -skip_optional: - return_value = isal_zlib_crc32_impl(module, &data, value); - -exit: - /* Cleanup for data */ - if (data.obj) { - PyBuffer_Release(&data); + if (nargs > 1) { + value = (uint32_t)PyLong_AsUnsignedLongMask(args[1]); + if (value == (uint32_t)-1 && PyErr_Occurred()) { + PyBuffer_Release(&data); + return NULL; + } } + value = crc32_gzip_refl(value, data.buf, (uint64_t)data.len); + return_value = PyLong_FromUnsignedLong(value & 0xffffffffU); + PyBuffer_Release(&data); return return_value; } PyDoc_STRVAR(zlib_compress__doc__, @@ -133,57 +232,32 @@ PyDoc_STRVAR(zlib_compress__doc__, " The window buffer size and container format."); #define ISAL_ZLIB_COMPRESS_METHODDEF \ - {"compress", (PyCFunction)(void(*)(void))isal_zlib_compress, METH_FASTCALL|METH_KEYWORDS, zlib_compress__doc__} + {"compress", (PyCFunction)(void(*)(void))isal_zlib_compress, METH_VARARGS|METH_KEYWORDS, zlib_compress__doc__} static PyObject * -isal_zlib_compress(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +isal_zlib_compress(PyObject *module, PyObject *args, PyObject *kwargs) { - PyObject *return_value = NULL; - static const char * const _keywords[] = {"", "level", "wbits", NULL}; - static _PyArg_Parser _parser = {NULL, _keywords, "compress", 0}; - PyObject *argsbuf[3]; - Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; - PyObject *ErrorClass = get_isal_zlib_state(module)->IsalError; + char *keywords[] = {"", "level", "wbits", NULL}; + char *format ="y*|ii:isal_zlib.compress"; Py_buffer data = {NULL, NULL}; int level = ISAL_DEFAULT_COMPRESSION; int wbits = ISAL_DEF_MAX_HIST_BITS; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 3, 0, argsbuf); - if (!args) { - goto exit; - } - if (PyObject_GetBuffer(args[0], &data, PyBUF_SIMPLE) != 0) { - goto exit; - } - if (!PyBuffer_IsContiguous(&data, 'C')) { - _PyArg_BadArgument("compress", "argument 1", "contiguous buffer", args[0]); - goto exit; - } - if (!noptargs) { - goto skip_optional_pos; - } - if (args[1]) { - level = _PyLong_AsInt(args[1]); - if (level == -1 && PyErr_Occurred()) { - goto exit; - } - if (!--noptargs) { - goto skip_optional_pos; - } - } - wbits = _PyLong_AsInt(args[2]); - if (wbits == -1 && PyErr_Occurred()) { - goto exit; + if (!PyArg_ParseTupleAndKeywords( + args, kwargs, format, keywords, &data, &level, &wbits)) { + return NULL; } -skip_optional_pos: - return_value = isal_zlib_compress_impl(ErrorClass, &data, level, wbits); -exit: - /* Cleanup for data */ - if (data.obj) { - PyBuffer_Release(&data); - } + int hist_bits = -1; + int flag = -1; + if (wbits_to_flag_and_hist_bits_deflate(wbits, &hist_bits, &flag) != 0) { + PyBuffer_Release(&data); + return NULL; + } + PyObject *return_value = igzip_lib_compress_impl( + &data, level, flag, MEM_LEVEL_DEFAULT, hist_bits); + PyBuffer_Release(&data); return return_value; } @@ -201,67 +275,563 @@ PyDoc_STRVAR(zlib_decompress__doc__, " The initial output buffer size."); #define ISAL_ZLIB_DECOMPRESS_METHODDEF \ - {"decompress", (PyCFunction)(void(*)(void))isal_zlib_decompress, METH_FASTCALL|METH_KEYWORDS, zlib_decompress__doc__} + {"decompress", (PyCFunction)(void(*)(void))isal_zlib_decompress, METH_VARARGS|METH_KEYWORDS, zlib_decompress__doc__} static PyObject * -isal_zlib_decompress(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +isal_zlib_decompress(PyObject *module, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; - static const char * const _keywords[] = {"", "wbits", "bufsize", NULL}; - static _PyArg_Parser _parser = {NULL, _keywords, "decompress", 0}; - PyObject *argsbuf[3]; - Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1; - PyObject *ErrorClass = get_isal_zlib_state(module)->IsalError; + char *keywords[] = {"", "wbits", "bufsize", NULL}; + char *format ="y*|in:isal_zlib.decompress"; Py_buffer data = {NULL, NULL}; int wbits = ISAL_DEF_MAX_HIST_BITS; Py_ssize_t bufsize = DEF_BUF_SIZE; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 3, 0, argsbuf); - if (!args) { - goto exit; + if (!PyArg_ParseTupleAndKeywords( + args, kwargs, format, keywords, &data, &wbits, &bufsize)) { + return NULL; } - if (PyObject_GetBuffer(args[0], &data, PyBUF_SIMPLE) != 0) { - goto exit; + int hist_bits; + int flag; + + int convert_result = wbits_to_flag_and_hist_bits_inflate(wbits, &hist_bits, &flag); + if (convert_result < 0) { + PyBuffer_Release(&data); + return NULL; } - if (!PyBuffer_IsContiguous(&data, 'C')) { - _PyArg_BadArgument("decompress", "argument 1", "contiguous buffer", args[0]); - goto exit; + if (convert_result > 0) { + if (data_is_gzip(&data)) + flag = ISAL_GZIP; + else + flag = ISAL_ZLIB; } - if (!noptargs) { - goto skip_optional_pos; + return_value = igzip_lib_decompress_impl(&data, flag, hist_bits, bufsize); + PyBuffer_Release(&data); + return return_value; +} + +typedef struct +{ + PyObject_HEAD + struct isal_zstream zst; + int is_initialised; + uint8_t * level_buf; + PyObject *zdict; +} compobject; + +static void +Comp_dealloc(compobject *self) +{ + if (self->is_initialised && self->level_buf != NULL) + PyMem_Free(self->level_buf); + Py_XDECREF(self->zdict); + Py_TYPE(self)->tp_free((PyObject *)self); +} + +static compobject * +newcompobject() +{ + compobject *self; + self = PyObject_New(compobject, &IsalZlibCompType); + if (self == NULL) + return NULL; + self->is_initialised = 0; + self->zdict = NULL; + self->level_buf = NULL; + return self; +} + + +typedef struct +{ + PyObject_HEAD + struct inflate_state zst; + PyObject *unused_data; + PyObject *unconsumed_tail; + char eof; + int is_initialised; + int method_set; + PyObject *zdict; +} decompobject; + +static void +Decomp_dealloc(decompobject *self) +{ + Py_XDECREF(self->unused_data); + Py_XDECREF(self->unconsumed_tail); + Py_XDECREF(self->zdict); + Py_TYPE(self)->tp_free((PyObject *)self); +} + +static int +set_inflate_zdict(decompobject *self) +{ + Py_buffer zdict_buf; + int err; + + if (PyObject_GetBuffer(self->zdict, &zdict_buf, PyBUF_SIMPLE) == -1) { + return -1; + } + if ((size_t)zdict_buf.len > UINT32_MAX) { + PyErr_SetString(PyExc_OverflowError, + "zdict length does not fit in an unsigned 32-bits int"); + PyBuffer_Release(&zdict_buf); + return -1; + } + err = isal_inflate_set_dict(&(self->zst), + zdict_buf.buf, (uint32_t)zdict_buf.len); + PyBuffer_Release(&zdict_buf); + if (err != ISAL_DECOMP_OK) { + isal_inflate_error(err); + return -1; + } + return 0; +} + +static decompobject * +newdecompobject() +{ + decompobject *self; + self = PyObject_New(decompobject, &IsalZlibDecompType); + if (self == NULL) + return NULL; + self->eof = 0; + self->is_initialised = 0; + self->method_set = 0; + self->zdict = NULL; + self->unused_data = PyBytes_FromStringAndSize("", 0); + if (self->unused_data == NULL) { + Py_DECREF(self); + return NULL; + } + self->unconsumed_tail = PyBytes_FromStringAndSize("", 0); + if (self->unconsumed_tail == NULL) { + Py_DECREF(self); + return NULL; + } + return self; +} + +static PyObject * +isal_zlib_compressobj_impl(PyObject *module, int level, int method, int wbits, + int memLevel, int strategy, Py_buffer *zdict) +{ + compobject *self = NULL; + int err; + uint32_t level_buf_size = 0; + int flag = -1; + int hist_bits = -1; + + if (method != Z_DEFLATED){ + PyErr_Format(PyExc_ValueError, + "Unsupported method: %d. Only DEFLATED is supported.", + method); + goto error; + } + if (strategy != Z_DEFAULT_STRATEGY){ + err = PyErr_WarnEx( + PyExc_UserWarning, + "Only one strategy is supported when using isal_zlib. Using the default strategy.", + 1); + if (err == -1) + // Warning was turned into an exception. + goto error; + } + if (zdict->buf != NULL && (size_t)zdict->len > UINT32_MAX) { + PyErr_SetString(PyExc_OverflowError, + "zdict length does not fit in an unsigned 32-bit int"); + goto error; + } + int isal_mem_level = zlib_mem_level_to_isal(memLevel); + if (isal_mem_level == -1) + goto error; + if (wbits_to_flag_and_hist_bits_deflate(wbits, &hist_bits, &flag) == -1) { + PyErr_Format(PyExc_ValueError, "Invalid wbits value: %d", wbits); + goto error; + } + if (mem_level_to_bufsize( + level, isal_mem_level, &level_buf_size) == -1) { + PyErr_Format(PyExc_ValueError, + "Invalid compression level: %d. Compression level should be between 0 and 3", + level); + goto error; + } + + self = newcompobject(); + if (self == NULL) + goto error; + self->level_buf = (uint8_t *)PyMem_Malloc(level_buf_size); + if (self->level_buf == NULL){ + PyErr_NoMemory(); + goto error; } - if (args[1]) { - wbits = _PyLong_AsInt(args[1]); - if (wbits == -1 && PyErr_Occurred()) { - goto exit; + isal_deflate_init(&(self->zst)); + self->zst.next_in = NULL; + self->zst.avail_in = 0; + self->zst.level_buf_size = level_buf_size; + self->zst.level_buf = self->level_buf; + self->zst.level = level; + self->zst.hist_bits = (uint16_t)hist_bits; + self->zst.gzip_flag = (uint16_t)flag; + + self->is_initialised = 1; + if (zdict->buf == NULL) { + goto success; + } else { + err = isal_deflate_set_dict(&(self->zst), + zdict->buf, (uint32_t)zdict->len); + if (err == COMP_OK) + goto success; + PyErr_SetString(PyExc_ValueError, "Invalid dictionary"); + goto error; + } + error: + if (self != NULL) { + if (self->level_buf != NULL) + PyMem_Free(self->level_buf); + Py_CLEAR(self); + } + + success: + return (PyObject *)self; +} + + +static PyObject * +isal_zlib_decompressobj_impl(PyObject *module, int wbits, PyObject *zdict) +{ + int err; + decompobject *self; + int flag; + int hist_bits; + if (zdict != NULL && !PyObject_CheckBuffer(zdict)) { + PyErr_SetString(PyExc_TypeError, + "zdict argument must support the buffer protocol"); + return NULL; + } + self = newdecompobject(); + if (self == NULL) + return NULL; + + isal_inflate_init(&(self->zst)); + err = wbits_to_flag_and_hist_bits_inflate(wbits, &hist_bits, &flag); + if (err < 0) { + PyErr_Format(PyExc_ValueError, "Invalid wbits value: %d", wbits); + return NULL; + } + else if (err == 0) { + self->zst.crc_flag = flag; + self->method_set = 1; + } + self->zst.hist_bits = hist_bits; + self->zst.next_in = NULL; + self->zst.avail_in = 0; + if (zdict != NULL) { + Py_INCREF(zdict); + self->zdict = zdict; + } + self->is_initialised = 1; + + if (self->zdict != NULL) { + if (set_inflate_zdict(self) < 0) { + Py_DECREF(self); + return NULL; + } + } + return (PyObject *)self; +} + +static PyObject * +isal_zlib_Compress_compress_impl(compobject *self, Py_buffer *data) +/*[clinic end generated code: output=5d5cd791cbc6a7f4 input=0d95908d6e64fab8]*/ +{ + PyObject *RetVal = NULL; + Py_ssize_t ibuflen, obuflen = DEF_BUF_SIZE; + int err; + + self->zst.next_in = data->buf; + ibuflen = data->len; + + do { + arrange_input_buffer(&(self->zst.avail_in), &ibuflen); + do { + obuflen = arrange_output_buffer(&(self->zst.avail_out), + &(self->zst.next_out), &RetVal, obuflen); + if (obuflen < 0) + goto error; + + err = isal_deflate(&self->zst); + + if (err != COMP_OK) { + isal_deflate_error(err); + goto error; + } + } while (self->zst.avail_out == 0); + assert(self->zst.avail_in == 0); + + } while (ibuflen != 0); + + if (_PyBytes_Resize(&RetVal, self->zst.next_out - + (uint8_t *)PyBytes_AS_STRING(RetVal)) == 0) + goto success; + + error: + Py_CLEAR(RetVal); + success: + return RetVal; +} + +/* Helper for objdecompress() and flush(). Saves any unconsumed input data in + self->unused_data or self->unconsumed_tail, as appropriate. */ +static int +save_unconsumed_input(decompobject *self, Py_buffer *data, int err) +{ + if (self->zst.block_state == ISAL_BLOCK_FINISH) { + /* The end of the compressed data has been reached. Store the leftover + input data in self->unused_data. */ + if (self->zst.avail_in > 0) { + Py_ssize_t old_size = PyBytes_GET_SIZE(self->unused_data); + Py_ssize_t new_size, left_size; + PyObject *new_data; + Py_ssize_t bytes_in_bitbuffer = bitbuffer_size(&(self->zst)); + left_size = (uint8_t *)data->buf + data->len - self->zst.next_in; + if (left_size + bytes_in_bitbuffer > (PY_SSIZE_T_MAX - old_size)) { + PyErr_NoMemory(); + return -1; + } + // There might also be data left in the bit_buffer. + new_size = old_size + left_size + bytes_in_bitbuffer; + new_data = PyBytes_FromStringAndSize(NULL, new_size); + if (new_data == NULL) + return -1; + char * new_data_ptr = PyBytes_AS_STRING(new_data); + memcpy(new_data_ptr, + PyBytes_AS_STRING(self->unused_data), old_size); + bitbuffer_copy(&(self->zst), new_data_ptr + old_size, bytes_in_bitbuffer); + memcpy(new_data_ptr + old_size + bytes_in_bitbuffer, + self->zst.next_in, left_size); + Py_SETREF(self->unused_data, new_data); + self->zst.avail_in = 0; + } + } + + if (self->zst.avail_in > 0 || PyBytes_GET_SIZE(self->unconsumed_tail)) { + /* This code handles two distinct cases: + 1. Output limit was reached. Save leftover input in unconsumed_tail. + 2. All input data was consumed. Clear unconsumed_tail. */ + Py_ssize_t left_size = (uint8_t *)data->buf + data->len - self->zst.next_in; + PyObject *new_data = PyBytes_FromStringAndSize( + (char *)self->zst.next_in, left_size); + if (new_data == NULL) + return -1; + Py_SETREF(self->unconsumed_tail, new_data); + } + + return 0; +} + +static PyObject * +isal_zlib_Decompress_decompress_impl(decompobject *self, Py_buffer *data, + Py_ssize_t max_length) +{ + int err = ISAL_DECOMP_OK; + Py_ssize_t ibuflen, obuflen = DEF_BUF_SIZE, hard_limit; + PyObject *RetVal = NULL; + + if (max_length < 0) { + PyErr_SetString(PyExc_ValueError, "max_length must be non-negative"); + return NULL; + } else if (max_length == 0) + hard_limit = PY_SSIZE_T_MAX; + else + hard_limit = max_length; + + if (!self->method_set) { + if (data_is_gzip(data)){ + self->zst.crc_flag = ISAL_GZIP; } - if (!--noptargs) { - goto skip_optional_pos; + else { + self->zst.crc_flag = ISAL_ZLIB; } + self->method_set = 1; + } + self->zst.next_in = data->buf; + ibuflen = data->len; + + /* limit amount of data allocated to max_length */ + if (max_length && obuflen > max_length) + obuflen = max_length; + + do { + arrange_input_buffer(&(self->zst.avail_in), &ibuflen); + + do { + obuflen = arrange_output_buffer_with_maximum(&(self->zst.avail_out), + &(self->zst.next_out), + &RetVal, + obuflen, hard_limit); + if (obuflen == -2) { + if (max_length > 0) { + goto save; + } + PyErr_NoMemory(); + } + if (obuflen < 0) { + goto abort; + } + + err = isal_inflate(&self->zst); + if (err != ISAL_DECOMP_OK){ + isal_inflate_error(err); + goto abort; + } + + } while (self->zst.avail_out == 0 && self->zst.block_state != ISAL_BLOCK_FINISH); + + } while (self->zst.block_state != ISAL_BLOCK_FINISH && ibuflen != 0); + + save: + if (save_unconsumed_input(self, data, err) < 0) + goto abort; + + if (self->zst.block_state == ISAL_BLOCK_FINISH) { + self->eof = 1; + } + + if (_PyBytes_Resize(&RetVal, self->zst.next_out - + (uint8_t *)PyBytes_AS_STRING(RetVal)) == 0) + goto success; + + abort: + Py_CLEAR(RetVal); + success: + return RetVal; +} + +static PyObject * +isal_zlib_Compress_flush_impl(compobject *self, int mode) +{ + int err; + Py_ssize_t length = DEF_BUF_SIZE; + PyObject *RetVal = NULL; + + /* Flushing with Z_NO_FLUSH is a no-op, so there's no point in + doing any work at all; just return an empty string. */ + if (mode == Z_NO_FLUSH) { + return PyBytes_FromStringAndSize(NULL, 0); + } else if (mode == Z_FINISH) { + self->zst.flush = FULL_FLUSH; + self->zst.end_of_stream = 1; + } else if (mode == Z_FULL_FLUSH){ + self->zst.flush = FULL_FLUSH; + } else if (mode == Z_SYNC_FLUSH) { + self->zst.flush = SYNC_FLUSH; + } else { + PyErr_Format(IsalError, + "Unsupported flush mode: %d", mode); + return NULL; } - { - Py_ssize_t ival = -1; - PyObject *iobj = PyNumber_Index(args[2]); - if (iobj != NULL) { - ival = PyLong_AsSsize_t(iobj); - Py_DECREF(iobj); + + self->zst.avail_in = 0; + + do { + length = arrange_output_buffer(&(self->zst.avail_out), + &(self->zst.next_out), &RetVal, length); + if (length < 0) { + Py_CLEAR(RetVal); + goto error; } - if (ival == -1 && PyErr_Occurred()) { - goto exit; + + err = isal_deflate(&self->zst); + + if (err != COMP_OK) { + isal_deflate_error(err); + Py_CLEAR(RetVal); + goto error; } - bufsize = ival; + } while (self->zst.avail_out == 0); + assert(self->zst.avail_in == 0); + + /* If mode is Z_FINISH, we free the level buffer. + Note we should only get ZSTATE_END when + mode is Z_FINISH, but checking both for safety*/ + if (self->zst.internal_state.state == ZSTATE_END && mode == Z_FINISH) { + PyMem_FREE(self->level_buf); + self->zst.level_buf_size = 0; + self->zst.level_buf = NULL; + self->is_initialised = 0; + } else { + // reset the flush mode back so compressobject can be used again. + self->zst.flush = NO_FLUSH; } -skip_optional_pos: - return_value = isal_zlib_decompress_impl(ErrorClass, &data, wbits, bufsize); -exit: - /* Cleanup for data */ - if (data.obj) { - PyBuffer_Release(&data); + if (_PyBytes_Resize(&RetVal, self->zst.next_out - + (uint8_t *)PyBytes_AS_STRING(RetVal)) < 0) + Py_CLEAR(RetVal); + + error: + return RetVal; +} + +static PyObject * +isal_zlib_Decompress_flush_impl(decompobject *self, Py_ssize_t length) +{ + int err; + Py_buffer data; + PyObject *RetVal = NULL; + Py_ssize_t ibuflen; + + if (length <= 0) { + PyErr_SetString(PyExc_ValueError, "length must be greater than zero"); + return NULL; } - return return_value; + if (PyObject_GetBuffer(self->unconsumed_tail, &data, PyBUF_SIMPLE) == -1) { + return NULL; + } + + self->zst.next_in = data.buf; + ibuflen = data.len; + + do { + arrange_input_buffer(&(self->zst.avail_in), &ibuflen); + + do { + length = arrange_output_buffer(&(self->zst.avail_out), + &(self->zst.next_out), &RetVal, length); + if (length < 0) + goto abort; + + err = isal_inflate(&self->zst); + + if (err != ISAL_DECOMP_OK) { + isal_inflate_error(err); + goto abort; + } + + } while (self->zst.avail_out == 0 && self->zst.block_state != ISAL_BLOCK_FINISH); + + } while (self->zst.block_state != ISAL_BLOCK_FINISH && ibuflen != 0); + + if (save_unconsumed_input(self, &data, err) < 0) + goto abort; + + /* If at end of stream, clean up any memory allocated by zlib. */ + if (self->zst.block_state == ISAL_BLOCK_FINISH) { + self->eof = 1; + self->is_initialised = 0; + } + + if (_PyBytes_Resize(&RetVal, self->zst.next_out - + (uint8_t *)PyBytes_AS_STRING(RetVal)) == 0) + goto success; + + abort: + Py_CLEAR(RetVal); + success: + PyBuffer_Release(&data); + return RetVal; } PyDoc_STRVAR(isal_zlib_compressobj__doc__, @@ -295,16 +865,14 @@ PyDoc_STRVAR(isal_zlib_compressobj__doc__, " containing subsequences that are likely to occur in the input data."); #define ISAL_ZLIB_COMPRESSOBJ_METHODDEF \ - {"compressobj", (PyCFunction)(void(*)(void))isal_zlib_compressobj, METH_FASTCALL|METH_KEYWORDS, isal_zlib_compressobj__doc__} + {"compressobj", (PyCFunction)(void(*)(void))isal_zlib_compressobj, METH_VARARGS|METH_KEYWORDS, isal_zlib_compressobj__doc__} static PyObject * -isal_zlib_compressobj(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +isal_zlib_compressobj(PyObject *module, PyObject *args, PyObject *kwargs) { PyObject *return_value = NULL; - static const char * const _keywords[] = {"level", "method", "wbits", "memLevel", "strategy", "zdict", NULL}; - static _PyArg_Parser _parser = {NULL, _keywords, "compressobj", 0}; - PyObject *argsbuf[6]; - Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + char *keywords[] = {"level", "method", "wbits", "memLevel", "strategy", "zdict", NULL}; + char *format = "|iiiiiy*:compressobj"; int level = ISAL_DEFAULT_COMPRESSION; int method = Z_DEFLATED; int wbits = ISAL_DEF_MAX_HIST_BITS; @@ -312,74 +880,13 @@ isal_zlib_compressobj(PyObject *module, PyObject *const *args, Py_ssize_t nargs, int strategy = Z_DEFAULT_STRATEGY; Py_buffer zdict = {NULL, NULL}; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 6, 0, argsbuf); - if (!args) { - goto exit; - } - if (!noptargs) { - goto skip_optional_pos; - } - if (args[0]) { - level = _PyLong_AsInt(args[0]); - if (level == -1 && PyErr_Occurred()) { - goto exit; - } - if (!--noptargs) { - goto skip_optional_pos; - } - } - if (args[1]) { - method = _PyLong_AsInt(args[1]); - if (method == -1 && PyErr_Occurred()) { - goto exit; - } - if (!--noptargs) { - goto skip_optional_pos; - } - } - if (args[2]) { - wbits = _PyLong_AsInt(args[2]); - if (wbits == -1 && PyErr_Occurred()) { - goto exit; - } - if (!--noptargs) { - goto skip_optional_pos; - } - } - if (args[3]) { - memLevel = _PyLong_AsInt(args[3]); - if (memLevel == -1 && PyErr_Occurred()) { - goto exit; - } - if (!--noptargs) { - goto skip_optional_pos; - } - } - if (args[4]) { - strategy = _PyLong_AsInt(args[4]); - if (strategy == -1 && PyErr_Occurred()) { - goto exit; - } - if (!--noptargs) { - goto skip_optional_pos; - } - } - if (PyObject_GetBuffer(args[5], &zdict, PyBUF_SIMPLE) != 0) { - goto exit; - } - if (!PyBuffer_IsContiguous(&zdict, 'C')) { - _PyArg_BadArgument("compressobj", "argument 'zdict'", "contiguous buffer", args[5]); - goto exit; + if (!PyArg_ParseTupleAndKeywords( + args, kwargs, format, keywords, + &level, &method, &wbits, &memLevel, &strategy, &zdict)) { + return NULL; } -skip_optional_pos: return_value = isal_zlib_compressobj_impl(module, level, method, wbits, memLevel, strategy, &zdict); - -exit: - /* Cleanup for zdict */ - if (zdict.obj) { - PyBuffer_Release(&zdict); - } - + PyBuffer_Release(&zdict); return return_value; } @@ -396,41 +903,22 @@ PyDoc_STRVAR(isal_zlib_decompressobj__doc__, " dictionary as used by the compressor that produced the input data."); #define ISAL_ZLIB_DECOMPRESSOBJ_METHODDEF \ - {"decompressobj", (PyCFunction)(void(*)(void))isal_zlib_decompressobj, METH_FASTCALL|METH_KEYWORDS, isal_zlib_decompressobj__doc__} + {"decompressobj", (PyCFunction)(void(*)(void))isal_zlib_decompressobj, METH_VARARGS|METH_KEYWORDS, isal_zlib_decompressobj__doc__} static PyObject * -isal_zlib_decompressobj(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +isal_zlib_decompressobj(PyObject *module, PyObject *args, PyObject *kwargs) { - PyObject *return_value = NULL; - static const char * const _keywords[] = {"wbits", "zdict", NULL}; - static _PyArg_Parser _parser = {NULL, _keywords, "decompressobj", 0}; - PyObject *argsbuf[2]; - Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0; + char *keywords[] = {"wbits", "zdict", NULL}; + char *format = "|iO:decompressobj"; int wbits = ISAL_DEF_MAX_HIST_BITS; PyObject *zdict = NULL; - args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 2, 0, argsbuf); - if (!args) { - goto exit; - } - if (!noptargs) { - goto skip_optional_pos; - } - if (args[0]) { - wbits = _PyLong_AsInt(args[0]); - if (wbits == -1 && PyErr_Occurred()) { - goto exit; - } - if (!--noptargs) { - goto skip_optional_pos; - } + if (!PyArg_ParseTupleAndKeywords( + args, kwargs, format, keywords, + &wbits, &zdict)) { + return NULL; } - zdict = args[1]; -skip_optional_pos: - return_value = isal_zlib_decompressobj_impl(module, wbits, zdict); - -exit: - return return_value; + return isal_zlib_decompressobj_impl(module, wbits, zdict); } PyDoc_STRVAR(isal_zlib_Compress_compress__doc__, @@ -447,29 +935,18 @@ PyDoc_STRVAR(isal_zlib_Compress_compress__doc__, "Call the flush() method to clear these buffers."); #define ISAL_ZLIB_COMPRESS_COMPRESS_METHODDEF \ - {"compress", (PyCFunction)(void(*)(void))isal_zlib_Compress_compress, METH_FASTCALL|METH_KEYWORDS, isal_zlib_Compress_compress__doc__} + {"compress", (PyCFunction)(void(*)(void))isal_zlib_Compress_compress, METH_O, isal_zlib_Compress_compress__doc__} static PyObject * -isal_zlib_Compress_compress(compobject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +isal_zlib_Compress_compress(compobject *self, PyObject *data) { - PyObject *return_value = NULL; - static const char * const _keywords[] = {"", NULL}; - static _PyArg_Parser _parser = {"y*:compress", _keywords, 0}; - Py_buffer data = {NULL, NULL}; - - if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, - &data)) { - goto exit; - } - return_value =isal_zlib_Compress_compress_impl(self, &data); - -exit: - /* Cleanup for data */ - if (data.obj) { - PyBuffer_Release(&data); + Py_buffer data_buf; + if (PyObject_GetBuffer(data, &data_buf, PyBUF_SIMPLE) < 0) { + return NULL; } - + PyObject *return_value = isal_zlib_Compress_compress_impl(self, &data_buf); + PyBuffer_Release(&data_buf); return return_value; } @@ -491,30 +968,23 @@ PyDoc_STRVAR(isal_zlib_Decompress_decompress__doc__, "Call the flush() method to clear these buffers."); #define ISAL_ZLIB_DECOMPRESS_DECOMPRESS_METHODDEF \ - {"decompress", (PyCFunction)(void(*)(void))isal_zlib_Decompress_decompress, METH_FASTCALL|METH_KEYWORDS, isal_zlib_Decompress_decompress__doc__} + {"decompress", (PyCFunction)(void(*)(void))isal_zlib_Decompress_decompress, METH_VARARGS|METH_KEYWORDS, isal_zlib_Decompress_decompress__doc__} static PyObject * -isal_zlib_Decompress_decompress(decompobject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +isal_zlib_Decompress_decompress(decompobject *self, PyObject *args, PyObject *kwargs) { - PyObject *return_value = NULL; - static const char * const _keywords[] = {"", "max_length", NULL}; - static _PyArg_Parser _parser = {"y*|n:decompress", _keywords, 0}; + char *keywords[] = {"", "max_length", NULL}; + char *format = "y*|n:decompress"; + Py_buffer data = {NULL, NULL}; Py_ssize_t max_length = 0; - - if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, - &data, &max_length)) { - goto exit; - } - return_value = isal_zlib_Decompress_decompress_impl(self, &data, max_length); - -exit: - /* Cleanup for data */ - if (data.obj) { - PyBuffer_Release(&data); + if (!PyArg_ParseTupleAndKeywords( + args, kwargs, format, keywords, &data, &max_length)) { + return NULL; } - + PyObject *return_value = isal_zlib_Decompress_decompress_impl(self, &data, max_length); + PyBuffer_Release(&data); return return_value; } @@ -537,19 +1007,31 @@ PyDoc_STRVAR(isal_zlib_Compress_flush__doc__, static PyObject * isal_zlib_Compress_flush(compobject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) { - PyObject *return_value = NULL; - static const char * const _keywords[] = {"", NULL}; - static _PyArg_Parser _parser = {"|i:flush", _keywords, 0}; - int mode = Z_FINISH; - - if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, - &mode)) { - goto exit; + Py_ssize_t mode; + if (nargs == 0) { + mode = Z_FINISH; } - return_value = isal_zlib_Compress_flush_impl(self, mode); - -exit: - return return_value; + else if (nargs == 1) { + PyObject *mode_arg = args[0]; + if (PyLong_Check(mode_arg)) { + mode = PyLong_AsSsize_t(mode_arg); + } + else { + mode = PyNumber_AsSsize_t(mode_arg, PyExc_OverflowError); + } + if (mode == -1 && PyErr_Occurred()) { + return NULL; + } + } + else { + PyErr_Format( + PyExc_TypeError, + "flush() only takes 0 or 1 positional arguments got %d", + nargs + ); + return NULL; + } + return isal_zlib_Compress_flush_impl(self, mode); } PyDoc_STRVAR(isal_zlib_Decompress_flush__doc__, "flush($self, length=zlib.DEF_BUF_SIZE, /)\n" @@ -562,24 +1044,36 @@ PyDoc_STRVAR(isal_zlib_Decompress_flush__doc__, #define ISAL_ZLIB_DECOMPRESS_FLUSH_METHODDEF \ - {"flush", (PyCFunction)(void(*)(void))isal_zlib_Decompress_flush, METH_FASTCALL|METH_KEYWORDS, isal_zlib_Decompress_flush__doc__} + {"flush", (PyCFunction)(void(*)(void))isal_zlib_Decompress_flush, METH_FASTCALL, isal_zlib_Decompress_flush__doc__} static PyObject * -isal_zlib_Decompress_flush(decompobject *self, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames) +isal_zlib_Decompress_flush(decompobject *self, PyObject *const *args, Py_ssize_t nargs) { - PyObject *return_value = NULL; - static const char * const _keywords[] = {"", NULL}; - static _PyArg_Parser _parser = {"|n:flush", _keywords, 0}; - Py_ssize_t length = DEF_BUF_SIZE; - - if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser, - &length)) { - goto exit; + Py_ssize_t length; + if (nargs == 0) { + length = DEF_BUF_SIZE; } - return_value = isal_zlib_Decompress_flush_impl(self, length); - -exit: - return return_value; + else if (nargs == 1) { + PyObject *length_arg = args[0]; + if (PyLong_Check(length_arg)) { + length = PyLong_AsSsize_t(length_arg); + } + else { + length = PyNumber_AsSsize_t(length_arg, PyExc_OverflowError); + } + if (length == -1 && PyErr_Occurred()) { + return NULL; + } + } + else { + PyErr_Format( + PyExc_TypeError, + "flush() only takes 0 or 1 positional arguments got %d", + nargs + ); + return NULL; + } + return isal_zlib_Decompress_flush_impl(self, length); } @@ -611,19 +1105,13 @@ static PyMethodDef Decomp_methods[] = {NULL, NULL} }; -static PyType_Slot Comptype_slots[] = { - {Py_tp_dealloc, Comp_dealloc}, - {Py_tp_methods, comp_methods}, - {Py_tp_doc, "Object returned by isal_zlib.compressobj."}, - {0, 0}, -}; - -static PyType_Spec Comptype_spec = { - "isal_zlib.Compress", - sizeof(compobject), - 0, - Py_TPFLAGS_DEFAULT, - Comptype_slots +static PyTypeObject IsalZlibCompType = { + .tp_name = "isal_zlib.Compress", + .tp_doc = "Object returned by isal_zlib.compressobj", + .tp_basicsize = sizeof(compobject), + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_dealloc = (destructor)Comp_dealloc, + .tp_methods = comp_methods, }; #define COMP_OFF(x) offsetof(decompobject, x) @@ -645,23 +1133,16 @@ static PyMemberDef Decomp_members[] = { {NULL}, }; -static PyType_Slot Decomptype_slots[] = { - {Py_tp_dealloc, Decomp_dealloc}, - {Py_tp_methods, Decomp_methods}, - {Py_tp_members, Decomp_members}, - {Py_tp_doc, "Object returned by isal_zlib.compressobj."}, - {0, 0}, +static PyTypeObject IsalZlibDecompType = { + .tp_name = "isal_zlib.Decompress", + .tp_doc = "Object returned by isal_zlib.compressobj.", + .tp_basicsize = sizeof(decompobject), + .tp_flags = Py_TPFLAGS_DEFAULT, + .tp_dealloc = (destructor)Decomp_dealloc, + .tp_methods = Decomp_methods, + .tp_members = Decomp_members, }; -static PyType_Spec Decomptype_spec = { - "isal_zlib.Decompress", - sizeof(decompobject), - 0, - Py_TPFLAGS_DEFAULT, - Decomptype_slots -}; - - PyDoc_STRVAR(isal_zlib_module_documentation, "The functions in this module allow compression and decompression using the\n" "zlib library, which is based on GNU zip.\n" @@ -678,82 +1159,57 @@ PyDoc_STRVAR(isal_zlib_module_documentation, "Compressor objects support compress() and flush() methods; decompressor\n" "objects support decompress() and flush()."); -static int -isal_zlib_clear(PyObject *m) -{ - _isal_zlibstate *state = get_isal_zlib_state(m); - Py_CLEAR(state->Comptype); - Py_CLEAR(state->Decomptype); - Py_CLEAR(state->IsalError); - return 0; -} - -static int -isal_zlib_traverse(PyObject *m, visitproc visit, void *arg) -{ - _isal_zlibstate *state = get_isal_zlib_state(m); - Py_VISIT(state->Comptype); - Py_VISIT(state->Decomptype); - Py_VISIT(state->IsalError); - return 0; -} - -static void -isal_zlib_free(void *m) -{ - isal_zlib_clear((PyObject *)m); -} - static struct PyModuleDef isal_zlib_module = { PyModuleDef_HEAD_INIT, "isal_zlib", /* name of module */ isal_zlib_module_documentation, /* module documentation, may be NULL */ - sizeof(isal_zlib_state), + 0, IsalZlibMethods, - NULL, - isal_zlib_traverse, - isal_zlib_clear, - isal_zlib_free, }; PyMODINIT_FUNC PyInit_isal_zlib(void) { PyObject *m; - PyObject *IsalError; m = PyModule_Create(&isal_zlib_module); if (m == NULL) return NULL; - IsalError = PyErr_NewException("isal_zlib.error", NULL, NULL); - Py_XINCREF(IsalError); - if (PyModule_AddObject(m, "error", IsalError) < 0) { - Py_XDECREF(IsalError); - Py_CLEAR(IsalError); - Py_DECREF(m); + PyObject *igzip_lib_module = PyImport_ImportModule("isal.igzip_lib"); + if (igzip_lib_module == NULL) { return NULL; } - get_isal_zlib_state(m)->IsalError = IsalError; - PyTypeObject *Comptype = (PyTypeObject *)PyType_FromSpec(&Comptype_spec); - if (Comptype == NULL) + IsalError = PyObject_GetAttrString(igzip_lib_module, "error"); + if (IsalError == NULL) { return NULL; - get_isal_zlib_state(m)->Comptype = Comptype; + } + Py_INCREF(IsalError); + if (PyModule_AddObject(m, "error", IsalError) < 0) { + return NULL; + } - PyTypeObject *Decomptype = (PyTypeObject *)PyType_FromSpec(&Decomptype_spec); - if (Decomptype == NULL) + Py_INCREF(IsalError); + if (PyModule_AddObject(m, "IsalError", IsalError) < 0) { return NULL; - get_isal_zlib_state(m)->Decomptype = Decomptype; + } - if (PyType_Ready(Comptype) != 0) + PyTypeObject *Comptype = (PyTypeObject *)&IsalZlibCompType; + if (PyType_Ready(Comptype) != 0) { return NULL; + } + Py_INCREF(Comptype); if (PyModule_AddObject(m, "Compress", (PyObject *)Comptype) < 0) { return NULL; } - if (PyType_Ready(Decomptype) != 0) + + PyTypeObject *Decomptype = (PyTypeObject *)&IsalZlibDecompType; + if (PyType_Ready(Decomptype) != 0) { return NULL; + } + Py_INCREF(Decomptype); if (PyModule_AddObject(m, "Decompress", (PyObject *)Decomptype) < 0) { return NULL; @@ -789,6 +1245,5 @@ PyInit_isal_zlib(void) PyModule_AddIntMacro(m, Z_BLOCK); PyModule_AddIntMacro(m, Z_TREES); - PyState_AddModule(m, &isal_zlib_module); return m; } diff --git a/src/isal/isal_zlib_impl.h b/src/isal/isal_zlib_impl.h deleted file mode 100644 index 625786ed..00000000 --- a/src/isal/isal_zlib_impl.h +++ /dev/null @@ -1,728 +0,0 @@ -// Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, -// 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022 -// Python Software Foundation; All Rights Reserved - -// This file is part of python-isal which is distributed under the -// PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2. - -// This file was modified from Cpython Modules/zlibmodule.c file from the 3.9 -// branch. This is because the BlocksBuffer used in Python 3.10 and higher is -// not available in python 3.7-3.9 which this project supports. - -// Changes compared to CPython: -// - All zlib naming changed to isal_zlib -// - Zlib to ISA-L conversion functions were included. -// - All compression and checksum functions from zlib replaced with ISA-L -// compatible functions. -// - No locks in Compress and Decompress objects. These were deemed unnecessary -// as the ISA-L functions do not allocate memory, unlike the zlib -// counterparts. -// - zlib.compress also has a 'wbits' argument. This change was included in -// Python 3.11. It allows for faster gzip compression by using -// isal_zlib.compress(data, wbits=31). - - -#define PY_SSIZE_T_CLEAN -#include - -#include "igzip_lib_impl.h" - -#include - -// Below values are copied from zlib.h -#define Z_DEFAULT_STRATEGY 0 -#define Z_FILTERED 1 -#define Z_HUFFMAN_ONLY 2 -#define Z_RLE 3 -#define Z_FIXED 4 - -#define Z_DEFLATED 8 - -// Flush modes copied from zlib.h -#define Z_NO_FLUSH 0 -#define Z_PARTIAL_FLUSH 1 -#define Z_SYNC_FLUSH 2 -#define Z_FULL_FLUSH 3 -#define Z_FINISH 4 -#define Z_BLOCK 5 -#define Z_TREES 6 - -#define DEF_MEM_LEVEL 8 - -typedef struct { - PyTypeObject *Comptype; - PyTypeObject *Decomptype; - PyObject *IsalError; -} _isal_zlibstate; - -static inline _isal_zlibstate* -get_isal_zlib_state(PyObject *module) -{ - void *state = PyModule_GetState(module); - assert(state != NULL); - return (_isal_zlibstate *)state; -} - -static PyModuleDef isal_zlib_module; -#define _isal_zlibstate_global ((_isal_zlibstate *)PyModule_GetState(PyState_FindModule(&isal_zlib_module))) - - -static PyObject * -isal_zlib_adler32_impl(PyObject *module, Py_buffer *data, uint32_t value) -{ - value = isal_adler32(value, data->buf, (uint64_t)data->len); - return PyLong_FromUnsignedLong(value & 0xffffffffU); -} - -static PyObject * -isal_zlib_crc32_impl(PyObject *module, Py_buffer *data, uint32_t value) -{ - value = crc32_gzip_refl(value, data->buf, (uint64_t)data->len); - return PyLong_FromUnsignedLong(value & 0xffffffffU); -} - - -static int -wbits_to_flag_and_hist_bits_deflate(int wbits, int *hist_bits, int *flag) -{ - if (wbits >= 9 && wbits <= 15){ - *hist_bits = wbits; - *flag = IGZIP_ZLIB; - } - else if (wbits >= 25 && wbits <= 31) { - *hist_bits = wbits - 16; - *flag = IGZIP_GZIP; - } - else if (wbits >=-15 && wbits <= -9) { - *hist_bits = -wbits; - *flag = IGZIP_DEFLATE; - } - else { - PyErr_Format(_isal_zlibstate_global->IsalError, "Invalid wbits value: %d", wbits); - return -1; - } - return 0; -} - -static int -wbits_to_flag_and_hist_bits_inflate(int wbits, int *hist_bits, int *flag) -{ - if (wbits == 0) { - *hist_bits = 0; - *flag = ISAL_ZLIB; - } - else if (wbits >= 8 && wbits <= 15){ - *hist_bits = wbits; - *flag = ISAL_ZLIB; - } - else if (wbits >= 24 && wbits <= 31) { - *hist_bits = wbits - 16; - *flag = ISAL_GZIP; - } - else if (wbits >=-15 && wbits <= -8) { - *hist_bits = -wbits; - *flag = ISAL_DEFLATE; - } - else if (wbits >=40 && wbits <= 47) { - *hist_bits = wbits - 32; - return 1; - } - else { - PyErr_Format(_isal_zlibstate_global->IsalError, "Invalid wbits value: %d", wbits); - return -1; - } - return 0; -} - -static const int ZLIB_MEM_LEVEL_TO_ISAL[10] = { - 0, // 0 Is an invalid mem_level in zlib, - MEM_LEVEL_MIN, // 1 -> min - MEM_LEVEL_SMALL, // 2-3 -> SMALL - MEM_LEVEL_SMALL, - MEM_LEVEL_MEDIUM, // 4-6 -> MEDIUM - MEM_LEVEL_MEDIUM, - MEM_LEVEL_MEDIUM, - MEM_LEVEL_LARGE, // 7-8 LARGE. The zlib module default = 8. Large is the ISA-L default value. - MEM_LEVEL_LARGE, - MEM_LEVEL_EXTRA_LARGE, // 9 -> EXTRA_LARGE. -}; - -static int zlib_mem_level_to_isal(int mem_level) { - if (mem_level < 1 || mem_level > 9) { - PyErr_Format(PyExc_ValueError, - "Invalid mem level: %d. Mem level should be between 1 and 9"); - return -1;} - return ZLIB_MEM_LEVEL_TO_ISAL[mem_level]; -} - -static int -data_is_gzip(Py_buffer *data){ - if (data->len < 2) - return 0; - uint8_t *buf = (uint8_t *)data->buf; - return (buf[0] == 31 && buf[1] == 139); -} - -static PyObject * -isal_zlib_compress_impl(PyObject *ErrorClass, Py_buffer *data, int level, int wbits) -{ - int hist_bits = -1; - int flag = -1; - if (wbits_to_flag_and_hist_bits_deflate(wbits, &hist_bits, &flag) != 0) - return NULL; - return igzip_lib_compress_impl(ErrorClass, data, level, - flag, MEM_LEVEL_DEFAULT, hist_bits); -} - -static PyObject * -isal_zlib_decompress_impl(PyObject *ErrorClass, Py_buffer *data, int wbits, - Py_ssize_t bufsize) -{ - int hist_bits; - int flag; - int convert_result = wbits_to_flag_and_hist_bits_inflate(wbits, &hist_bits, &flag); - if (convert_result < 0) - return NULL; - if (convert_result > 0) { - if (data_is_gzip(data)) - flag = ISAL_GZIP; - else - flag = ISAL_ZLIB; - } - return igzip_lib_decompress_impl(ErrorClass, data, flag, hist_bits, bufsize); -} - -typedef struct -{ - PyObject_HEAD - struct isal_zstream zst; - int is_initialised; - uint8_t * level_buf; - PyObject *zdict; -} compobject; - -static void -Comp_dealloc(compobject *self) -{ - if (self->is_initialised && self->level_buf != NULL) - PyMem_Free(self->level_buf); - PyObject *type = (PyObject *)Py_TYPE(self); - Py_XDECREF(self->zdict); - PyObject_Del(self); - Py_DECREF(type); -} - -static compobject * -newcompobject(PyTypeObject *type) -{ - compobject *self; - self = PyObject_New(compobject, type); - #if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION == 7 - // Apparently the type refcount is not increased automatically with - // PyObject_New on 3.7 - # pragma message("Include python 3.7 type ref fix.") - Py_INCREF(type); - #endif - if (self == NULL) - return NULL; - self->is_initialised = 0; - self->zdict = NULL; - self->level_buf = NULL; - return self; -} - -static PyObject * -isal_zlib_compressobj_impl(PyObject *module, int level, int method, int wbits, - int memLevel, int strategy, Py_buffer *zdict) -{ - compobject *self = NULL; - int err; - uint32_t level_buf_size = 0; - int flag = -1; - int hist_bits = -1; - - if (method != Z_DEFLATED){ - PyErr_Format(PyExc_ValueError, - "Unsupported method: %d. Only DEFLATED is supported.", - method); - goto error; - } - if (strategy != Z_DEFAULT_STRATEGY){ - err = PyErr_WarnEx( - PyExc_UserWarning, - "Only one strategy is supported when using isal_zlib. Using the default strategy.", - 1); - if (err == -1) - // Warning was turned into an exception. - goto error; - } - if (zdict->buf != NULL && (size_t)zdict->len > UINT32_MAX) { - PyErr_SetString(PyExc_OverflowError, - "zdict length does not fit in an unsigned 32-bit int"); - goto error; - } - int isal_mem_level = zlib_mem_level_to_isal(memLevel); - if (isal_mem_level == -1) - goto error; - if (wbits_to_flag_and_hist_bits_deflate(wbits, &hist_bits, &flag) == -1) { - PyErr_Format(PyExc_ValueError, "Invalid wbits value: %d", wbits); - goto error; - } - if (mem_level_to_bufsize( - level, isal_mem_level, &level_buf_size) == -1) { - PyErr_Format(PyExc_ValueError, - "Invalid compression level: %d. Compression level should be between 0 and 3", - level); - goto error; - } - - self = newcompobject(_isal_zlibstate_global->Comptype); - if (self == NULL) - goto error; - self->level_buf = (uint8_t *)PyMem_Malloc(level_buf_size); - if (self->level_buf == NULL){ - PyErr_NoMemory(); - goto error; - } - isal_deflate_init(&(self->zst)); - self->zst.next_in = NULL; - self->zst.avail_in = 0; - self->zst.level_buf_size = level_buf_size; - self->zst.level_buf = self->level_buf; - self->zst.level = level; - self->zst.hist_bits = (uint16_t)hist_bits; - self->zst.gzip_flag = (uint16_t)flag; - - self->is_initialised = 1; - if (zdict->buf == NULL) { - goto success; - } else { - err = isal_deflate_set_dict(&(self->zst), - zdict->buf, (uint32_t)zdict->len); - if (err == COMP_OK) - goto success; - PyErr_SetString(PyExc_ValueError, "Invalid dictionary"); - goto error; - } - error: - if (self != NULL) { - if (self->level_buf != NULL) - PyMem_Free(self->level_buf); - Py_CLEAR(self); - } - - success: - return (PyObject *)self; -} - -typedef struct -{ - PyObject_HEAD - struct inflate_state zst; - PyObject *unused_data; - PyObject *unconsumed_tail; - char eof; - int is_initialised; - int method_set; - PyObject *zdict; -} decompobject; - -static void -Decomp_dealloc(decompobject *self) -{ - PyObject *type = (PyObject *)Py_TYPE(self); - Py_XDECREF(self->unused_data); - Py_XDECREF(self->unconsumed_tail); - Py_XDECREF(self->zdict); - PyObject_Del(self); - Py_DECREF(type); -} - -static int -set_inflate_zdict(decompobject *self) -{ - Py_buffer zdict_buf; - int err; - - if (PyObject_GetBuffer(self->zdict, &zdict_buf, PyBUF_SIMPLE) == -1) { - return -1; - } - if ((size_t)zdict_buf.len > UINT32_MAX) { - PyErr_SetString(PyExc_OverflowError, - "zdict length does not fit in an unsigned 32-bits int"); - PyBuffer_Release(&zdict_buf); - return -1; - } - err = isal_inflate_set_dict(&(self->zst), - zdict_buf.buf, (uint32_t)zdict_buf.len); - PyBuffer_Release(&zdict_buf); - if (err != ISAL_DECOMP_OK) { - isal_inflate_error(err, _isal_zlibstate_global->IsalError); - return -1; - } - return 0; -} - -static decompobject * -newdecompobject(PyTypeObject *type) -{ - decompobject *self; - self = PyObject_New(decompobject, type); - #if PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION == 7 - // Apparently the type refcount is not increased automatically with - // PyObject_New on 3.7 - Py_INCREF(type); - #endif - if (self == NULL) - return NULL; - self->eof = 0; - self->is_initialised = 0; - self->method_set = 0; - self->zdict = NULL; - self->unused_data = PyBytes_FromStringAndSize("", 0); - if (self->unused_data == NULL) { - Py_DECREF(self); - return NULL; - } - self->unconsumed_tail = PyBytes_FromStringAndSize("", 0); - if (self->unconsumed_tail == NULL) { - Py_DECREF(self); - return NULL; - } - return self; -} - -static PyObject * -isal_zlib_decompressobj_impl(PyObject *module, int wbits, PyObject *zdict) -{ - int err; - decompobject *self; - int flag; - int hist_bits; - if (zdict != NULL && !PyObject_CheckBuffer(zdict)) { - PyErr_SetString(PyExc_TypeError, - "zdict argument must support the buffer protocol"); - return NULL; - } - self = newdecompobject(_isal_zlibstate_global->Decomptype); - if (self == NULL) - return NULL; - - isal_inflate_init(&(self->zst)); - err = wbits_to_flag_and_hist_bits_inflate(wbits, &hist_bits, &flag); - if (err < 0) { - PyErr_Format(PyExc_ValueError, "Invalid wbits value: %d", wbits); - return NULL; - } - else if (err == 0) { - self->zst.crc_flag = flag; - self->method_set = 1; - } - self->zst.hist_bits = hist_bits; - self->zst.next_in = NULL; - self->zst.avail_in = 0; - if (zdict != NULL) { - Py_INCREF(zdict); - self->zdict = zdict; - } - self->is_initialised = 1; - - if (self->zdict != NULL) { - if (set_inflate_zdict(self) < 0) { - Py_DECREF(self); - return NULL; - } - } - return (PyObject *)self; -} - -static PyObject * -isal_zlib_Compress_compress_impl(compobject *self, Py_buffer *data) -/*[clinic end generated code: output=5d5cd791cbc6a7f4 input=0d95908d6e64fab8]*/ -{ - PyObject *RetVal = NULL; - Py_ssize_t ibuflen, obuflen = DEF_BUF_SIZE; - int err; - - self->zst.next_in = data->buf; - ibuflen = data->len; - - do { - arrange_input_buffer(&(self->zst.avail_in), &ibuflen); - do { - obuflen = arrange_output_buffer(&(self->zst.avail_out), - &(self->zst.next_out), &RetVal, obuflen); - if (obuflen < 0) - goto error; - - err = isal_deflate(&self->zst); - - if (err != COMP_OK) { - isal_deflate_error(err, _isal_zlibstate_global->IsalError); - goto error; - } - } while (self->zst.avail_out == 0); - assert(self->zst.avail_in == 0); - - } while (ibuflen != 0); - - if (_PyBytes_Resize(&RetVal, self->zst.next_out - - (uint8_t *)PyBytes_AS_STRING(RetVal)) == 0) - goto success; - - error: - Py_CLEAR(RetVal); - success: - return RetVal; -} - -/* Helper for objdecompress() and flush(). Saves any unconsumed input data in - self->unused_data or self->unconsumed_tail, as appropriate. */ -static int -save_unconsumed_input(decompobject *self, Py_buffer *data, int err) -{ - if (self->zst.block_state == ISAL_BLOCK_FINISH) { - /* The end of the compressed data has been reached. Store the leftover - input data in self->unused_data. */ - if (self->zst.avail_in > 0) { - Py_ssize_t old_size = PyBytes_GET_SIZE(self->unused_data); - Py_ssize_t new_size, left_size; - PyObject *new_data; - Py_ssize_t bytes_in_bitbuffer = bitbuffer_size(&(self->zst)); - left_size = (uint8_t *)data->buf + data->len - self->zst.next_in; - if (left_size + bytes_in_bitbuffer > (PY_SSIZE_T_MAX - old_size)) { - PyErr_NoMemory(); - return -1; - } - // There might also be data left in the bit_buffer. - new_size = old_size + left_size + bytes_in_bitbuffer; - new_data = PyBytes_FromStringAndSize(NULL, new_size); - if (new_data == NULL) - return -1; - char * new_data_ptr = PyBytes_AS_STRING(new_data); - memcpy(new_data_ptr, - PyBytes_AS_STRING(self->unused_data), old_size); - bitbuffer_copy(&(self->zst), new_data_ptr + old_size, bytes_in_bitbuffer); - memcpy(new_data_ptr + old_size + bytes_in_bitbuffer, - self->zst.next_in, left_size); - Py_SETREF(self->unused_data, new_data); - self->zst.avail_in = 0; - } - } - - if (self->zst.avail_in > 0 || PyBytes_GET_SIZE(self->unconsumed_tail)) { - /* This code handles two distinct cases: - 1. Output limit was reached. Save leftover input in unconsumed_tail. - 2. All input data was consumed. Clear unconsumed_tail. */ - Py_ssize_t left_size = (uint8_t *)data->buf + data->len - self->zst.next_in; - PyObject *new_data = PyBytes_FromStringAndSize( - (char *)self->zst.next_in, left_size); - if (new_data == NULL) - return -1; - Py_SETREF(self->unconsumed_tail, new_data); - } - - return 0; -} - -static PyObject * -isal_zlib_Decompress_decompress_impl(decompobject *self, Py_buffer *data, - Py_ssize_t max_length) -{ - int err = ISAL_DECOMP_OK; - Py_ssize_t ibuflen, obuflen = DEF_BUF_SIZE, hard_limit; - PyObject *RetVal = NULL; - - if (max_length < 0) { - PyErr_SetString(PyExc_ValueError, "max_length must be non-negative"); - return NULL; - } else if (max_length == 0) - hard_limit = PY_SSIZE_T_MAX; - else - hard_limit = max_length; - - if (!self->method_set) { - if (data_is_gzip(data)){ - self->zst.crc_flag = ISAL_GZIP; - } - else { - self->zst.crc_flag = ISAL_ZLIB; - } - self->method_set = 1; - } - self->zst.next_in = data->buf; - ibuflen = data->len; - - /* limit amount of data allocated to max_length */ - if (max_length && obuflen > max_length) - obuflen = max_length; - - do { - arrange_input_buffer(&(self->zst.avail_in), &ibuflen); - - do { - obuflen = arrange_output_buffer_with_maximum(&(self->zst.avail_out), - &(self->zst.next_out), - &RetVal, - obuflen, hard_limit); - if (obuflen == -2) { - if (max_length > 0) { - goto save; - } - PyErr_NoMemory(); - } - if (obuflen < 0) { - goto abort; - } - - err = isal_inflate(&self->zst); - if (err != ISAL_DECOMP_OK){ - isal_inflate_error(err, _isal_zlibstate_global->IsalError); - goto abort; - } - - } while (self->zst.avail_out == 0 && self->zst.block_state != ISAL_BLOCK_FINISH); - - } while (self->zst.block_state != ISAL_BLOCK_FINISH && ibuflen != 0); - - save: - if (save_unconsumed_input(self, data, err) < 0) - goto abort; - - if (self->zst.block_state == ISAL_BLOCK_FINISH) { - self->eof = 1; - } - - if (_PyBytes_Resize(&RetVal, self->zst.next_out - - (uint8_t *)PyBytes_AS_STRING(RetVal)) == 0) - goto success; - - abort: - Py_CLEAR(RetVal); - success: - return RetVal; -} - -static PyObject * -isal_zlib_Compress_flush_impl(compobject *self, int mode) -{ - int err; - Py_ssize_t length = DEF_BUF_SIZE; - PyObject *RetVal = NULL; - - /* Flushing with Z_NO_FLUSH is a no-op, so there's no point in - doing any work at all; just return an empty string. */ - if (mode == Z_NO_FLUSH) { - return PyBytes_FromStringAndSize(NULL, 0); - } else if (mode == Z_FINISH) { - self->zst.flush = FULL_FLUSH; - self->zst.end_of_stream = 1; - } else if (mode == Z_FULL_FLUSH){ - self->zst.flush = FULL_FLUSH; - } else if (mode == Z_SYNC_FLUSH) { - self->zst.flush = SYNC_FLUSH; - } else { - PyErr_Format(_isal_zlibstate_global->IsalError, - "Unsupported flush mode: %d", mode); - } - - self->zst.avail_in = 0; - - do { - length = arrange_output_buffer(&(self->zst.avail_out), - &(self->zst.next_out), &RetVal, length); - if (length < 0) { - Py_CLEAR(RetVal); - goto error; - } - - err = isal_deflate(&self->zst); - - if (err != COMP_OK) { - isal_deflate_error(err, _isal_zlibstate_global->IsalError); - Py_CLEAR(RetVal); - goto error; - } - } while (self->zst.avail_out == 0); - assert(self->zst.avail_in == 0); - - /* If mode is Z_FINISH, we free the level buffer. - Note we should only get ZSTATE_END when - mode is Z_FINISH, but checking both for safety*/ - if (self->zst.internal_state.state == ZSTATE_END && mode == Z_FINISH) { - PyMem_FREE(self->level_buf); - self->zst.level_buf_size = 0; - self->zst.level_buf = NULL; - self->is_initialised = 0; - } else { - // reset the flush mode back so compressobject can be used again. - self->zst.flush = NO_FLUSH; - } - - if (_PyBytes_Resize(&RetVal, self->zst.next_out - - (uint8_t *)PyBytes_AS_STRING(RetVal)) < 0) - Py_CLEAR(RetVal); - - error: - return RetVal; -} - -static PyObject * -isal_zlib_Decompress_flush_impl(decompobject *self, Py_ssize_t length) -{ - int err; - Py_buffer data; - PyObject *RetVal = NULL; - Py_ssize_t ibuflen; - - if (length <= 0) { - PyErr_SetString(PyExc_ValueError, "length must be greater than zero"); - return NULL; - } - - if (PyObject_GetBuffer(self->unconsumed_tail, &data, PyBUF_SIMPLE) == -1) { - return NULL; - } - - self->zst.next_in = data.buf; - ibuflen = data.len; - - do { - arrange_input_buffer(&(self->zst.avail_in), &ibuflen); - - do { - length = arrange_output_buffer(&(self->zst.avail_out), - &(self->zst.next_out), &RetVal, length); - if (length < 0) - goto abort; - - err = isal_inflate(&self->zst); - - if (err != ISAL_DECOMP_OK) { - isal_inflate_error(err, _isal_zlibstate_global->IsalError); - goto abort; - } - - } while (self->zst.avail_out == 0 && self->zst.block_state != ISAL_BLOCK_FINISH); - - } while (self->zst.block_state != ISAL_BLOCK_FINISH && ibuflen != 0); - - if (save_unconsumed_input(self, &data, err) < 0) - goto abort; - - /* If at end of stream, clean up any memory allocated by zlib. */ - if (self->zst.block_state == ISAL_BLOCK_FINISH) { - self->eof = 1; - self->is_initialised = 0; - } - - if (_PyBytes_Resize(&RetVal, self->zst.next_out - - (uint8_t *)PyBytes_AS_STRING(RetVal)) == 0) - goto success; - - abort: - Py_CLEAR(RetVal); - success: - PyBuffer_Release(&data); - return RetVal; -} diff --git a/src/isal/python_args.h b/src/isal/python_args.h deleted file mode 100644 index 74771ca2..00000000 --- a/src/isal/python_args.h +++ /dev/null @@ -1,564 +0,0 @@ -// Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, -// 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022 -// Python Software Foundation; All Rights Reserved - -// This file comes from CPython which is distributed under the -// PYTHON SOFTWARE FOUNDATION LICENSE VERSION 2. - -// This code comes from github.com/python/cpython. The file Python/getargs.c -// was used. _PyTuple_CAST was copied from Include/pytupleobject.h -// This code was included to ensure that fast parsing was also enabled on -// python 3.7. - -#define PY_SSIZE_T_CLEAN -#include - -#pragma message("Including code from CPython getargs.c to enable faster arg parsing on python 3.7") -#define IS_END_OF_FORMAT(c) (c == '\0' || c == ';' || c == ':') - -#define FLAG_COMPAT 1 -#define FLAG_SIZE_T 2 - -#define _PyTuple_CAST(op) (assert(PyTuple_Check(op)), (PyTupleObject *)(op)) - -static const char * -skipitem(const char **p_format, va_list *p_va, int flags) -{ - const char *format = *p_format; - char c = *format++; - - switch (c) { - - /* - * codes that take a single data pointer as an argument - * (the type of the pointer is irrelevant) - */ - - case 'b': /* byte -- very short int */ - case 'B': /* byte as bitfield */ - case 'h': /* short int */ - case 'H': /* short int as bitfield */ - case 'i': /* int */ - case 'I': /* int sized bitfield */ - case 'l': /* long int */ - case 'k': /* long int sized bitfield */ - case 'L': /* long long */ - case 'K': /* long long sized bitfield */ - case 'n': /* Py_ssize_t */ - case 'f': /* float */ - case 'd': /* double */ - case 'D': /* complex double */ - case 'c': /* char */ - case 'C': /* unicode char */ - case 'p': /* boolean predicate */ - case 'S': /* string object */ - case 'Y': /* string object */ - case 'U': /* unicode string object */ - { - if (p_va != NULL) { - (void) va_arg(*p_va, void *); - } - break; - } - - /* string codes */ - - case 'e': /* string with encoding */ - { - if (p_va != NULL) { - (void) va_arg(*p_va, const char *); - } - if (!(*format == 's' || *format == 't')) - /* after 'e', only 's' and 't' is allowed */ - goto err; - format++; - } - /* fall through */ - - case 's': /* string */ - case 'z': /* string or None */ - case 'y': /* bytes */ - case 'u': /* unicode string */ - case 'Z': /* unicode string or None */ - case 'w': /* buffer, read-write */ - { - if (p_va != NULL) { - (void) va_arg(*p_va, char **); - } - if (*format == '#') { - if (p_va != NULL) { - if (!(flags & FLAG_SIZE_T)) { - PyErr_SetString(PyExc_SystemError, - "PY_SSIZE_T_CLEAN macro must be defined for '#' formats"); - return NULL; - } - (void) va_arg(*p_va, Py_ssize_t *); - } - format++; - } else if ((c == 's' || c == 'z' || c == 'y' || c == 'w') - && *format == '*') - { - format++; - } - break; - } - - case 'O': /* object */ - { - if (*format == '!') { - format++; - if (p_va != NULL) { - (void) va_arg(*p_va, PyTypeObject*); - (void) va_arg(*p_va, PyObject **); - } - } - else if (*format == '&') { - typedef int (*converter)(PyObject *, void *); - if (p_va != NULL) { - (void) va_arg(*p_va, converter); - (void) va_arg(*p_va, void *); - } - format++; - } - else { - if (p_va != NULL) { - (void) va_arg(*p_va, PyObject **); - } - } - break; - } - - case '(': /* bypass tuple, not handled at all previously */ - { - const char *msg; - for (;;) { - if (*format==')') - break; - if (IS_END_OF_FORMAT(*format)) - return "Unmatched left paren in format " - "string"; - msg = skipitem(&format, p_va, flags); - if (msg) - return msg; - } - format++; - break; - } - - case ')': - return "Unmatched right paren in format string"; - - default: -err: - return "impossible"; - - } - - *p_format = format; - return NULL; -} - -int -_PyArg_CheckPositional(const char *name, Py_ssize_t nargs, - Py_ssize_t min, Py_ssize_t max) -{ - assert(min >= 0); - assert(min <= max); - - if (nargs < min) { - if (name != NULL) - PyErr_Format( - PyExc_TypeError, - "%.200s expected %s%zd argument%s, got %zd", - name, (min == max ? "" : "at least "), min, min == 1 ? "" : "s", nargs); - else - PyErr_Format( - PyExc_TypeError, - "unpacked tuple should have %s%zd element%s," - " but has %zd", - (min == max ? "" : "at least "), min, min == 1 ? "" : "s", nargs); - return 0; - } - - if (nargs == 0) { - return 1; - } - - if (nargs > max) { - if (name != NULL) - PyErr_Format( - PyExc_TypeError, - "%.200s expected %s%zd argument%s, got %zd", - name, (min == max ? "" : "at most "), max, max == 1 ? "" : "s", nargs); - else - PyErr_Format( - PyExc_TypeError, - "unpacked tuple should have %s%zd element%s," - " but has %zd", - (min == max ? "" : "at most "), max, max == 1 ? "" : "s", nargs); - return 0; - } - - return 1; -} - -void -_PyArg_BadArgument(const char *fname, const char *displayname, - const char *expected, PyObject *arg) -{ - PyErr_Format(PyExc_TypeError, - "%.200s() %.200s must be %.50s, not %.50s", - fname, displayname, expected, - arg == Py_None ? "None" : Py_TYPE(arg)->tp_name); -} - - -static PyObject* -find_keyword(PyObject *kwnames, PyObject *const *kwstack, PyObject *key) -{ - Py_ssize_t i, nkwargs; - - nkwargs = PyTuple_GET_SIZE(kwnames); - for (i = 0; i < nkwargs; i++) { - PyObject *kwname = PyTuple_GET_ITEM(kwnames, i); - - /* kwname == key will normally find a match in since keyword keys - should be interned strings; if not retry below in a new loop. */ - if (kwname == key) { - return kwstack[i]; - } - } - - for (i = 0; i < nkwargs; i++) { - PyObject *kwname = PyTuple_GET_ITEM(kwnames, i); - assert(PyUnicode_Check(kwname)); - if (_PyUnicode_EQ(kwname, key)) { - return kwstack[i]; - } - } - return NULL; -} - -/* List of static parsers. */ -static struct _PyArg_Parser *static_arg_parsers = NULL; - -static int -parser_init(struct _PyArg_Parser *parser) -{ - const char * const *keywords; - const char *format, *msg; - int i, len, min, max, nkw; - PyObject *kwtuple; - - assert(parser->keywords != NULL); - if (parser->kwtuple != NULL) { - return 1; - } - - keywords = parser->keywords; - /* scan keywords and count the number of positional-only parameters */ - for (i = 0; keywords[i] && !*keywords[i]; i++) { - } - parser->pos = i; - /* scan keywords and get greatest possible nbr of args */ - for (; keywords[i]; i++) { - if (!*keywords[i]) { - PyErr_SetString(PyExc_SystemError, - "Empty keyword parameter name"); - return 0; - } - } - len = i; - - format = parser->format; - if (format) { - /* grab the function name or custom error msg first (mutually exclusive) */ - parser->fname = strchr(parser->format, ':'); - if (parser->fname) { - parser->fname++; - parser->custom_msg = NULL; - } - else { - parser->custom_msg = strchr(parser->format,';'); - if (parser->custom_msg) - parser->custom_msg++; - } - - min = max = INT_MAX; - for (i = 0; i < len; i++) { - if (*format == '|') { - if (min != INT_MAX) { - PyErr_SetString(PyExc_SystemError, - "Invalid format string (| specified twice)"); - return 0; - } - if (max != INT_MAX) { - PyErr_SetString(PyExc_SystemError, - "Invalid format string ($ before |)"); - return 0; - } - min = i; - format++; - } - if (*format == '$') { - if (max != INT_MAX) { - PyErr_SetString(PyExc_SystemError, - "Invalid format string ($ specified twice)"); - return 0; - } - if (i < parser->pos) { - PyErr_SetString(PyExc_SystemError, - "Empty parameter name after $"); - return 0; - } - max = i; - format++; - } - if (IS_END_OF_FORMAT(*format)) { - PyErr_Format(PyExc_SystemError, - "More keyword list entries (%d) than " - "format specifiers (%d)", len, i); - return 0; - } - - msg = skipitem(&format, NULL, 0); - if (msg) { - PyErr_Format(PyExc_SystemError, "%s: '%s'", msg, - format); - return 0; - } - } - parser->min = Py_MIN(min, len); - parser->max = Py_MIN(max, len); - - if (!IS_END_OF_FORMAT(*format) && (*format != '|') && (*format != '$')) { - PyErr_Format(PyExc_SystemError, - "more argument specifiers than keyword list entries " - "(remaining format:'%s')", format); - return 0; - } - } - - nkw = len - parser->pos; - kwtuple = PyTuple_New(nkw); - if (kwtuple == NULL) { - return 0; - } - keywords = parser->keywords + parser->pos; - for (i = 0; i < nkw; i++) { - PyObject *str = PyUnicode_FromString(keywords[i]); - if (str == NULL) { - Py_DECREF(kwtuple); - return 0; - } - PyUnicode_InternInPlace(&str); - PyTuple_SET_ITEM(kwtuple, i, str); - } - parser->kwtuple = kwtuple; - - assert(parser->next == NULL); - parser->next = static_arg_parsers; - static_arg_parsers = parser; - return 1; -} - -PyObject * const * -_PyArg_UnpackKeywords(PyObject *const *args, Py_ssize_t nargs, - PyObject *kwargs, PyObject *kwnames, - struct _PyArg_Parser *parser, - int minpos, int maxpos, int minkw, - PyObject **buf) -{ - PyObject *kwtuple; - PyObject *keyword; - int i, posonly, minposonly, maxargs; - int reqlimit = minkw ? maxpos + minkw : minpos; - Py_ssize_t nkwargs; - PyObject *current_arg; - PyObject * const *kwstack = NULL; - - assert(kwargs == NULL || PyDict_Check(kwargs)); - assert(kwargs == NULL || kwnames == NULL); - - if (parser == NULL) { - PyErr_BadInternalCall(); - return NULL; - } - - if (kwnames != NULL && !PyTuple_Check(kwnames)) { - PyErr_BadInternalCall(); - return NULL; - } - - if (args == NULL && nargs == 0) { - args = buf; - } - - if (!parser_init(parser)) { - return NULL; - } - - kwtuple = parser->kwtuple; - posonly = parser->pos; - minposonly = Py_MIN(posonly, minpos); - maxargs = posonly + (int)PyTuple_GET_SIZE(kwtuple); - - if (kwargs != NULL) { - nkwargs = PyDict_GET_SIZE(kwargs); - } - else if (kwnames != NULL) { - nkwargs = PyTuple_GET_SIZE(kwnames); - kwstack = args + nargs; - } - else { - nkwargs = 0; - } - if (nkwargs == 0 && minkw == 0 && minpos <= nargs && nargs <= maxpos) { - /* Fast path. */ - return args; - } - if (nargs + nkwargs > maxargs) { - /* Adding "keyword" (when nargs == 0) prevents producing wrong error - messages in some special cases (see bpo-31229). */ - PyErr_Format(PyExc_TypeError, - "%.200s%s takes at most %d %sargument%s (%zd given)", - (parser->fname == NULL) ? "function" : parser->fname, - (parser->fname == NULL) ? "" : "()", - maxargs, - (nargs == 0) ? "keyword " : "", - (maxargs == 1) ? "" : "s", - nargs + nkwargs); - return NULL; - } - if (nargs > maxpos) { - if (maxpos == 0) { - PyErr_Format(PyExc_TypeError, - "%.200s%s takes no positional arguments", - (parser->fname == NULL) ? "function" : parser->fname, - (parser->fname == NULL) ? "" : "()"); - } - else { - PyErr_Format(PyExc_TypeError, - "%.200s%s takes %s %d positional argument%s (%zd given)", - (parser->fname == NULL) ? "function" : parser->fname, - (parser->fname == NULL) ? "" : "()", - (minpos < maxpos) ? "at most" : "exactly", - maxpos, - (maxpos == 1) ? "" : "s", - nargs); - } - return NULL; - } - if (nargs < minposonly) { - PyErr_Format(PyExc_TypeError, - "%.200s%s takes %s %d positional argument%s" - " (%zd given)", - (parser->fname == NULL) ? "function" : parser->fname, - (parser->fname == NULL) ? "" : "()", - minposonly < maxpos ? "at least" : "exactly", - minposonly, - minposonly == 1 ? "" : "s", - nargs); - return NULL; - } - - /* copy tuple args */ - for (i = 0; i < nargs; i++) { - buf[i] = args[i]; - } - - /* copy keyword args using kwtuple to drive process */ - for (i = Py_MAX((int)nargs, posonly); i < maxargs; i++) { - if (nkwargs) { - keyword = PyTuple_GET_ITEM(kwtuple, i - posonly); - if (kwargs != NULL) { - current_arg = PyDict_GetItemWithError(kwargs, keyword); - if (!current_arg && PyErr_Occurred()) { - return NULL; - } - } - else { - current_arg = find_keyword(kwnames, kwstack, keyword); - } - } - else if (i >= reqlimit) { - break; - } - else { - current_arg = NULL; - } - - buf[i] = current_arg; - - if (current_arg) { - --nkwargs; - } - else if (i < minpos || (maxpos <= i && i < reqlimit)) { - /* Less arguments than required */ - keyword = PyTuple_GET_ITEM(kwtuple, i - posonly); - PyErr_Format(PyExc_TypeError, "%.200s%s missing required " - "argument '%U' (pos %d)", - (parser->fname == NULL) ? "function" : parser->fname, - (parser->fname == NULL) ? "" : "()", - keyword, i+1); - return NULL; - } - } - - if (nkwargs > 0) { - Py_ssize_t j; - /* make sure there are no arguments given by name and position */ - for (i = posonly; i < nargs; i++) { - keyword = PyTuple_GET_ITEM(kwtuple, i - posonly); - if (kwargs != NULL) { - current_arg = PyDict_GetItemWithError(kwargs, keyword); - if (!current_arg && PyErr_Occurred()) { - return NULL; - } - } - else { - current_arg = find_keyword(kwnames, kwstack, keyword); - } - if (current_arg) { - /* arg present in tuple and in dict */ - PyErr_Format(PyExc_TypeError, - "argument for %.200s%s given by name ('%U') " - "and position (%d)", - (parser->fname == NULL) ? "function" : parser->fname, - (parser->fname == NULL) ? "" : "()", - keyword, i+1); - return NULL; - } - } - /* make sure there are no extraneous keyword arguments */ - j = 0; - while (1) { - int match; - if (kwargs != NULL) { - if (!PyDict_Next(kwargs, &j, &keyword, NULL)) - break; - } - else { - if (j >= PyTuple_GET_SIZE(kwnames)) - break; - keyword = PyTuple_GET_ITEM(kwnames, j); - j++; - } - - match = PySequence_Contains(kwtuple, keyword); - if (match <= 0) { - if (!match) { - PyErr_Format(PyExc_TypeError, - "'%S' is an invalid keyword " - "argument for %.200s%s", - keyword, - (parser->fname == NULL) ? "this function" : parser->fname, - (parser->fname == NULL) ? "" : "()"); - } - return NULL; - } - } - } - - return buf; -} \ No newline at end of file