Skip to content

Commit 0df30f0

Browse files
committed
[GR-13036] Implement zlib.decompressobj
1 parent b6786b6 commit 0df30f0

File tree

2 files changed

+111
-0
lines changed

2 files changed

+111
-0
lines changed

graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/ZLibModuleBuiltins.java

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,76 @@ Object deflateCompress(DeflaterWrapper stream, PIBytesLike pb, int mode) {
453453
}
454454
}
455455

456+
@Builtin(name = "zlib_inflateInit", fixedNumOfPositionalArgs = 2)
457+
@GenerateNodeFactory
458+
abstract static class InflateInitNode extends PythonBinaryBuiltinNode {
459+
@Child BytesNodes.ToBytesNode toBytes = BytesNodes.ToBytesNode.create();
460+
461+
@Specialization
462+
@TruffleBoundary
463+
Object init(int wbits, PBytes zdict) {
464+
Inflater inflater;
465+
if (wbits < 0) {
466+
// generate a RAW stream, i.e., no wrapping
467+
inflater = new Inflater(true);
468+
} else if (wbits >= 25) {
469+
// include gzip container
470+
throw raise(PythonBuiltinClassType.NotImplementedError, "gzip containers");
471+
} else {
472+
// wrap stream with zlib header and trailer
473+
inflater = new Inflater(false);
474+
}
475+
476+
inflater.setDictionary(toBytes.execute(zdict));
477+
return new InflaterWrapper(inflater);
478+
}
479+
}
480+
481+
static class InflaterWrapper implements TruffleObject {
482+
private final Inflater inflater;
483+
484+
public InflaterWrapper(Inflater inflater) {
485+
this.inflater = inflater;
486+
}
487+
488+
public ForeignAccess getForeignAccess() {
489+
return null;
490+
}
491+
}
492+
493+
@Builtin(name = "zlib_inflateDecompress", fixedNumOfPositionalArgs = 3)
494+
@GenerateNodeFactory
495+
abstract static class InflaterDecompress extends PythonTernaryBuiltinNode {
496+
@Child BytesNodes.ToBytesNode toBytes = BytesNodes.ToBytesNode.create();
497+
498+
@Specialization
499+
@TruffleBoundary
500+
Object decompress(InflaterWrapper stream, PIBytesLike pb, int maxLen) {
501+
int maxLength = maxLen == 0 ? Integer.MAX_VALUE : maxLen;
502+
503+
ByteArrayOutputStream baos = new ByteArrayOutputStream();
504+
byte[] data = toBytes.execute(pb);
505+
byte[] result = new byte[DEF_BUF_SIZE];
506+
stream.inflater.setInput(data);
507+
508+
int bytesWritten = result.length;
509+
while (baos.size() < maxLength && bytesWritten == result.length) {
510+
try {
511+
bytesWritten = stream.inflater.inflate(result, 0, result.length);
512+
} catch (DataFormatException e) {
513+
throw raise(ZLibError, e.getMessage());
514+
}
515+
baos.write(result, 0, bytesWritten);
516+
}
517+
518+
return factory().createTuple(new Object[]{
519+
factory().createBytes(baos.toByteArray()),
520+
stream.inflater.needsInput(),
521+
stream.inflater.getRemaining()
522+
});
523+
}
524+
}
525+
456526
// zlib.compress(data, level=-1)
457527
@Builtin(name = "compress", minNumOfPositionalArgs = 1, maxNumOfPositionalArgs = 2, keywordArguments = {"level"})
458528
@TypeSystemReference(PythonArithmeticTypes.class)

graalpython/lib-graalpython/zlib.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,44 @@ def flush(self, mode=Z_FINISH):
104104
return result
105105
else:
106106
raise error("compressor object already flushed")
107+
108+
109+
class decompressobj():
110+
"""decompressobj([wbits]) -- Return a decompressor object.
111+
112+
Optional arg wbits is the window buffer size.
113+
"""
114+
def __new__(cls, wbits=MAX_WBITS, zdict=b""):
115+
self = object.__new__(cls)
116+
self.unused_data = b""
117+
self.unconsumed_tail = b""
118+
self.eof = False
119+
self.stream = zlib_inflateInit(wbits, zdict)
120+
return self
121+
122+
def decompress(self, data, max_length=0):
123+
"""
124+
decompress(data[, max_length]) -- Return a string containing the
125+
decompressed version of the data.
126+
127+
If the max_length parameter is specified then the return value will be
128+
no longer than max_length. Unconsumed input data will be stored in the
129+
unconsumed_tail attribute.
130+
"""
131+
if max_length < 0:
132+
raise ValueError("max_length must be greater than zero")
133+
result, self.eof, unused_len = zlib_inflateDecompress(self.stream, data, max_length)
134+
tail = data[len(data) - unused_len:]
135+
if self.eof:
136+
self.unconsumed_tail = b""
137+
self.unused_data += tail
138+
else:
139+
self.unconsumed_tail = tail
140+
141+
return result
142+
143+
def flush(self, length=None):
144+
try:
145+
return decompress(self.unconsumed_tail)
146+
except error:
147+
return b""

0 commit comments

Comments
 (0)