@@ -17,187 +17,23 @@ straight from your own data.
1717
1818## Documentation
1919
20- The docs follow the [ Diátaxis] ( https://diataxis.fr ) framework — four kinds of
21- documentation, each serving a different need:
20+ The docs follow the [ Diátaxis] ( https://diataxis.fr ) framework:
2221
2322| | Purpose | Start here |
2423| ---| ---| ---|
25- | ** [ Tutorial] ( docs/tutorial.md ) ** | Learning by doing | [ Getting started] ( docs/tutorial.md ) |
26- | ** [ How-to guides] ( #how-to-guides ) ** | Solving a specific task | [ Hot paths] ( #compress-on-a-hot-path ) , [ Dictionaries] ( #compress-many-small-payloads-with-a-dictionary ) , [ Zero-copy] ( #avoid-heap-copies-with-memorysegment ) , [ Self-built lib] ( #run-against-a-self-built-libzstd ) |
27- | ** [ Reference] ( #reference ) ** | Looking up facts | [ Platforms] ( #supported-platforms ) , [ API surface] ( #api-surface ) , [ Symbol coverage] ( docs/supported.md ) , [ Build] ( #build-from-source ) |
28- | ** [ Explanation] ( #explanation ) ** | Understanding the why | [ Why FFM + Zig] ( #why-ffm-and-zig ) , [ When zero-copy pays] ( docs/zero-copy.md ) , [ Benchmarks] ( docs/benchmarks.md ) |
29-
30- ---
31-
32- ## Tutorial: Getting started
33-
34- New here? ** [ docs/tutorial.md] ( docs/tutorial.md ) ** takes you from a clean checkout
35- to your first compress/decompress round-trip, step by step.
36-
37- ## How-to guides
38-
39- Task-focused recipes. Each assumes you have the library on the classpath (see the
40- [ tutorial] ( #tutorial-getting-started ) ).
41-
42- ### Compress on a hot path
43-
44- Reuse a context to amortise native allocation across many calls:
45-
46- ``` java
47- try (ZstdCompressCtx cctx = new ZstdCompressCtx (). level(19 );
48- ZstdDecompressCtx dctx = new ZstdDecompressCtx ()) {
49- byte [] packed = cctx. compress(message);
50- byte [] restored = dctx. decompress(packed, message. length);
51- }
52- ```
53-
54- Pick the level explicitly with ` Zstd.maxCompressionLevel() ` /
55- ` minCompressionLevel() ` when you need the extreme ends.
56-
57- ### Compress many small payloads with a dictionary
58-
59- For many small, similar payloads (log lines, JSON records, protobufs), a
60- dictionary compresses each one far smaller than it could be alone. Train one on
61- representative samples:
62-
63- ``` java
64- ZstdDictionary dict = ZstdDictionary . train(sampleRecords, 16 * 1024 );
65-
66- try (ZstdCompressCtx cctx = new ZstdCompressCtx ();
67- ZstdDecompressCtx dctx = new ZstdDecompressCtx ()) {
68- byte [] packed = cctx. compress(record, dict);
69- byte [] restored = dctx. decompress(packed, record. length, dict);
70- }
71-
72- byte [] persisted = dict. toByteArray(); // store / ship the dictionary
73- ZstdDictionary reloaded = ZstdDictionary . of(persisted);
74- ```
75-
76- On a hot path, digest the dictionary once to skip per-call setup:
77-
78- ``` java
79- try (ZstdCompressDict cdict = new ZstdCompressDict (dict, 19 );
80- ZstdDecompressDict ddict = new ZstdDecompressDict (dict);
81- ZstdCompressCtx cctx = new ZstdCompressCtx ();
82- ZstdDecompressCtx dctx = new ZstdDecompressCtx ()) {
83- byte [] packed = cctx. compress(record, cdict);
84- byte [] restored = dctx. decompress(packed, record. length, ddict);
85- }
86- ```
87-
88- ### Avoid heap copies with ` MemorySegment `
89-
90- When your data is already off-heap — an ` mmap ` slice in, an arena buffer out —
91- use the ` MemorySegment ` overloads to skip the heap ` byte[] ` bounce entirely. FFM
92- hands zstd the segment address directly: no copy in, no copy out, no GC churn.
93-
94- ``` java
95- try (Arena arena = Arena . ofConfined();
96- ZstdDecompressCtx dctx = new ZstdDecompressCtx ()) {
97- MemorySegment frame = reader. mmapSlice(); // already native
98- long n = Zstd . decompressedSize(frame); // read header, no copy
99- MemorySegment out = arena. allocate(n); // becomes the backing buffer
100- dctx. decompress(out, frame); // native → native
101- }
102- ```
103-
104- There are matching ` compress(dst, src) ` / ` decompress(dst, src) ` overloads (plus
105- dictionary variants) returning the number of bytes written. For * why and when*
106- this pays off, see the [ explanation] ( docs/zero-copy.md ) .
107-
108- ### Run against a self-built libzstd
109-
110- To use a ` libzstd ` you built yourself instead of the bundled one, point the
111- loader at it:
112-
113- ``` bash
114- java -Dzstd.lib.path=/path/to/libzstd.dylib --enable-native-access=ALL-UNNAMED ...
115- ```
116-
117- Build any of the six targets from any host:
118-
119- ``` bash
120- ./scripts/build-zstd.sh < output-resources-dir> < classifier>
121- # classifier: osx-aarch64 | osx-x86_64 | linux-x86_64 | linux-aarch64
122- # | windows-x86_64 | windows-aarch64
123- ```
124-
125- ## Reference
126-
127- ### Supported platforms
128-
129- The library — ` io.github.dfa1.zstd:zstd ` — ships as a pure-Java module plus one
130- native artifact per platform:
131-
132- | OS | aarch64 | x86_64 |
133- | ---------| :-------:| :------:|
134- | macOS | ✅ | ✅ |
135- | Linux | ✅ | ✅ |
136- | Windows | ✅ | ✅ |
137-
138- ### API surface
139-
140- | Type | Role |
141- | ---| ---|
142- | ` Zstd ` | one-shot ` compress ` / ` decompress ` , level + version queries, ` compressBound ` , ` decompressedSize ` |
143- | ` ZstdCompressCtx ` / ` ZstdDecompressCtx ` | reusable contexts; ` byte[] ` and ` MemorySegment ` overloads, dictionary variants |
144- | ` ZstdDictionary ` | train (` ZDICT ` ), load, persist, query dict id |
145- | ` ZstdCompressDict ` / ` ZstdDecompressDict ` | pre-digested dictionaries for hot paths |
146- | ` ZstdFrame ` | frame inspection: header, sizes, dict id, skippable frames |
147- | ` ZstdException ` / ` ZstdErrorCode ` | typed errors mapped from zstd's sentinels |
148-
149- ### Symbol coverage
150-
151- Which zstd C symbols are bound (and which deprecated ones are intentionally not),
152- with a per-area breakdown and a comparison against zstd-jni:
153- [ docs/supported.md] ( docs/supported.md ) .
154-
155- ### Runtime requirement
156-
157- Native access requires ` --enable-native-access=ALL-UNNAMED ` (or your module name)
158- on the JVM command line.
159-
160- ### Build from source
161-
162- Requires JDK 25+, Maven, and [ Zig] ( https://ziglang.org/ ) on ` PATH ` .
24+ | ** [ Tutorial] ( docs/tutorial.md ) ** | Learning by doing | Clean checkout → first round-trip |
25+ | ** [ How-to guides] ( docs/how-to.md ) ** | Solving a specific task | Hot paths, dictionaries, zero-copy, self-built lib |
26+ | ** [ Reference] ( docs/reference.md ) ** | Looking up facts | Platforms, API surface, symbol coverage, build |
27+ | ** [ Explanation] ( docs/explanation.md ) ** | Understanding the why | Why FFM + Zig, when zero-copy pays, benchmarks |
16328
16429``` bash
16530git clone --recurse-submodules https://github.com/dfa1/zstd-java.git
166- cd zstd-java
167- mvn test
31+ cd zstd-java && mvn test
16832```
16933
170- ` scripts/build-zstd.sh ` compiles ` libzstd.{dylib,so,dll} ` from the
171- ` third_party/zstd ` submodule (pinned to tag ` v1.5.7 ` ) with ` zig cc ` , cross-compiling
172- any of the six targets from any host.
34+ Requires JDK 25+ and ` --enable-native-access=ALL-UNNAMED ` at runtime.
17335
174- ### License
36+ ## License
17537
17638[ BSD 3-Clause] ( LICENSE ) — the same primary license as zstd, which is bundled
17739under its BSD terms (zstd is dual BSD / GPLv2, © Meta Platforms, Inc.).
178-
179- ## Explanation
180-
181- ### Why FFM and Zig
182-
183- The bindings use the ** Foreign Function & Memory API** rather than JNI: no
184- hand-written C glue, no separate native compile step for the binding layer, and a
185- direct path from Java to zstd's addresses — which is what makes the zero-copy
186- ` MemorySegment ` API possible.
187-
188- The native library itself is built from vendored zstd source via ** ` zig cc ` ** as
189- a drop-in C compiler. zstd is pure C with no build-system dependencies, so the
190- sources are compiled directly — no autotools, no CMake. Zig bundles clang and
191- libc for every target, enabling hermetic cross-compilation without a sysroot:
192- any host can build all six platform artifacts.
193-
194- ### When zero-copy pays off
195-
196- The ` MemorySegment ` fast path eliminates the heap ` byte[] ` bounce and the
197- per-call allocation it implies. The reasoning, and the cases where it does and
198- does not matter, is in [ docs/zero-copy.md] ( docs/zero-copy.md ) .
199-
200- ### Benchmarks
201-
202- Throughput and allocation versus zstd-jni (JNI) and aircompressor (pure Java),
203- including an async-profiler breakdown: [ docs/benchmarks.md] ( docs/benchmarks.md ) .
0 commit comments