Skip to content

Commit 6c3f14a

Browse files
dfa1claude
andcommitted
docs(tutorial): add zero-copy MemorySegment and streaming steps
Show the library's strong point — off-heap MemorySegment round-trip with no copy or per-call allocation — plus incremental ZstdOutputStream/ZstdInputStream for data that doesn't fit in memory. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent c2d6ae2 commit 6c3f14a

1 file changed

Lines changed: 44 additions & 0 deletions

File tree

docs/tutorial.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,49 @@ The FFM API requires an explicit flag:
3636
java --enable-native-access=ALL-UNNAMED Demo.java
3737
```
3838

39+
## 4. Skip the heap: zero-copy with `MemorySegment`
40+
41+
The `byte[]` round-trip above copies your data onto the heap on the way in and
42+
off it on the way out. When your bytes are **already off-heap** — an `mmap` slice,
43+
an arena buffer — that copy is pure waste. This is the library's strong point:
44+
the `MemorySegment` overloads hand zstd the segment address directly, so there is
45+
**no copy in, no copy out, and no per-call heap allocation** (hence no GC churn).
46+
47+
```java
48+
try (Arena arena = Arena.ofConfined();
49+
ZstdDecompressCtx dctx = new ZstdDecompressCtx()) {
50+
MemorySegment frame = reader.mmapSlice(); // already native — never touches the heap
51+
long n = Zstd.decompressedSize(frame); // read the header, no copy
52+
MemorySegment out = arena.allocate(n); // this segment *is* the output buffer
53+
dctx.decompress(out, frame); // native → native
54+
}
55+
```
56+
57+
In benchmarks this path allocates ~0 bytes/op regardless of payload size, while
58+
the `byte[]` path allocates the full output every call. See
59+
[docs/benchmarks.md](benchmarks.md) for numbers and [docs/zero-copy.md](zero-copy.md)
60+
for when it pays.
61+
62+
## 5. Stream data that doesn't fit in memory
63+
64+
For large or unbounded data, don't buffer the whole thing — wrap an ordinary
65+
`java.io` stream. `ZstdOutputStream` / `ZstdInputStream` compress and decompress
66+
incrementally, so memory stays flat no matter how big the payload.
67+
68+
```java
69+
// compress a file as you write it
70+
try (var out = new ZstdOutputStream(Files.newOutputStream(packed), 9)) {
71+
Files.copy(source, out);
72+
}
73+
74+
// decompress as you read it back
75+
try (var in = new ZstdInputStream(Files.newInputStream(packed))) {
76+
in.transferTo(Files.newOutputStream(restored));
77+
}
78+
```
79+
80+
They are plain `OutputStream` / `InputStream` subclasses — drop them into any code
81+
that already speaks `java.io`.
82+
3983
That's the whole loop. From here, pick a [how-to guide](../README.md#how-to-guides)
4084
for your actual task, or browse the [reference](../README.md#reference).

0 commit comments

Comments
 (0)