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
13 changes: 6 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,9 @@ You will only need the `dev.freya02:discord-zstd-java-api:VERSION` dependency, i

### Usage

First, get a `ZstdDecompressorFactoryProvider` by loading one using a `ServiceLoader`,
doing this first ensures you can throw an error when missing an implementation before it can throw one because of missing natives, as they are brought by the implementation.

Then, you can check if the natives are loaded by checking `ZstdNativesLoader.isLoaded()`,
but usually you'll want to call `loadFromJar()`, you should do it late enough so the bot developer has a chance to load different natives.

Finally, get a factory from the provider, it will be configured with the values you pass.
The main interface is `DiscordZstd`, you can get an instance with `DiscordZstdProvider.get()`.
Then, you can either:
1. Do bulk processing with a decompressor obtained with `DiscordZstd#createDecompressor` and kept per gateway connection,
calling `ZstdDecompressor#decompress` on each gateway message
2. Process gradually with a context obtained from `DiscordZstd#createContext` and kept per gateway connection,
then making input streams with `ZstdContext#createInputStream` from each gateway message
33 changes: 33 additions & 0 deletions api/src/main/java/dev/freya02/discord/zstd/api/DiscordZstd.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package dev.freya02.discord.zstd.api;

import org.jspecify.annotations.NullMarked;

import java.io.InputStream;

@NullMarked
public interface DiscordZstd {
// TODO rename type
/**
* Creates a new {@link DiscordZstdContext}.
* <br>This is used to keep track of streaming decompression after each input is consumed via an {@link InputStream}.
*
* @return A new {@link DiscordZstdContext} instance
*/
DiscordZstdContext createContext();

// TODO rename type
/**
* Creates a new {@link DiscordZstdDecompressorFactory} with the provided decompression buffer size.
*
* @param bufferSizeHint
* A hint for the size of the buffer used for decompression,
* must be larger than {@value DiscordZstdDecompressor#MIN_BUFFER_SIZE} or be equal to {@value DiscordZstdDecompressor#ZSTD_RECOMMENDED_BUFFER_SIZE}.
* Typically, bigger buffers mean less decompression loops, it does not change inputs or outputs
*
* @throws IllegalArgumentException
* If {@code bufferSize} is less than {@value DiscordZstdDecompressor#MIN_BUFFER_SIZE} and not {@value DiscordZstdDecompressor#ZSTD_RECOMMENDED_BUFFER_SIZE}
*
* @return A new {@link DiscordZstdDecompressorFactory} instance
*/
DiscordZstdDecompressorFactory createDecompressorFactory(int bufferSizeHint);
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import java.io.InputStream;

@NullMarked
public interface ZstdContext {
public interface DiscordZstdContext {
void close();

void reset();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
* <p>Note: Instances are <b>not</b> thread safe, as there should be an instance per gateway connection, which uses 1 read thread.
*/
@NullMarked
public interface ZstdDecompressor {
public interface DiscordZstdDecompressor {
/**
* The "recommended" buffer size as defined by {@code ZSTD_DStreamOutSize()} (128 KB as of v1.5.7). This isn't a default.
*
Expand Down Expand Up @@ -57,7 +57,7 @@ public interface ZstdDecompressor {
* @throws IllegalStateException
* If this decompressor is closed,
* or if the decompressor is an errored state and needs to be {@linkplain #reset() reset}
* @throws ZstdException
* @throws DiscordZstdException
* If Zstd was unable to decompress the data for any reason, if this exception occurs,
* the decompressor will be in an errored state and will need to be {@linkplain #reset() reset}
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package dev.freya02.discord.zstd.api;

import org.jspecify.annotations.NullMarked;

/**
* Factory of {@link DiscordZstdDecompressor}.
*
* <p>Instances are thread safe.
*/
@NullMarked
public interface DiscordZstdDecompressorFactory {
/**
* Creates a new {@link DiscordZstdDecompressor} configured with the parameters passed to this factory.
*
* @return A new {@link DiscordZstdDecompressor} instance
*/
DiscordZstdDecompressor create();
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
/**
* An exception thrown when Zstd returns an error code.
*/
public class ZstdException extends RuntimeException {
public ZstdException(String message) {
public class DiscordZstdException extends RuntimeException {
public DiscordZstdException(String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
* to the <b>absolute</b> path of the native library.
*/
@NullMarked
public class ZstdNativesLoader {
public class DiscordZstdNativesLoader {
private static boolean init = false;

/**
Expand Down Expand Up @@ -41,6 +41,8 @@ public static synchronized boolean load(Path path) {
//noinspection ConstantValue
if (path == null)
throw new IllegalArgumentException("path is null");
if (!path.isAbsolute())
throw new IllegalArgumentException("path is not absolute: " + path);

final String pathStr = path.toAbsolutePath().toString();
System.setProperty("zstd.lib", pathStr);
Expand Down Expand Up @@ -86,7 +88,7 @@ public static synchronized boolean loadFromJar() throws IOException {
}

String resourcePath = String.format("/natives/%s/libzstd.%s", platform, extension);
Path nativePath = NativeUtil.copyNativeFromJar(resourcePath, ZstdNativesLoader.class);
Path nativePath = NativeUtil.copyNativeFromJar(resourcePath, DiscordZstdNativesLoader.class);
load(nativePath);
return true;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package dev.freya02.discord.zstd.api;

import org.jspecify.annotations.NullMarked;

import java.util.Iterator;
import java.util.ServiceConfigurationError;
import java.util.ServiceLoader;

@NullMarked
public class DiscordZstdProvider {

private static final class InstanceHolder {
private static final DiscordZstd INSTANCE = load();
}

/**
* Loads the first implementation of {@link DiscordZstd} found on the classpath.
*
* @throws ServiceConfigurationError If the provider failed to load
*
* @return An implementation of {@link DiscordZstd}
*/
public static DiscordZstd get() {
return InstanceHolder.INSTANCE;
}

private static DiscordZstd load() {
Iterator<DiscordZstd> instances = ServiceLoader.load(DiscordZstd.class).iterator();
if (!instances.hasNext()) {
throw new IllegalStateException("No implementations of discord-zstd-java could be found, make sure you added the dependency as described in https://github.com/freya022/discord-zstd-java/blob/master/README.md#-for-bot-developers");
}

return instances.next();
}
}

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package dev.freya02.discord.zstd.internal;

import dev.freya02.discord.zstd.api.ZstdDecompressor;
import dev.freya02.discord.zstd.api.DiscordZstdDecompressor;

import java.util.List;

public abstract class AbstractZstdDecompressor implements ZstdDecompressor {
public abstract class AbstractZstdDecompressor implements DiscordZstdDecompressor {

protected AbstractZstdDecompressor() {
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package dev.freya02.discord.zstd;

import dev.freya02.discord.zstd.api.ZstdDecompressor;
import dev.freya02.discord.zstd.api.ZstdDecompressorFactory;
import dev.freya02.discord.zstd.api.ZstdNativesLoader;
import dev.freya02.discord.zstd.jni.ZstdJNIDecompressorFactoryProvider;
import dev.freya02.discord.zstd.api.DiscordZstdDecompressor;
import dev.freya02.discord.zstd.api.DiscordZstdDecompressorFactory;
import dev.freya02.discord.zstd.api.DiscordZstdNativesLoader;
import dev.freya02.discord.zstd.jni.DiscordZstdJNI;
import net.dv8tion.jda.api.utils.data.DataObject;
import net.dv8tion.jda.internal.utils.IOUtil;
import org.openjdk.jmh.annotations.*;
Expand All @@ -26,21 +26,21 @@
@Fork(1)
public class ZstdDecompressorBenchmark {

private static final int ZSTD_BUFFER_SIZE = ZstdDecompressor.DEFAULT_BUFFER_SIZE;
private static final int ZSTD_BUFFER_SIZE = DiscordZstdDecompressor.DEFAULT_BUFFER_SIZE;
private static final int ZLIB_BUFFER_SIZE = 2048; // JDA default

@State(Scope.Benchmark)
public static class ZstdDecompressorState {
@Param({"jni"})
private String impl;

public ZstdDecompressor decompressor;
public DiscordZstdDecompressor decompressor;

@Setup
public void setup() throws IOException {
ZstdNativesLoader.loadFromJar();
ZstdDecompressorFactory factory = switch (impl) {
case "jni" -> new ZstdJNIDecompressorFactoryProvider().get(ZSTD_BUFFER_SIZE);
DiscordZstdNativesLoader.loadFromJar();
DiscordZstdDecompressorFactory factory = switch (impl) {
case "jni" -> new DiscordZstdJNI().createDecompressorFactory(ZSTD_BUFFER_SIZE);
default -> throw new AssertionError("Unknown implementation: " + impl);
};
decompressor = factory.create();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package dev.freya02.discord.zstd;

import dev.freya02.discord.zstd.api.ZstdContext;
import dev.freya02.discord.zstd.api.ZstdNativesLoader;
import dev.freya02.discord.zstd.jni.ZstdJNIContextFactoryProvider;
import dev.freya02.discord.zstd.api.DiscordZstdContext;
import dev.freya02.discord.zstd.api.DiscordZstdNativesLoader;
import dev.freya02.discord.zstd.jni.DiscordZstdJNI;
import net.dv8tion.jda.api.utils.data.DataObject;
import net.dv8tion.jda.internal.utils.IOUtil;
import org.jetbrains.annotations.Nullable;
Expand Down Expand Up @@ -30,16 +30,15 @@ public static class ZstdDecompressorState {
@Param({"jni"})
private String impl;

public ZstdContext context;
public DiscordZstdContext context;

@Setup
public void setup() throws IOException {
ZstdNativesLoader.loadFromJar();
var factory = switch (impl) {
case "jni" -> new ZstdJNIContextFactoryProvider().get();
DiscordZstdNativesLoader.loadFromJar();
context = switch (impl) {
case "jni" -> new DiscordZstdJNI().createContext();
default -> throw new AssertionError("Unknown implementation: " + impl);
};
context = factory.create();
}

@TearDown
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package dev.freya02.discord.zstd.ffm;

import dev.freya02.discord.zstd.api.*;
import org.jspecify.annotations.NullMarked;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;

@NullMarked
public class DiscordZstdFFM implements DiscordZstd {
private static final Logger LOGGER = LoggerFactory.getLogger(DiscordZstdFFM.class);

public DiscordZstdFFM() throws IOException {
LOGGER.debug("Using FFM implementation of discord-zstd-java");

// Load natives if they weren't already
DiscordZstdNativesLoader.loadFromJar();
}

@Override
public DiscordZstdContext createContext() {
return new DiscordZstdFFMContext();
}

@Override
public DiscordZstdDecompressorFactory createDecompressorFactory(int bufferSizeHint) {
if (bufferSizeHint < DiscordZstdDecompressor.MIN_BUFFER_SIZE && bufferSizeHint != DiscordZstdDecompressor.ZSTD_RECOMMENDED_BUFFER_SIZE)
throw new IllegalArgumentException("Buffer must be larger than or equal to " + DiscordZstdDecompressor.MIN_BUFFER_SIZE + ", provided " + bufferSizeHint);
return new DiscordZstdFFMDecompressorFactory(bufferSizeHint);
}
}
Loading