The COOLJAPAN compression layer just learned to write Zstandard from the ground up.
Today we released OxiArc 0.2.2 — pure Rust archive and compression, now with a complete, from-scratch Zstandard encoder so the workspace can write real .zst, not just read it.
No C. No Fortran. No zlib. No libzstd. No libzip. No libarchive. No 7-zip. No external shared libraries.
No FFI overhead. No build hell.
Just clean, memory-safe, blazing-fast archiving and compression that compiles to a single static binary (or WASM) and runs everywhere.
Why OxiArc 0.2.2 matters
Handling archives and compression has long meant leaning on heavy native libraries — zlib, libzstd, libzip, libarchive, 7-zip — with all the baggage that brings:
- Complex build systems and dependency hell
- Security risks from large, aging C codebases
- Platform-specific binaries and vendor lock-in
- Difficulty in WASM, embedded, or no_std environments
- Inconsistent support for legacy formats like LZH and Shift_JIS (still widely used in Japan)
Zstandard is the modern default for fast, high-ratio compression — but until now a pure-Rust pipeline could read Zstd while still reaching for libzstd (or the zstd crate’s C bindings) the moment it needed to produce a .zst. 0.2.2 closes that gap. oxiarc-zstd now contains a real entropy-coding encoder — Finite State Entropy tables, canonical Huffman for literals, and an LZ77 match finder — assembled into valid Zstd compressed blocks. The write path is sovereign too.
Technical Deep Dive: A Zstandard Encoder, Built From Entropy Coding Up
OxiArc keeps its layered shape — a core layer, codec crates, a container layer, and a CLI — and 0.2.2 lands almost entirely in the Codec Layer and Container Layer.
-
Core Layer (
oxiarc-core) SharedCompressor/Decompressortraits, CRC-32/64/16 with slicing-by-8, and bitstream utilities — unchanged and carried forward. -
Codec Layer —
oxiarc-zstdgains a full encoder, written as a stack of new modules:bitwriter— aForwardBitWriter(LSB-first) for FSE table headers, and a sentinel-markedBackwardBitWriterthat emits the reversed bitstream FSE sequence encoding needs.lz77— a hash-chainMatchFinderwith multiply-shift hashing, 22 compression levels,MIN_MATCH = 3/MAX_MATCH = 65539, and a publicLz77Sequence/LevelConfig/MatchFindersurface.huffman_encoder— canonical Huffman for literals with Kraft-inequality length limiting toMAX_CODE_LENGTH = 11, plus weight-table serialization.fse_encoder— Finite State Entropy tables and state machine, withll_code/ml_code/of_codehelpers and the predefined LL/OF/ML tables.compressed_block— assembles full Zstd compressed blocks from LZ77 sequences.- A public
streamingmodule (ZstdStreamEncoder<W: Write>withfinish(), andZstdStreamDecoder<R: Read>), and a publicdictmodule (ZstdDict, an n-gramtrain_dictionary(samples, dict_size)trainer,MAX_DICT_SIZE = 1 MB). The newZstdEncoderexposescompress_with_level/encode_all/decode_all, plus a feature-gatedcompress_parallel(Rayon, behind theparallelfeature). Levels 0–22. oxiarc-lz4also picks up dictionary frame support:Lz4DictFrameEncoder/Lz4DictFrameDecoder,compress_frame_with_dict/decompress_frame_with_dict(the dict ID is stored and verified in the frame header), andFrameDescriptor::with_dict_id. The monolithic frame module was refactored intotypes/compress/decompress/streaming/frame_dictsubmodules with no public API breakage.
-
Container Layer (
oxiarc-archive) The ZIPheadermodule split intotypes/reader/writer, with richer ZIP64: aDataDescriptor, and automatic ZIP64 upgrade of local headers, the central directory, and the EOCD when sizes or offsets exceed0xFFFFFFFF(extra field0x0001). Encryption read/write paths landed too — AES-256 (WinZip AE-2, HMAC-SHA1 tag verification) and ZipCrypto — viaextract_with_password,extract_with_password_aes, andadd_encrypted_file. -
CLI Layer (
oxiarc-cli) The unifiedoxiarcbinary, carried forward — and now able to produce Zstd-compressed archives end to end.
Getting Started
0.2.2 is on crates.io. To write real Zstd as a library, add the codec:
cargo add oxiarc-zstd
use oxiarc_zstd::{ZstdStreamEncoder, encode_all, decode_all};
use std::io::Write;
// Streaming: write a .zst as you go, then finish().
let mut out = Vec::new();
let mut enc = ZstdStreamEncoder::new(&mut out, 19)?; // level 0-22
enc.write_all(b"Hello, Zstandard, written in pure Rust!")?;
enc.finish()?;
// Or one-shot:
let compressed = encode_all(b"some bytes", 19)?;
let original = decode_all(&compressed)?;
Prefer the CLI? You can now create a Zstd-compressed tarball directly:
oxiarc create backup.tar.zst folder/
What’s New in 0.2.2
- A from-scratch Zstandard encoder in
oxiarc-zstd. Newbitwriter,lz77,huffman_encoder,fse_encoder, andcompressed_blockmodules assemble valid Zstd compressed blocks; publicstreaming(ZstdStreamEncoder/ZstdStreamDecoder) anddictmodules ship alongside. The newZstdEncoderaddscompress_with_level/encode_all/decode_all, with feature-gatedcompress_parallel(Rayon). Levels 0–22. - A Zstd dictionary trainer.
train_dictionary(samples, dict_size)builds an n-gram dictionary (ZstdDict,MAX_DICT_SIZE = 1 MB) — a big win for many small, similar payloads. - LZ4 dictionary frames.
Lz4DictFrameEncoder/Lz4DictFrameDecoderwithcompress_frame_with_dict/decompress_frame_with_dict; the dict ID is written and verified in the frame header (FrameDescriptor::with_dict_id). The frame module was split into focused submodules with no API breakage. - Richer ZIP64 and encryption in
oxiarc-archive. The ZIPheadermodule split intotypes/reader/writer; automatic ZIP64 upgrade kicks in past0xFFFFFFFF(extra field0x0001) with aDataDescriptor; AES-256 (WinZip AE-2, HMAC-SHA1) and ZipCrypto read/write paths viaextract_with_password,extract_with_password_aes, andadd_encrypted_file. - Changed.
ZstdEncodergainedlevel(0–22), dictionary, anddict_idfields (the dict ID is a 4-byte little-endian frame field). Dependency bumps: clap 4.5.60, clap_complete 4.5.66, indicatif 0.18.4, dialoguer 0.12.0, tokio 1.50.0, memmap2 0.9.10. READMEs refreshed across every subcrate, and the security audit passed.
Quality this release: zero Clippy warnings (-D warnings), zero rustdoc warnings, and a 100% test pass.
Tips
Getting the most out of the new write path:
- Pick a Zstd level for the job. Levels run 0–22; low single digits are fast, the high teens and low twenties chase ratio. Pass it to
ZstdStreamEncoder::new(writer, level)orencode_all(data, level). - Train a dictionary for many small payloads. For lots of similar little blobs (log lines, JSON records, telemetry),
train_dictionary(samples, dict_size)produces aZstdDictthat dramatically improves ratio on each one. - Use LZ4 dictionary frames for streaming similar data.
compress_frame_with_dictkeeps a shared dictionary across frames, with the dict ID verified on decode so you can’t decompress against the wrong one. - Choose your ZIP encryption deliberately. AES-256 (WinZip AE-2) is the strong, modern choice; ZipCrypto exists for legacy interop only — reach for
extract_with_password_aes/add_encrypted_filewhen you control both ends. - Turn on the
parallelfeature for big inputs. It unlockscompress_parallel(Rayon) for multi-block Zstd work. - ZIP64 is automatic. Cross 4 GiB in size or offset and the writer upgrades local headers, the central directory, and the EOCD for you — no flag required.
This is the foundation
OxiArc is the compression and archival backend other COOLJAPAN projects build on. It still depends on no sibling projects — just small, focused Rust crates — so siblings sit on top of it, not beside it.
With a real Zstd write path in hand, OxiMedia gains a sovereign way to package media assets, and SciRS2, NumRS2, and PandRS get pure-Rust .zst output for dataset and long-term storage — the same fast, high-ratio format the rest of the world uses, now without the C dependency underneath. It is the quiet data-packaging layer beneath the COOLJAPAN scientific and media stack.
Repository: https://github.com/cool-japan/oxiarc
Star the repo if you want high-performance archiving and compression without the traditional native toolchain headaches.
Pure Rust archiving and compression is here — fast, safe, and sovereign.
— KitaSan at COOLJAPAN OÜ March 10, 2026