Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.oxd.sh/llms.txt

Use this file to discover all available pages before exploring further.

Setup

Java calls the C library through a thin JNI bridge. You need two native libraries:
  1. liboxaccel.so / liboxaccel.dylib / oxaccel.dll — the Accel SDK
  2. liboxaccel_jni.so — the JNI bridge (compiled from OxAccelJNI.c)

Compile the JNI bridge

gcc -shared -fPIC -o liboxaccel_jni.so OxAccelJNI.c \
  -I$JAVA_HOME/include -I$JAVA_HOME/include/linux \
  -L/path/to/lib -loxaccel

JNI bridge (C)

This thin bridge maps Java native methods to oxaccel.h calls. Uses GetDirectBufferAddress for zero-copy ByteBuffer access and GetPrimitiveArrayCritical for byte[] pinning.
#include <jni.h>
#include "oxaccel.h"
#include <string.h>

JNIEXPORT jlong JNICALL Java_sh_oxd_accel_OxAccel_nCreate(
    JNIEnv *env, jclass cls,
    jstring apiKey, jstring relayHost, jint relayPort,
    jboolean fec, jboolean compression, jboolean multipath)
{
    const char *key  = apiKey    ? (*env)->GetStringUTFChars(env, apiKey, NULL)    : NULL;
    const char *host = relayHost ? (*env)->GetStringUTFChars(env, relayHost, NULL) : NULL;

    OxAccelConfig config;
    memset(&config, 0, sizeof(config));
    config.api_key            = key;
    config.relay_host         = host;
    config.relay_port         = (uint16_t)relayPort;
    config.enable_fec         = fec;
    config.enable_compression = compression;
    config.enable_multipath   = multipath;

    OxAccel *ctx = ox_accel_create(&config);

    if (host) (*env)->ReleaseStringUTFChars(env, relayHost, host);
    if (key)  (*env)->ReleaseStringUTFChars(env, apiKey, key);

    return (jlong)(uintptr_t)ctx;
}

JNIEXPORT jint JNICALL Java_sh_oxd_accel_OxAccel_nConnect(
    JNIEnv *env, jclass cls, jlong ctx)
{
    return (jint)ox_accel_connect((OxAccel *)(uintptr_t)ctx);
}

JNIEXPORT jint JNICALL Java_sh_oxd_accel_OxAccel_nSend(
    JNIEnv *env, jclass cls, jlong ctx, jobject buf, jint len)
{
    uint8_t *data = (uint8_t *)(*env)->GetDirectBufferAddress(env, buf);
    if (!data) return InvalidParam;
    return (jint)ox_accel_send((OxAccel *)(uintptr_t)ctx, data, (uintptr_t)len);
}

JNIEXPORT jint JNICALL Java_sh_oxd_accel_OxAccel_nSendBytes(
    JNIEnv *env, jclass cls, jlong ctx, jbyteArray arr, jint len)
{
    jbyte *data = (*env)->GetPrimitiveArrayCritical(env, arr, NULL);
    if (!data) return InvalidParam;
    jint result = (jint)ox_accel_send(
        (OxAccel *)(uintptr_t)ctx, (uint8_t *)data, (uintptr_t)len);
    (*env)->ReleasePrimitiveArrayCritical(env, arr, data, JNI_ABORT);
    return result;
}

JNIEXPORT jint JNICALL Java_sh_oxd_accel_OxAccel_nRecv(
    JNIEnv *env, jclass cls, jlong ctx, jobject buf, jint bufLen)
{
    uint8_t *data = (uint8_t *)(*env)->GetDirectBufferAddress(env, buf);
    if (!data) return -1;
    uintptr_t received = 0;
    enum OxAccelError err = ox_accel_recv(
        (OxAccel *)(uintptr_t)ctx, data, (uintptr_t)bufLen, &received);
    return (err == Ok) ? (jint)received : -(jint)err;
}

JNIEXPORT jlongArray JNICALL Java_sh_oxd_accel_OxAccel_nStats(
    JNIEnv *env, jclass cls, jlong ctx)
{
    AccelStats stats;
    ox_accel_stats((OxAccel *)(uintptr_t)ctx, &stats);
    jlongArray result = (*env)->NewLongArray(env, 4);
    jlong vals[4] = {
        (jlong)stats.packets_sent, (jlong)stats.packets_recv,
        (jlong)stats.bytes_sent,   (jlong)stats.bytes_recv
    };
    (*env)->SetLongArrayRegion(env, result, 0, 4, vals);
    return result;
}

JNIEXPORT void JNICALL Java_sh_oxd_accel_OxAccel_nDestroy(
    JNIEnv *env, jclass cls, jlong ctx)
{
    ox_accel_destroy((OxAccel *)(uintptr_t)ctx);
}

JNIEXPORT jstring JNICALL Java_sh_oxd_accel_OxAccel_nVersion(
    JNIEnv *env, jclass cls)
{
    return (*env)->NewStringUTF(env, ox_accel_version());
}

Usage

import java.nio.ByteBuffer;

public class AccelDemo {
    static { System.loadLibrary("oxaccel_jni"); }

    private static native long   nCreate(String apiKey, String relayHost, int relayPort,
                                         boolean fec, boolean compression, boolean multipath);
    private static native int    nConnect(long ctx);
    private static native int    nSend(long ctx, ByteBuffer data, int len);
    private static native int    nSendBytes(long ctx, byte[] data, int len);
    private static native int    nRecv(long ctx, ByteBuffer buf, int bufLen);
    private static native long[] nStats(long ctx);
    private static native void   nDestroy(long ctx);
    private static native String nVersion();

    public static void main(String[] args) {
        System.out.println("Oxidize Accel SDK v" + nVersion());

        long ctx = nCreate("your-api-key", "relay.oxd.sh", 51820, true, false, true);
        if (ctx == 0) throw new RuntimeException("Failed to create context");

        try {
            int err = nConnect(ctx);
            if (err != 0) throw new RuntimeException("Connect failed: " + err);

            // Zero-copy send via direct ByteBuffer
            byte[] payload = "hello world".getBytes();
            ByteBuffer direct = ByteBuffer.allocateDirect(payload.length);
            direct.put(payload).flip();
            nSend(ctx, direct, payload.length);

            // Receive
            ByteBuffer recvBuf = ByteBuffer.allocateDirect(65536);
            int received = nRecv(ctx, recvBuf, 65536);
            if (received > 0) {
                System.out.println("Received " + received + " bytes");
            }

            long[] stats = nStats(ctx);
            System.out.printf("Sent: %d pkts / %d bytes%n", stats[0], stats[2]);
        } finally {
            nDestroy(ctx);
        }
    }
}

Performance notes

Always use ByteBuffer.allocateDirect() for send/recv buffers. Only direct ByteBuffers give you GetDirectBufferAddress in JNI — heap ByteBuffers require a copy.
  • GetPrimitiveArrayCritical pins the Java array in place (no copy on most JVMs). The byte[] overload uses this.
  • The JNI bridge is ~5 function calls with zero intermediate allocations on the hot path.
  • For Android, place liboxaccel.so and liboxaccel_jni.so in jniLibs/{abi}/.