Index: Doc/library/zlib.rst =================================================================== --- Doc/library/zlib.rst (revision 71045) +++ Doc/library/zlib.rst (working copy) @@ -111,7 +111,7 @@ regardless of platform. -.. function:: decompress(string[, wbits[, bufsize]]) +.. function:: decompress(string[, wbits[, bufsize[, offset]]]) Decompresses the data in *string*, returning a string containing the uncompressed data. The *wbits* parameter controls the size of the window @@ -132,7 +132,16 @@ don't have to get this value exactly right; tuning it will only save a few calls to :cfunc:`malloc`. The default size is 16384. + *offset* is the initial position in *string* to start decompression. + When specified, it will cause the function's return value to be a (uncompressed, offset) + tuple, with the second part giving the position in the input string just after + any compressed data. This is useful if *string* contains additional data after + any initial compressed part. +.. versionchanged:: 2.7 + Added the *offset* argument + + .. function:: decompressobj([wbits]) Returns a decompression object, to be used for decompressing data streams that Index: Lib/test/test_zlib.py =================================================================== --- Lib/test/test_zlib.py (revision 71045) +++ Lib/test/test_zlib.py (working copy) @@ -103,9 +103,19 @@ x = zlib.compress(data) self.assertEqual(zlib.decompress(x), data) + def test_offset(self): + # test the "tail" feature of decompression + a = HAMLET_SCENE + b = a.swapcase()*2 + c = zlib.compress(a)+zlib.compress(b) + + aa, offset = zlib.decompress(c, offset=0) + bb, offset = zlib.decompress(c, offset=offset) + self.assertEqual(a, aa) + self.assertEqual(b, bb) + self.assertEqual(offset, len(c)) - class CompressObjectTestCase(unittest.TestCase): # Test compression object def test_pair(self): @@ -380,6 +390,16 @@ d.flush() self.assertRaises(ValueError, d.copy) + def test_unused_data(self): + #test any unused data + data = HAMLET_SCENE + unused = data.swapcase()*2 + x = zlib.compress(data) + unused + dco = zlib.decompressobj() + y = dco.decompress(x) + dco.flush() + self.assertEqual(y, data) + self.assertEqual(dco.unused_data, unused) + def genblock(seed, length, step=1024, generator=random): """length-byte stream of random data from a seed (in step-byte blocks).""" if seed is not None: Index: Modules/zlibmodule.c =================================================================== --- Modules/zlibmodule.c (revision 71045) +++ Modules/zlibmodule.c (working copy) @@ -186,28 +186,40 @@ } PyDoc_STRVAR(decompress__doc__, -"decompress(string[, wbits[, bufsize]]) -- Return decompressed string.\n" +"decompress(string[, wbits[, bufsize[, offset]]]) -- Return decompressed string.\n" "\n" "Optional arg wbits is the window buffer size. Optional arg bufsize is\n" -"the initial output buffer size."); +"the initial output buffer size. Optional arg offset specifies\n" +"the start position in the string to start decompresion and, if spceified,\n" +"causes the return value to be a tuple, with the second element being\n" +"the position in the string just afer the last piece of compressed data."); static PyObject * -PyZlib_decompress(PyObject *self, PyObject *args) +PyZlib_decompress(PyObject *self, PyObject *args, PyObject *kw) { PyObject *result_str; Byte *input; int length, err; int wsize=DEF_WBITS; Py_ssize_t r_strlen=DEFAULTALLOC; + Py_ssize_t offset = -1; z_stream zst; + char *keywords[] = {"string", "wbits", "bufsize", "offset", 0}; - if (!PyArg_ParseTuple(args, "s#|in:decompress", - &input, &length, &wsize, &r_strlen)) + if (!PyArg_ParseTupleAndKeywords(args, kw, "s#|inn:decompress", keywords, + &input, &length, &wsize, &r_strlen, &offset)) return NULL; - if (r_strlen <= 0) r_strlen = 1; + /* if specified, don't start at the beginning of the input string */ + if (offset > 0) { + if (offset > length) + offset = length; + length -= offset; + input += offset; + } + zst.avail_in = length; zst.avail_out = r_strlen; @@ -279,6 +291,11 @@ } _PyString_Resize(&result_str, zst.total_out); + if (result_str != NULL && offset >= 0) { + /* compute new offset to current pos */ + offset = length-zst.avail_in+offset; + return Py_BuildValue("Nn", result_str, offset); + } return result_str; error: @@ -941,7 +958,7 @@ compressobj__doc__}, {"crc32", (PyCFunction)PyZlib_crc32, METH_VARARGS, crc32__doc__}, - {"decompress", (PyCFunction)PyZlib_decompress, METH_VARARGS, + {"decompress", (PyCFunction)PyZlib_decompress, METH_KEYWORDS, decompress__doc__}, {"decompressobj", (PyCFunction)PyZlib_decompressobj, METH_VARARGS, decompressobj__doc__},