Minor tweaks and optimizations in the JNI bindings.

FossilOrigin-Name: 41fb5898f1a78d9fd85a020e28a6048a7359b54e35632e9072917cbdbcd8b07d
This commit is contained in:
stephan 2023-07-30 13:30:52 +00:00
parent 502a5c2e26
commit 13b059025f
7 changed files with 101 additions and 75 deletions

View File

@ -285,8 +285,7 @@ struct NphCacheLine {
from the ClassNames struct. */;
jclass klazz /* global ref to concrete NativePointerHolder class */;
jmethodID midCtor /* klazz's constructor */;
jmethodID midSet /* NativePointerHolder.setNativePointer() */;
jmethodID midGet /* NativePointerHolder.getNativePointer() */;
jfieldID fidValue /* NativePointerHolder.nativePointer and OutputPointer.X.value */;
jfieldID fidSetAgg /* sqlite3_context::aggregateContext */;
};
@ -565,11 +564,12 @@ static struct NphCacheLine * S3Global_nph_cache(JNIEnv *env, const char *zClassN
for( i = 0; i < NphCache_SIZE; ++i ){
cacheLine = &envRow->nph[i];
if(zClassName == cacheLine->zClassName){
#if 0
#define DUMP_NPH_CACHES 0
#if DUMP_NPH_CACHES
static unsigned int n = 0;
MARKER(("Cache hit #%u %s klazz@%p getter@%p, setter@%p, ctor@%p\n",
++n, zClassName, cacheLine->klazz, cacheLine->midGet,
cacheLine->midSet, cacheLine->midCtor));
MARKER(("Cache hit #%u %s klazz@%p nativePointer field@%p, ctor@%p\n",
++n, zClassName, cacheLine->klazz, cacheLine->fidValue,
cacheLine->midCtor));
#endif
assert(cacheLine->klazz);
return cacheLine;
@ -580,18 +580,27 @@ static struct NphCacheLine * S3Global_nph_cache(JNIEnv *env, const char *zClassN
if(freeSlot){
freeSlot->zClassName = zClassName;
freeSlot->klazz = REF_G((*env)->FindClass(env, zClassName));
#if 0
#if DUMP_NPH_CACHES
static unsigned int cacheMisses = 0;
MARKER(("Cache miss #%u %s klazz@%p getter@%p, setter@%p, ctor@%p\n",
MARKER(("Cache miss #%u %s klazz@%p nativePointer field@%p, ctor@%p\n",
++cacheMisses, zClassName, freeSlot->klazz,
freeSlot->midGet, freeSlot->midSet, freeSlot->midCtor));
freeSlot->fidValue, freeSlot->midCtor));
#endif
#undef DUMP_NPH_CACHES
}else{
(*env)->FatalError(env, "MAINTENANCE REQUIRED: NphCache_SIZE is too low.");
}
return freeSlot;
}
static jfieldID getNativePointerField(JNIEnv *env, jclass klazz){
jfieldID rv = (*env)->GetFieldID(env, klazz, "nativePointer", "J");
IFTHREW{
(*env)->FatalError(env, "Maintenance required: missing nativePointer field.");
}
return rv;
}
/**
Sets a native ptr value in NativePointerHolder object ppOut.
zClassName must be a static string so we can use its address
@ -599,24 +608,24 @@ static struct NphCacheLine * S3Global_nph_cache(JNIEnv *env, const char *zClassN
*/
static void setNativePointer(JNIEnv * env, jobject ppOut, void * p,
const char *zClassName){
jmethodID setter = 0;
jfieldID setter = 0;
struct NphCacheLine * const cacheLine = S3Global_nph_cache(env, zClassName);
if(cacheLine && cacheLine->klazz && cacheLine->midSet){
if(cacheLine && cacheLine->klazz && cacheLine->fidValue){
assert(zClassName == cacheLine->zClassName);
setter = cacheLine->midSet;
setter = cacheLine->fidValue;
assert(setter);
}else{
jclass const klazz =
cacheLine ? cacheLine->klazz : (*env)->GetObjectClass(env, ppOut);
setter = (*env)->GetMethodID(env, klazz, "setNativePointer", "(J)V");
setter = getNativePointerField(env, klazz);
if(cacheLine){
assert(cacheLine->klazz);
assert(!cacheLine->midSet);
assert(!cacheLine->fidValue);
assert(zClassName == cacheLine->zClassName);
cacheLine->midSet = setter;
cacheLine->fidValue = setter;
}
}
(*env)->CallVoidMethod(env, ppOut, setter, (jlong)p);
(*env)->SetLongField(env, ppOut, setter, (jlong)p);
IFTHREW_REPORT;
}
@ -628,22 +637,22 @@ static void setNativePointer(JNIEnv * env, jobject ppOut, void * p,
static void * getNativePointer(JNIEnv * env, jobject pObj, const char *zClassName){
if( 0==pObj ) return 0;
else{
jmethodID getter = 0;
jfieldID getter = 0;
void * rv = 0;
struct NphCacheLine * const cacheLine = S3Global_nph_cache(env, zClassName);
if(cacheLine && cacheLine->midGet){
getter = cacheLine->midGet;
if(cacheLine && cacheLine->fidValue){
getter = cacheLine->fidValue;
}else{
jclass const klazz =
cacheLine ? cacheLine->klazz : (*env)->GetObjectClass(env, pObj);
getter = (*env)->GetMethodID(env, klazz, "getNativePointer", "()J");
getter = getNativePointerField(env, klazz);
if(cacheLine){
assert(cacheLine->klazz);
assert(zClassName == cacheLine->zClassName);
cacheLine->midGet = getter;
cacheLine->fidValue = getter;
}
}
rv = (void*)(*env)->CallLongMethod(env, pObj, getter);
rv = (void*)(*env)->GetLongField(env, pObj, getter);
IFTHREW_REPORT;
return rv;
}
@ -845,34 +854,23 @@ static int udf_setAggregateContext(JNIEnv * env, jobject jCx,
/* Sets a native int32 value in OutputPointer.Int32 object ppOut. */
static void setOutputInt32(JNIEnv * env, jobject ppOut, int v){
jmethodID setter = 0;
jfieldID setter = 0;
struct NphCacheLine * const cacheLine =
S3Global_nph_cache(env, ClassNames.OutputPointer_Int32);
if(cacheLine && cacheLine->klazz && cacheLine->midSet){
setter = cacheLine->midSet;
if(cacheLine && cacheLine->klazz && cacheLine->fidValue){
setter = cacheLine->fidValue;
}else{
const jclass klazz = (*env)->GetObjectClass(env, ppOut);
setter = (*env)->GetMethodID(env, klazz, "setValue", "(I)V");
setter = (*env)->GetFieldID(env, klazz, "value", "I");
if(cacheLine){
assert(!cacheLine->midSet);
cacheLine->midSet = setter;
assert(!cacheLine->fidValue);
cacheLine->fidValue = setter;
}
}
(*env)->CallVoidMethod(env, ppOut, setter, (jint)v);
(*env)->SetIntField(env, ppOut, setter, (jint)v);
IFTHREW_REPORT;
}
#if 0
/* Fetches a native int32 value from OutputPointer.Int32 object pObj. */
static int getOutputInt(JNIEnv * env, jobject pObj){
const jclass klazz = (*env)->GetObjectClass(env, pObj);
const jmethodID getter =
(*env)->GetMethodID(env, klazz, "getValue", "(V)I;");
return (int)(*env)->CallIntMethod(env, pObj, getter);
}
#define VAL_GET_INT(OBJ) getOutputInt(env, OBJ)
#endif
static int encodingTypeIsValid(int eTextRep){
switch(eTextRep){
case SQLITE_UTF8: case SQLITE_UTF16:

View File

@ -23,18 +23,11 @@ package org.sqlite.jni;
NativePointerHolder is not inadvertently passed to an incompatible
function signature.
These objects are not intended to _own_ the pointer they refer to.
They are intended to simply communicate that pointer between C and
Java.
These objects do not _own_ the pointer they refer to. They are
intended simply to communicate that pointer between C and Java.
*/
public class NativePointerHolder<ContextType> {
private long pointer;
public NativePointerHolder(long pointer){
this.pointer = pointer;
}
public NativePointerHolder(){
this.pointer = 0;
}
public final long getNativePointer(){ return pointer; }
public final void setNativePointer(long p){ pointer = p; }
//! Only set from JNI, where access permissions don't matter.
private long nativePointer = 0;
public final long getNativePointer(){ return nativePointer; }
}

View File

@ -25,12 +25,10 @@ package org.sqlite.jni;
public final class OutputPointer {
public static final class Int32 {
private int value;
public final void setValue(int v){value = v;}
public final int getValue(){return value;}
}
public static final class Int64 {
private long value;
public final void setValue(long v){value = v;}
public final long getValue(){return value;}
}
}

View File

@ -154,9 +154,6 @@ public final class SQLite3Jni {
public static native int sqlite3_close_v2(@NotNull sqlite3 db);
//TODO? public static native int sqlite3_collation_needed(sqlite3*,void*,void(*)(void*,sqlite3*,int eTextRep,const char*));
//TODO? public static native int sqlite3_collation_needed16(sqlite3*,void*,void(*)(void*,sqlite3*,int eTextRep,const void*));
public static native byte[] sqlite3_column_blob(@NotNull sqlite3_stmt stmt, int ndx);
public static native int sqlite3_column_bytes(@NotNull sqlite3_stmt stmt, int ndx);
@ -214,7 +211,8 @@ public final class SQLite3Jni {
the db if the db uses the default encoding of UTF-8.
To extract _standard_ UTF-8, use sqlite3_column_text_utf8().
This API includes no functions for working with Modified UTF-8.
This API includes no functions for working with Java's Modified
UTF-8.
[^1]: https://stackoverflow.com/questions/7921016
*/
@ -226,8 +224,43 @@ public final class SQLite3Jni {
*/
public static native byte[] sqlite3_column_text_utf8(@NotNull sqlite3_stmt stmt,
int ndx);
//TODO public static native ?type? sqlite3_column_text16(@NotNull sqlite3_stmt stmt, int ndx);
//TODO: public static Object sqlite3_column_to_java(@NotNull sqlite3_value v){...}
// The real utility of this function is questionable.
// /**
// Returns a Java value representation based on the value of
// sqlite_value_type(). For integer types it returns either Integer
// or Long, depending on whether the value will fit in an
// Integer. For floating-point values it always returns type Double.
// If the column was bound using sqlite3_result_java_object() then
// that value, as an Object, is returned.
// */
// public static Object sqlite3_column_to_java(@NotNull sqlite3_stmt stmt,
// int ndx){
// sqlite3_value v = sqlite3_column_value(stmt, ndx);
// Object rv = null;
// if(null == v) return v;
// v = sqlite3_value_dup(v)/*need a protected value*/;
// if(null == v) return v /* OOM error in C */;
// if(112/* 'p' */ == sqlite3_value_subtype(v)){
// rv = sqlite3_value_java_object(v);
// }else{
// switch(sqlite3_value_type(v)){
// case SQLITE_INTEGER: {
// final long i = sqlite3_value_int64(v);
// rv = (i<=0x7fffffff && i>=-0x7fffffff-1)
// ? new Integer((int)i) : new Long(i);
// break;
// }
// case SQLITE_FLOAT: rv = new Double(sqlite3_value_double(v)); break;
// case SQLITE_BLOB: rv = sqlite3_value_blob(v); break;
// case SQLITE_TEXT: rv = sqlite3_value_text(v); break;
// default: break;
// }
// }
// sqlite3_value_free(v);
// return rv;
// }
public static native int sqlite3_column_type(@NotNull sqlite3_stmt stmt,
int ndx);
@ -476,6 +509,8 @@ public final class SQLite3Jni {
allocate such strings and store them somewhere for long-term use
(leaking them more likely than not). Even then, passing around a
pointer via Java like that has little practical use.
Note that there is no sqlite3_bind_java_object() counterpart.
*/
public static native void sqlite3_result_java_object(@NotNull sqlite3_context cx,
@NotNull Object o);

View File

@ -483,7 +483,7 @@ public class Tester1 {
private static void testUdfJavaObject(){
final sqlite3 db = createNewDb();
final ValueHolder<Long> testResult = new ValueHolder<>(42L);
final ValueHolder<sqlite3> testResult = new ValueHolder<>(db);
SQLFunction func = new SQLFunction.Scalar(){
public void xFunc(sqlite3_context cx, sqlite3_value args[]){
sqlite3_result_java_object(cx, testResult.value);
@ -494,14 +494,16 @@ public class Tester1 {
sqlite3_stmt stmt = new sqlite3_stmt();
sqlite3_prepare(db, "select myfunc()", stmt);
affirm( 0 != stmt.getNativePointer() );
affirm( testResult.value == db );
int n = 0;
if( SQLITE_ROW == sqlite3_step(stmt) ){
sqlite3_value v = sqlite3_column_value(stmt, 0);
final sqlite3_value v = sqlite3_column_value(stmt, 0);
affirm( testResult.value == sqlite3_value_java_object(v) );
affirm( testResult.value == sqlite3_value_java_casted(v, Long.class) );
affirm( testResult.value == sqlite3_value_java_casted(v, sqlite3.class) );
affirm( testResult.value ==
sqlite3_value_java_casted(v, testResult.value.getClass()) );
affirm( null == sqlite3_value_java_casted(v, Double.class) );
affirm( testResult.value == sqlite3_value_java_casted(v, Object.class) );
affirm( null == sqlite3_value_java_casted(v, String.class) );
++n;
}
sqlite3_finalize(stmt);

View File

@ -1,5 +1,5 @@
C Bind\ssqlite3_collation_needed()\sto\sJNI.\sRelated\sadjacent\scleanups\sand\sfixes.
D 2023-07-30T11:36:41.439
C Minor\stweaks\sand\soptimizations\sin\sthe\sJNI\sbindings.
D 2023-07-30T13:30:52.663
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@ -232,19 +232,19 @@ F ext/icu/icu.c c074519b46baa484bb5396c7e01e051034da8884bad1a1cb7f09bbe6be3f0282
F ext/icu/sqliteicu.h fa373836ed5a1ee7478bdf8a1650689294e41d0c89c1daab26e9ae78a32075a8
F ext/jni/GNUmakefile 56a014dbff9516774d895ec1ae9df0ed442765b556f79a0fc0b5bc438217200d
F ext/jni/README.md c0e6e80935e7761acead89b69c87765b23a6bcb2858c321c3d05681fd338292a
F ext/jni/src/c/sqlite3-jni.c 1934a72f33fe356d8af810a8a662dd8109026cd0bbf298dda1fe8bd1146603ad
F ext/jni/src/c/sqlite3-jni.c d3ce5d96feb5eebf8dd171f041704798f3d0a5da1ee93a43788059d1d9f167ff
F ext/jni/src/c/sqlite3-jni.h 28def286ee305c1c89a43ac5918a6862d985d0534f7ccbbd74df4885d3918b73
F ext/jni/src/org/sqlite/jni/BusyHandler.java 1b1d3e5c86cd796a0580c81b6af6550ad943baa25e47ada0dcca3aff3ebe978c
F ext/jni/src/org/sqlite/jni/Collation.java 8dffbb00938007ad0967b2ab424d3c908413af1bbd3d212b9c9899910f1218d1
F ext/jni/src/org/sqlite/jni/CollationNeeded.java ebc7cd96d46a70daa76016a308e80f70a3f21d3282787c8d139aa840fdcb1bd7
F ext/jni/src/org/sqlite/jni/CommitHook.java 87c6a8e5138c61a8eeff018fe16d23f29219150239746032687f245938baca1a
F ext/jni/src/org/sqlite/jni/NativePointerHolder.java 70dc7bc41f80352ff3d4331e2e24f45fcd23353b3641e2f68a81bd8262215861
F ext/jni/src/org/sqlite/jni/OutputPointer.java 08a752b58a33696c5eaf0eb9361a0966b188dec40f4a3613eb133123951f6c5f
F ext/jni/src/org/sqlite/jni/NativePointerHolder.java 9c5d901cce4f7e57c3d623f4e2476f9f79a8eed6e51b2a603f37866018e040ee
F ext/jni/src/org/sqlite/jni/OutputPointer.java a5cb651df3b3adb65a9aca6cf9a094dea1346fc9ee5f341f79276348ac268351
F ext/jni/src/org/sqlite/jni/ProgressHandler.java 5979450e996416d28543f1d42634d308439565a99332a8bd84e424af667116cc
F ext/jni/src/org/sqlite/jni/RollbackHook.java b04c8abcc6ade44a8a57129e33765793f69df0ba909e49ba18d73f4268d92564
F ext/jni/src/org/sqlite/jni/SQLFunction.java 663a4e479ec65bfbf893586439e12d30b8237898064a22ab64f5658b57315f37
F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 2c4564b19f5366927c9a5062e36ffb7744e7f69d00b3f8ce35fe59b2f3d60698
F ext/jni/src/org/sqlite/jni/Tester1.java a89a87f8debd89f3488a65cb42af8e14fb0150b05d5a4a3592fb86d0cfda3287
F ext/jni/src/org/sqlite/jni/SQLite3Jni.java 42ca7686d009a56e4f5ceb74a0bd32ca69c025f2bf30d3e906696ad36ac72510
F ext/jni/src/org/sqlite/jni/Tester1.java 1690172fccafbf8d8170b55b950003db182265c26dbb5a510122ec46a44d2611
F ext/jni/src/org/sqlite/jni/Tracer.java c2fe1eba4a76581b93b375a7b95ab1919e5ae60accfb06d6beb067b033e9bae1
F ext/jni/src/org/sqlite/jni/UpdateHook.java e58645a1727f8a9bbe72dc072ec5b40d9f9362cb0aa24acfe93f49ff56a9016d
F ext/jni/src/org/sqlite/jni/ValueHolder.java f022873abaabf64f3dd71ab0d6037c6e71cece3b8819fa10bf26a5461dc973ee
@ -2071,8 +2071,8 @@ F vsixtest/vsixtest.tcl 6a9a6ab600c25a91a7acc6293828957a386a8a93
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
P 7ac6614e69b03304d09745619ed83f12c7eb775aaf4a636a79289b01642ddd14
R fe2ec7cfe7eced93fd3b168114e0d2e0
P 16ff167691733350907d2d995c774a885214acd0fe8ec491c16b786f00fe85d4
R fed5a38c4ce39785114a9bf68d9f61c4
U stephan
Z 9246dbb52619ce19a9defcf2e690b44f
Z a065a6cc3e3d2dbbd261c22dc07adbce
# Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
16ff167691733350907d2d995c774a885214acd0fe8ec491c16b786f00fe85d4
41fb5898f1a78d9fd85a020e28a6048a7359b54e35632e9072917cbdbcd8b07d