Add jextract module: SQLite bindings generated with jextract, FFM arenas, C strings, out-parameters, errors, upcalls, and a JMH comparison with JNI
jextract-25 early-access build (25-jextract+2-4) on JDK 25.0.5+8 against SQLite 3.45.1: 25 functions and 13 constants generated into 4 committed files (the whole header would be 83 files), a small Db wrapper, the four arena kinds used wrongly on purpose, ADDRESS vs C_POINTER zero-length vs unbounded segments and the SIGSEGV from getString on a NULL pointer, sqlite3_open out-parameter, return codes to exceptions, a sqlite3_exec upcall and the VM exit when it throws, the JDK 25 native-access warning and --illegal-native-access=deny, library loading failures, and a JMH benchmark of FFM vs hand-written JNI vs plain Java (indicative only: shared 2-CPU machine). Co-Authored-By: Claude Sonnet 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_017ic4b6Kg9RWSTJzyz6KP9X
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
package bench;
|
||||
|
||||
/** The JNI twin of the jextract bindings: the same SQLite calls, through a hand-written C shim (jni/sqlite_jni.c). */
|
||||
public final class SqliteJni {
|
||||
static {
|
||||
System.loadLibrary("sqlitejni"); // libsqlitejni.so, found through -Djava.library.path
|
||||
}
|
||||
|
||||
private SqliteJni() {}
|
||||
|
||||
public static native int versionNumber();
|
||||
public static native int strlike(String glob, String text);
|
||||
public static native long open(String path);
|
||||
public static native int exec(long db, String sql);
|
||||
public static native long prepare(long db, String sql);
|
||||
public static native int bindInt(long stmt, int index, int value);
|
||||
public static native int bindText(long stmt, int index, String value);
|
||||
public static native int step(long stmt);
|
||||
public static native int reset(long stmt);
|
||||
public static native int finalizeStmt(long stmt);
|
||||
public static native int close(long db);
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/* The hand-written JNI shim: one C function per Java native method, with the marshalling done by hand.
|
||||
Header generated by `javac -h`; see scripts/build-jni.sh. */
|
||||
#include <stdint.h>
|
||||
#include <jni.h>
|
||||
#include <sqlite3.h>
|
||||
#include "bench_SqliteJni.h"
|
||||
|
||||
JNIEXPORT jint JNICALL Java_bench_SqliteJni_versionNumber(JNIEnv *env, jclass c) {
|
||||
return sqlite3_libversion_number();
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_bench_SqliteJni_strlike(JNIEnv *env, jclass c, jstring glob, jstring text) {
|
||||
const char *g = (*env)->GetStringUTFChars(env, glob, NULL);
|
||||
const char *t = (*env)->GetStringUTFChars(env, text, NULL);
|
||||
int r = sqlite3_strlike(g, t, 0);
|
||||
(*env)->ReleaseStringUTFChars(env, text, t);
|
||||
(*env)->ReleaseStringUTFChars(env, glob, g);
|
||||
return r;
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL Java_bench_SqliteJni_open(JNIEnv *env, jclass c, jstring path) {
|
||||
const char *p = (*env)->GetStringUTFChars(env, path, NULL);
|
||||
sqlite3 *db = NULL;
|
||||
int rc = sqlite3_open(p, &db);
|
||||
(*env)->ReleaseStringUTFChars(env, path, p);
|
||||
return rc == SQLITE_OK ? (jlong)(intptr_t)db : 0;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_bench_SqliteJni_exec(JNIEnv *env, jclass c, jlong db, jstring sql) {
|
||||
const char *s = (*env)->GetStringUTFChars(env, sql, NULL);
|
||||
int rc = sqlite3_exec((sqlite3 *)(intptr_t)db, s, NULL, NULL, NULL);
|
||||
(*env)->ReleaseStringUTFChars(env, sql, s);
|
||||
return rc;
|
||||
}
|
||||
|
||||
JNIEXPORT jlong JNICALL Java_bench_SqliteJni_prepare(JNIEnv *env, jclass c, jlong db, jstring sql) {
|
||||
const char *s = (*env)->GetStringUTFChars(env, sql, NULL);
|
||||
sqlite3_stmt *st = NULL;
|
||||
int rc = sqlite3_prepare_v2((sqlite3 *)(intptr_t)db, s, -1, &st, NULL);
|
||||
(*env)->ReleaseStringUTFChars(env, sql, s);
|
||||
return rc == SQLITE_OK ? (jlong)(intptr_t)st : 0;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_bench_SqliteJni_bindInt(JNIEnv *env, jclass c, jlong st, jint i, jint v) {
|
||||
return sqlite3_bind_int((sqlite3_stmt *)(intptr_t)st, i, v);
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_bench_SqliteJni_bindText(JNIEnv *env, jclass c, jlong st, jint i, jstring v) {
|
||||
const char *s = (*env)->GetStringUTFChars(env, v, NULL);
|
||||
int rc = sqlite3_bind_text((sqlite3_stmt *)(intptr_t)st, i, s, -1, SQLITE_TRANSIENT);
|
||||
(*env)->ReleaseStringUTFChars(env, v, s);
|
||||
return rc;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_bench_SqliteJni_step(JNIEnv *env, jclass c, jlong st) {
|
||||
return sqlite3_step((sqlite3_stmt *)(intptr_t)st);
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_bench_SqliteJni_reset(JNIEnv *env, jclass c, jlong st) {
|
||||
return sqlite3_reset((sqlite3_stmt *)(intptr_t)st);
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_bench_SqliteJni_finalizeStmt(JNIEnv *env, jclass c, jlong st) {
|
||||
return sqlite3_finalize((sqlite3_stmt *)(intptr_t)st);
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_bench_SqliteJni_close(JNIEnv *env, jclass c, jlong db) {
|
||||
return sqlite3_close((sqlite3 *)(intptr_t)db);
|
||||
}
|
||||
Reference in New Issue
Block a user