Exercise 2 solution import struct import zlib class FormatError(ValueError): pass MAX_OUT = 1_000_000 def safe_inflate(data, max_out): d = zlib.decompressobj() try: out = d.decompress(data, max_out + 1) except zlib.error as e: raise FormatError(f"corrupt stream: {e}") from None if len(out) > max_out: raise FormatError(f"output is over {max_out} bytes") if not d.eof: raise FormatError("stream ends early") if d.unused_data: raise FormatError(f"{len(d.unused_data)} unexpected bytes after the stream") return out def pack_blob(payload): return struct.pack(" MAX_OUT: raise FormatError(f"declared size {declared} is over the {MAX_OUT} limit") out = safe_inflate(buf[4:], declared) if len(out) != declared: raise FormatError(f"declared {declared} bytes but the stream holds {len(out)}") return out text = b"the quick brown fox " * 50 blob = pack_blob(text) print(len(text), len(blob), unpack_blob(blob) == text) bomb_data = zlib.compress(b"\x00" * 50_000_000, 9) print("bomb stream:", len(bomb_data), "bytes, ratio about", 50_000_000 // len(bomb_data)) lying_small = struct.pack(" accepted") except FormatError as e: print(label, "->", e) # 1000 42 True # bomb stream: 48610 bytes, ratio about 1028 # bomb, honest size -> declared size 50000000 is over the 1000000 limit # bomb, lying size -> output is over 100 bytes # size too small -> output is over 100 bytes # size too large -> declared 5000 bytes but the stream holds 1000 # size absurd -> declared size 4000000000 is over the 1000000 limit # cut stream -> stream ends early # trailing bytes -> 4 unexpected bytes after the stream # corrupt middle -> corrupt stream: Error -3 while decompressing data: incorrect data check # not zlib -> corrupt stream: Error -3 while decompressing data: incorrect header check # too short -> too short for a size field (Run as written; the output matched the comments.) Explanation: The decompressor is asked for at most one byte more than the allowed size, so a stream that expands enormously stops almost at once instead of filling memory. The size in the header is only a claim: it is checked against the limit before any work is done, and against the real result afterwards. An unfinished stream, extra bytes after the end and corrupt data each get their own message.