MCPcopy Create free account
hub / github.com/aio-libs/aiohttp / ZLibDecompressor

Class ZLibDecompressor

aiohttp/compression_utils.py:268–336  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

266
267
268class ZLibDecompressor(DecompressionBaseHandler):
269 def __init__(
270 self,
271 encoding: str | None = None,
272 suppress_deflate_header: bool = False,
273 executor: Executor | None = None,
274 max_sync_chunk_size: int | None = MAX_SYNC_CHUNK_SIZE,
275 ):
276 super().__init__(executor=executor, max_sync_chunk_size=max_sync_chunk_size)
277 self._mode = encoding_to_mode(encoding, suppress_deflate_header)
278 self._zlib_backend: Final = ZLibBackendWrapper(ZLibBackend._zlib_backend)
279 self._decompressor = self._zlib_backend.decompressobj(wbits=self._mode)
280 self._last_empty = False
281 self._pending_unused_data: bytes | None = None
282
283 def decompress_sync(
284 self, data: Buffer, max_length: int = ZLIB_MAX_LENGTH_UNLIMITED
285 ) -> bytes:
286 if self._pending_unused_data is not None:
287 data = self._pending_unused_data + bytes(data)
288 self._pending_unused_data = None
289 result = self._decompressor.decompress(
290 self._decompressor.unconsumed_tail + data, max_length
291 )
292 # Only way to know that isal has no further data is checking we get no output
293 self._last_empty = result == b""
294
295 # Handle concatenated gzip/deflate streams (multi-member).
296 # After a member ends, unused_data holds the start of the next member.
297 # Create a fresh decompressor for each subsequent member.
298 while self._decompressor.eof and self._decompressor.unused_data:
299 unused = self._decompressor.unused_data
300 self._decompressor = self._zlib_backend.decompressobj(wbits=self._mode)
301 if max_length != ZLIB_MAX_LENGTH_UNLIMITED:
302 max_length -= len(result)
303 if max_length <= 0:
304 self._pending_unused_data = unused
305 break
306 chunk = self._decompressor.decompress(unused, max_length)
307 self._last_empty = chunk == b""
308 result += chunk
309
310 # Member ended exactly at chunk boundary — no unused_data, but the
311 # next feed_data() call would fail on the spent decompressor.
312 # Only reset for gzip; deflate's feed_eof() relies on eof=True to
313 # confirm the stream is complete.
314 if self._decompressor.eof and self._mode > self._zlib_backend.MAX_WBITS:
315 self._decompressor = self._zlib_backend.decompressobj(wbits=self._mode)
316
317 return result
318
319 def flush(self, length: int = 0) -> bytes:
320 return (
321 self._decompressor.flush(length)
322 if length > 0
323 else self._decompressor.flush()
324 )
325

Calls

no outgoing calls