Newer
Older
/* Copyright (C) European XFEL GmbH Schenefeld. All rights reserved.
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at https://mozilla.org/MPL/2.0/. */
//#define Py_LIMITED_API 0x030B0000
#include <stdlib.h>
#include <Python.h>
#include "zlib.h"
#define DEF_MEM_LEVEL 8
static void*
PyZlib_Malloc(voidpf ctx, uInt items, uInt size)
{
if (size != 0 && items > (size_t)PY_SSIZE_T_MAX / size)
return NULL;
/* PyMem_Malloc() cannot be used: the GIL is not held when
inflate() and deflate() are called */
/* The builtin zlib module uses PyMem_RawMalloc here, but this was only
added to the stable ABI from Python 3.13, so use plain malloc for now */
return malloc((size_t)items * (size_t)size);
}
static void
PyZlib_Free(voidpf ctx, void *ptr)
{
/* The builtin zlib module uses PyMem_RawFree here, but this was only
added to the stable ABI from Python 3.13, so use plain free for now */
free(ptr);
}
// zlib_error copied from CPython
static void
zlib_error(z_stream zst, int err, const char *msg)
{
const char *zmsg = Z_NULL;
/* In case of a version mismatch, zst.msg won't be initialized.
Check for this case first, before looking at zst.msg. */
if (err == Z_VERSION_ERROR)
zmsg = "library version mismatch";
if (zmsg == Z_NULL)
zmsg = zst.msg;
if (zmsg == Z_NULL) {
switch (err) {
case Z_BUF_ERROR:
zmsg = "incomplete or truncated stream";
break;
case Z_STREAM_ERROR:
zmsg = "inconsistent stream state";
break;
case Z_DATA_ERROR:
zmsg = "invalid input data";
break;
}
}
if (zmsg == Z_NULL)
PyErr_Format(PyExc_RuntimeError, "Error %d %s", err, msg);
else
PyErr_Format(PyExc_RuntimeError, "Error %d %s: %.200s", err, msg, zmsg);
}
compress_into(PyObject *module, PyObject *args, PyObject *kwargs) {
static char *keywords[] = {"data", "output", "level", "wbits", NULL};
PyObject *return_value = NULL;
Py_ssize_t bytes_written;
Py_buffer input, output;
int level=Z_DEFAULT_COMPRESSION, wbits=MAX_WBITS;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "y*w*|ii", keywords,
&input, &output, &level, &wbits)) {
return NULL;
}
zst.opaque = NULL;
zst.zalloc = PyZlib_Malloc;
zst.zfree = PyZlib_Free;
zst.next_in = input.buf;
zst.avail_in = input.len;
zst.next_out = output.buf;
zst.avail_out = output.len;
int err = deflateInit2(&zst, level, Z_DEFLATED, wbits, DEF_MEM_LEVEL,
Z_DEFAULT_STRATEGY);
switch (err) {
case Z_OK:
break;
case Z_MEM_ERROR:
PyErr_SetString(PyExc_MemoryError,
"Out of memory while compressing data");
goto done;
case Z_STREAM_ERROR:
PyErr_SetString(PyExc_ValueError, "Bad compression level");
goto done;
default:
deflateEnd(&zst);
zlib_error(zst, err, "while preparing to compress data");
goto done;
}
Py_BEGIN_ALLOW_THREADS
err = deflate(&zst, Z_FINISH);
Py_END_ALLOW_THREADS
switch (err) {
case Z_STREAM_END:
break;
case Z_OK:
case Z_BUF_ERROR:
deflateEnd(&zst);
PyErr_SetString(PyExc_ValueError, "Not enough space in output buffer");
goto done;
}
bytes_written = output.len - zst.avail_out;
err = deflateEnd(&zst);
if (err != Z_OK) {
zlib_error(zst, err, "while finishing compression");
goto done;
}
return_value = PyLong_FromSsize_t(bytes_written);
done:
PyBuffer_Release(&input);
PyBuffer_Release(&output);
return return_value;
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
static PyObject *
decompress_into(PyObject *module, PyObject *args, PyObject *kwargs) {
static char *keywords[] = {"data", "output", "wbits", NULL};
PyObject *return_value = NULL;
Py_ssize_t bytes_written;
Py_buffer input, output;
int wbits=MAX_WBITS;
z_stream zst;
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "y*w*|i", keywords,
&input, &output, &wbits)) {
return NULL;
}
zst.opaque = NULL;
zst.zalloc = PyZlib_Malloc;
zst.zfree = PyZlib_Free;
zst.next_in = input.buf;
zst.avail_in = input.len;
zst.next_out = output.buf;
zst.avail_out = output.len;
int err = inflateInit2(&zst, wbits);
switch (err) {
case Z_OK:
break;
case Z_MEM_ERROR:
PyErr_SetString(PyExc_MemoryError,
"Out of memory while decompressing data");
goto done;
default:
deflateEnd(&zst);
zlib_error(zst, err, "while preparing to decompress data");
goto done;
}
Py_BEGIN_ALLOW_THREADS
err = inflate(&zst, Z_FINISH);
Py_END_ALLOW_THREADS
switch (err) {
case Z_STREAM_END:
break;
case Z_OK:
case Z_BUF_ERROR:
deflateEnd(&zst);
PyErr_SetString(PyExc_ValueError, "Not enough space in output buffer");
goto done;
default:
deflateEnd(&zst);
zlib_error(zst, err, "while compressing data");
goto done;
}
bytes_written = output.len - zst.avail_out;
err = inflateEnd(&zst);
if (err != Z_OK) {
zlib_error(zst, err, "while finishing decompression");
goto done;
}
return_value = PyLong_FromSsize_t(bytes_written);
done:
PyBuffer_Release(&input);
PyBuffer_Release(&output);
return return_value;
{"compress_into", (PyCFunction)compress_into, METH_VARARGS | METH_KEYWORDS, "zlib compress data into a buffer"},
{"decompress_into", (PyCFunction)decompress_into, METH_VARARGS | METH_KEYWORDS, "zlib decompress data into a buffer"},
{NULL, NULL, 0, NULL}
};
static struct PyModuleDef zlibintomodule = {
PyModuleDef_HEAD_INIT,
"zlib_into",
NULL, // docstring
-1,
ZlibIntoMethods
};
PyMODINIT_FUNC
PyInit_zlib_into(void) {
return PyModule_Create(&zlibintomodule);
}