Releases: hellobertrand/zxc
Release list
ZXC v0.13.3
Release Notes
v0.13.3 is a hardening and packaging release.
The headline isn't really in the code: ZXC is now enrolled in Google OSS-Fuzz, which runs five harnesses around the clock. It has already turned up two decoder edge cases, both fixed here. Neither is reachable from an archive ZXC itself produced β you need a hand-crafted file to hit them β but a decompressor that can be walked out of its buffer is a decompressor worth fixing.
The rest is housekeeping. The GHI header's enc_off field is now formally unused, which is the ambiguity that let the first bug through; match-offset validation lost a counter it never needed; and static linking on Windows works again.
Nothing changes in the format or the ABI. v0.13.3 reads and writes the same format v7 archives as the rest of the v0.13.x line, and the soname is still libzxc.so.4.
Drop-in for v0.13.x. No format break, no ABI change, nothing to re-link. Archives move both ways between v0.13.3 and any earlier v0.13.x release. One detail worth knowing: at levels 1 and 2 the encoder now writes the unused
enc_offGHI header field as0rather than1, so output at those levels is no longer byte-for-byte what v0.13.2 produced. It is still standard format v7, and every v0.13.x release reads it. Levels 3-7 are byte-identical.
Reliability & Security Fixes
Out-of-bounds read on forged GHI offsets (decoder)
The GHI safe loop took its offset-validation threshold from the enc_off header field: 256 bytes when enc_off == 1. That field never described GHI offsets, which live inline in the sequence word and are always 16 bits wide. So a crafted archive could claim enc_off = 1, carry a much larger offset, and once 256 bytes had been written the loop simply stopped checking β and the copy read from before the start of the output buffer. The threshold is now the 16-bit bound, unconditionally. Found by OSS-Fuzz (544800535). (#367)
Output-buffer overflow on escaped literal lengths (decoder)
When a sequence escapes its literal length into a varint, the GLO and GHI decode macros used to check that the literals fit in the remaining output space β and nothing else. They ignored the match length that follows, and the output still owed by the rest of the batch. A crafted archive could clear that check and then write past the end of the destination buffer. Both macros now weigh literals, match length and the batch remainder together, and the trailing-literal copy has an explicit guard in front of it. Found by OSS-Fuzz (546426939). (#369)
enc_off is unused in GHI blocks and must be ignored
#367 was really a spec problem, so the field is now nailed down on both sides. The compressor always writes enc_off = 0 in GHI headers, and FORMAT.md says decoders must ignore it. GHI has no offset stream and its sequence words always carry 16-bit offsets, so there is nothing for the field to bound. (#368)
Stale dictionary size on a reused compression context
zxc_compress_cctx() never reset dict_size. Reuse a context after a dictionary compression and the old dictionary size came along for the ride into the next, plain one. It is reset on every call now. (#351)
Performance
Leaner match-offset validation
Match-offset validation used to lean on a running count of bytes written, updated on every single sequence. That counter is gone. The decoder now compares the match source directly to the start of the output buffer, or of the dictionary prefix when one is attached. Same guarantee, less bookkeeping in the hot loop. (#368)
Build, Packaging & Portability
Windows static-library linking
__declspec(dllimport) and static libraries don't get along on Windows: the link fails outright. ZXC_DLL_IMPORT is opt-in from now on, so plain declarations link against both the DLL and the static library. CMake, Meson and pkg-config each propagate the right define β ZXC_DLL_IMPORT for shared, ZXC_STATIC_DEFINE for static β and a new packaging workflow builds a real consumer against every combination on Linux and Windows, so it stays fixed. (#350)
CMake build modularized
CMakeLists.txt had grown past 700 lines. It is now split into focused modules under cmake/ β version, options, platform detection, compiler flags, dependencies, SIMD variants, tests, docs, formatting, WASM, install, summary β with the top-level file left as the orchestrator. Consumers see no difference. (#347)
Meson CLI parity
The Meson build now passes the MSVC compiler and linker flags CMake already had, and installs the unzxc symlink on POSIX systems. (#348)
Quality & Docs
- Continuous fuzzing moved to Google OSS-Fuzz. Five harnesses β roundtrip, decompress, streaming, seekable, dictionary β run there around the clock. The same harnesses still run under ClusterFuzzLite (ASan + UBSan) on every pull request that touches the library. README badge and workflow triggers updated to match.
- Public headers and internal comments got a pass: Doxygen blocks tightened, internal explanations corrected where they had drifted from the code,
clang-formatextended totests/. (#366) - The level-ordered layout behind the PivCo-Huffman section codec is credited to Marcin Ε»ukowski (@MarcinZukowski), and the joint flat/length nudge to Dougall Johnson (@dougallj). Both are implemented independently here.
- Dependency bumps across the Rust, Python and Node.js wrappers, the fuzzing base image and the GitHub Actions workflows.
Changelog
- api: Refine comments for improved clarity (#366)
- perf: Optimizes decode offset validation and clarifies GHI format (#368)
- fix: Reset dict_size in compression context (#351)
- fix: Ensure correct offset validation bounds for GHI decompression (ossfuzz #544800535) (#367)
- fix: Enhance output buffer overflow checks during decompression (ossfuzz #546426939) (#369)
- cli: Add MSVC CLI build configuration and unzxc alias (#348)
- build: Tag the Go submodule on release (#371)
- build: Publish the WebAssembly build to npm (#372)
- build: Update node.js wrapper dependencies
- build: bump scikit-build-core from 0.12.2 to 1.0.3 in /wrappers/python (#362)
- build: bump cibuildwheel from 4.1.0 to 4.1.1 in /wrappers/python (#360)
- build: bump setuptools-scm from 10.2.0 to 10.2.1 in /wrappers/python (#361)
- build: Remove scheduled workflow trigger
- build: bump cc from 1.2.65 to 1.4.0 in /wrappers/rust (#355)
- build: bump thiserror from 2.0.18 to 2.0.19 in /wrappers/rust (#354)
- build: bump libc from 0.2.186 to 0.2.189 in /wrappers/rust (#357)
- build: bump vitest from 4.1.9 to 4.1.10 in /wrappers/nodejs (#358)
- build: Prevent Windows static library linking failures (#350)
- build: Modularize CMakeLists.txt into dedicated modules (#347)
- doc: Update fuzzing status and details in README
- doc: Add PivCo-Huffman attribution for algorithm elements
- misc: bump meson from 1.11.1 to 1.11.2 in /.github/requirements (#363)
- misc: bump oss-fuzz-base/base-builder in /.clusterfuzzlite (#359)
- misc: bump cross-platform-actions/action (#365)
- misc: bump github/codeql-action/upload-sarif (#364)
- misc: bump actions/checkout from 6.0.2 to 7.0.1 (#356)
- misc: bump softprops/action-gh-release from 3.0.1 to 3.0.2 (#353)
- misc: bump actions/setup-python from 6.3.0 to 7.0.0 (#352)
Full Changelog: v0.13.2...v0.13.3
ZXC v0.13.2
Release Notes
Release v0.13.2 is a performance and hardening patch release. The headline change: levels 6 and 7 now decompress +2β3 % and +10β11 % faster respectively, thanks to a decode-oriented nudge of the Huffman code lengths, at a near-zero ratio cost: compressed output grows by just 0.05 % at level 6 and 0.27 % at level 7.
It also fixes two decoder edge cases (an out-of-bounds read on short RAW literal sections, and an in-place bound that could under-reserve the trailing framing), makes ZXC embeddable as a CMake subproject, and shrinks the shared library by removing duplicate SIMD variants.
There are no changes to the container format or the ABI: v0.13.2 reads and writes the same format v7 archives as v0.13.0 and v0.13.1, and the shared-library soname stays libzxc.so.4.
Drop-in for v0.13.0 / v0.13.1. No format break, no ABI change, no re-link required. Every archive written by any v0.13.x release reads back identically in v0.13.2 and vice versa. One nuance: at levels 6 and 7 the encoder now emits slightly flatter Huffman trees, so archives written at those levels are no longer byte-for-byte identical to v0.13.1's output but they remain standard format v7, readable by every v0.13.x release. Levels 1-5 produce byte-identical output.
Performance
Faster decompression at levels 6 and 7
The encoder now applies a joint flat/length nudge to Huffman code lengths (#336): trees are flattened wherever it costs almost nothing in compressed size, so the PivCo decoder spends fewer cycles per symbol. At level 7, a dynamic-programming tier picks the best nudge per block; level 6 gets the same nudge in its lighter form (#343). Idea by @dougallj.
| Level | Decompression speed | Compression size |
|---|---|---|
| 6 | +2% to +3% | +0.05% |
| 7 | +10% to +11% | +0.27% |
Levels 1-5 are unaffected.
Reliability Fixes
Out-of-bounds read on short RAW literal sections (decoder)
RAW literal sections decoded directly from the input buffer could read a few bytes past the end of the compressed input when the section tail was shorter than the decoder's padding requirement and no data followed it. Such sections are now staged through a padded internal buffer, and the conformance tests cover these layouts. (#341)
In-place bound now reserves the trailing framing
zxc_inplace_margin() ignored what the encoder writes after the last data block β the EOF block header and, for seekable archives, the seek table β so the returned bound could fall below the compressed size for seekable archives of incompressible data with small blocks. The worst-case seek table is now reserved unconditionally (4 bytes per block, at most 0.1 % of the payload at the minimum block size, negligible at the default). (#337)
Embedding & Portability
Vendor ZXC as a CMake subproject
ZXC can now be embedded directly into another CMake build via add_subdirectory() or FetchContent (#338). When built as a subproject: the CLI, tests, LTO and -march=native default to OFF; install, doc and formatting targets are disabled; compiler flags are inherited from the parent project instead of being overridden; and a zxc::zxc_lib ALIAS target is provided for consistent linking. A new ZXC_USE_SYSTEM_RAPIDHASH option controls whether the vendored or system rapidhash.h is used. The README documents the vendoring workflow.
No more compiler-rt dependency for x86 CPU detection
CPUID and XGETBV are now issued through direct intrinsics and inline assembly instead of compiler-specific builtins, removing the implicit compiler-rt dependency and making the runtime dispatch robust across more toolchains. (#339)
Build, Binary Size & CI
- Duplicate SIMD variants removed: the SSE2 and NEON function-multi-versioning variants duplicated the baseline code paths and are gone, shrinking the shared library by roughly a third on arm64 and a quarter on amd64.
-mfmais dropped, and the x86 dispatch gates now also check BMI1/BMI2/LZCNT before selecting a variant that uses them. (#331) - The ISA-independent dictionary tree setup is compiled once instead of once per SIMD variant. (#327)
- MSVC
/archmapping and/wd4244flags fixed, silencing the wrapdb CI warnings. (#326) - Wrapper sources are formatted and the formatting is enforced in CI. (#330)
- Remaining compiler warnings resolved. (#342)
- The AVX-512 tier is now tested under Intel SDE instead of QEMU, which cannot emulate AVX-512. (#325)
- npm dev dependencies updated to address audit alerts. (#218, #335)
Internal
- The GLO/GHI decode loops are deduplicated and macro-folded (β666 lines); the hot paths are byte-identical. (#328)
- Typo fixes in the documentation.
Changelog
- fix: fix typos
- fix: Fix MSVC /arch + /wd4244 flags (wrapdb CI warnings) (#326)
- perf: Extend code length nudging to level 6 (#343)
- perf: Optimizes Huffman code lengths for faster PivCo decode (#336)
- cli: Make project embeddable as a subproject (#338)
- cli: Resolve compiler warnings (#342)
- build: Format wrapper sources and guard it in CI (#330)
- portability: Remove compiler-rt dependency for x86 CPUID (#339)
- misc: Stage short RAW literal sections to prevent overread (#341)
- misc: Reserve the trailing framing in the in-place bound (#337)
- misc: Deduplicate the GLO/GHI decode loops (#328)
- misc: Update npm dev dependencies to address alerts (#218) (#335)
- misc: Remove duplicate SSE2/NEON FMV variants, drop -mfma, harden x86 dispatch gates (#331)
- misc: Compile ISA-independent dict tree setup once, not per-variant (#327)
- misc: Test the AVX-512 tier under Intel SDE instead of QEMU (#325)
Full Changelog: v0.13.1...v0.13.2
ZXC v0.13.1
Release Notes
v0.13.1 is a build-only patch release. It fixes the Meson build so the Windows DLL correctly exports its public API, raises the minimum Meson version so c_std is honoured in subproject builds, and corrects a README heading. There are no changes to the codec, the container format, or the ABI: v0.13.1 reads and writes the exact same format v7 archives as v0.13.0, and the shared-library soname stays libzxc.so.4.
Drop-in for v0.13.0. No format break, no ABI change, no re-link required. Archives written by v0.13.0 and 8000 v0.13.1 are byte-for-byte interchangeable. If you build ZXC from source with Meson on Windows, or vendor it as a Meson subproject, this is the release to use.
Build Fixes
Windows DLL now exports its API (Meson)
The Meson build did not define the export-control macro, so zxc.dll was built with its public symbols hidden β leaving downstream projects unable to link against it. The build now defines -Dzxc_lib_EXPORTS for shared builds (and -DZXC_STATIC_DEFINE for static builds and consumers), so the Windows DLL exports its API exactly as the CMake build already did.
c_std honoured in subproject builds (Meson)
The minimum Meson version is raised to >= 0.63.0 so that the project's c_std=c17 from default_options is applied when ZXC is consumed as a Meson subproject. On older Meson, that option was silently ignored in the subproject case, which could downgrade the C standard used to build the vendored library.
Documentation
- README: the Windows install section (Option 6) is now correctly titled Winget, and the download example is bumped to 0.13.1.
- API.md: library version references updated to 0.13.1 (SOVERSION unchanged at 4).
Changelog
- api: Define
zxc_lib_EXPORTSin Meson so the Windows DLL exports its API - misc: Require Meson >= 0.63.0 so
c_stdapplies in subproject builds - docs: Update Option 6 to specify
Wingetfor installation
Full Changelog: v0.13.0...v0.13.1
ZXC v0.13.0
Release Notes
This release introduces ZXC format v7 and a new Level 7 (ULTRA) compression tier (Huffman-coded literals and sequence tokens over the new PivCo SIMD-decodable bitstream layout), that pushes the ratio past zstd -1 while still decoding roughly 2x faster than it. The entropy-decode path was reworked for a +5-13 % Level 6 decode speedup across every reference CPU, and decompression can now run in-place in a single buffer. The container format advances to v7; the shared-library ABI is unchanged (libzxc.so.4).
v0.13 is the last milestone before v1.0, which will lock the container format and guarantee format stability and backward compatibility going forward.
β οΈ Breaking change (format). The container format is now v7. v0.13.0 writes v7 archives that v0.12.0 and earlier decoders reject, and it cannot read v6/v5 archives: the header version byte is checked for exact equality (ZXC_ERROR_BAD_VERSION). The shared-library ABI is unchanged (soname stayslibzxc.so.4), so no re-link is required, but note that upgrading the library alone makes newly written archives v7.Migrating v6 β v7 archives is a one-time transcode β decompress with your old (v6) build and recompress with the new one:
zxc-v6 -dc old.zxc | zxc-v7 -z -c > new.zxcKeep a v6 build around until your data at rest is converted. Full guide (bulk migration, dictionaries, verification):
docs/MIGRATION.md.
Road to v1.0. This is the last planned format break. v0.13 is the final milestone before v1.0, which will freeze the ZXC container format and guarantee format stability and backward compatibility from then on. If you're deciding when to standardize your data at rest, v1.0 is the line to wait for.
New Features
Level 7 (ULTRA) Maximum Density
A new top compression level for storage and bandwidth-bound, write-once / read-many workloads. On top of Level 6's Huffman-coded literals, Level 7 additionally Huffman-codes the sequence-token stream (length-limited canonical codes up to 11 bits) and runs a deeper optimal parse selected by a space-speed Lagrangian (J = size + Ξ» Β· decoded_bytes).
- On the Silesia corpus it reaches a 33.00 % ratio : 9 % denser than Level 6 (36.27 %) and denser than
zstd -1(34.53 %), while still decoding 2x faster thanzstd -1. It fills the gap between lz4hc's speed and zstd's density. (#315) - Exposed as
-7/ZXC_LEVEL_ULTRAin the CLI and library, and across Go, Rust, Python, Node.js, and WASM. (#317)
Pivot-Coded Huffman Bitstream (format v7)
Huffman-coded sections now place their bits on the wire in the PivCo level-ordered layout: the same length-limited canonical Huffman code, but the bits are transposed by tree level into per-node branch bitmaps so the decoder runs data-parallel SIMD list merges instead of a serial bit chain. This is the source of the Level 6 decode speedup and what makes Level 7's token coding practical. (#315)
In-Place Decompression
Decompression can now run in a single buffer: the compressed input sits flush-right and is decoded left-to-right within the same allocation; for lower peak memory in tightly-budgeted integrations. (#315)
CLI: Progress, Robust Parsing, and File I/O
The zxc CLI gains a progress indicator, more robust argument parsing, and hardened file-I/O handling. (#318) A new CLI install option (incl. winget on Windows) is documented in the README. (#316)
Performance & Memory
Measured versus v0.12.0 (Silesia, single-thread):
- Level 6 decode: +5% to +13% faster on every reference CPU: Apple M2 +6%, Google Axion / Neoverse-V2 +6%, AMD EPYC Zen 5 +13%, AMD EPYC Zen 3 +5%, from the reworked PivCo Huffman decoder.
- New Level 7 (ULTRA): 33.00% ratio : +9% denser than Level 6, denser than
zstd -1, and decoding ~2x faster than it.- Ratio at Levels 1-6 is unchanged (Levels 1-5 are byte-identical to v0.12.0).
- Reworked Huffman decoder and refined compression margins across the entropy path. (#301, #319)
- Space-speed Lagrangian selection at ULTRA trades ratio against decode cost per block. (#315)
- Cached dictionary Huffman tree: the dictionary's shared literal table is decoded into its ready-to-use tree once, at attach time, instead of per block: a large small-block dictionary-decode speedup. (#320)
- Robustness and memory-management refinements across the library. (#321)
Bug Fixes & Robustness
- Improved robustness and memory management across compression and decompression. (#321)
- Refined LZ77 search-loop conditions and general hardening across components. (#296, #298)
- Type-safety and modernization passes over the JS and Go wrappers. (#297)
Development & CI
- OS & libc compatibility CI for musl / Alpine Linux and FreeBSD, validating the freestanding core beyond glibc. (#291)
- ABI-check accuracy improved and the libabigail baseline updated for 0.13.0: opaque-handle layout changes are now correctly recognised as ABI-compatible (no soname bump).
- Refined code-coverage reporting with vendored code excluded. (#322)
- Meson project version and SOVERSION are now statically defined. (#290)
- Benchmark tooling updated to lzbench 2.3 with the ZXC 0.13.0 codec.
Documentation
- README: refreshed Silesia benchmarks (lzbench 2.3) across Apple M2, Google Axion (Neoverse-V2), AMD EPYC 9B45 (Zen 5) and 7B13 (Zen 3), plus a new Ultra (Level 7 vs
zstd -1) comparison tier. - MIGRATION: v6 -> v7 transcode guide.
- API / FORMAT / man page: Level 7 (
ZXC_LEVEL_ULTRA), the PivCo layout, and the v7 header documented.
Acknowledgements
ZXC's new v7 Huffman bitstream is built on PivCo-Huffman. Sincere thanks to @MarcinZukowski for the technique and its paper, and to @rygorous whose breakdown of the PivCo merge operations made the SIMD decoder practical.
Thanks also to @inikep (lzbench) and @powturbo (TurboBench) for keeping ZXC in the independent benchmark suites, to @guglovich for packaging ZXC for the Arch Linux AUR, and to @jeanga for continued testing and packaging help.
Changelog
- api: Refines code coverage reporting (#322)
- api: Adds Level 7 (Ultra) compression to wrappers (#317)
- api: ZXC format v7: PivCo Huffman, level 7 (ULTRA), space-speed selection, in-place decode (#315)
- api: bump node-addon-api from 8.8.0 to 8.9.0 in /wrappers/nodejs (#308)
- api: Refines documentation and API option descriptions (#299)
- api: Enhances code quality and analysis setup (#294)
- perf: Optimizes compression and decompression performance (#319)
- perf: Optimizes Huffman decoding and compression margins (#301)
- cli: Refines CLI with progress, robust parsing, and file I/O (#318)
- cli: Update README with zxc CLI installation option (#316)
- cli: Enhances code quality and robustness across components (#296)
- cli: Enhance README clarity and quick start
- build: bump pytest from 9.0.3 to 9.1.1 in /wrappers/python (#310)
- build: bump setuptools-scm in /wrappers/python (#311)
- build: bump cibuildwheel from 3.4.1 to 4.1.0 in /wrappers/python (#309)
- build: bump vitest from 4.1.7 to 4.1.9 in /wrappers/nodejs (#307)
- build: Refines code style, type safety, and modernizes JS/Go wrappers (#297)
- build: Refines CI/CD workflows and test stability (#292)
- portability: Add OS and libc compatibility CI for musl Linux and FreeBSD (#291)
- doc: Refactor LZ77 search loop conditions (#298)
- doc: Update README badges
- misc: Ignore vendors code in coverage reports
- misc: Improves robustness and refines memory management (#321)
- misc: Update lzbench benchmark source to v0.13.0
- misc: Caches dict Huffman tree upon attach (#320)
- misc: bump actions/setup-python from 6.2.0 to 6.3.0 (#305)
- misc: bump oss-fuzz-base/base-builder in /.clusterfuzzlite (#312)
- misc: bump msys2/setup-msys2 from 2.31.1 to 2.32.0 (#304)
- misc: bump actions-rust-lang/setup-rust-toolchain (#306)
- misc: bump actions/attest-build-provenance from 4.1.0 to 4.1.1 (#303)
- misc: bump softprops/action-gh-release from 3.0.0 to 3.0.1 (#302)
- misc: Refines code quality and addresses warnings (#295)
- misc: Update bash conditional expressions to '[[ ... ]]' (#293)
- misc: Statically define Meson project version and SOVERSION (#290)
Full Changelog: v0.12.0...v0.13.0
ZXC v0.12.0
Release Notes
This release introduces pre-trained dictionary compression - Train a dictionary from representative samples and ship dramatically smaller archives for small-block / many-small-file workloads - together with a shared dictionary Huffman table, a static context API for zero-allocation caller-managed workspaces, and a custom reader interface for seekable archives. The container format advances to v6 and the shared library SOVERSION bumps from 3 to 4.
β οΈ Breaking change. This is an ABI and format break. The format version is now v6 (the legacyNUMblock type is removed), so v0.12.0 cannot read archives written by v0.11.0 and earlier, and older decoders reject v6 archives. Re-link againstlibzxc.so.4.Migrating v5 β v6 archives is a one-time transcode, decompress with your old (v5) build and recompress with the new one:
zxc-v5 -dc old.zxc | zxc-v6 -z -c > new.zxcKeep a v5 build around until your data at rest is converted (it is the only thing that can read v5). Full guide: bulk migration, dictionaries, verification: docs/MIGRATION.md.
New Features
Pre-Trained Dictionary Compression
For workloads compressed in small blocks (4 KBβ128 KB), a pre-trained dictionary prefills the LZ77 sliding window at the start of every block, so even the earliest bytes have representative history to match against. This helps any time the block size is small enough that early bytes would otherwise lack history: a single small payload, or a large payload split into many small blocks.
- Train from a corpus, serialize to a self-describing
.zxd, and compress/decompress against it. Dictionaries are capped at 64 KB (ZXC_DICT_SIZE_MAX). - New public API in
zxc_dict.h:zxc_train_dict/zxc_train_dict_huf(train),zxc_dict_save/zxc_dict_save_bound(serialize),zxc_dict_train(one-shot train + serialize),zxc_dict_load/zxc_dict_huf(load), andzxc_dict_id/zxc_dict_get_id(identity). - Each archive records its 32-bit
dict_id; supplying the wrong dictionary (or none) fails cleanly with a dictionary-required / mismatch error instead of silently corrupting output. (#261)
Shared Dictionary Huffman Table
The .zxd carries a 128-byte packed literal Huffman code-lengths table trained on the same corpus. Small blocks no longer each pay to embed their own literal table β the decoder reuses the dictionary's shared table, which is where most of the small-block ratio gain comes from. The dict_id covers both the content and the table, so a single id pins the complete decode state. (#275)
Static Context API (Caller-Managed Workspaces)
New zero-allocation path for embedded and tightly-budgeted integrators: query the required workspace size up front, then hand libzxc a buffer you own.
zxc_static_cctx_workspace_size/zxc_static_dctx_workspace_sizeandzxc_init_static_cctx/zxc_init_static_dctxinitialise compression/decompression contexts in caller-provided memory: nomallocon the hot path. (#242)
Custom Reader Interface for Seekable Archives
zxc_seekable_open_reader lets seekable random-access decompression run over an arbitrary user-supplied reader callback (network, memory map, custom VFS), not just FILE*. zxc_seekable_set_dict wires dictionary support into the seekable path. (#240)
Dictionary API in Every Wrapper
Full dictionary training, serialization, and dictionary-backed compress/decompress exposed idiomatically across Go, Rust, Python, Node.js, and WASM, backed by an overhauled, hardened dictionary fuzzer. (#269, #270)
The seekable random-access decompression API is now exposed across Go, Rust, Node.js, and WASM via the umbrella header, and added to the Python wrapper. (#224, #250)
CLI: Dictionaries and the unzxc Alias
--traintrains a dictionary from the input files (output path via-o, defaulting todictionary_<dict_id>.zxd). This renames the former--train-dict PATH.-D, --dict FILEcompresses or decompresses against a.zxd;-l, --listreports an archive'sDict IDand inspects.zxdfiles.unzxcis a new decompression alias: installed as a symlink tozxc, it defaults to decompress mode (equivalent tozxc -d). (#272)- Native wildcard expansion on Windows: the CLI expands glob patterns itself on Windows (where the shell doesn't), so
zxc *.logbehaves as on POSIX. (#284)
Performance & Memory
Measured versus v0.11.0:
- ~10 % faster compression at level 6, from the repeat-offset seed feeding the optimal parser (see below).
- ~3 % faster decompression at levels 1 and 2.
- Repeat-offset seed at L6: the optimal parser seeds match finding with the current repeat offset, accelerating LZ77 search on repetitive data at maximum density. (#257)
- Bucket-sort Huffman leaf ordering: replaces comparison sorting of literal frequencies with a bucket sort during table construction. (#244)
- Dedicated SSE2 SIMD path for x86-64: a sub-AVX2 tier so older / baseline x86-64 CPUs get vectorised decode and match finding instead of falling back to scalar. (#259)
- Short-offset LZ run decoding: faster run/overlap copies for small match offsets, improving decode on highly repetitive data (neutral on Silesia). (#276)
Bug Fixes & Robustness
- Harden decompressor buffer bounds checks against malformed input. (#229)
- Decompressor output-buffer tail padding adjusted to match the fast decoder's overwrite contract. (#249)
- Empty-data compression/decompression handled correctly end-to-end across the wrappers. (#265)
- Enhanced format validation with stricter rejection of malformed frames. (#271)
- Snyk findings addressed and general robustness improvements. (#273)
- Gate AVX2/AVX-512 detection on OS-enabled vector state: feature detection now also checks
OSXSAVE/XCR0, so the AVX2/AVX-512 decode paths are used only when the OS has enabled the YMM/ZMM state, preventing illegal-instruction faults on misconfigured systems. (#283)
Development & CI
- Decoder conformance test suite plus a
maketarget to run it, locking in cross-version decode behaviour. (#246) - Golden-file format-stability tests to catch unintended on-disk format drift. (#256)
- Native Meson build system support alongside CMake. (#245)
- Automated ABI stability check workflow diffs every change against a committed libabigail baseline. (#222)
- Multi-compiler matrix extended with GCC 15 and 16. (#274)
- Format advanced to v6 with the
NUMblock type removed (#264); the Sans-IO API and frame primitives are now internalised (#225). Codecov action updated to v7 (#267). - Consolidated ClusterFuzzLite fuzzer builds and runs into a single workflow, simplifying continuous fuzzing CI. (#287)
Documentation
- README: dictionary guide (when small-block workloads benefit and why), specific decode-performance claims, and a format conformance & stability overview.
- FORMAT / man page / EXAMPLES: document the
.zxddictionary format,--train,-D/--dict, and theunzxcalias. - Doxygen output now includes the README and brief per-symbol descriptions.
Acknowledgements
Special thanks to @jeanga for the help, support, and testing that went into the new pre-trained dictionary mode.
Changelog
- api: Implements shared dictionary Huffman table (#275)
- api: Enhances format validation (#271)
- api: Overhauls dictionary fuzzer for robust testing (#270)
- api: Adds comprehensive dictionary API to all wrappers (#269)
- api: Introduces pre-trained dictionary compression (#261)
- api: bump node-addon-api from 8.7.0 to 8.8.0 in /wrappers/nodejs (#253)
- api: Adds conformance test suite and improves empty frame handling (#246)
- api: Harden decompressor buffer bounds checks (#229)
- api: Introduces static context API for caller-managed workspaces (#242)
- api: Adds custom reader interface for seekable archives (#240)
- api: wrappers: Adds seekable random-access decompression API (#224)
- api: Internalizes Sans-IO API and frame primitives (#225)
- perf: Optimize LZ run decoding for short offsets (#276)
- perf: Add make target to run decoder conformance suite
- perf: Improve Huffman leaf sorting with bucket sort (#244)
- cli: enable native wildcard expansion for CLI on Windows (#284)
- cli: Adds
unzxcalias and renames dictionary training option (#272) - cli: Remove Snyk policy ignore
- cli: Addresses Snyk scan findings and improves robustness (#273)
- cli: Adds native Meson build system support (#245)
- build: bump tar from 7.5.13 to 7.5.16 in /wrappers/nodejs (#286)
- build: bump vite from 8.0.14 to 8.0.16 in /wrappers/nodejs (#285)
- build: Add GCC 15 and 16 to CI multi-compiler matrix (#274)
- build: Update LZbench branch for benchmark workflow
- build: Enables empty data compression/decompression (#265)
- build: Update package descriptions
- build: Introduce dedicated SSE2 SIMD optimization path for x86-64 (#259)
- build: tests: Enforces golden file format stability (#256)
- build: bump vitest from 4.1.6 to 4.1.7 in /wrappers/nodejs (#254)
- build: Restrict SBOM generation to tag pushes
- build: Add qemu cpu targeting for simd dispatch coverage (#238)
- build: Bump cibuildwheel from 3.3.1 to 3.4.1 in /wrappers/python (#233)
- build: Bump vitest from 4.1.5 to 4.1.6 in /wrappers/nodejs (#234)
- build: Use upstream LZbench for benchmarks
- build: Add automated ABI stability check workflow (#222)
- build: Generates SBOM for GitHub releases (#226)
- build: Automate CHANGELOG generation for releases (#220)
- build: Standardizes release artifact structure and naming (#223)
- build: Pins Python wrapper build dependencies (#221)
- build: Pin Python wrapper dependencies with ...
ZXC v0.11.0
Release Notes
This release introduces Level 6 (ZXC_LEVEL_DENSITY) with Huffman-coded literals and an optimal LZ77 parser, a push-based streaming API for non-blocking integrations, and idiomatic streaming I/O adapters across every official wrapper. It raises the default block size from 256 KB to 512 KB for better ratio and decode throughput.
No ABI breaks: SOVERSION stays at 3.
New Features
Level 6: ZXC_LEVEL_DENSITY
New maximum compression preset combining:
- Canonical Huffman literal encoding (8-bit length-limited). Decoder uses a 2048-entry multi-symbol lookup table over an 11-bit window, each entry decodes 1 or 2 symbols, with a 4-way interleaved bitstream. Selected only when Huffman saves >= 3 % over RAW/RLE.
- Optimal LZ77 parser based on dynamic programming over per-position cost estimates. Scratch is allocated lazily per cctx and reused across blocks.
The Huffman codec is integrated into the function-multi-versioning system (default / NEON / AVX2 / AVX-512). Format spec updated with enc_lit=2. (#208, #214)
Push-Based Streaming API (zxc_pstream.h)
Caller-driven, single-threaded streaming API for environments where blocking on FILE* is not possible: async event loops, callback-driven libraries, network protocols. Uses explicit zxc_inbuf_t / zxc_outbuf_t descriptors, allowing input/output in arbitrary chunks. Output is bit-identical to the Buffer and Stream APIs. Bindings provided in Go, Rust, Python, Node.js, and WASM, with a dedicated fuzzer. (#204)
Streaming I/O Adapters in Every Wrapper
Idiomatic streaming adapters across all wrappers, plus a magic-word detection utility for content-type sniffing.
- Go:
io.Reader/io.Writer/io.Closer. (#209) - Python:
ZxcReader/ZxcWriter(io.RawIOBase). (#210) - WHATWG
TransformStream: forfetch().body.pipeThrough(), Deno, Bun, Node.js. (#211) - Node.js
stream.Transform. (#212) - Rust
std::io:Encoder/Decoder. (#213)
Block API Safety Helpers
Introduces zxc_decompress_block_bound (minimum destination capacity required by the fast decoder) and zxc_decompress_block_safe (strict-sized variant for page-aligned or exactly-sized buffers where tail padding is impossible). (#198)
Compression Context Memory Estimation
zxc_estimate_cctx_size lets integrators with tight memory budgets compute the full context footprint before allocation. Exposed too in the Rust and Go wrappers. (#198)
Automatic Block Size in Block API
zxc_compress_block now automatically rounds the effective block size up to the next power of two that fits the source when no explicit block_size is provided. (#198)
Benchmarks vs v0.10.0
Silesia corpus, lzbench 2.2.1, single-threaded, -march=native.
Compression Ratio
Smaller output at every level:
- -1: 61.56 % β 61.50 % (β0.10 %)
- -2: 54.00 % β 53.61 % (β0.72 %)
- -3: 46.35 % β 45.79 % (β1.21 %)
- -4: 43.14 % β 42.65 % (β1.14 %)
- -5: 40.67 % β 40.27 % (β0.98 %)
- -6 (new): 36.28 %. ~10.8 % smaller than L5
Decompression Throughput
- Apple M2 (ARM64): L1 12 195 β 12 530 MB/s (+2.7 %), L2 +3.1 %, L3βL5 +0.6 to +1.4 %.
- Google Axion (ARM64): L1 8 924 β 9 067 MB/s (+1.6 %), L2 +0.8 %, L3βL5 within Β±0.3 %.
- AMD EPYC 9B45 (x86_64): L1 10 803 β 10 844 MB/s (+0.4 %), L2 +0.6 %, L4βL5 β1.1 to β1.3 %.
- AMD EPYC 7763 (x86_64): L1 6 921 β 7 077 MB/s (+2.3 %), L2 +2.1 %, L5 +1.7 %.
Compression Throughput
Fast levels accelerate sharply on Axion / x86 thanks to the tag-first hash filter; mid levels trade a few % for the ratio gains:
- L1: M2 -3.1 %, Axion +7.2 %, EPYC 9B45 +9.1 %, EPYC 7763 +6.5 %.
- L2: M2 -2.3 %, Axion +12.0 %, EPYC 9B45 +16.1 %, EPYC 7763 +11.1 %.
- L3βL5: -1 to -5 % across all platforms, cost of the tighter ratio target and DP-based decisions feeding the parser.
- L6 (new): ~9-12 MB/s. ~10Γ slower than L5 by design, paying compression CPU once to ship a smaller artifact to millions of decoders.
Performance & Memory
- Tag-first hash filter for fast levels: at ZXC_LEVEL_FAST and below, an early hash-tag check short-circuits the position lookup on tag mismatch, with speculative prefetch of the next entries. (#215)
- NEON nibble masks via
SHRN: 128-bit byte comparison masks consolidated into 64-bit nibble masks. (#208) - Vectorized optimal-parser DP updates: AVX-512 / AVX2 / NEON for constant-cost match-length updates. (#208)
- Bulk-flush Huffman bit writer + cache-line-aligned tables. (#208)
- Chain table as a 64 KB ring buffer: entries beyond the 64 KB
uint16_tdelta reach were never read back. Saves over 3.8 MB percctxat the 2 MB block size with no change in ratio or throughput. (#198) - Aliased sequence/offset/token buffers: GLO and GHI paths are mutually exclusive per block; the three pointers now share a single region. (#198)
- Tighter max-sequence bound:
chunk_size / ZXC_LZ_MIN_MATCH_LEN. (#198) - Unified fast/safe decoders via a compile-time constant, eliminating duplicated logic with zero-cost rollback in the fast path. (#198)
Bug Fixes & Robustness
- TSan data race in stream engine shutdown: the async writer read
job->result_szoutside the lock while the main thread wrote the shutdown sentinel under it. Fixed by snapshottingresult_szandin_szunder the lock in the writer. (#201) - Varint truncation for chunk > 2 MB:
zxc_write_varintwas capped at 3 bytes, silently truncating literal runs or match lengths above 2 MB. Extended to 5 bytes to match the decoder. (#198) - Block API auto-resize: no longer rejects inputs larger than the explicit
block_size; rounds up to the next power of two. (#198) - Block API re-init safety: guards against zero-capacity destinations to prevent underflow. (#198)
- Harden decompression logic against integer overflows on 32-bit platforms. (#202)
- Pstream input validation: comprehensive bounds and NULL-pointer checks; rejects malformed buffer descriptors; footer validation distinguishes corruption from checksum failures. (#204)
- Wrapper decompression flush: Python, Node.js, and WASM dstream loops now drain internal buffers after user input is exhausted. Output buffer growth checks ma 9E88 x-capacity overflow. (#204)
Development & CI
- Seekable API fuzzer (
fuzz_seekable.c) exercising range reads, metadata getters, multi-threaded decompression, and raw SEK block parsing. All fuzzers now use persistent static buffers and a 4 MB input cap. (#196) - Pstream API fuzzer targeting the state machine, chunk boundaries, and parser logic of the push-based streaming API. (#204)
- Windows ARM64 Go CI:
windows-11-armrunner with llvm-mingw toolchain (aarch64-w64-mingw32-gcc). (#195) - Monolithic test suite split: per-API files, individual CTest entries (96 instead of one aggregate). (#200)
Documentation
- Block API safety helpers: documents
zxc_decompress_block_bound,zxc_decompress_block_safe, andzxc_estimate_cctx_size, explaining the tail-pad requirement of the fast decoder. (#198) - Format spec:
enc_lit=2Huffman literal section. - API: documents the push-based streaming API and the new
ZXC_LEVEL_DENSITYpreset. - Nim bindings: community-maintained
zxc-nimadded to the README's examples and language support table, thanks to @georgelemon - Free Pascal bindings: community-maintained
Free-Pascal-port-of-ZXC-compressor-decompressoradded to the README, thanks to @Xelitan - TurboBench verification: documents TurboBench as an industry-standard benchmark suite that has officially merged zxc into its master branch.
Changes
Full Changelog: v0.10.0...v0.11.0
ZXC v0.10.0
Release Notes
This release introduces seekable archives for random-access decompression, WebAssembly support for browser and server-side JavaScript, a block-level API for filesystem integrations, a runtime library information API, and an official Go wrapper. It improves stream engine robustness with thread-safety fixes and includes several breaking CLI changes (default extension, checksum behavior, pkg-config module name). It also delivers significant encoder optimizations (split hash table, semi-branchless varint decoding, unified literal copy fast path, BMI/LZCNT extensions, parameterized lazy matching, SIMD register optimization) and internal code quality improvements. SOVERSION is incremented to 3.
Breaking Changes
- Default file extension changed to
.zxc: Compressed files now use the.zxcextension instead of the previous.xcsuffix. File detection, recursive processing, and documentation have been updated accordingly. (#143) - pkg-config module renamed to
libzxc: Follows the naming convention used bylibzstd,liblz4,liblzma,libbrotli. Consumers should update their build files to usepkg_check_modules(LIBZXC IMPORTED_TARGET libzxc). (#144) - Checksums enabled by default: The CLI now enables checksums for all operation modes except
--bench, where they remain disabled to avoid measuring checksum overhead. (#142) - SOVERSION incremented to 3: The shared library SOVERSION is bumped from 2 to 3 due to ABI-breaking changes (new seekable API, block-level API, runtime info API). Consumers linking against the shared library (
libzxc.so.3/libzxc.3.dylib) must rebuild. Static library users are unaffected.
New Features
Seekable Archives
Introduces seekable ZXC archives for random-access decompression. When seekable = 1 is set in zxc_compress_opts_t, the compressor appends a seek table block (ZXC_BLOCK_SEK) containing per-block compressed sizes, enabling O(1) block lookup and byte-range decompression without scanning the full stream.
New APIs: zxc_seekable_open, zxc_seekable_open_file, zxc_seekable_read, zxc_seekable_close, zxc_seekable_get_num_blocks, zxc_seekable_get_decompressed_size. (#188)
WebAssembly (WASM) Support
Adds an Emscripten-based WebAssembly build target, enabling browser and server-side JavaScript compression. Includes a JavaScript API wrapper for buffer-based operations, Node.js roundtrip tests, and a GitHub Actions workflow for automated WASM builds on push and release events. (#189)
Block-Level API
Introduces zxc_compress_block, zxc_decompress_block, and zxc_compress_block_bound for single-block compression and decompression without file headers, footers, or EOF blocks. This API is designed for filesystem integrations (DwarFS, EROFS, SquashFS) where the caller manages its own block indexing. (#148)
Runtime Library Information API
Introduces zxc_min_level, zxc_max_level, zxc_default_level, and zxc_version_string to query the supported compression level range and library version string at runtime. This allows callers, such as filesystem integrations, to discover metadata and capabilities without relying on compile-time constants alone. (#163)
Go Wrapper
Introduces the official Go package for zxc, located in wrappers/go. Built on CGo, it exposes two idiomatic APIs:
- Buffer API: for in-memory compression and decompression.
- Streaming API: for large files, backed by the multi-threaded stream engine with separate reader, worker, and writer threads.
Typed sentinel errors map every C error code to a named Go error value. Cross-platform CI (ubuntu-latest, ubuntu-24.04-arm, macos-latest, windows-latest) runs both tests and benchmarks on every release via the Test Go Package workflow. (#149)
Scalar-Only Build Option
Introduces the ZXC_DISABLE_SIMD CMake option to bypass all hand-written SIMD intrinsics and inline assembly, forcing the library into a scalar-only execution path. Compiler auto-vectorization remains unaffected. This is useful for baseline performance benchmarking, portability testing, and security auditing of the scalar code paths. (#174)
Performance
- Split hash table architecture: Separates the LZ77 hash table into a position array (
uint32_t, 128 KB) and an 8-bit tag array (uint8_t, 32 KB) with 15-bit addressing (32 768 buckets, up from 16 384). The compact tag array fits in L1 cache and rejects mismatches before loading positions, while the doubled bucket count shortens chain walks. (#179) - Semi-branchless varint decoding: Replaces conditional branching in the variable-byte integer decoder with look-up tables and bitwise operations, reducing branch mispredictions in the decompression hot path. Also standardizes bit-width constants to use
CHAR_BITfrom<limits.h>. (#186) - Unified 32-byte literal copy: Replaces conditional 16-byte and 32-byte literal copies with a single 32-byte copy when buffer padding allows, reducing branching in the compression hot path. (#181)
- Varint encoding simplification: Removes unreachable 4-byte and 5-byte varint encoding paths (bounded by the 2 MB block size limit), replaces hardcoded integer limits with bit-shift constants, and simplifies lazy evaluation naming for clarity. (#183)
- Decoupled LZ77 lazy evaluation: Improves the parallelism of lazy evaluation checks within
zxc_lz77_find_best_matchby evaluating ip+1 and ip+2 positions independently before a single decision, enhancing match finding efficiency. (#177) - Refined numeric data detection: The
zxc_probe_is_numericfunction now employs multi-region sampling, analyzing both the start and middle of a block to more accurately determine if data is suitable for numeric compression. (#177) - Optimized decompression loop: The safe loop for sequence decoding in
zxc_decode_block_ghiis now 4x unrolled to process multiple sequences concurrently, significantly boosting decompression speed. (#177) - SIMD register optimization: Eliminates GPR round-trips in SIMD batch loops across all platforms. On ARM64, uses
vdupq_laneq_u32for direct NEON lane broadcast. On AVX2, uses_mm256_permute2x128_si256+_mm256_shuffle_epi32. On AVX-512, uses_mm512_shuffle_i32x4+_mm512_shuffle_epi32. This keeps the running sum in vector registers throughout the decode batch loop, reducing instruction latency. (#164) - ARM64 encoder vectorization: Leverages
vminvq_u8andvmaxvq_u8vector reduction intrinsics for 16-byte block uniformity checks during RLE and literal scanning, avoiding premature extraction to general-purpose registers. Also removes an unnecessary prefetch in the LZ77 match-finding loop. (#164) - Encoder micro-optimization: Extracts
offset_bitsandoffset_maskfrom the context structure into local constants inzxc_encode_block_gloandzxc_encode_block_ghi, reducing repeated member access in performance-critical paths. (#141) - BMI/LZCNT extensions: Optimizes
zxc_log2_u32with platform-specific intrinsics (_BitScanReverse,__builtin_clz) and enables BMI1/BMI2/LZCNT instruction set flags for AVX2 and AVX512 build variants. Fixes potential undefined behavior in 64-bit shift calculations for offset mask and epoch values. (#159) - Parameterized lazy matching threshold: Replaces the hardcoded match length limit (128) for lazy evaluation with a configurable parameter in
zxc_lz77_params_t, allowing each compression level to independently tune the trade-off between search depth and encoding speed. (#158) - I/O buffer alignment: Replaces hardcoded buffer constants in the CLI with the
ZXC_IO_BUFFER_SIZEmacro (1 MB), ensuring consistent I/O buffer sizing during file processing. (#162) - Decompression macro refactor: Introduces
DECODE_COPY_LITERALSandDECODE_COPY_MATCHsub-macros to deduplicate the literal and match copying logic across SAFE and FAST decoding paths in bothgloandghiblock decoders. (#150)
Bug Fixes & Robustness
- Improved bit reader robustness: Corrects edge-case handling in the bit reader's
zxc_br_ensurefunction, ensuring proper behavior when working with 64-bit masks and shifts. - Stream engine synchronization: Destroys mutexes and condition variables after worker threads have joined to prevent resource leaks. (#146)
- Thread creation failure handling: Properly destroys synchronization primitives and joins started threads when thread creation or context initialization fails, preventing resource leaks and deadlocks. (#146)
- Worker thread data races: Job status and result size updates in worker threads are now performed while holding the ...
ZXC v0.9.1
Release Notes
This is a maintenance release with CLI usability improvements and documentation updates. No ABI changes, SOVERSION remains 2.
Bug Fixes (CLI)
-
Fix: --bench duration parsing:
--bench [N]was not correctly consuming the duration argument. The value was left as a positional argument, causing the next filename to be misinterpreted as a duration. All three forms now work correctly:-b2, --bench=2, --bench 2. -
-B / --block-size in help text: The
--block-sizeoption was functional since v0.9.0 but missing fromzxc --helpoutput. Now documented: -B, --block-size Block size: 4K..2M, power of 2 {256K}.
Documentation
- tar integration: Added usage examples for using zxc as an external compressor with tar:
- GNU tar:
tar -I 'zxc -5' -cf archive.tar.zxc data/ - bsdtar (macOS):
tar --use-compress-program='zxc -5' -cf archive.tar.zxc data/ - Universal pipes:
tar cf - data/ | zxc > archive.tar.zxc
- GNU tar:
Changes
Full Changelog: v0.9.0...v0.9.1
ZXC v0.9.0
Release Notes
This release introduces the Reusable Context API, configurable block sizes, a comprehensive API & ABI reference, and several security fixes; including a heap-buffer-overflow found by fuzzing the compression path.
SOVERSION bumped to 2: this release contains ABI-breaking changes.
Breaking Changes
- SOVERSION 2: The shared library ABI version has been incremented. Binaries compiled against
SOVERSION 1must be recompiled. - Options structs:
zxc_compress()andzxc_decompress()now takezxc_compress_opts_t*/zxc_decompress_opts_t*instead of positional parameters. PassNULLfor default behavior. - Block size encoding: The file header now stores block size as a log2 exponent
[12..21]instead of the previous dual-scale encoding. Legacy value64is still accepted for backward compatibility.
New Features
Reusable Context API
Opaque, heap-allocated compression and decompression contexts that eliminate per-call allocation overhead, ideal for filesystem plug-ins (squashfs, dwarfs) and batch processing:
zxc_create_cctx()/zxc_free_cctx()/zxc_compress_cctx()zxc_create_dctx()/zxc_free_dctx()/zxc_decompress_dctx()
Internal buffers are only reallocated when block_size or level changes between calls.
Configurable Block Sizes
- Block size is now configurable via the
block_sizefield inzxc_compress_opts_t. - Valid range: 4 KB - 2 MB (power of two).
- Default remains 256 KB.
- CLI: new
--block-sizeor-Boption with unit suffixes (4K,1M, or4KB,1MBetc.).
Direct Decompression Fast Path
- When the destination buffer is sufficiently padded, decompression skips the intermediate work buffer copy, reducing memory traffic and latency.
Security & Stability
- Fix: heap-buffer-overflow in bitpacking (
zxc_bitpack_stream_32): The packing loop could write up to 4 bytes past the output buffer when the last packed value straddled a byte boundary. - Fix: stream validation during decompression (
zxc_decompress.c): Added validation to ensure the sequence stream size is sufficient for the declared number of sequences, preventing out-of-bounds reads. - Block size validation: Both
zxc_cctx_init()andzxc_read_file_header()now validate that block sizes are within[ZXC_BLOCK_SIZE_MIN, ZXC_BLOCK_SIZE_MAX]and are powers of two. - C++ compatibility: All public headers now include
extern "C"guards for seamless C++ consumption. - Fuzzing corpus storage: Configured persistent corpus storage for ClusterFuzzLite CI.
Documentation
- docs/API.md: New comprehensive API & ABI reference documenting all 21 exported symbols, type definitions, visibility strategy, ABI versioning, thread safety guarantees, and error handling patterns.
- Updated
manpage andREADMEwith installation instructions and packaging status (vcpkg, Conan, Homebrew). - Updated docs/FORMAT.md for exponent-based block size encoding.
Wrappers
All language wrappers updated to use the new options struct API:
- Python: Updated CFFI bindings for options structs.
- Rust: Updated FFI layer and safe wrapper for options structs and reusable context API.
- Node.js: Updated N-API bindings for options structs.
Changes
Full Changelog: v0.8.3...v0.9.0
ZXC v0.8.3
Release Notes
Patch release fixing compilation errors with Clang 12-16 and suppressing warnings on MinGW (GCC). Also introduces a new CI workflow testing 22 compiler configurations.
Changes
Fixes
- Fix
_Atomicstatic initializer for Clang 12-16 (zxc_dispatch.c). Replaced with explicit casts to the target function pointer type. - Fix
-Wunused-parameterwarnings on MinGW forattrandretvalparameters in the Win32 pthread.
CI
- Add compiler compatibility CI workflow. Added a comprehensive CI workflow testing 22 compiler x architecture combinations (#134)
Full Changelog: v0.8.2...v0.8.3