Skip to content

Copy overlapping matches in bulk - #143

Merged
yunzheng merged 3 commits into
fox-it:mainfrom
falklindner:feature/bulk-match-copy
Oct 27, 2026
Merged

yunzheng merged 3 commits into
fox-it:mainfrom
falklindner:feature/bulk-match-copy

Conversation

@falklindner

Copy link
Copy Markdown
Contributor

Follow-up to the suggestion in #141: the same bulk copy of overlapping matches, applied to the other pure Python decompressors.

  • Bulk copy: LZO, LZXPRESS, LZXPRESS Huffman, LZVN and LZFSE copied an overlapping match one distance at a time, and ADC copied matches byte by byte. Matches are now appended in at most two steps.
  • Distance check: LZO, LZXPRESS and LZXPRESS Huffman now raise ValueError("Invalid match distance") for a match before the start of the output, like ADC, LZVN and LZFSE. Before, they silently returned wrong data.

Snappy and LZNT1 already copy in bulk. LZBITMAP decides literal or match per output byte, so it's unchanged.

Performance (CodSpeed simulation, estimated cycles, main → this PR):

basic large
ADC −61% −85%
LZXPRESS −56% −5%
LZVN −44% −2%
LZO −43% −10%
LZFSE −39% 0%
LZXPRESS Huffman −3% 0%

Testing: run tox -e py3,native. New tests cover the distance check. I also fuzzed the bulk copy against the old loop: identical output or the identical error on 12,000 mutated inputs, plus 600 liblzfse round-trips.

The native LZO (lzokay-native 0.1.0) panics on such a stream instead of raising an error, so the LZO test only covers the pure Python implementation.

@falklindner falklindner changed the title Copy overlapping matches in bulktch copy Copy overlapping matches in bulkt Oct 7, 2026
@falklindner falklindner changed the title Copy overlapping matches in bulkt Copy overlapping matches in bulk Oct 7, 2026
falklindner added a commit to falklindner/dissect.util that referenced this pull request Oct 26, 2026
fox-it#143 adds a test at the same place in test_lzo.py. With this one after
the benchmark test, the two PRs merge cleanly in either order.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HJvjcGbjsG74UunzfT5zNE
@codecov

codecov Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 38 lines in your changes missing coverage. Please review.
✅ Project coverage is 0.00%. Comparing base (04ef326) to head (97bfaf4).

Files with missing lines Patch % Lines
dissect/util/compression/lzo.py 0.00% 8 Missing ⚠️
dissect/util/compression/lzxpress.py 0.00% 7 Missing ⚠️
dissect/util/compression/lzxpress_huffman.py 0.00% 7 Missing ⚠️
dissect/util/compression/adc.py 0.00% 6 Missing ⚠️
dissect/util/compression/lzfse.py 0.00% 5 Missing ⚠️
dissect/util/compression/lzvn.py 0.00% 5 Missing ⚠️
Additional details and impacted files
@@          Coverage Diff          @@
##            main    #143   +/-   ##
=====================================
  Coverage   0.00%   0.00%           
=====================================
  Files         31      31           
  Lines       2170    2180   +10     
=====================================
- Misses      2170    2180   +10     
Flag Coverage Δ
unittests 0.00% <0.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment thread dissect/util/compression/adc.py Outdated
@@ -40,7 +40,12 @@ def decompress(src: bytes | BinaryIO) -> bytes:
if distance > len(dst):
raise ValueError("Invalid match distance in ADC stream")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
raise ValueError("Invalid match distance in ADC stream")
raise ValueError("Invalid match distance")

Might as well make this the same error message.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 1afd205.

🤖 Generated with Claude Code

@codspeed

codspeed Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

Merging this PR will improve performance by 99.23%

1 warning

⚠️ Base and head ran on different runner types

Some benchmarks with significant performance changes were compared across different runner types, so the changes might not come from the code. Run base and head on the same runner type, or use CodSpeed Macro Runners for more stable results.

Learn more about Macro Runners

Open the report in CodSpeed to investigate

⚡ 9 improved benchmarks
✅ 30 untouched benchmarks

Performance Changes

Benchmark BASE HEAD Efficiency
⚡ test_benchmark_adc_decompress[large] 4,832.8 µs 747.6 µs ×6.5
⚡ test_benchmark_adc_decompress[basic] 199.3 µs 68.3 µs ×2.9
⚡ test_benchmark_lzxpress_decompress[basic] 139.2 µs 52 µs ×2.7
⚡ test_benchmark_lzo_decompress[python-basic] 94.9 µs 47.1 µs ×2
⚡ test_benchmark_lzvn_decompress[basic] 177.4 µs 95.6 µs +85.51%
⚡ test_benchmark_lzfse_decompress[basic] 195.8 µs 114.1 µs +71.64%
⚡ test_benchmark_lzxpress_decompress[large] 562.3 µs 482.2 µs +16.6%
⚡ test_benchmark_lz4_decompress[python-large] 132.9 µs 114.7 µs +15.86%
⚡ test_benchmark_lzo_decompress[python-large] 592.9 µs 524.3 µs +13.08%

Tip

Curious why performance improved? Comment @codspeedbot explain why performance improved on this PR, or directly use the CodSpeed MCP with your agent.


Comparing falklindner:feature/bulk-match-copy (97bfaf4) with main (04ef326)

Open in CodSpeed

@Schamper
Schamper requested a review from yunzheng October 27, 2026 12:30
yunzheng pushed a commit to falklindner/dissect.util that referenced this pull request Oct 27, 2026
fox-it#143 adds a test at the same place in test_lzo.py. With this one after
the benchmark test, the two PRs merge cleanly in either order.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HJvjcGbjsG74UunzfT5zNE
falklindner and others added 3 commits October 27, 2026 18:17
The pure Python LZO, LZXPRESS and LZXPRESS Huffman decompressors did
not check that a match refers back to data that is actually there. A
corrupt stream with a match before the start of the output silently
produced wrong output, as slicing the output clamps out of range
indices.

Raise a ValueError for such a match instead, as the ADC, LZVN and
LZFSE decompressors already do.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LNZJRJFni88V8JGtP2tj29
The pure Python LZO, LZXPRESS, LZXPRESS Huffman, LZVN and LZFSE
decompressors copied an overlapping match one distance worth of bytes
at a time, and ADC copied every match one byte at a time. A long match
with a short distance took many small appends to the output.

An overlapping match repeats the last distance bytes of the output, so
append those in bulk instead, in at most two steps per match. Matches
that do not overlap are copied with a single slice, as before.

This is the same change as made to LZ4 in fox-it#141.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LNZJRJFni88V8JGtP2tj29
ADC now raises ValueError("Invalid match distance"), like the other
decompressors.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HJvjcGbjsG74UunzfT5zNE
@yunzheng
yunzheng force-pushed the feature/bulk-match-copy branch from 1afd205 to 97bfaf4 Compare October 27, 2026 17:17
@yunzheng
yunzheng merged commit dd93fb4 into fox-it:main Oct 27, 2026
34 checks passed
@falklindner
falklindner deleted the feature/bulk-match-copy branch November 14, 2026 14:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants