#define s3jni_jbyteArray_commit(jByteArray,jBytes) \
if( jBytes ) (*env)->ReleaseByteArrayElements(env, jByteArray, jBytes, JNI_COMMIT)
+/*
+** If jbb is-a java.nio.Buffer object and the JNI environment
+** supports it, *pBuf is set to the buffer's memory and *pN is set to
+** its length. If jbb is NULL, not a Buffer, or the JNI environment
+** does not support that operation, *pBuf is set to 0 and *pN is set
+** to 0.
+**
+** Note that the length of the buffer can be larger than SQLITE_LIMIT
+** but this function does not know what byte range of the buffer is
+** required so cannot check for that violation. The caller is required
+** to ensure that any to-be-bind()ed range fits within SQLITE_LIMIT.
+ */
+/*static*/ void s3jni__get_nio_buffer(JNIEnv * const env, jobject jbb, void **pBuf, jint * pN ){
+ *pBuf = 0;
+ *pN = 0;
+ if( jbb ){
+ *pBuf = (*env)->GetDirectBufferAddress(env, jbb);
+ *pN = *pBuf ? (jint)(*env)->GetDirectBufferCapacity(env, jbb) : 0
+ /* why the Java limits the buffer length to int but the JNI API
+ uses a jlong for the length is a mystery. */;
+ }
+}
+#define s3jni_get_nio_buffer(JOBJ,vpOut,jpOut) \
+ s3jni__get_nio_buffer(env,(JOBJ),(vpOut),(jpOut))
+
/*
** Returns the current JNIEnv object. Fails fatally if it cannot find
** the object.
** argument is a Java sqlite3 object, as this operation only has void
** pointers to work with.
*/
-#define PtrGet_T(T,OBJ) (T*)NativePointerHolder_get(OBJ, S3JniNph(T))
-#define PtrGet_sqlite3(OBJ) PtrGet_T(sqlite3, OBJ)
-#define PtrGet_sqlite3_backup(OBJ) PtrGet_T(sqlite3_backup, OBJ)
-#define PtrGet_sqlite3_blob(OBJ) PtrGet_T(sqlite3_blob, OBJ)
-#define PtrGet_sqlite3_context(OBJ) PtrGet_T(sqlite3_context, OBJ)
-#define PtrGet_sqlite3_stmt(OBJ) PtrGet_T(sqlite3_stmt, OBJ)
-#define PtrGet_sqlite3_value(OBJ) PtrGet_T(sqlite3_value, OBJ)
+#define PtrGet_T(T,JOBJ) (T*)NativePointerHolder_get((JOBJ), S3JniNph(T))
+#define PtrGet_sqlite3(JOBJ) PtrGet_T(sqlite3, (JOBJ))
+#define PtrGet_sqlite3_backup(JOBJ) PtrGet_T(sqlite3_backup, (JOBJ))
+#define PtrGet_sqlite3_blob(JOBJ) PtrGet_T(sqlite3_blob, (JOBJ))
+#define PtrGet_sqlite3_context(JOBJ) PtrGet_T(sqlite3_context, (JOBJ))
+#define PtrGet_sqlite3_stmt(JOBJ) PtrGet_T(sqlite3_stmt, (JOBJ))
+#define PtrGet_sqlite3_value(JOBJ) PtrGet_T(sqlite3_value, (JOBJ))
/*
** LongPtrGet_T(X,Y) expects X to be an unqualified sqlite3 struct
** type name and Y to be a native pointer to such an object in the
** a difference of microseconds (i.e. below our testing measurement
** threshold) might add up.
*/
-#define LongPtrGet_T(T,JLongAsPtr) (T*)((intptr_t)(JLongAsPtr))
-#define LongPtrGet_sqlite3(JLongAsPtr) LongPtrGet_T(sqlite3,JLongAsPtr)
-#define LongPtrGet_sqlite3_backup(JLongAsPtr) LongPtrGet_T(sqlite3_backup,JLongAsPtr)
-#define LongPtrGet_sqlite3_blob(JLongAsPtr) LongPtrGet_T(sqlite3_blob,JLongAsPtr)
-#define LongPtrGet_sqlite3_stmt(JLongAsPtr) LongPtrGet_T(sqlite3_stmt,JLongAsPtr)
-#define LongPtrGet_sqlite3_value(JLongAsPtr) LongPtrGet_T(sqlite3_value,JLongAsPtr)
+#define LongPtrGet_T(T,JLongAsPtr) (T*)((intptr_t)((JLongAsPtr)))
+#define LongPtrGet_sqlite3(JLongAsPtr) LongPtrGet_T(sqlite3,(JLongAsPtr))
+#define LongPtrGet_sqlite3_backup(JLongAsPtr) LongPtrGet_T(sqlite3_backup,(JLongAsPtr))
+#define LongPtrGet_sqlite3_blob(JLongAsPtr) LongPtrGet_T(sqlite3_blob,(JLongAsPtr))
+#define LongPtrGet_sqlite3_stmt(JLongAsPtr) LongPtrGet_T(sqlite3_stmt,(JLongAsPtr))
+#define LongPtrGet_sqlite3_value(JLongAsPtr) LongPtrGet_T(sqlite3_value,(JLongAsPtr))
/*
** Extracts the new S3JniDb instance from the free-list, or allocates
** one if needed, associates it with pDb, and returns. Returns NULL
return (jint)rc;
}
+S3JniApi(sqlite3_bind_nio_buffer(),jint,1bind_1nio_1buffer)(
+ JniArgsEnvClass, jobject jpStmt, jint ndx, jobject jBuffer,
+ jint iBegin, jint iN
+){
+ sqlite3_stmt * pStmt = PtrGet_sqlite3_stmt(jpStmt);
+ void * pBuf = 0;
+ jint nBuf = 0;
+ jlong iEnd = 0;
+ if( !SJG.g.cByteBuffer || !pStmt || iBegin<0 ){
+ return (jint)SQLITE_MISUSE;
+ }
+ s3jni_get_nio_buffer(jBuffer, &pBuf, &nBuf);
+ if( !pBuf || iBegin>=nBuf ){
+ return (jint)sqlite3_bind_null(pStmt, ndx);
+ }
+ assert( nBuf > 0 );
+ assert( iBegin < nBuf );
+ iEnd = iN<0 ? nBuf - iBegin : iBegin + iN;
+ if( iEnd>(jlong)nBuf ) iEnd = nBuf-iBegin;
+ if( iEnd-iBegin >(jlong)SQLITE_MAX_LENGTH ){
+ return SQLITE_MISUSE;
+ }
+ assert( iBegin>=0 );
+ assert( iEnd > iBegin );
+ return (jint)sqlite3_bind_blob(pStmt, (int)ndx, pBuf + iBegin,
+ (int)(iEnd - iBegin), SQLITE_TRANSIENT);
+}
+
S3JniApi(sqlite3_bind_double(),jint,1bind_1double)(
JniArgsEnvClass, jlong jpStmt, jint ndx, jdouble val
){
JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1bind_1java_1object
(JNIEnv *, jclass, jlong, jint, jobject);
+/*
+ * Class: org_sqlite_jni_capi_CApi
+ * Method: sqlite3_bind_nio_buffer
+ * Signature: (Lorg/sqlite/jni/capi/sqlite3_stmt;ILjava/nio/ByteBuffer;II)I
+ */
+JNIEXPORT jint JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1bind_1nio_1buffer
+ (JNIEnv *, jclass, jobject, jint, jobject, jint, jint);
+
/*
* Class: org_sqlite_jni_capi_CApi
* Method: sqlite3_bind_null
If n is negative, SQLITE_MISUSE is returned. If n>data.length
then n is silently truncated to data.length.
*/
- static int sqlite3_bind_blob(
+ public static int sqlite3_bind_blob(
@NotNull sqlite3_stmt stmt, int ndx, @Nullable byte[] data, int n
){
return sqlite3_bind_blob(stmt.getNativePointer(), ndx, data, n);
: sqlite3_bind_blob(stmt.getNativePointer(), ndx, data, data.length);
}
+ /**
+ Convenience overload which is a simple proxy for
+ sqlite3_bind_nio_buffer().
+ */
+ public static int sqlite3_bind_blob(
+ @NotNull sqlite3_stmt stmt, int ndx, @Nullable java.nio.ByteBuffer data,
+ int begin, int n
+ ){
+ return sqlite3_bind_nio_buffer(stmt, ndx, data, begin, n);
+ }
+
+ /**
+ Convenience overload which is equivalant to passing its arguments
+ to sqlite3_bind_nio_buffer() with the values 0 and -1 for the
+ final two arguments.
+ */
+ public static int sqlite3_bind_blob(
+ @NotNull sqlite3_stmt stmt, int ndx, @Nullable java.nio.ByteBuffer data
+ ){
+ return sqlite3_bind_nio_buffer(stmt, ndx, data, 0, -1);
+ }
+
private static native int sqlite3_bind_double(
@NotNull long ptrToStmt, int ndx, double v
);
@NotNull long ptrToStmt, int ndx, @Nullable Object o
);
+ /**
+ Binds the contents of the given buffer object as a blob.
+
+ The byte range of the buffer may be restricted by providing a
+ start index and a number of bytes. beginPos may not be negative
+ but a negative howMany is interpretated as the remainder of the
+ buffer past the given start position.
+
+ If beginPos+howMany would extend past the end of the buffer, the
+ range is silently truncated to fit the buffer.
+
+ If any of the following are true, this function behaves like
+ sqlite3_bind_null(): the buffer is null, beginPos is past the end
+ of the buffer, howMany is 0, or the calculated slice of the blob
+ has a length of 0.
+
+ If ndx is out of range, it returns SQLITE_RANGE, as documented
+ for sqlite3_bind_blob(). If any other arguments are invalid or
+ if sqlite3_jni_supports_nio() is false then SQLITE_MISUSE is
+ returned. Note that this function is bound (as it were) by the
+ SQLITE_LIMIT_LENGTH constraint and SQLITE_MISUSE is returned if
+ that's violated.
+
+ This function does not modify the buffer's streaming-related
+ cursors.
+
+ If the buffer is modified in a separate thread while this
+ operation is running, results are undefined and will likely
+ result in corruption of the bound data or a segmentation fault.
+
+ Design note: this function should arguably take a java.nio.Buffer
+ instead of ByteBuffer, but it can only operate on "direct"
+ buffers and the only such class offered by Java is (apparently)
+ ByteBuffer.
+
+ @see https://docs.oracle.com/javase/8/docs/api/java/nio/Buffer.html
+ */
+ public static native int sqlite3_bind_nio_buffer(
+ @NotNull sqlite3_stmt stmt, int ndx, @Nullable java.nio.ByteBuffer data,
+ int beginPos, int howMany
+ );
+
+ /**
+ Convenience overload which binds the given buffer's entire contents.
+ */
+ public static int sqlite3_bind_nio_buffer(
+ @NotNull sqlite3_stmt stmt, int ndx, @Nullable java.nio.ByteBuffer data
+ ){
+ return sqlite3_bind_nio_buffer(stmt, ndx, data, 0, -1);
+ }
+
/**
Binds the given object at the given index. If o is null then this behaves like
sqlite3_bind_null().
@java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD})
@interface SingleThreadOnly{}
+/**
+ Annotation for Tester1 tests which must only be run if JNI-level support for
+ java.nio.Buffer is available.
+*/
+@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
+@java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD})
+@interface RequiresNioBuffer{}
+
public class Tester1 implements Runnable {
//! True when running in multi-threaded mode.
private static boolean mtMode = false;
sqlite3_close_v2(db);
}
+ @RequiresNioBuffer
+ private void testBindByteBuffer(){
+ sqlite3 db = createNewDb();
+ execSql(db, "CREATE TABLE t(a)");
+ sqlite3_stmt stmt = prepare(db, "INSERT INTO t(a) VALUES(?);");
+ java.nio.ByteBuffer buf = java.nio.ByteBuffer.allocateDirect(10);
+ buf.put((byte)0x31)/*note that we'll skip this one*/
+ .put((byte)0x32)
+ .put((byte)0x33)
+ .put((byte)0x34);
+ int rc = sqlite3_bind_blob(stmt, 1, buf, -1, 0);
+ affirm( SQLITE_MISUSE==rc );
+ rc = sqlite3_bind_blob(stmt, 1, buf, 1, 3);
+ affirm( 0==rc );
+ rc = sqlite3_step(stmt);
+ affirm(SQLITE_DONE == rc);
+ sqlite3_finalize(stmt);
+ stmt = prepare(db, "SELECT a FROM t ORDER BY a DESC;");
+ int n = 0;
+ int total = 0;
+ while( SQLITE_ROW == sqlite3_step(stmt) ){
+ byte[] blob = sqlite3_column_blob(stmt, 0);
+ affirm(3 == blob.length);
+ int i = 0;
+ for(byte b : blob){
+ affirm( i<=3 );
+ affirm(b == buf.get(1 + i++));
+ total += b;
+ }
+ ++n;
+ }
+ sqlite3_finalize(stmt);
+ affirm(1 == n);
+ affirm(total == 0x32 + 0x33 + 0x34);
+ /* TODO: these tests need to be much more extensive to check the
+ begin range handling. */
+ sqlite3_close_v2(db);
+ }
+
private void testSql(){
sqlite3 db = createNewDb();
sqlite3_stmt stmt = prepare(db, "SELECT 1");
if( forceFail ){
testMethods.add(m);
}
+ }else if( m.isAnnotationPresent( RequiresNioBuffer.class )
+ && !sqlite3_jni_supports_nio() ){
+ outln("Skipping test for lack JNI nio.Buffer support: ",name,"()\n");
+ ++nSkipped;
}else if( !m.isAnnotationPresent( ManualTest.class ) ){
if( nThread>1 && m.isAnnotationPresent( SingleThreadOnly.class ) ){
- if( 0==nSkipped++ ){
- out("Skipping tests in multi-thread mode:");
- }
- out(" "+name+"()");
+ out("Skipping test in multi-thread mode: ",name,"()\n");
+ ++nSkipped;
}else if( name.startsWith("test") ){
testMethods.add(m);
}
}
}
- if( nSkipped>0 ) out("\n");
}
final long timeStart = System.currentTimeMillis();
-C JNI\swrapper1:\swhen\schecking\sfor\san\sout-of-bounds\sstatement\scolumn\sindex,\sperform\sthe\sis-statement-finalized\scheck\sbefore\sthe\srange\scheck\sso\sthat\sthe\sformer\sexception\strumps\sthe\slatter.
-D 2023-11-11T14:50:01.933
+C JNI:\sadd\ssqlite3_bind_nio_buffer()\sand\sinitial\stests\sfor\sbinding\sByteBuffer\sobjects\sas\sblobs\son\sJVMs\swhich\shave\sJNI\ssupport\sfor\snio\sbuffers.
+D 2023-11-13T14:58:37.421
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
F ext/jni/GNUmakefile f2f3a31923293659b95225e932a286af1f2287d75bf88ad6c0fd1b9d9cd020d4
F ext/jni/README.md ef9ac115e97704ea995d743b4a8334e23c659e5534c3b64065a5405256d5f2f4
F ext/jni/jar-dist.make 030aaa4ae71dd86e4ec5e7c1e6cd86f9dfa47c4592c070d2e35157e42498e1fa
-F ext/jni/src/c/sqlite3-jni.c 3774703e5865e7ff776b762de5386af8aa703e569bbb3a85c423c3f8473a3c26
-F ext/jni/src/c/sqlite3-jni.h 891444578550a7aa69fe5e0dedb3e6dedad752501ba99801f17797be51796934
+F ext/jni/src/c/sqlite3-jni.c a04d0d77e7391a69f7f8ca4b38e24de59b3a8f61610f2e91698c190c07283850
+F ext/jni/src/c/sqlite3-jni.h 2848299f845d36b4b6123d360e7a4eb960d040637a10158079af49f4ded16453
F ext/jni/src/org/sqlite/jni/annotation/NotNull.java 02091a8112e33389f1c160f506cd413168c8dfacbeda608a4946c6e3557b7d5a
F ext/jni/src/org/sqlite/jni/annotation/Nullable.java 0b1879852707f752512d4db9d7edd0d8db2f0c2612316ce1c832715e012ff6ba
F ext/jni/src/org/sqlite/jni/annotation/package-info.java 977b374aed9d5853cbf3438ba3b0940abfa2ea4574f702a2448ee143b98ac3ca
F ext/jni/src/org/sqlite/jni/capi/AuthorizerCallback.java c045a5b47e02bb5f1af91973814a905f12048c428a3504fbc5266d1c1be3de5a
F ext/jni/src/org/sqlite/jni/capi/AutoExtensionCallback.java 74cc4998a73d6563542ecb90804a3c4f4e828cb4bd69e61226d1a51f4646e759
F ext/jni/src/org/sqlite/jni/capi/BusyHandlerCallback.java 7b8e19810c42b0ad21a04b5d8c804b32ee5905d137148703f16a75b612c380ca
-F ext/jni/src/org/sqlite/jni/capi/CApi.java bd4a6490548f913bf9719443dee3d8a233f920ed1614b622738527d746e00f5d
+F ext/jni/src/org/sqlite/jni/capi/CApi.java 5ef54290c17dca46d7f24001ac3b689559e1b37ee40d06b88fa5315d64863789
F ext/jni/src/org/sqlite/jni/capi/CallbackProxy.java 57e2d275dcebe690b1fc1f3d34eb96879b2d7039bce30b563aee547bf45d8a8b
F ext/jni/src/org/sqlite/jni/capi/CollationCallback.java e29bcfc540fdd343e2f5cca4d27235113f2886acb13380686756d5cabdfd065a
F ext/jni/src/org/sqlite/jni/capi/CollationNeededCallback.java 5bfa226a8e7a92e804fd52d6e42b4c7b875fa7a94f8e2c330af8cc244a8920ab
F ext/jni/src/org/sqlite/jni/capi/SQLTester.java 09bee15aa0eedac68d767ae21d9a6a62a31ade59182a3ccbf036d6463d9e30b1
F ext/jni/src/org/sqlite/jni/capi/ScalarFunction.java 93b9700fca4c68075ccab12fe0fbbc76c91cafc9f368e835b9bd7cd7732c8615
F ext/jni/src/org/sqlite/jni/capi/TableColumnMetadata.java addf120e0e76e5be1ff2260daa7ce305ff9b5fafd64153a7a28e9d8f000a815f
-F ext/jni/src/org/sqlite/jni/capi/Tester1.java b1a0c015d92a8d0c07a8f6751e9b057557cec9d803e002d48ee5f3b9963abd55
+F ext/jni/src/org/sqlite/jni/capi/Tester1.java 4ec21172917f641787767443f418854329bf9b9779807b644e000dac1ec77013
F ext/jni/src/org/sqlite/jni/capi/TraceV2Callback.java 0a25e117a0daae3394a77f24713e36d7b44c67d6e6d30e9e1d56a63442eef723
F ext/jni/src/org/sqlite/jni/capi/UpdateHookCallback.java c8bdf7848e6599115d601bcc9427ff902cb33129b9be32870ac6808e04b6ae56
F ext/jni/src/org/sqlite/jni/capi/ValueHolder.java 22d365746a78c5cd7ae10c39444eb7bbf1a819aad4bb7eb77b1edc47773a3950
F vsixtest/vsixtest.vcxproj.data 2ed517e100c66dc455b492e1a33350c1b20fbcdc
F vsixtest/vsixtest.vcxproj.filters 37e51ffedcdb064aad6ff33b6148725226cd608e
F vsixtest/vsixtest_TemporaryKey.pfx e5b1b036facdb453873e7084e1cae9102ccc67a0
-P a6ab88e9a67f23ab7885402776282b94033cb48dbe34d4d18356e4dc22aae7cd
-R 78a63cfa87011ebb24c6cac260db14c9
+P 0832f9a8e9f574b157c791c5cddc73aff7b2ff403509f5d78f310494d4a7f93d
+R 7292f27854539ba720bc8a8fcd0c273f
U stephan
-Z ad92dad6ace48bd20f7cf67cbd6f4f40
+Z 1881d5bf6b59348a7807616e897efc0f
# Remove this line to create a well-formed Fossil manifest.
-0832f9a8e9f574b157c791c5cddc73aff7b2ff403509f5d78f310494d4a7f93d
\ No newline at end of file
+b10ce1ef82d84726fbf6a8f624d6530f84fefb505f7868b4a0ea910fed7a877f
\ No newline at end of file