Skip to content

[3.14] gh-160310: Fix quadratic one-shot decompress() with many members (GH-160311) - #161038

Merged
gpshead merged 1 commit into
python:3.14from
gpshead:backport-e80962a-3.14
Oct 31, 2026
Merged

gpshead merged 1 commit into
python:3.14from
gpshead:backport-e80962a-3.14

Conversation

@gpshead

@gpshead gpshead commented Oct 31, 2026 •

Copy link
Copy Markdown
Member

gzip.decompress(), bz2.decompress(), lzma.decompress() and
compression.zstd.decompress() handed each new decompressor all of the
remaining input and copied the rest back through unused_data, so input
made of many concatenated members took quadratic time. Each member is
now fed in chunks that start at 1 KiB and double, and the loop steps
back over unused_data by offset. The first chunk is the whole input,
so single-member data still takes one decompressor call.

bz2, lzma and zstd share the loop through a private helper in
compression._common._streams; gzip keeps its own because it parses the
member header, trailer and padding itself.

ANT-2026-9R8SO5S2

(cherry picked from commit e80962a)

Manual backport notes (3.14): the cherry-pick conflicted in Lib/bz2.py, Lib/compression/zstd/init.py and Lib/test/test_gzip.py, only on surrounding context that exists on main but not on 3.14: the module-level getattr at the end of bz2.py, the extra names main imports from _zstd, and the test_truncated_header and test_corrupted_gzip_header tests next to the new gzip tests. Each conflict was resolved by keeping 3.14's own code and adding only what the original commit adds or changes. The rest applied cleanly, and test_gzip, test_bz2, test_lzma, test_zstd, test_tarfile, test_shutil, test_zipfile and test_zipimport pass.

…ny members (pythonGH-160311)

gzip.decompress(), bz2.decompress(), lzma.decompress() and
compression.zstd.decompress() handed each new decompressor all of the
remaining input and copied the rest back through unused_data, so input
made of many concatenated members took quadratic time.  Each member is
now fed in chunks that start at 1 KiB and double, and the loop steps
back over unused_data by offset.  The first chunk is the whole input,
so single-member data still takes one decompressor call.

bz2, lzma and zstd share the loop through a private helper in
compression._common._streams; gzip keeps its own because it parses the
member header, trailer and padding itself.

ANT-2026-9R8SO5S2

(cherry picked from commit e80962a)
@bedevere-app bedevere-app Bot added the sprint label Oct 31, 2026
@github-project-automation github-project-automation Bot moved this to Todo in Sprint Oct 31, 2026
@gpshead
gpshead enabled auto-merge (squash) October 31, 2026 22:20
@gpshead
gpshead merged commit c67d3db into python:3.14 Oct 31, 2026
53 checks passed
@github-project-automation github-project-automation Bot moved this from Todo to Done in Sprint Oct 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant