[pypy-svn] r46669 - in pypy/branch/zlib-rffi-impl/pypy/module/zlib: . test

exarkun at codespeak.net exarkun at codespeak.net
Sun Sep 16 02:29:34 CEST 2007


Author: exarkun
Date: Sun Sep 16 02:29:34 2007
New Revision: 46669

Added:
   pypy/branch/zlib-rffi-impl/pypy/module/zlib/
   pypy/branch/zlib-rffi-impl/pypy/module/zlib/__init__.py
   pypy/branch/zlib-rffi-impl/pypy/module/zlib/app_zlib.py
   pypy/branch/zlib-rffi-impl/pypy/module/zlib/interp_zlib.py
   pypy/branch/zlib-rffi-impl/pypy/module/zlib/test/
   pypy/branch/zlib-rffi-impl/pypy/module/zlib/test/test_zlib.py
Log:
first attempt

Added: pypy/branch/zlib-rffi-impl/pypy/module/zlib/__init__.py
==============================================================================
--- (empty file)
+++ pypy/branch/zlib-rffi-impl/pypy/module/zlib/__init__.py	Sun Sep 16 02:29:34 2007
@@ -0,0 +1,38 @@
+
+"""
+Mixed-module definition for the zlib module.
+"""
+
+import zlib
+
+from pypy.interpreter.mixedmodule import MixedModule
+
+def constant(value):
+    return 'space.wrap(%s)' % (value,)
+
+
+class Module(MixedModule):
+    interpleveldefs = {
+        'crc32': 'interp_zlib.crc32',
+        'adler32': 'interp_zlib.adler32',
+        'Compress': 'interp_zlib.Compress',
+        'Decompress': 'interp_zlib.Decompress',
+        }
+
+#     # Constants exposed by zlib.h
+#     interpleveldefs.update((
+#             (name, constant(getattr(zlib, name)))
+#             for name
+#             in ['DEFLATED', 'DEF_MEM_LEVEL', 'MAX_WBITS',
+#                 'Z_BEST_COMPRESSION', 'Z_BEST_SPEED',
+#                 'Z_DEFAULT_COMPRESSION', 'Z_DEFAULT_STRATEGY',
+#                 'Z_FILTERED', 'Z_FINISH', 'Z_FULL_FLUSH',
+#                 'Z_HUFFMAN_ONLY', 'Z_NO_FLUSH', 'Z_SYNC_FLUSH']))
+
+    appleveldefs = {
+        'error': 'app_zlib.error',
+        'compress': 'app_zlib.compress',
+        'decompress': 'app_zlib.decompress',
+        'compressobj': 'app_zlib.compressobj',
+        'decompressobj': 'app_zlib.decompressobj',
+        }

Added: pypy/branch/zlib-rffi-impl/pypy/module/zlib/app_zlib.py
==============================================================================
--- (empty file)
+++ pypy/branch/zlib-rffi-impl/pypy/module/zlib/app_zlib.py	Sun Sep 16 02:29:34 2007
@@ -0,0 +1,54 @@
+
+"""
+Application-level definitions for the zlib module.
+
+NOT_RPYTHON
+"""
+
+class error(Exception):
+    """
+    Raised by zlib operations.
+    """
+
+
+def compressobj(level=None):
+    """
+    compressobj([level]) -- Return a compressor object.
+
+    Optional arg level is the compression level, in 1-9.
+    """
+    import zlib
+    return zlib.Compress(level)
+
+
+
+def decompressobj(wbits=None):
+    """
+    decompressobj([wbits]) -- Return a decompressor object.
+
+    Optional arg wbits is the window buffer size.
+    """
+    import zlib
+    return zlib.Decompress(wbits)
+
+
+def compress(string, level=None):
+    """
+    compress(string[, level]) -- Returned compressed string.
+
+    Optional arg level is the compression level, in 1-9.
+    """
+    compressor = compressobj(level)
+    return compressor.compress(string) + compressor.flush()
+
+
+def decompress(string, wbits=None, bufsize=None):
+    """
+    decompress(string[, wbits[, bufsize]]) -- Return decompressed string.
+
+    Optional arg wbits is the window buffer size.  Optional arg bufsize is
+    the initial output buffer size.
+    """
+    # XXX bufsize is ignored because it's basically useless.
+    decompressor = decompressobj(wbits)
+    return decompressor.decompress(string) + decompressor.flush()

Added: pypy/branch/zlib-rffi-impl/pypy/module/zlib/interp_zlib.py
==============================================================================
--- (empty file)
+++ pypy/branch/zlib-rffi-impl/pypy/module/zlib/interp_zlib.py	Sun Sep 16 02:29:34 2007
@@ -0,0 +1,316 @@
+# import zlib
+
+from pypy.interpreter.gateway import ObjSpace, interp2app
+from pypy.interpreter.baseobjspace import Wrappable
+from pypy.interpreter.typedef import TypeDef
+from pypy.interpreter.error import OperationError
+
+from pypy.rpython.lltypesystem import rffi, lltype
+from pypy.rpython.tool import rffi_platform
+
+includes = ['zlib.h']
+libraries = ['z']
+
+class SimpleCConfig:
+    """
+    Definitions for basic types defined by zlib.
+    """
+    _includes_ = includes
+
+    # XXX If Z_PREFIX was defined for the libz build, then these types are
+    # named z_uInt, z_uLong, and z_Bytef instead.
+    uInt = rffi_platform.SimpleType('uInt', rffi.UINT)
+    uLong = rffi_platform.SimpleType('uLong', rffi.ULONG)
+    Bytef = rffi_platform.SimpleType('Bytef', rffi.UCHAR)
+    voidpf = rffi_platform.SimpleType('voidpf', rffi.VOIDP)
+
+    Z_OK = rffi_platform.ConstantInteger('Z_OK')
+    Z_STREAM_ERROR = rffi_platform.ConstantInteger('Z_STREAM_ERROR')
+
+    ZLIB_VERSION = rffi_platform.DefinedConstantString('ZLIB_VERSION')
+    Z_DEFAULT_COMPRESSION = rffi_platform.ConstantInteger(
+        'Z_DEFAULT_COMPRESSION')
+    Z_NO_FLUSH = rffi_platform.ConstantInteger(
+        'Z_NO_FLUSH')
+
+config = rffi_platform.configure(SimpleCConfig)
+voidpf = config['voidpf']
+uInt = config['uInt']
+uLong = config['uLong']
+Bytef = config['Bytef']
+Bytefp = lltype.Ptr(lltype.Array(Bytef, hints={'nolength': True}))
+
+Z_OK = config['Z_OK']
+Z_STREAM_ERROR = config['Z_STREAM_ERROR']
+
+ZLIB_VERSION = config['ZLIB_VERSION']
+Z_DEFAULT_COMPRESSION = config['Z_DEFAULT_COMPRESSION']
+Z_NO_FLUSH = config['Z_DEFAULT_COMPRESSION']
+
+class ComplexCConfig:
+    """
+    Definitions of structure types defined by zlib and based on SimpleCConfig
+    definitions.
+    """
+    _includes_ = includes
+
+    z_stream = rffi_platform.Struct(
+        'z_stream',
+        [('next_in', Bytefp),
+         ('avail_in', uInt),
+         ('total_in', uLong),
+
+         ('next_out', Bytefp),
+         ('avail_out', uInt),
+         ('total_out', uLong),
+
+         ('msg', rffi.CCHARP),
+
+         ('zalloc', lltype.Ptr(
+                    lltype.FuncType([voidpf, uInt, uInt], voidpf))),
+         ('zfree', lltype.Ptr(
+                    lltype.FuncType([voidpf, voidpf], lltype.Void))),
+
+         ('opaque', voidpf),
+
+         ('data_type', rffi.INT),
+         ('adler', uLong),
+         ('reserved', uLong)
+         ])
+
+config = rffi_platform.configure(ComplexCConfig)
+z_stream = config['z_stream']
+z_stream_p = lltype.Ptr(z_stream)
+
+def zlib_external(*a, **kw):
+    kw['includes'] = includes
+    kw['libraries'] = libraries
+    return rffi.llexternal(*a, **kw)
+
+_crc32 = zlib_external('crc32', [uLong, Bytefp, uInt], uLong)
+_adler32 = zlib_external('adler32', [uLong, Bytefp, uInt], uLong)
+_deflateInit = zlib_external(
+    'deflateInit_', [z_stream_p, rffi.INT, rffi.CCHARP, rffi.INT],
+    rffi.INT)
+_deflate = zlib_external('deflate', [z_stream_p, rffi.INT], rffi.INT)
+
+def crc32(space, string, start=0):
+    """
+    crc32(string[, start]) -- Compute a CRC-32 checksum of string.
+
+    An optional starting value can be specified.  The returned checksum is
+    an integer.
+    """
+    bytes = rffi.str2charp(string)
+    checksum = _crc32(start, rffi.cast(Bytefp, bytes), len(string))
+    rffi.free_charp(bytes)
+
+    # This is, perhaps, a little stupid.  zlib returns the checksum unsigned.
+    # CPython exposes it as a signed value, though. -exarkun
+    checksum = rffi.cast(rffi.INT, checksum)
+
+    return space.wrap(checksum)
+crc32.unwrap_spec = [ObjSpace, str, int]
+
+
+def adler32(space, string, start=1):
+    """
+    adler32(string[, start]) -- Compute an Adler-32 checksum of string.
+
+    An optional starting value can be specified.  The returned checksum is
+    an integer.
+    """
+    bytes = rffi.str2charp(string)
+    checksum = _adler32(start, rffi.cast(Bytefp, bytes), len(string))
+    rffi.free_charp(bytes)
+
+    # This is, perhaps, a little stupid.  zlib returns the checksum unsigned.
+    # CPython exposes it as a signed value, though. -exarkun
+    checksum = rffi.cast(rffi.INT, checksum)
+
+    return space.wrap(checksum)
+adler32.unwrap_spec = [ObjSpace, str, int]
+
+
+class _StreamBase(Wrappable):
+    """
+    Base for classes which want to have a z_stream allocated when they are
+    initialized and de-allocated when they are freed.
+    """
+    def __init__(self, space):
+        self.space = space
+        self.stream = lltype.malloc(z_stream, flavor='raw')
+        self.stream.c_zalloc = lltype.nullptr(z_stream.c_zalloc.TO)
+        self.stream.c_zfree = lltype.nullptr(z_stream.c_zfree.TO)
+        self.stream.c_avail_in = rffi.cast(lltype.Unsigned, 0)
+
+
+    def __del__(self):
+        lltype.free(self.stream, flavor='raw')
+
+
+
+def error_from_zlib(space, status):
+    if status == Z_STREAM_ERROR:
+        return OperationError(
+            space.w_ValueError,
+            space.wrap("Invalid initialization option"))
+    assert False, "unhandled status %s" % (status,)
+
+
+class Compress(_StreamBase):
+    """
+    Wrapper around zlib's z_stream structure which provides convenient
+    compression functionality.
+    """
+    def __init__(self, space, w_level):
+        # XXX CPython actually exposes 4 more undocumented parameters beyond
+        # level.
+        if space.is_w(w_level, space.w_None):
+            level = Z_DEFAULT_COMPRESSION
+        else:
+            level = space.int_w(w_level)
+
+        _StreamBase.__init__(self, space)
+
+        # XXX I want to call deflateInit, not deflateInit_
+        size = z_stream._hints['size']
+        version = rffi.str2charp(ZLIB_VERSION)
+        result = _deflateInit(self.stream, level, version, size)
+        rffi.free_charp(version)
+
+        if result != Z_OK:
+            raise error_from_zlib(self.space, result)
+
+
+    def compress(self, data, length=16384):
+        """
+        compress(data) -- Return a string containing data compressed.
+
+        After calling this function, some of the input data may still be stored
+        in internal buffers for later processing.
+
+        Call the flush() method to clear these buffers.
+        """
+        self.stream.c_avail_in = rffi.cast(lltype.Unsigned, len(data))
+        self.stream.c_next_in = lltype.malloc(
+            Bytefp.TO, len(data), flavor='raw')
+        for i in xrange(len(data)):
+            self.stream.c_next_in[i] = rffi.cast(Bytef, data[i])
+        try:
+            self.stream.c_avail_out = rffi.cast(lltype.Unsigned, length)
+            self.stream.c_next_out = lltype.malloc(
+                Bytefp.TO, length, flavor='raw')
+            try:
+                result = _deflate(self.stream, Z_NO_FLUSH)
+                if result != Z_OK:
+                    raise error_from_zlib(self.space, result)
+                else:
+                    return rffi.charp2str(self.stream.c_next_out)
+            finally:
+                lltype.free(self.stream.c_next_out, flavor='raw')
+                self.stream.c_avail_out = rffi.cast(lltype.Unsigned, 0)
+        finally:
+            lltype.free(self.stream.c_next_in, flavor='raw')
+            self.stream.c_avail_in = rffi.cast(lltype.Unsigned, 0)
+    compress.unwrap_spec = ['self', str, int]
+
+
+    def flush(self, mode=0): # XXX =Z_FINISH
+        """
+        flush( [mode] ) -- Return a string containing any remaining compressed
+        data.
+
+        mode can be one of the constants Z_SYNC_FLUSH, Z_FULL_FLUSH, Z_FINISH;
+        the default value used when mode is not specified is Z_FINISH.
+
+        If mode == Z_FINISH, the compressor object can no longer be used after
+        calling the flush() method.  Otherwise, more data can still be
+        compressed.
+        """
+        return self.space.wrap('')
+    flush.unwrap_spec = ['self', int]
+
+
+def Compress___new__(space, w_subtype, w_anything=None):
+    """
+    Create a new z_stream and call its initializer.
+    """
+    stream = space.allocate_instance(Compress, w_subtype)
+    stream = space.interp_w(Compress, stream)
+    Compress.__init__(stream, space, w_anything)
+    return space.wrap(stream)
+
+
+Compress.typedef = TypeDef(
+    'Compress',
+    __new__ = interp2app(Compress___new__),
+    compress = interp2app(Compress.compress),
+    flush = interp2app(Compress.flush))
+
+
+
+class Decompress(_StreamBase):
+    """
+    Wrapper around zlib's z_stream structure which provides convenient
+    decompression functionality.
+    """
+    def __init__(self, space, wbits):
+        """
+        Initialize a new decompression object.
+
+        wbits is an integer between 8 and MAX_WBITS or -8 and -MAX_WBITS
+        (inclusive) giving the number of "window bits" to use for compression
+        and decompression.  See the documentation for deflateInit2 and
+        inflateInit2.
+        """
+        _StreamBase.__init__(self, space)
+        self.wbits = wbits
+
+
+    def decompress(self, data, max_length=0): # XXX =None
+        """
+        decompress(data[, max_length]) -- Return a string containing the
+        decompressed version of the data.
+
+        After calling this function, some of the input data may still be stored
+        in internal buffers for later processing.
+
+        Call the flush() method to clear these buffers.
+
+        If the max_length parameter is specified then the return value will be
+        no longer than max_length.  Unconsumed input data will be stored in the
+        unconsumed_tail attribute.
+        """
+        return self.space.wrap('')
+    decompress.unwrap_spec = ['self', str, int]
+
+
+    def flush(self, length=0):
+        """
+        flush( [length] ) -- Return a string containing any remaining
+        decompressed data. length, if given, is the initial size of the output
+        buffer.
+
+        The decompressor object can no longer be used after this call.
+        """
+        return self.space.wrap('')
+    flush.unwrap_spec = ['self', int]
+
+
+def Decompress___new__(space, w_subtype, w_anything=None):
+    """
+    Create a new Decompress and call its initializer.
+    """
+    stream = space.allocate_instance(Decompress, w_subtype)
+    stream = space.interp_w(Decompress, stream)
+    Decompress.__init__(stream, space, w_anything)
+    return space.wrap(stream)
+
+
+
+Decompress.typedef = TypeDef(
+    'Decompress',
+    __new__ = interp2app(Decompress___new__),
+    decompress = interp2app(Decompress.decompress),
+    flush = interp2app(Decompress.flush))

Added: pypy/branch/zlib-rffi-impl/pypy/module/zlib/test/test_zlib.py
==============================================================================
--- (empty file)
+++ pypy/branch/zlib-rffi-impl/pypy/module/zlib/test/test_zlib.py	Sun Sep 16 02:29:34 2007
@@ -0,0 +1,115 @@
+
+"""
+Tests for the zlib module.
+"""
+
+import zlib
+
+from pypy.conftest import gettestobjspace
+
+class AppTestZlib(object):
+    def setup_class(cls):
+        """
+        Create a space with the zlib module and import it for use by the tests.
+        Also create some compressed data with the bootstrap zlib module so that
+        compression and decompression tests have a little real data to assert
+        against.
+        """
+        cls.space = gettestobjspace(usemodules=['zlib'])
+        cls.w_zlib = cls.space.call_function(
+            cls.space.builtin.get('__import__'),
+            cls.space.wrap('zlib'))
+        expanded = 'some bytes which will be compressed'
+        cls.w_expanded = cls.space.wrap(expanded)
+        cls.w_compressed = cls.space.wrap(zlib.compress(expanded))
+
+
+    def test_error(self):
+        """
+        zlib.error should be an exception class.
+        """
+        assert issubclass(self.zlib.error, Exception)
+
+
+    def test_crc32(self):
+        """
+        When called with a string, zlib.crc32 should compute its CRC32 and
+        return it as a signed 32 bit integer.
+        """
+        assert self.zlib.crc32('') == 0
+        assert self.zlib.crc32('\0') == -771559539
+        assert self.zlib.crc32('hello, world.') == -936931198
+
+
+    def test_crc32_start_value(self):
+        """
+        When called with a string and an integer, zlib.crc32 should compute the
+        CRC32 of the string using the integer as the starting value.
+        """
+        assert self.zlib.crc32('', 42) == 42
+        assert self.zlib.crc32('\0', 42) == 163128923
+        assert self.zlib.crc32('hello, world.', 42) == 1090960721
+        hello = 'hello, '
+        hellocrc = self.zlib.crc32(hello)
+        world = 'world.'
+        helloworldcrc = self.zlib.crc32(world, hellocrc)
+        assert helloworldcrc == self.zlib.crc32(hello + world)
+
+
+    def test_adler32(self):
+        """
+        When called with a string, zlib.crc32 should compute its adler 32
+        checksum and return it as a signed 32 bit integer.
+        """
+        assert self.zlib.adler32('') == 1
+        assert self.zlib.adler32('\0') == 65537
+        assert self.zlib.adler32('hello, world.') == 571147447
+        assert self.zlib.adler32('x' * 23) == -2122904887
+
+
+    def test_adler32_start_value(self):
+        """
+        When called with a string and an integer, zlib.adler32 should compute
+        the adler 32 checksum of the string using the integer as the starting
+        value.
+        """
+        assert self.zlib.adler32('', 42) == 42
+        assert self.zlib.adler32('\0', 42) == 2752554
+        assert self.zlib.adler32('hello, world.', 42) == 606078176
+        assert self.zlib.adler32('x' * 23, 42) == -2061104398
+        hello = 'hello, '
+        hellosum = self.zlib.adler32(hello)
+        world = 'world.'
+        helloworldsum = self.zlib.adler32(world, hellosum)
+        assert helloworldsum == self.zlib.adler32(hello + world)
+
+
+    def test_invalidLevel(self):
+        """
+        zlib.compressobj should raise ValueError when an out of bounds level is
+        passed to it.
+        """
+        raises(ValueError, self.zlib.compressobj, -2)
+        raises(ValueError, self.zlib.compressobj, 10)
+
+
+    def test_compression(self):
+        """
+        zlib.compressobj should return an object which can be used to compress
+        bytes.
+        """
+        compressor = self.zlib.compressobj()
+        bytes = compressor.compress(self.expanded)
+        bytes += compressor.flush()
+        assert bytes == self.compressed
+
+
+    def test_decompression(self):
+        """
+        zlib.decompressobj should return an object which can be used to
+        decompress bytes.
+        """
+        decompressor = self.zlib.decompressobj()
+        bytes = decompressor.decompress(self.compressed)
+        bytes += decompressor.flush()
+        assert bytes == self.expanded



More information about the Pypy-commit mailing list