Skip to content

Commit 03046ae

Browse files
committed
[GR-12615] Create minimal implementation of zlib module.
1 parent 2bfb146 commit 03046ae

File tree

7 files changed

+802
-8
lines changed

7 files changed

+802
-8
lines changed
Lines changed: 260 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,260 @@
1+
# Copyright (c) 2018, Oracle and/or its affiliates.
2+
# Copyright (c) 2013, Regents of the University of California
3+
#
4+
# All rights reserved.
5+
#
6+
# Redistribution and use in source and binary forms, with or without modification, are
7+
# permitted provided that the following conditions are met:
8+
#
9+
# 1. Redistributions of source code must retain the above copyright notice, this list of
10+
# conditions and the following disclaimer.
11+
# 2. Redistributions in binary form must reproduce the above copyright notice, this list of
12+
# conditions and the following disclaimer in the documentation and/or other materials provided
13+
# with the distribution.
14+
#
15+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
16+
# OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
17+
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
18+
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19+
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
20+
# GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
21+
# AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22+
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
23+
# OF THE POSSIBILITY OF SUCH DAMAGE.
24+
# Qunaibit 02/05/2014
25+
# With Statement
26+
27+
import unittest
28+
import zlib
29+
import binascii
30+
import re
31+
32+
pintNumber = 98765432109876543210
33+
longNumber = 9876543210
34+
35+
class MyIntObject:
36+
def __init__(self, value):
37+
self.value = value
38+
def __int__(self):
39+
return self.value
40+
41+
class MyIndexObject:
42+
def __init__(self, value):
43+
self.value = value
44+
def __index__(self):
45+
return self.value
46+
47+
class CustomInt:
48+
def __int__(self):
49+
return 100
50+
51+
class ChecksumTests(unittest.TestCase):
52+
53+
# checksum test cases
54+
def test_crc32start(self):
55+
self.assertEqual(zlib.crc32(b""), zlib.crc32(b"", 0))
56+
self.assertTrue(zlib.crc32(b"abc", 0xffffffff))
57+
58+
def test_crc32empty(self):
59+
self.assertEqual(zlib.crc32(b"", 0), 0)
60+
self.assertEqual(zlib.crc32(b"", 1), 1)
61+
self.assertEqual(zlib.crc32(b"", 432), 432)
62+
self.assertEqual(zlib.crc32(b"", -1), 4294967295)
63+
self.assertEqual(zlib.crc32(b"", longNumber), 1286608618)
64+
self.assertEqual(zlib.crc32(b"", pintNumber), 3844505322)
65+
self.assertEqual(zlib.crc32(b"", MyIntObject(10)), 10)
66+
67+
def test_adler32start(self):
68+
self.assertEqual(zlib.adler32(b""), zlib.adler32(b"", 1))
69+
self.assertTrue(zlib.adler32(b"abc", 0xffffffff))
70+
71+
def test_adler32empty(self):
72+
self.assertEqual(zlib.adler32(b"", 0), 0)
73+
self.assertEqual(zlib.adler32(b"", 1), 1)
74+
self.assertEqual(zlib.adler32(b"", 432), 432)
75+
self.assertEqual(zlib.adler32(b"", longNumber), 1286608618)
76+
self.assertEqual(zlib.adler32(b"", pintNumber), 3844505322)
77+
self.assertEqual(zlib.adler32(b"", MyIntObject(10)), 10)
78+
79+
def test_penguins(self):
80+
self.assertEqual(zlib.crc32(b"penguin", 0), 0x0e5c1a120)
81+
self.assertEqual(zlib.crc32(b"penguin", 1), 0x43b6aa94)
82+
self.assertEqual(zlib.adler32(b"penguin", 0), 0x0bcf02f6)
83+
self.assertEqual(zlib.adler32(b"penguin", 1), 0x0bd602f7)
84+
self.assertEqual(zlib.crc32(b"penguin"), zlib.crc32(b"penguin", 0))
85+
self.assertEqual(zlib.adler32(b"penguin"),zlib.adler32(b"penguin",1))
86+
87+
def test_crc32_adler32_unsigned(self):
88+
foo = b'abcdefghijklmnop'
89+
# explicitly test signed behavior
90+
self.assertEqual(zlib.crc32(foo), 2486878355)
91+
self.assertEqual(zlib.crc32(b'spam'), 1138425661)
92+
self.assertEqual(zlib.adler32(foo+foo), 3573550353)
93+
self.assertEqual(zlib.adler32(b'spam'), 72286642)
94+
95+
def test_same_as_binascii_crc32(self):
96+
foo = b'abcdefghijklmnop'
97+
crc = 2486878355
98+
self.assertEqual(binascii.crc32(foo), crc)
99+
self.assertEqual(zlib.crc32(foo), crc)
100+
self.assertEqual(binascii.crc32(b'spam'), zlib.crc32(b'spam'))
101+
102+
def test_wrong_inputs(self):
103+
self.assertRaises(TypeError, zlib.crc32, 10)
104+
self.assertRaises(TypeError, zlib.crc32, 'ahoj')
105+
self.assertRaises(TypeError, zlib.crc32, b'ahoj', MyIndexObject(10))
106+
self.assertRaises(TypeError, zlib.adler32, 10)
107+
self.assertRaises(TypeError, zlib.adler32, 'ahoj')
108+
self.assertRaises(TypeError, zlib.adler32, b'ahoj', MyIndexObject(10))
109+
110+
class BaseCompressTestCase(object):
111+
def check_big_compress_buffer(self, size, compress_func):
112+
_1M = 1024 * 1024
113+
# Generate 10 MiB worth of random, and expand it by repeating it.
114+
# The assumption is that zlib's memory is not big enough to exploit
115+
# such spread out redundancy.
116+
data = b''.join([random.getrandbits(8 * _1M).to_bytes(_1M, 'little')
117+
for i in range(10)])
118+
data = data * (size // len(data) + 1)
119+
try:
120+
compress_func(data)
121+
finally:
122+
# Release memory
123+
data = None
124+
125+
def check_big_decompress_buffer(self, size, decompress_func):
126+
data = b'x' * size
127+
try:
128+
compressed = zlib.compress(data, 1)
129+
finally:
130+
# Release memory
131+
data = None
132+
data = decompress_func(compressed)
133+
# Sanity check
134+
try:
135+
self.assertEqual(len(data), size)
136+
self.assertEqual(len(data.strip(b'x')), 0)
137+
finally:
138+
data = None
139+
140+
def assertRaisesRegex(self, expected_exception, expected_regex, function,
141+
*args, **kwargs):
142+
"""Asserts that the message in a raised exception matches a regex.
143+
144+
Args:
145+
expected_exception: Exception class expected to be raised.
146+
expected_regex: Regex (re.Pattern object or string) expected
147+
to be found in error message.
148+
args: Function to be called and extra positional args.
149+
kwargs: Extra kwargs.
150+
msg: Optional message used in case of failure. Can only be used
151+
when assertRaisesRegex is used as a context manager.
152+
"""
153+
try:
154+
function(*args, **kwargs)
155+
except expected_exception as e:
156+
if not re.compile(expected_regex).search(str(e)):
157+
assert False, "exception message '%r' does not match '%r'" % (e, expected_regex)
158+
else:
159+
assert False, "expected '%r' to raise '%r'" % (self.function, exc_type)
160+
161+
class CompressTests(BaseCompressTestCase, unittest.TestCase):
162+
# Test compression in one go (whole message compression)
163+
def test_speech(self):
164+
x = zlib.compress(HAMLET_SCENE)
165+
self.assertEqual(zlib.decompress(x), HAMLET_SCENE)
166+
167+
def test_keywords(self):
168+
x = zlib.compress(HAMLET_SCENE, level=3)
169+
self.assertEqual(zlib.decompress(x), HAMLET_SCENE)
170+
with self.assertRaises(TypeError):
171+
zlib.compress(data=HAMLET_SCENE, level=3)
172+
self.assertEqual(zlib.decompress(x,
173+
wbits=zlib.MAX_WBITS,
174+
bufsize=zlib.DEF_BUF_SIZE),
175+
HAMLET_SCENE)
176+
177+
def test_speech128(self):
178+
# compress more data
179+
data = HAMLET_SCENE * 128
180+
x = zlib.compress(data)
181+
self.assertEqual(zlib.compress(bytearray(data)), x)
182+
for ob in x, bytearray(x):
183+
self.assertEqual(zlib.decompress(ob), data)
184+
185+
def test_incomplete_stream(self):
186+
187+
# A useful error message is given
188+
x = zlib.compress(HAMLET_SCENE)
189+
self.assertRaisesRegex(zlib.error,
190+
"Error -5 while decompressing data: incomplete or truncated stream",
191+
zlib.decompress, x[:-1])
192+
193+
def test_custom_bufsize(self):
194+
data = HAMLET_SCENE * 10
195+
compressed = zlib.compress(data, 1)
196+
self.assertEqual(zlib.decompress(compressed, 15, CustomInt()), data)
197+
198+
HAMLET_SCENE = b"""
199+
LAERTES
200+
201+
O, fear me not.
202+
I stay too long: but here my father comes.
203+
204+
Enter POLONIUS
205+
206+
A double blessing is a double grace,
207+
Occasion smiles upon a second leave.
208+
209+
LORD POLONIUS
210+
211+
Yet here, Laertes! aboard, aboard, for shame!
212+
The wind sits in the shoulder of your sail,
213+
And you are stay'd for. There; my blessing with thee!
214+
And these few precepts in thy memory
215+
See thou character. Give thy thoughts no tongue,
216+
Nor any unproportioned thought his act.
217+
Be thou familiar, but by no means vulgar.
218+
Those friends thou hast, and their adoption tried,
219+
Grapple them to thy soul with hoops of steel;
220+
But do not dull thy palm with entertainment
221+
Of each new-hatch'd, unfledged comrade. Beware
222+
Of entrance to a quarrel, but being in,
223+
Bear't that the opposed may beware of thee.
224+
Give every man thy ear, but few thy voice;
225+
Take each man's censure, but reserve thy judgment.
226+
Costly thy habit as thy purse can buy,
227+
But not express'd in fancy; rich, not gaudy;
228+
For the apparel oft proclaims the man,
229+
And they in France of the best rank and station
230+
Are of a most select and generous chief in that.
231+
Neither a borrower nor a lender be;
232+
For loan oft loses both itself and friend,
233+
And borrowing dulls the edge of husbandry.
234+
This above all: to thine ownself be true,
235+
And it must follow, as the night the day,
236+
Thou canst not then be false to any man.
237+
Farewell: my blessing season this in thee!
238+
239+
LAERTES
240+
241+
Most humbly do I take my leave, my lord.
242+
243+
LORD POLONIUS
244+
245+
The time invites you; go; your servants tend.
246+
247+
LAERTES
248+
249+
Farewell, Ophelia; and remember well
250+
What I have said to you.
251+
252+
OPHELIA
253+
254+
'Tis in my memory lock'd,
255+
And you yourself shall keep the key of it.
256+
257+
LAERTES
258+
259+
Farewell.
260+
"""

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/Python3Core.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
import com.oracle.graal.python.builtins.modules.UnicodeDataModuleBuiltins;
8585
import com.oracle.graal.python.builtins.modules.WeakRefModuleBuiltins;
8686
import com.oracle.graal.python.builtins.modules.ZipImportModuleBuiltins;
87+
import com.oracle.graal.python.builtins.modules.ZLibModuleBuiltins;
8788
import com.oracle.graal.python.builtins.objects.array.ArrayBuiltins;
8889
import com.oracle.graal.python.builtins.objects.bool.BoolBuiltins;
8990
import com.oracle.graal.python.builtins.objects.bytes.ByteArrayBuiltins;
@@ -312,7 +313,8 @@ private static final PythonBuiltins[] initializeBuiltins() {
312313
new PyExpatModuleBuiltins(),
313314
new SysConfigModuleBuiltins(),
314315
new ZipImporterBuiltins(),
315-
new ZipImportModuleBuiltins()));
316+
new ZipImportModuleBuiltins(),
317+
new ZLibModuleBuiltins()));
316318
if (!TruffleOptions.AOT) {
317319
ServiceLoader<PythonBuiltins> providers = ServiceLoader.load(PythonBuiltins.class);
318320
for (PythonBuiltins builtin : providers) {

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/PythonBuiltinClassType.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ public enum PythonBuiltinClassType implements LazyPythonClass {
137137
ProcessLookupError("ProcessLookupError", "builtins"),
138138
TimeoutError("TimeoutError", "builtins"),
139139
ZipImportError("ZipImportError", "zipimport"),
140+
ZLibError("error", "zlib"),
140141

141142
// todo: all OS errors
142143

@@ -267,6 +268,7 @@ public Shape getInstanceShape() {
267268
ProcessLookupError.base = OSError;
268269
TimeoutError.base = OSError;
269270
ZipImportError.base = ImportError;
271+
ZLibError.base = Exception;
270272

271273
ReferenceError.base = Exception;
272274
RuntimeError.base = Exception;

0 commit comments

Comments
 (0)