build(rust): add legacy feature scaffolding

The legacy decoders (lib/legacy/zstd_v01.c .. zstd_v07.c) are next in
the Rust migration. Each of those files is a frozen snapshot of the
FSE/Huff0 entropy coders and frame logic of one historical release, so
their ports must not reuse the modern Rust entropy modules and must not
share code with each other: outputs and error codes have to stay
byte-identical to the frozen C forever. This commit installs the
build-system scaffolding so seven per-version ports can land
independently, each adding only its own module file plus a one-line
registration in rust/src/legacy/mod.rs.

Cargo grows features legacy-v01 .. legacy-v07. They are never default
features: the C build defaults differ per build system, so each build
system passes the list explicitly, derived from its own legacy
configuration:

- lib/Makefile and programs/Makefile map ZSTD_LEGACY_SUPPORT=N to the
  features for versions N..7 (0 disables legacy), mirroring the
  ZSTD_LEGACY_FILES selection in lib/libzstd.mk.
- tests/Makefile always enables all seven features because its
  ZSTDLEGACY_FILES wildcard compiles every lib/legacy/*.c regardless of
  the dispatch level.
- build/meson maps legacy_level exactly like the makefiles; build/cmake
  enables all seven whenever ZSTD_LEGACY_SUPPORT is ON because it
  always compiles all seven C files (ZSTD_LEGACY_LEVEL only selects the
  C dispatch).

Every build system also encodes the legacy selection in the Rust target
directory name (e.g. c1-d1-default-legacy5), for the same reason the
HUF mode is encoded there: a cached archive built for one configuration
must never be linked into a build expecting another. In tests/Makefile
the legacy level additionally flows into the existing HUF C-mode stamp,
so the flat C test objects (which bake -DZSTD_LEGACY_SUPPORT into the
dispatch) are rebuilt whenever the level changes. In programs/Makefile
the compress-only, decompress-only, and CLI archives keep
level-independent directories (RUST_HUF_MODE) because they are only
linked into ZSTD_LEGACY_SUPPORT=0 program variants and carry no legacy
features.

A feature whose version has not been ported yet gates nothing: the
module registration in rust/src/legacy/mod.rs is added by each port,
so enabling e.g. legacy-v05 today simply leaves that decoder in C.
This is what makes mixed C/Rust legacy levels link cleanly while the
seven ports land in any order.

Test plan:
- cd rust && cargo fmt --check && cargo clippy --all-targets
  -- -D warnings && cargo test --all-targets
- cargo clippy with --no-default-features --features
  decompression,legacy-v01 and with all seven legacy features
- make -C tests fuzzer && ./tests/fuzzer -i1 --no-big-tests
- make -C tests test-rust-lib-smoke; make -C tests test-legacy
- make -C lib libzstd.a with ZSTD_LEGACY_SUPPORT=0, 1 and default (5)
- cmake configure and meson setup (including -Dlegacy_level=1) emit the
  expected --features lists and legacy-suffixed target directories
This commit is contained in:
2026-07-11 23:07:55 +02:00
parent fef5f4478a
commit bdd35c838d
9 changed files with 170 additions and 19 deletions
+38
View File
@@ -0,0 +1,38 @@
//! Frozen decoders for the legacy zstd formats (v0.1 through v0.7).
//!
//! Frozen-decoder policy
//! =====================
//!
//! Each `lib/legacy/zstd_v0N.c` translation unit is a self-contained snapshot
//! of the entropy coders and frame logic of that historical release. The
//! Rust ports mirror that property:
//!
//! - Every version keeps its own frozen FSE/Huff0 and frame logic. The
//! modern `fse_decompress`, `huf_decompress`, `bitstream`, or `mem`
//! modules must NOT be reused here, and legacy versions must not share
//! code with each other, even where functions look identical. The legacy
//! formats are frozen; the modern modules keep evolving.
//! - Ports are line-by-line translations of the corresponding C file: same
//! table layouts, same arithmetic, same error codes. Outputs must be
//! byte-identical to the C implementation, including error behavior.
//! - The only shared dependency is `crate::errors`, because the C files
//! include `error_private.h` for the public `ZSTD_ErrorCode` values.
//!
//! Registration
//! ============
//!
//! Cargo features `legacy-v01` .. `legacy-v07` are all declared in
//! `Cargo.toml`. The build systems always pass the feature list derived
//! from the C configuration (`ZSTD_LEGACY_SUPPORT=N` enables versions N
//! and newer), so a feature may be enabled before its port exists. A version
//! without a Rust module simply stays implemented by its C file.
//!
//! To port version `v0N`: add `zstd_v0N.rs` next to this file, reduce
//! `lib/legacy/zstd_v0N.c` to a declaration-only shim, and register the
//! module here with exactly one line:
//!
//! ```text
//! #[cfg(feature = "legacy-v0N")]
//! pub mod zstd_v0N;
//! ```
+1
View File
@@ -18,6 +18,7 @@ pub mod hist;
pub mod huf_compress;
#[cfg(feature = "decompression")]
pub mod huf_decompress;
pub mod legacy;
pub mod mem;
pub mod pool;
pub mod threading;