Commit 39079946 authored by Nadeem Vawda's avatar Nadeem Vawda

Issue #16350: Fix zlib decompressor handling of unused_data with multiple...

Issue #16350: Fix zlib decompressor handling of unused_data with multiple calls to decompress() after EOF.

Patch by Serhiy Storchaka.
parent 6c5f5210
...@@ -434,6 +434,19 @@ class CompressObjectTestCase(BaseCompressTestCase, unittest.TestCase): ...@@ -434,6 +434,19 @@ class CompressObjectTestCase(BaseCompressTestCase, unittest.TestCase):
y += dco.flush() y += dco.flush()
self.assertEqual(y, b'foo') self.assertEqual(y, b'foo')
def test_decompress_unused_data(self):
# Repeated calls to decompress() after EOF should accumulate data in
# dco.unused_data, instead of just storing the arg to the last call.
x = zlib.compress(HAMLET_SCENE) + HAMLET_SCENE
for step in 1, 2, 100:
dco = zlib.decompressobj()
data = b''.join(dco.decompress(x[i : i + step])
for i in range(0, len(x), step))
data += dco.flush()
self.assertEqual(data, HAMLET_SCENE)
self.assertEqual(dco.unused_data, HAMLET_SCENE)
if hasattr(zlib.compressobj(), "copy"): if hasattr(zlib.compressobj(), "copy"):
def test_compresscopy(self): def test_compresscopy(self):
# Test copying a compression object # Test copying a compression object
......
...@@ -156,6 +156,10 @@ Core and Builtins ...@@ -156,6 +156,10 @@ Core and Builtins
Library Library
------- -------
- Issue #16350: zlib.Decompress.decompress() now accumulates data from
successive calls after EOF in unused_data, instead of only saving the argument
to the last call. Patch by Serhiy Storchaka.
- Issue #12759: sre_parse now raises a proper error when the name of the group - Issue #12759: sre_parse now raises a proper error when the name of the group
is missing. Initial patch by Serhiy Storchaka. is missing. Initial patch by Serhiy Storchaka.
......
...@@ -610,12 +610,29 @@ PyZlib_objdecompress(compobject *self, PyObject *args) ...@@ -610,12 +610,29 @@ PyZlib_objdecompress(compobject *self, PyObject *args)
preserved. preserved.
*/ */
if (err == Z_STREAM_END) { if (err == Z_STREAM_END) {
Py_XDECREF(self->unused_data); /* Free original empty string */ if (self->zst.avail_in > 0) {
self->unused_data = PyBytes_FromStringAndSize( /* Append the leftover data to the existing value of unused_data. */
(char *)self->zst.next_in, self->zst.avail_in); Py_ssize_t old_size = PyBytes_GET_SIZE(self->unused_data);
if (self->unused_data == NULL) { Py_ssize_t new_size = old_size + self->zst.avail_in;
Py_DECREF(RetVal); PyObject *new_data;
goto error; if (new_size <= old_size) { /* Check for overflow. */
PyErr_NoMemory();
Py_DECREF(RetVal);
RetVal = NULL;
goto error;
}
new_data = PyBytes_FromStringAndSize(NULL, new_size);
if (new_data == NULL) {
Py_DECREF(RetVal);
RetVal = NULL;
goto error;
}
Py_MEMCPY(PyBytes_AS_STRING(new_data),
PyBytes_AS_STRING(self->unused_data), old_size);
Py_MEMCPY(PyBytes_AS_STRING(new_data) + old_size,
self->zst.next_in, self->zst.avail_in);
Py_DECREF(self->unused_data);
self->unused_data = new_data;
} }
/* We will only get Z_BUF_ERROR if the output buffer was full /* We will only get Z_BUF_ERROR if the output buffer was full
but there wasn't more output when we tried again, so it is but there wasn't more output when we tried again, so it is
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment