Patch #1435422: zlib's compress and decompress objects now have a

copy() method.
This commit is contained in:
Georg Brandl 2006-05-16 07:38:27 +00:00
parent 5f5d99c215
commit 8d3342b489
4 changed files with 175 additions and 0 deletions

View File

@ -123,6 +123,12 @@ prevents compressing any more data. After calling
action is to delete the object. action is to delete the object.
\end{methoddesc} \end{methoddesc}
\begin{methoddesc}[Compress]{copy}{}
Returns a copy of the compression object. This can be used to efficiently
compress a set of data that share a common initial prefix.
\versionadded{2.5}
\end{methoddesc}
Decompression objects support the following methods, and two attributes: Decompression objects support the following methods, and two attributes:
\begin{memberdesc}{unused_data} \begin{memberdesc}{unused_data}
@ -176,6 +182,13 @@ The optional parameter \var{length} sets the initial size of the
output buffer. output buffer.
\end{methoddesc} \end{methoddesc}
\begin{methoddesc}[Decompress]{copy}{}
Returns a copy of the decompression object. This can be used to save the
state of the decompressor midway through the data stream in order to speed up
random seeks into the stream at a future point.
\versionadded{2.5}
\end{methoddesc}
\begin{seealso} \begin{seealso}
\seemodule{gzip}{Reading and writing \program{gzip}-format files.} \seemodule{gzip}{Reading and writing \program{gzip}-format files.}
\seeurl{http://www.zlib.net}{The zlib library home page.} \seeurl{http://www.zlib.net}{The zlib library home page.}

View File

@ -302,6 +302,63 @@ class CompressObjectTestCase(unittest.TestCase):
dco = zlib.decompressobj() dco = zlib.decompressobj()
self.assertEqual(dco.flush(), "") # Returns nothing self.assertEqual(dco.flush(), "") # Returns nothing
def test_compresscopy(self):
# Test copying a compression object
data0 = HAMLET_SCENE
data1 = HAMLET_SCENE.swapcase()
c0 = zlib.compressobj(zlib.Z_BEST_COMPRESSION)
bufs0 = []
bufs0.append(c0.compress(data0))
c1 = c0.copy()
bufs1 = bufs0[:]
bufs0.append(c0.compress(data0))
bufs0.append(c0.flush())
s0 = ''.join(bufs0)
bufs1.append(c1.compress(data1))
bufs1.append(c1.flush())
s1 = ''.join(bufs1)
self.assertEqual(zlib.decompress(s0),data0+data0)
self.assertEqual(zlib.decompress(s1),data0+data1)
def test_badcompresscopy(self):
# Test copying a compression object in an inconsistent state
c = zlib.compressobj()
c.compress(HAMLET_SCENE)
c.flush()
self.assertRaises(ValueError, c.copy)
def test_decompresscopy(self):
# Test copying a decompression object
data = HAMLET_SCENE
comp = zlib.compress(data)
d0 = zlib.decompressobj()
bufs0 = []
bufs0.append(d0.decompress(comp[:32]))
d1 = d0.copy()
bufs1 = bufs0[:]
bufs0.append(d0.decompress(comp[32:]))
s0 = ''.join(bufs0)
bufs1.append(d1.decompress(comp[32:]))
s1 = ''.join(bufs1)
self.assertEqual(s0,s1)
self.assertEqual(s0,data)
def test_baddecompresscopy(self):
# Test copying a compression object in an inconsistent state
data = zlib.compress(HAMLET_SCENE)
d = zlib.decompressobj()
d.decompress(data)
d.flush()
self.assertRaises(ValueError, d.copy)
def genblock(seed, length, step=1024, generator=random): def genblock(seed, length, step=1024, generator=random):
"""length-byte stream of random data from a seed (in step-byte blocks).""" """length-byte stream of random data from a seed (in step-byte blocks)."""

View File

@ -28,6 +28,9 @@ Core and builtins
Extension Modules Extension Modules
----------------- -----------------
- Patch #1435422: zlib's compress and decompress objects now have a
copy() method.
- On Win32, os.listdir now supports arbitrarily-long Unicode path names - On Win32, os.listdir now supports arbitrarily-long Unicode path names
(up to the system limit of 32K characters). (up to the system limit of 32K characters).

View File

@ -653,6 +653,104 @@ PyZlib_flush(compobject *self, PyObject *args)
return RetVal; return RetVal;
} }
PyDoc_STRVAR(comp_copy__doc__,
"copy() -- Return a copy of the compression object.");
static PyObject *
PyZlib_copy(compobject *self)
{
compobject *retval = NULL;
int err;
retval = newcompobject(&Comptype);
if (!retval) return NULL;
/* Copy the zstream state
* We use ENTER_ZLIB / LEAVE_ZLIB to make this thread-safe
*/
ENTER_ZLIB
err = deflateCopy(&retval->zst, &self->zst);
switch(err) {
case(Z_OK):
break;
case(Z_STREAM_ERROR):
PyErr_SetString(PyExc_ValueError, "Inconsistent stream state");
goto error;
case(Z_MEM_ERROR):
PyErr_SetString(PyExc_MemoryError,
"Can't allocate memory for compression object");
goto error;
default:
zlib_error(self->zst, err, "while copying compression object");
goto error;
}
retval->unused_data = self->unused_data;
retval->unconsumed_tail = self->unconsumed_tail;
Py_INCREF(retval->unused_data);
Py_INCREF(retval->unconsumed_tail);
/* Mark it as being initialized */
retval->is_initialised = 1;
LEAVE_ZLIB
return (PyObject *)retval;
error:
LEAVE_ZLIB
Py_XDECREF(retval);
return NULL;
}
PyDoc_STRVAR(decomp_copy__doc__,
"copy() -- Return a copy of the decompression object.");
static PyObject *
PyZlib_uncopy(compobject *self)
{
compobject *retval = NULL;
int err;
retval = newcompobject(&Decomptype);
if (!retval) return NULL;
/* Copy the zstream state
* We use ENTER_ZLIB / LEAVE_ZLIB to make this thread-safe
*/
ENTER_ZLIB
err = inflateCopy(&retval->zst, &self->zst);
switch(err) {
case(Z_OK):
break;
case(Z_STREAM_ERROR):
PyErr_SetString(PyExc_ValueError, "Inconsistent stream state");
goto error;
case(Z_MEM_ERROR):
PyErr_SetString(PyExc_MemoryError,
"Can't allocate memory for decompression object");
goto error;
default:
zlib_error(self->zst, err, "while copying decompression object");
goto error;
}
retval->unused_data = self->unused_data;
retval->unconsumed_tail = self->unconsumed_tail;
Py_INCREF(retval->unused_data);
Py_INCREF(retval->unconsumed_tail);
/* Mark it as being initialized */
retval->is_initialised = 1;
LEAVE_ZLIB
return (PyObject *)retval;
error:
LEAVE_ZLIB
Py_XDECREF(retval);
return NULL;
}
PyDoc_STRVAR(decomp_flush__doc__, PyDoc_STRVAR(decomp_flush__doc__,
"flush( [length] ) -- Return a string containing any remaining\n" "flush( [length] ) -- Return a string containing any remaining\n"
"decompressed data. length, if given, is the initial size of the\n" "decompressed data. length, if given, is the initial size of the\n"
@ -725,6 +823,8 @@ static PyMethodDef comp_methods[] =
comp_compress__doc__}, comp_compress__doc__},
{"flush", (binaryfunc)PyZlib_flush, METH_VARARGS, {"flush", (binaryfunc)PyZlib_flush, METH_VARARGS,
comp_flush__doc__}, comp_flush__doc__},
{"copy", (PyCFunction)PyZlib_copy, METH_NOARGS,
comp_copy__doc__},
{NULL, NULL} {NULL, NULL}
}; };
@ -734,6 +834,8 @@ static PyMethodDef Decomp_methods[] =
decomp_decompress__doc__}, decomp_decompress__doc__},
{"flush", (binaryfunc)PyZlib_unflush, METH_VARARGS, {"flush", (binaryfunc)PyZlib_unflush, METH_VARARGS,
decomp_flush__doc__}, decomp_flush__doc__},
{"copy", (PyCFunction)PyZlib_uncopy, METH_NOARGS,
decomp_copy__doc__},
{NULL, NULL} {NULL, NULL}
}; };