Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: Publish to Maven Central

on:
push:
tags:
- 'v*'

permissions:
contents: write

jobs:
publish:
runs-on: ubuntu-latest
environment: maven
steps:
- name: Checkout (with zstd submodule)
uses: actions/checkout@v4
with:
submodules: recursive

- name: Set up JDK 25 with Maven Central publishing
uses: actions/setup-java@v4
with:
java-version: '25'
distribution: 'zulu'
cache: 'maven'
server-id: central
server-username: CENTRAL_USERNAME
server-password: CENTRAL_PASSWORD
gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
gpg-passphrase: GPG_PASSPHRASE

- name: Set up Zig
uses: mlugg/setup-zig@v2
with:
version: 0.16.0

- name: Publish to Maven Central
env:
CENTRAL_USERNAME: ${{ secrets.CENTRAL_USERNAME }}
CENTRAL_PASSWORD: ${{ secrets.CENTRAL_PASSWORD }}
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
run: ./mvnw deploy -Prelease -DskipTests --batch-mode -ntp

- name: Create GitHub release
env:
GH_TOKEN: ${{ github.token }}
run: |
VERSION="${GITHUB_REF_NAME#v}"
NOTES=$(awk "/^## \[$VERSION\]/{found=1; next} found && /^## \[/{exit} found{print}" CHANGELOG.md)
gh release create "$GITHUB_REF_NAME" \
--title "$GITHUB_REF_NAME" \
--notes "$NOTES" \
--verify-tag
33 changes: 33 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Changelog

