feat(zst): own the full .rt <-> .rt.zst round trip in-tree (zstd-19) - #135
Merged
Merged
Conversation
libzstd-backed in-memory decompression of .rt.zst tables, mirroring rtc_decompress. -lzstd wired into linux/cuda/macos builds. Verified by round-trip CPU unit test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rebase of feat/zstd-ingest onto post-RAR master. The per-call-site parse_name copies are replaced by strip_wrapper_suffix(), a widening of the RAR feature's strip_rar_suffix() that handles both wrappers, and count_tables() keeps .rti2 in its extension list. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
GPU e2e verified on dell5: lookup cracks a known hash from a .rt.zst-only directory identically to the raw .rt, and corrupt .zst files are skipped with a clear error (no crash). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Compresses a raw .rt (from disk or memory) into a zstd frame at level 19 by default. Pledges the source size so the frame header carries the content size, which zst_decompress() requires. Writes via temp file plus rename and never touches the source. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The streaming read loop never detected a zero-byte fread() that arrived before `total` bytes were consumed (e.g. the source file shrinks between the initial size probe and the read loop). consumed stayed short of total forever, so `last` never became true and the outer loop spun without end. Extract the boundary check into zst_is_premature_eof() (zst_compress.h) so the read loop fails cleanly with a distinct error code instead, and so the regression test can exercise the exact hang-triggering condition directly without racing a real truncation against the read loop. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Compresses .rt to .rt.zst at level 19 by default, with -d for the reverse so the round trip is verifiable with cmp and the benchmark can time the same zst_decompress() the lookup loader uses. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
make clean enumerated every sibling binary by name but omitted the new tool, leaving it behind in the project root after a clean build -- breaking the documented "make clean removes all binaries" contract. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Compresses the sorted table straight from the in-memory buffer, so no re-read is needed, and removes the raw .rt only after the compressed output is renamed into place. Already-sorted tables are still compressed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sweeps compression levels through crackalack_rt2zst measuring ratio, throughput, peak RSS, and round-trip byte identity, then compares end-to-end lookup time across .rt/.rtc/.rt.zst. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The one-shot path held the compressed file and the decompressed table in memory at once, roughly 1.5x the table size at typical ratios, which the lookup RAM budget does not account for. Streaming the input caps the peak at the decompressed size plus one input window. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The streaming zst_decompress() rewrite treated "destination buffer full" as "frame complete", but ZSTD_decompressStream stops writing once the buffer fills regardless of whether the frame's own decoder state still has more to flush. A corrupted/understated Frame_Content_Size field could therefore produce a silently truncated table instead of an error, unlike the one-shot API it replaced. Track ZSTD_decompressStream's own return value (0 means the frame is genuinely finished) and fail with the existing error code 8 if the buffer filled before the frame did. Adds test_zst_decompress_rejects_undersized_content_size(), which patches a real frame's content-size field down by one chain and asserts zst_decompress() rejects it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
- Guard all zstd wiring (Makefile SRCS/BINARIES/link rules, crackalack_lookup.c, crackalack_sort.c) behind -DHAVE_ZSTD, excluded from BUILD=windows, mirroring the existing HAVE_UNRAR pattern -- Windows has no <zstd.h>/-lzstd and zst_compress.c relies on fseeko/ftello, which MinGW lacks. - Fix bench_zstd.py's vacuous "cracked" substring check (both success and failure messages contain "cracked") by parsing the real crack count. - Clear stale pot files before each format's lookup run in bench_zstd.py, fixing a "already cracked" early-exit that had been silently producing identical, meaningless format_comparison rows; regenerate the streaming benchmark with real data. - Document in the README that crackalack_verify only reads .rt, so tables must be verified before --zst compression (or decompressed first). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ZSTD Round-2 fix for the Windows build gap: cpu_tests_common.c (linked into crackalack_unit_tests, which is part of the Windows BINARIES set) unconditionally called six test_zst_* functions, while the Makefile only adds test_zst.o/zst_decompress.o/zst_compress.o to UNITTEST_OBJS for non-Windows builds. This left six undefined references at link time for crackalack_unit_tests.exe. Wrap the #include and both test-calling/reporting blocks in #ifdef HAVE_ZSTD, mirroring the existing crackalack_lookup.c and crackalack_sort.c guards. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this adds
Compress and decompress
.rttables at zstd level 19 inside the codebase, instead of telling users to shell out tozstd -19 -T0 --rm.zst_compressmodule, exposed as acrackalack_rt2zstbinary and as a--zst[=LEVEL]flag oncrackalack_sort.crackalack_lookupingests.rt.zstdirectly at all four dispatch points, with no flags — the wrapper suffix is stripped before filename params are parsed, so compressed tables stay self-describing.This supersedes the older unmerged
feat/zstd-ingestbranch, which was read-only and had conflicts with the RAR-ingest work that landed in #128. Those conflicts are resolved here.Measured
On a real 16 MB NTLM table (RTX 3080 Ti / M3 Max as noted):
-T Nparallelizes).rtvs.rt.zstThe RSS overhead was confirmed to be the decompression window by varying the level: L1 costs +1.1 MB, L10 +4.7 MB, L19 +8.8 MB, matching zstd's 512 KB / 4 MiB / 8 MiB windows. Loaders are capped at 8 (
compute_load_thread_count()), so the worst case is ~71 MB regardless of table size — noise against the multi-GB budget lookup auto-detects.Mask and Markov tables compress far better (7–10x), since their restricted index spaces are more compressible.
Correctness
cmp.crackalack_sort --zst=19emits bytes identical tocrackalack_rt2zst -l 19.%l%l%l%l%d), Markov (-mk1000000), combined mask+Markov, and custom charset (!1-61626378797a) — each cracking agen_known_hashtarget from a.rt.zst-only directory with results identical to the plain-.rtcontrol, and each round-tripping byte-identical.Per the branch notes it was also exercised on Linux/OpenCL and CUDA; Windows is not wired up (see below).
Notes and caveats
crackalack_rt2zstdoes not auto-detect direction — decompression requires an explicit-d. Given a.rt.zstwithout it, it fails loudly rather than producing garbage.crackalack_sort --zstdeletes the raw.rt, andcrackalack_verifyneeds an uncompressed table, so decompress first if you need to verify later. Documented in CLAUDE.md.HAVE_ZSTDand are not wired into the Windows cross-build.libzstd-devon Linux,brew install zstdon macOS.🤖 Generated with Claude Code