All notable changes to this project are documented here. Format loosely follows
Keep a Changelog; versions are released as v*
git tags, which trigger publication to Maven Central.
module-info.java:zstdnow ships as a named JPMS module (module io.github.dfa1.zstd), exporting the single public API package. Module-path consumers grant--enable-native-access=io.github.dfa1.zstdinstead ofALL-UNNAMED; classpath consumers are unaffected. See ADR 0011.
- Breaking: renamed public types to spell out abbreviations, matching the
Zstd<Compress|Decompress><Stream|Parameter>family and zstd's own prose ("compression context", "dictionary"):ZstdCompressCtx→ZstdCompressContext,ZstdDecompressCtx→ZstdDecompressContext,ZstdCompressDict→ZstdCompressDictionary,ZstdDecompressDict→ZstdDecompressDictionary.
ZstdCompressCtx.refPrefix(MemorySegment)/ZstdDecompressCtx.refPrefix(...)— reference native content as a single-use prefix (raw-content dictionary) for the next frame only: the building block for delta compression (compress a new version against a similar previous one). The prefix is referenced, not copied or digested, and writes no dictionary ID; the decompressor must reference the same prefix to decode. BindsZSTD_CCtx_refPrefix/ZSTD_DCtx_refPrefix. Segment-only by design — heap callers that need a copy should useloadDictionaryinstead.Zstd.dictId(byte[])/Zstd.dictId(MemorySegment)— read the dictionary id stamped in raw dictionary bytes without wrapping them in aZstdDictionary. BindsZSTD_getDictID_fromDict.ZstdDictionaryIdvalue type — arecordwrapping the 32-bit dictionary id with an unsignedvalue(),isPresent(), and theNONEsentinel for "no id".ZstdFrame.decompressedSize(byte[])/ZstdFrame.decompressedSize(MemorySegment)— the exact combined decompressed size of all concatenated frames, summed from each frame header (throws if any frame does not record its size). ComplementsdecompressedBound(upper bound). BindsZSTD_findDecompressedSize.ZstdFrame.headerSize(byte[])/ZstdFrame.headerSize(MemorySegment)— the size of a frame's header computed from just its leading bytes (as few as 5), without a full parse. BindsZSTD_frameHeaderSize.ZstdFrame.decompressionMargin(byte[])/ZstdFrame.decompressionMargin(MemorySegment)— the extra room needed to decompress a frame in place (output buffer overlaps the compressed input at its tail), sizeddecompressedSize + margin. BindsZSTD_decompressionMargin.ZstdDictionary.compressDict(int)/compressDict()/decompressDict()— factories for digested dictionaries, e.g.dict.compressDict(19)instead ofnew ZstdCompressDict(dict, 19). They signal that the result isAutoCloseableand are for sharing one digest across contexts viarefDictionary; a single context should prefer the context-ownedloadDictionary.
- Every dictionary-id accessor now returns
ZstdDictionaryIdinstead ofint:ZstdDictionary.id(),ZstdCompressDict.id(),ZstdDecompressDict.id(),ZstdFrame.dictId(...), andZstdFrameHeader.dictId(). The0sentinel is nowZstdDictionaryId.NONE, and the id reads as unsigned viavalue(). Zstd.decompress(byte[])now throwsZstdException(instead of letting a rawArithmeticExceptionescape) when a frame declares a content size larger than a Java array can hold. The size comes from the untrusted frame header; usedecompress(byte[], int)to bound output for untrusted input.
ZstdCompressCtx.reset(ZstdResetDirective)/ZstdDecompressCtx.reset(...)— recycle a context's native state between frames without freeing and recreating it.SESSION_ONLYkeeps the level, parameters, and dictionary;PARAMETERS/SESSION_AND_PARAMETERSrestore the defaults. BindsZSTD_CCtx_reset/ZSTD_DCtx_reset. (3dfd5b8)ZstdCompressCtx.loadDictionary(...)/ZstdDecompressCtx.loadDictionary(...)(aZstdDictionaryor a nativeMemorySegment) andrefDictionary(...)(a pre-digestedZstdCompressDict/ZstdDecompressDict, attached by reference, no copy). A sticky dictionary on the context lets compression combine a dictionary with the advanced parameters (checksum, window log, long-distance matching) — impossible through the per-callcompress(src, dict)overloads, which route the legacy dictionary path. A parameterreset(...)clears it. BindsZSTD_CCtx_loadDictionary/ZSTD_DCtx_loadDictionary(now on contexts, not just streams),ZSTD_CCtx_refCDict,ZSTD_DCtx_refDDict. (3dfd5b8)
NativeLibrary.classifier()now throws a clearUnsatisfiedLinkErrornaming the unsupported CPU arch instead of silently mapping it to x86_64 (which deferred failure to a crypticdlopenerror). Added an explicitamd64branch so Linux JVMs (which reportos.arch=amd64) still resolve x86_64. (ea1ac84)
- Native JARs are much smaller. The ELF shared library is now stripped at link
time (
-s), dropping debug info (libzstd.so4.0M -> ~650K), and the multi-MB.pdbdebug database and.libimport library that lld emits next to the Windows.dllare no longer bundled (neither is needed at runtime). Net: linux-x86_64 native jar 1.2M -> 285K, windows-x86_64 1.2M -> 372K. (ea1ac84)
Zstd.versionNumber()— the linked zstd version as a single integer (MAJOR * 10000 + MINOR * 100 + PATCH, e.g.10507for1.5.7), for programmatic version checks alongsideversion().
ZstdSkippableContentis now a true immutable value: it defensively copies its bytes on the way in and out, and compares by content (equals/hashCode/toStringover the payload, not array identity).- Public methods fail fast with a named
NullPointerExceptionon nullbyte[], dictionary, or sample arguments, instead of an opaque failure deep in native code. Streams are documented as not thread-safe; digested dictionaries (ZstdCompressDict/ZstdDecompressDict) as immutable and safe to share.
- A streaming wrapper that failed partway through construction (e.g. an invalid parameter or dictionary) leaked the native context. The context is now freed on every constructor error path.
- The bundled library is extracted into a directory created owner-only
(
rwx------) atomically at creation, not just by default. The third-partysetup-zigCI action is pinned to a full commit SHA.