All notable changes to this project are documented here. Format loosely follows
[Keep a Changelog](https://keepachangelog.com/); versions are released as `v*`
git tags, which trigger publication to Maven Central.

## [0.1]

First release. Java 25 Foreign Function & Memory (FFM) bindings for
[Zstandard](https://github.com/facebook/zstd) 1.5.7, built hermetically from
vendored source with `zig cc` (no JNI, no prebuilt binaries). 68 of the public
zstd symbols are bound; see `docs/supported.md`.

### Added
- One-shot compression/decompression over `byte[]` and zero-copy `MemorySegment`
(`Zstd`, `ZstdCompressCtx`, `ZstdDecompressCtx`).
- Dictionaries: training (`ZDICT_trainFromBuffer`, COVER / fast-COVER optimisers,
`finalizeDictionary`), digested `ZstdCompressDict` / `ZstdDecompressDict`,
dictionary ids and header size.
- Streaming: `ZstdOutputStream` / `ZstdInputStream` (java.io) and a zero-copy
`MemorySegment` driver (`ZstdCompressStream` / `ZstdDecompressStream`), with
dictionaries, `pledgedSrcSize`, and live `progress()`.
- All advanced parameters (`ZstdCompressParameter` / `ZstdDecompressParameter`)
with bounds queries; checksum, long-distance matching, window log, etc.
- Frame inspection (`ZstdFrame`): header, content/compressed size, dictionary id,
skippable frames.
- Typed errors (`ZstdException.code()` / `ZstdErrorCode`) and memory accounting
(`sizeOf()`, `Zstd.estimate*Size`).
- Native artifacts for macOS, Linux and Windows on x86_64 and aarch64,
cross-compiled from a single host with `zig cc`.
- Format-compatibility tests against the reference zstd-jni binding.

[0.1]: https://github.com/dfa1/zstd-java/releases/tag/v0.1
1 change: 1 addition & 0 deletions bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<artifactId>zstd-java-bom</artifactId>
<packaging>pom</packaging>
<name>zstd FFM BOM</name>
<description>Bill of materials for the zstd-java modules</description>

<dependencyManagement>
<dependencies>
Expand Down
1 change: 1 addition & 0 deletions native/linux-aarch64/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

<artifactId>zstd-java-native-linux-aarch64</artifactId>
<name>zstd FFM Native linux-aarch64</name>
<description>Native zstd library for Linux aarch64</description>
<packaging>jar</packaging>

<!-- No Java sources: this module only packages the native shared library. -->
Expand Down
1 change: 1 addition & 0 deletions native/linux-x86_64/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

<artifactId>zstd-java-native-linux-x86_64</artifactId>
<name>zstd FFM Native linux-x86_64</name>
<description>Native zstd library for Linux x86_64</description>
<packaging>jar</packaging>

<!-- No Java sources: this module only packages the native shared library. -->
Expand Down
1 change: 1 addition & 0 deletions native/osx-aarch64/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

<artifactId>zstd-java-native-osx-aarch64</artifactId>
<name>zstd FFM Native osx-aarch64</name>
<description>Native zstd library for macOS aarch64</description>
<packaging>jar</packaging>

<!-- No Java sources: this module only packages the native shared library. -->
Expand Down
1 change: 1 addition & 0 deletions native/osx-x86_64/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

<artifactId>zstd-java-native-osx-x86_64</artifactId>
<name>zstd FFM Native osx-x86_64</name>
<description>Native zstd library for macOS x86_64</description>
<packaging>jar</packaging>

<!-- No Java sources: this module only packages the native shared library. -->
Expand Down
1 change: 1 addition & 0 deletions native/windows-aarch64/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

<artifactId>zstd-java-native-windows-aarch64</artifactId>
<name>zstd FFM Native windows-aarch64</name>
<description>Native zstd library for Windows aarch64</description>
<packaging>jar</packaging>

<!-- No Java sources: this module only packages the native shared library. -->
Expand Down
1 change: 1 addition & 0 deletions native/windows-x86_64/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

<artifactId>zstd-java-native-windows-x86_64</artifactId>
<name>zstd FFM Native windows-x86_64</name>
<description>Native zstd library for Windows x86_64</description>
<packaging>jar</packaging>

<!-- No Java sources: this module only packages the native shared library. -->
Expand Down
105 changes: 105 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,13 @@
</developer>
</developers>

<scm>
<connection>scm:git:https://github.com/dfa1/zstd-java.git</connection>
<developerConnection>scm:git:git@github.com:dfa1/zstd-java.git</developerConnection>
<url>https://github.com/dfa1/zstd-java</url>
<tag>HEAD</tag>
</scm>

<modules>
<module>native/osx-aarch64</module>
<module>native/osx-x86_64</module>
Expand All @@ -43,6 +50,11 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.release>25</maven.compiler.release>
<central-publishing-plugin.version>0.11.0</central-publishing-plugin.version>
<maven-gpg-plugin.version>3.2.8</maven-gpg-plugin.version>
<maven-source-plugin.version>3.4.0</maven-source-plugin.version>
<maven-javadoc-plugin.version>3.12.0</maven-javadoc-plugin.version>
<maven-release-plugin.version>3.3.1</maven-release-plugin.version>
</properties>

<dependencyManagement>
Expand Down Expand Up @@ -164,6 +176,17 @@
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<version>${maven-release-plugin.version}</version>
<configuration>
<autoVersionSubmodules>true</autoVersionSubmodules>
<tagNameFormat>v@{project.version}</tagNameFormat>
<pushChanges>false</pushChanges>
<localCheckout>true</localCheckout>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<plugins>
Expand All @@ -186,4 +209,86 @@
</plugin>
</plugins>
</build>

<profiles>
<!-- Cut a release with maven-release-plugin (configured in build above):
./mvnw release:prepare
drops -SNAPSHOT, commits, tags v<version> (pushChanges=false), then
bumps to the next snapshot. Push the commits and the tag; the v* tag
triggers .github/workflows/publish.yml, which runs
./mvnw deploy -Prelease
to sign and upload to Maven Central.
Prerequisites:
1. Register namespace io.github.dfa1 at central.sonatype.com.
2. A Central user token (Account / User Tokens); in CI it comes from
the CENTRAL_USERNAME / CENTRAL_PASSWORD secrets.
3. A published GPG key (GPG_PRIVATE_KEY / GPG_PASSPHRASE secrets). -->
<profile>
<id>release</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>${maven-source-plugin.version}</version>
<executions>
<execution>
<id>attach-sources</id>
<goals>
<goal>jar-no-fork</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>${maven-javadoc-plugin.version}</version>
<executions>
<execution>
<id>attach-javadocs</id>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
<configuration>
<doclint>none</doclint>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-gpg-plugin</artifactId>
<version>${maven-gpg-plugin.version}</version>
<executions>
<execution>
<id>sign-artifacts</id>
<phase>verify</phase>
<goals>
<goal>sign</goal>
</goals>
<configuration>
<gpgArguments>
<arg>--pinentry-mode</arg>
<arg>loopback</arg>
</gpgArguments>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.sonatype.central</groupId>
<artifactId>central-publishing-maven-plugin</artifactId>
<version>${central-publishing-plugin.version}</version>
<extensions>true</extensions>
<configuration>
<publishingServerId>central</publishingServerId>
<autoPublish>true</autoPublish>
<waitUntil>published</waitUntil>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</project>
1 change: 1 addition & 0 deletions zstd/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

<artifactId>zstd-java</artifactId>
<name>zstd FFM Core</name>
<description>Java Foreign Function &amp; Memory (FFM) bindings for zstd</description>
<packaging>jar</packaging>

<dependencies>
Expand Down
12 changes: 12 additions & 0 deletions zstd/src/main/java/io/github/dfa1/zstd/Zstd.java
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ public static byte[] decompress(byte[] compressed, int maxSize) {
/// @return the decompressed length in bytes
/// @throws ZstdException if the frame is invalid or does not store its size
public static long decompressedSize(MemorySegment frame) {
requireNative(frame, "frame");
long size;
try {
size = (long) Bindings.GET_FRAME_CONTENT_SIZE.invokeExact(frame, frame.byteSize());
Expand Down Expand Up @@ -275,6 +276,17 @@ private static String errorName(long code) {
}
}

/// Guards a zero-copy entry point: the segment handed to zstd must be backed
/// by native (off-heap) memory, since its address is dereferenced in C. Fails
/// fast with a clear message instead of the FFM linker's cryptic error.
static MemorySegment requireNative(MemorySegment seg, String name) {
if (!seg.isNative()) {
throw new IllegalArgumentException(
name + " must be a native (off-heap) MemorySegment; got a heap segment");
}
return seg;
}

static MemorySegment copyIn(Arena arena, byte[] src) {
MemorySegment seg = arena.allocate(Math.max(src.length, 1));
MemorySegment.copy(src, 0, seg, JAVA_BYTE, 0, src.length);
Expand Down
4 changes: 4 additions & 0 deletions zstd/src/main/java/io/github/dfa1/zstd/ZstdCompressCtx.java
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ public byte[] compress(byte[] src, ZstdCompressDict dict) {
/// @return the number of bytes written into `dst`
/// @throws ZstdException if `dst` is too small or compression fails
public long compress(MemorySegment dst, MemorySegment src) {
Zstd.requireNative(dst, "dst");
Zstd.requireNative(src, "src");
return Zstd.call(() -> (long) Bindings.COMPRESS2.invokeExact(
ptr(), dst, dst.byteSize(), src, src.byteSize()));
}
Expand All @@ -173,6 +175,8 @@ public long compress(MemorySegment dst, MemorySegment src) {
/// @param dict the pre-digested compression dictionary
/// @return the number of bytes written into `dst`
public long compress(MemorySegment dst, MemorySegment src, ZstdCompressDict dict) {
Zstd.requireNative(dst, "dst");
Zstd.requireNative(src, "src");
MemorySegment cdict = dict.ptr();
return Zstd.call(() -> (long) Bindings.COMPRESS_USING_CDICT.invokeExact(
ptr(), dst, dst.byteSize(), src, src.byteSize(), cdict));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ private static MemorySegment create(int level, ZstdDictionary dictionary) {
/// @return how much was consumed and produced, and the remaining hint
/// @throws ZstdException if compression fails
public ZstdStreamResult compress(MemorySegment dst, MemorySegment src, ZstdEndDirective directive) {
Zstd.requireNative(dst, "dst");
Zstd.requireNative(src, "src");
in.set(src, src.byteSize(), 0);
out.set(dst, dst.byteSize(), 0);
long remaining = Zstd.call(() -> (long) Bindings.COMPRESS_STREAM2.invokeExact(
Expand Down
4 changes: 4 additions & 0 deletions zstd/src/main/java/io/github/dfa1/zstd/ZstdDecompressCtx.java
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ public byte[] decompress(byte[] compressed, int maxSize, ZstdDecompressDict dict
/// @return the number of bytes written into `dst`
/// @throws ZstdException if `dst` is too small or the frame is invalid
public long decompress(MemorySegment dst, MemorySegment src) {
Zstd.requireNative(dst, "dst");
Zstd.requireNative(src, "src");
return Zstd.call(() -> (long) Bindings.DECOMPRESS_DCTX.invokeExact(
ptr(), dst, dst.byteSize(), src, src.byteSize()));
}
Expand All @@ -126,6 +128,8 @@ public long decompress(MemorySegment dst, MemorySegment src) {
/// @param dict the pre-digested decompression dictionary
/// @return the number of bytes written into `dst`
public long decompress(MemorySegment dst, MemorySegment src, ZstdDecompressDict dict) {
Zstd.requireNative(dst, "dst");
Zstd.requireNative(src, "src");
MemorySegment ddict = dict.ptr();
return Zstd.call(() -> (long) Bindings.DECOMPRESS_USING_DDICT.invokeExact(
ptr(), dst, dst.byteSize(), src, src.byteSize(), ddict));
Expand Down
Loading
Loading