⚠️ Not actionable yet
This issue documents a decision and a future migration path — do not schedule or start this work. Half of it is blocked on a .NET runtime release that hasn't shipped. It exists now so the decision and its rationale aren't lost before that release lands. See "Why this is blocked" below for the specific unblock condition.
Context
Midge's SST block compression (src/sst/compression/mod.rs) defines a fixed, on-disk algorithm discriminant, stored as a u8 in the block trailer — this is a wire-format detail Pants must match exactly, not an internal implementation choice:
CompressionAlgo::None = 0
CompressionAlgo::Lz4 = 1 (lz4_flex crate)
CompressionAlgo::Zstd3 = 2 (zstd crate, level 3)
CompressionAlgo::Zstd9 = 3 (zstd crate, level 9+)
Default policy is Adaptive: try LZ4 then Zstd3 then Zstd9 per block (in that order), keep whichever result saves at least min_savings_bytes = 256 bytes and beats a min_ratio = 0.95 compressed/original threshold; blocks under MIN_COMPRESSION_INPUT_BYTES = 256 are stored raw regardless, since framing/codec overhead dominates at that size. Pants already implements this discriminant (MidgeCompressionAlgorithm.cs) and the adaptive selection logic (MidgeSstCodec.CompressBlock/CompressAdaptive), currently via two third-party NuGet packages: K4os.Compression.LZ4 and ZstdSharp.Port (both pure-managed C#, no native binary/P-Invoke — worth noting, since "third-party" here is a vendor-trust question, not a native-deployment-risk one).
The ask: reduce third-party dependency where an out-of-box (BCL/Microsoft-first-party) implementation is available, without breaking the wire-format contract above or the cross-engine SST-readability goal (any conformant Zstd/LZ4 decoder reads any conformant encoder's output — the constraint is bitstream-format compliance, not "same library on both sides").
What changed the calculus
.NET 11 adds native Zstandard support to System.IO.Compression: ZstandardStream, ZstandardEncoder, ZstandardDecoder, ZstandardDictionary, ZstandardCompressionOptions — mirroring the existing BrotliEncoder/Decoder API shape, with quality levels 1–22 and streaming + span-based one-shot operations. It produces standard Zstd-frame bytes, so it's drop-in interoperable with Midge's Rust-side zstd crate output — no shared-spec change required, this is purely an implementation-library swap on the Pants side.
LZ4 has no equivalent. There is no indication of native LZ4 support landing in System.IO.Compression for .NET 10 or .NET 11. Options if/when this is picked up:
- Keep
K4os.Compression.LZ4 (pure-managed, single-purpose, widely used) — the pragmatic default.
- Hand-write an in-house LZ4 block-format codec. The format is small and well-specified enough to be feasible, but this trades a dependency-count win for taking on a hand-rolled decompressor parsing untrusted-shaped byte streams (length/offset fields, overlapping-copy edge cases) — exactly the class of code where a narrow, widely-used, single-purpose library is usually the lower-risk choice, not the higher one. Do not default to "just write it" without weighing that trade-off explicitly when this is picked up.
- Drop LZ4 support — not viable; a Midge-written store using LZ4-compressed blocks would become unreadable, breaking the interop goal.
Why this is blocked
System.IO.Compression's native Zstandard types ship in .NET 11, which is currently Preview 7 (final GA expected November 2026). Pants intentionally targets net10.0 in Directory.Build.props; CI selects the supported 10.0.x SDK line. A prior .NET 11 preview trial was deliberately reverted rather than weakening the analyzer baseline, and this repository intentionally has no global.json.
- There is no supported way to consume the new
ZstandardEncoder/Decoder types from a net10.0 target — this is not a "reference a preview package" situation, it's the BCL of a not-yet-released runtime.
- Unblock condition: .NET 11 reaches general availability and Pants makes a deliberate decision to retarget (fully or via multi-targeting) to
net11.0. That retarget decision is bigger than this issue and shouldn't be made just to get native Zstd — it should be evaluated on its own merits when the time comes.
Requirements (once unblocked)
- Retarget (or multi-target) Pants to
net11.0 as a separate, deliberate decision — this issue does not itself justify that move on its own.
- Replace
ZstdSharp.Port usage in MidgeSstCodec with System.IO.Compression.ZstandardEncoder/ZstandardDecoder, preserving the existing Zstd3/Zstd9 discriminant mapping (quality level 3 and level 9, respectively) and the existing adaptive-selection logic (min_savings_bytes, min_ratio, try-order).
- Add a round-trip interop regression test: compress with the new native encoder, decompress with the existing/legacy path (and vice versa if a transition period exists), and confirm byte-for-byte payload equality against a Midge-produced fixture (extend
MidgeCompatibilityFixtureTests.cs's pattern).
- Re-evaluate LZ4 (keep
K4os.Compression.LZ4 vs. in-house vs. any newer .NET native option that may have shipped by then) as an explicit, separate decision at that time — don't let it ride along silently on the Zstd migration.
- Update
docs/compatibility/ to record the library swap and confirm it required no changes to the persisted CompressionAlgo discriminant values or block trailer format.
Acceptance Criteria (once unblocked)
Evidence / References
- Midge
src/sst/compression/mod.rs (algorithm discriminants, CompressionPolicy::Adaptive defaults, MIN_COMPRESSION_INPUT_BYTES, MAX_BLOCK_SIZE)
- Midge
Cargo.toml (lz4_flex = "0.14", zstd = "0.13")
src/Pants/Internal/Storage/Sst/MidgeCompressionAlgorithm.cs, MidgeSstCodec.cs (current adaptive compression implementation)
Directory.Packages.props (K4os.Compression.LZ4, ZstdSharp.Port)
Directory.Build.props (net10.0 target) and .github/workflows/*.yml (10.0.x SDK selection); no global.json by design
- .NET 11 native Zstandard: .NET 11 Preview 7 announcement, System.IO.Compression move
test/Pants.Tests/MidgeCompatibilityFixtureTests.cs (existing pattern for cross-engine fixture round-trip tests)
This issue documents a decision and a future migration path — do not schedule or start this work. Half of it is blocked on a .NET runtime release that hasn't shipped. It exists now so the decision and its rationale aren't lost before that release lands. See "Why this is blocked" below for the specific unblock condition.
Context
Midge's SST block compression (
src/sst/compression/mod.rs) defines a fixed, on-disk algorithm discriminant, stored as au8in the block trailer — this is a wire-format detail Pants must match exactly, not an internal implementation choice:Default policy is
Adaptive: try LZ4 then Zstd3 then Zstd9 per block (in that order), keep whichever result saves at leastmin_savings_bytes = 256bytes and beats amin_ratio = 0.95compressed/original threshold; blocks underMIN_COMPRESSION_INPUT_BYTES = 256are stored raw regardless, since framing/codec overhead dominates at that size. Pants already implements this discriminant (MidgeCompressionAlgorithm.cs) and the adaptive selection logic (MidgeSstCodec.CompressBlock/CompressAdaptive), currently via two third-party NuGet packages:K4os.Compression.LZ4andZstdSharp.Port(both pure-managed C#, no native binary/P-Invoke — worth noting, since "third-party" here is a vendor-trust question, not a native-deployment-risk one).The ask: reduce third-party dependency where an out-of-box (BCL/Microsoft-first-party) implementation is available, without breaking the wire-format contract above or the cross-engine SST-readability goal (any conformant Zstd/LZ4 decoder reads any conformant encoder's output — the constraint is bitstream-format compliance, not "same library on both sides").
What changed the calculus
.NET 11 adds native Zstandard support to
System.IO.Compression:ZstandardStream,ZstandardEncoder,ZstandardDecoder,ZstandardDictionary,ZstandardCompressionOptions— mirroring the existingBrotliEncoder/DecoderAPI shape, with quality levels 1–22 and streaming + span-based one-shot operations. It produces standard Zstd-frame bytes, so it's drop-in interoperable with Midge's Rust-sidezstdcrate output — no shared-spec change required, this is purely an implementation-library swap on the Pants side.LZ4 has no equivalent. There is no indication of native LZ4 support landing in
System.IO.Compressionfor .NET 10 or .NET 11. Options if/when this is picked up:K4os.Compression.LZ4(pure-managed, single-purpose, widely used) — the pragmatic default.Why this is blocked
System.IO.Compression's native Zstandard types ship in .NET 11, which is currently Preview 7 (final GA expected November 2026). Pants intentionally targetsnet10.0inDirectory.Build.props; CI selects the supported10.0.xSDK line. A prior .NET 11 preview trial was deliberately reverted rather than weakening the analyzer baseline, and this repository intentionally has noglobal.json.ZstandardEncoder/Decodertypes from anet10.0target — this is not a "reference a preview package" situation, it's the BCL of a not-yet-released runtime.net11.0. That retarget decision is bigger than this issue and shouldn't be made just to get native Zstd — it should be evaluated on its own merits when the time comes.Requirements (once unblocked)
net11.0as a separate, deliberate decision — this issue does not itself justify that move on its own.ZstdSharp.Portusage inMidgeSstCodecwithSystem.IO.Compression.ZstandardEncoder/ZstandardDecoder, preserving the existingZstd3/Zstd9discriminant mapping (quality level 3 and level 9, respectively) and the existing adaptive-selection logic (min_savings_bytes,min_ratio, try-order).MidgeCompatibilityFixtureTests.cs's pattern).K4os.Compression.LZ4vs. in-house vs. any newer .NET native option that may have shipped by then) as an explicit, separate decision at that time — don't let it ride along silently on the Zstd migration.docs/compatibility/to record the library swap and confirm it required no changes to the persistedCompressionAlgodiscriminant values or block trailer format.Acceptance Criteria (once unblocked)
net11.0(or multi-targetsnet10.0/net11.0) as a tracked, separate decision referenced from this issue, not decided implicitly here.Zstd3/Zstd9blocks are produced and consumed viaSystem.IO.Compression's native types;ZstdSharp.Portpackage reference is removed.dotnet test --configuration Releasepasses on the newly targeted SDK.CompressionAlgodiscriminant values (None=0, Lz4=1, Zstd3=2, Zstd9=3) or block trailer format.Evidence / References
src/sst/compression/mod.rs(algorithm discriminants,CompressionPolicy::Adaptivedefaults,MIN_COMPRESSION_INPUT_BYTES,MAX_BLOCK_SIZE)Cargo.toml(lz4_flex = "0.14",zstd = "0.13")src/Pants/Internal/Storage/Sst/MidgeCompressionAlgorithm.cs,MidgeSstCodec.cs(current adaptive compression implementation)Directory.Packages.props(K4os.Compression.LZ4,ZstdSharp.Port)Directory.Build.props(net10.0target) and.github/workflows/*.yml(10.0.xSDK selection); noglobal.jsonby designtest/Pants.Tests/MidgeCompatibilityFixtureTests.cs(existing pattern for cross-engine fixture round-trip tests)