** 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 ){
+static void s3jni__get_nio_buffer(JNIEnv * const env, jobject jbb, void **pBuf, jint * pN ){
*pBuf = 0;
*pN = 0;
if( jbb ){
return (jint)rc;
}
+/**
+ Helper for use with s3jni_setup_nio_args().
+*/
+struct S3JniNioArgs {
+ jobject jBuf; /* input - ByteBuffer */
+ jint iBegin; /* input - byte offset */
+ jint iN; /* input - byte count to bind */
+ jint nBuf; /* output - jBuf's buffer size */
+ void * p; /* output - jBuf's buffer memory */
+ const void * pStart; /* output - offset of p to bind */
+ int iOutLen; /* output - number of bytes from pStart to bind */
+};
+typedef struct S3JniNioArgs S3JniNioArgs;
+static const S3JniNioArgs S3JniNioArgs_empty = {
+ 0,0,0,0,0,0,0
+};
+
+/**
+ Internal helper for sqlite3_bind_nio_buffer() and
+ sqlite3_result_nio_buffer(). Populates pArgs and returns 0 on
+ success, non-0 if the operation should fail. The caller is
+ required to check for SJG.g.cByteBuffer!=0 before calling
+ this and reporting it in a way appropriate for that routine.
+ This function may assert() that SJG.g.cByteBuffer is not 0.
+
+ The (jBuffer, iBegin, iN) arguments are the (ByteBuffer, offset,
+ length) arguments to the bind/result method.
+*/
+static int s3jni_setup_nio_args(
+ JNIEnv *env, S3JniNioArgs * pArgs,
+ jobject jBuffer, jint iBegin, jint iN
+){
+ jlong iEnd = 0;
+ *pArgs = S3JniNioArgs_empty;
+ pArgs->jBuf = jBuffer;
+ pArgs->iBegin = iBegin;
+ pArgs->iN = iN;
+ assert( SJG.g.cByteBuffer );
+ if( pArgs->iBegin<0 ){
+ return SQLITE_MISUSE;
+ }
+ s3jni_get_nio_buffer(pArgs->jBuf, &pArgs->p, &pArgs->nBuf);
+ if( !pArgs->p ){
+ return SQLITE_MISUSE;
+ }else if( pArgs->iBegin>=pArgs->nBuf ){
+ pArgs->pStart = 0;
+ pArgs->iOutLen = 0;
+ return 0;
+ }
+ assert( pArgs->nBuf > 0 );
+ assert( pArgs->iBegin < pArgs->nBuf );
+ iEnd = pArgs->iN<0 ? pArgs->nBuf - pArgs->iBegin : pArgs->iBegin + pArgs->iN;
+ if( iEnd>(jlong)pArgs->nBuf ) iEnd = pArgs->nBuf - pArgs->iBegin;
+ if( iEnd - pArgs->iBegin > (jlong)SQLITE_MAX_LENGTH ){
+ return SQLITE_TOOBIG;
+ }
+ assert( pArgs->iBegin >= 0 );
+ assert( iEnd > pArgs->iBegin );
+ pArgs->pStart = pArgs->p + pArgs->iBegin;
+ pArgs->iOutLen = (int)(iEnd - pArgs->iBegin);
+ assert( pArgs->iOutLen > 0 );
+ return 0;
+}
+
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;
+ S3JniNioArgs args;
+ int rc;
+ if( !pStmt || !SJG.g.cByteBuffer ) return SQLITE_MISUSE;
+ rc = s3jni_setup_nio_args(env, &args, jBuffer, iBegin, iN);
+ if(rc){
+ return rc;
+ }else if( !args.pStart || !args.iOutLen ){
+ return sqlite3_bind_null(pStmt, ndx);
}
- assert( iBegin>=0 );
- assert( iEnd > iBegin );
- return (jint)sqlite3_bind_blob(pStmt, (int)ndx, pBuf + iBegin,
- (int)(iEnd - iBegin), SQLITE_TRANSIENT);
+ assert( args.iOutLen>0 );
+ assert( args.nBuf > 0 );
+ assert( args.pStart != 0 );
+ assert( (args.pStart + args.iOutLen) <= (args.p + args.nBuf) );
+ return sqlite3_bind_blob( pStmt, (int)ndx, args.pStart,
+ args.iOutLen, SQLITE_TRANSIENT );
}
S3JniApi(sqlite3_bind_double(),jint,1bind_1double)(
}
}
+S3JniApi(sqlite3_result_nio_buffer(),void,1result_1nio_1buffer)(
+ JniArgsEnvClass, jobject jpCtx, jobject jBuffer,
+ jint iBegin, jint iN
+){
+ sqlite3_context * pCx = PtrGet_sqlite3_context(jpCtx);
+ int rc;
+ S3JniNioArgs args;
+ if( !pCx ){
+ return;
+ }else if( !SJG.g.cByteBuffer ){
+ sqlite3_result_error(
+ pCx, "This JVM does not support JNI access to ByteBuffers.", -1
+ );
+ return;
+ }
+ rc = s3jni_setup_nio_args(env, &args, jBuffer, iBegin, iN);
+ if(rc){
+ if( iBegin<0 ){
+ sqlite3_result_error(pCx, "Start index may not be negative.", -1);
+ }else if( SQLITE_TOOBIG==rc ){
+ sqlite3_result_error_toobig(pCx);
+ }else{
+ sqlite3_result_error(
+ pCx, "Invalid arguments to sqlite3_result_nio_buffer().", -1
+ );
+ }
+ }else if( !args.pStart || !args.iOutLen ){
+ sqlite3_result_null(pCx);
+ }else{
+ sqlite3_result_blob(pCx, args.pStart, args.iOutLen, SQLITE_TRANSIENT);
+ }
+}
+
+
S3JniApi(sqlite3_result_null(),void,1result_1null)(
JniArgsEnvClass, jobject jpCx
){
JNIEXPORT void JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1result_1java_1object
(JNIEnv *, jclass, jobject, jobject);
+/*
+ * Class: org_sqlite_jni_capi_CApi
+ * Method: sqlite3_result_nio_buffer
+ * Signature: (Lorg/sqlite/jni/capi/sqlite3_context;Ljava/nio/ByteBuffer;II)V
+ */
+JNIEXPORT void JNICALL Java_org_sqlite_jni_capi_CApi_sqlite3_1result_1nio_1buffer
+ (JNIEnv *, jclass, jobject, jobject, jint, jint);
+
/*
* Class: org_sqlite_jni_capi_CApi
* Method: sqlite3_result_null
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.
+ sqlite3_bind_null(): the buffer is null, beginPos is at or 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
+ for sqlite3_bind_blob(). If beginPos is negative or if
+ sqlite3_jni_supports_nio() returns 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.
+ SQLITE_LIMIT_LENGTH constraint and SQLITE_TOOBIG is returned if
+ the resulting slice of the buffer exceeds that limit.
This function does not modify the buffer's streaming-related
cursors.
buffers and the only such class offered by Java is (apparently)
ByteBuffer.
+ Design note: there are no sqlite3_column_nio_buffer() and
+ sqlite3_value_nio_buffer() counterparts because the ByteBuffer
+ interface does not enable sensible implementations of those.
+
@see https://docs.oracle.com/javase/8/docs/api/java/nio/Buffer.html
*/
public static native int sqlite3_bind_nio_buffer(
@NotNull sqlite3_context cx, @NotNull Object o
);
+ /**
+ Similar to sqlite3_bind_nio_buffer(), this works like
+ sqlite3_result_blob() but accepts a java.nio.ByteBuffer as its
+ input source. See sqlite3_bind_nio_buffer() for the semantics of
+ the second and subsequent arguments.
+
+ If cx is null then this function will silently fail. If
+ sqlite3_jni_supports_nio() returns false or iBegin is negative,
+ an error result is set. If (begin+n) extends beyond the end of
+ the buffer, it is silently truncated to fit.
+
+ If any of the following apply, this function behaves like
+ sqlite3_result_null(): the blob is null, the resulting slice of
+ the blob is empty.
+
+ If the resulting slice of the buffer exceeds SQLITE_LIMIT_LENGTH
+ then this function behaves like sqlite3_result_error_toobig().
+ */
+ public static native void sqlite3_result_nio_buffer(
+ @NotNull sqlite3_context cx, @Nullable java.nio.ByteBuffer blob,
+ int begin, int n
+ );
+
+ /**
+ Convenience overload which uses the whole input object
+ as the result blob content.
+ */
+ public static void sqlite3_result_nio_buffer(
+ @NotNull sqlite3_context cx, @Nullable java.nio.ByteBuffer blob
+ ){
+ sqlite3_result_nio_buffer(cx, blob, 0, -1);
+ }
+
public static native void sqlite3_result_null(
@NotNull sqlite3_context cx
);
sqlite3_result_blob(cx, blob, (int)(null==blob ? 0 : blob.length));
}
+ /**
+ Convenience overload which behaves like
+ sqlite3_result_nio_buffer().
+ */
+ public static void sqlite3_result_blob(
+ @NotNull sqlite3_context cx, @Nullable java.nio.ByteBuffer blob,
+ int begin, int n
+ ){
+ sqlite3_result_nio_buffer(cx, blob, begin, n);
+ }
+
+ /**
+ Convenience overload which behaves like the two-argument overload of
+ sqlite3_result_nio_buffer().
+ */
+ public static void sqlite3_result_blob(
+ @NotNull sqlite3_context cx, @Nullable java.nio.ByteBuffer blob
+ ){
+ sqlite3_result_nio_buffer(cx, blob);
+ }
+
/**
Binds the given text using C's sqlite3_result_blob64() unless:
@interface SingleThreadOnly{}
/**
- Annotation for Tester1 tests which must only be run if JNI-level support for
- java.nio.Buffer is available.
+ Annotation for Tester1 tests which must only be run if
+ sqlite3_jni_supports_nio() is true.
*/
@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
@java.lang.annotation.Target({java.lang.annotation.ElementType.METHOD})
-@interface RequiresNioBuffer{}
+@interface RequiresJniNio{}
public class Tester1 implements Runnable {
//! True when running in multi-threaded mode.
sqlite3_close_v2(db);
}
- @RequiresNioBuffer
+ @RequiresJniNio
private void testBindByteBuffer(){
+ /* TODO: these tests need to be much more extensive to check the
+ begin/end range handling. */
+
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);
+
+ final 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);
+ .put((byte)0x34)
+ .put((byte)0x35)/*we'll skip this one too*/;
+
+ final int expectTotal = buf.get(1) + buf.get(2) + buf.get(3);
+ sqlite3_stmt stmt = prepare(db, "INSERT INTO t(a) VALUES(?);");
+ affirm( SQLITE_MISUSE == sqlite3_bind_blob(stmt, 1, buf, -1, 0) );
+ affirm( 0 == sqlite3_bind_blob(stmt, 1, buf, 1, 3) );
+ affirm( SQLITE_DONE == sqlite3_step(stmt) );
sqlite3_finalize(stmt);
- stmt = prepare(db, "SELECT a FROM t ORDER BY a DESC;");
- int n = 0;
+ stmt = prepare(db, "SELECT a FROM t;");
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;
+ affirm( 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;
}
+ affirm( SQLITE_DONE == sqlite3_step(stmt) );
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. */
+ affirm(total == expectTotal);
+
+ SQLFunction func =
+ new ScalarFunction(){
+ public void xFunc(sqlite3_context cx, sqlite3_value[] args){
+ sqlite3_result_blob(cx, buf, 1, 3);
+ }
+ };
+
+ affirm( 0 == sqlite3_create_function(db, "myfunc", -1, SQLITE_UTF8, func) );
+ stmt = prepare(db, "SELECT myfunc()");
+ affirm( SQLITE_ROW == sqlite3_step(stmt) );
+ blob = sqlite3_column_blob(stmt, 0);
+ affirm(3 == blob.length);
+ i = 0;
+ total = 0;
+ for(byte b : blob){
+ affirm( i<=3 );
+ affirm(b == buf.get(1 + i++));
+ total += b;
+ }
+ affirm( SQLITE_DONE == sqlite3_step(stmt) );
+ sqlite3_finalize(stmt);
+ affirm(total == expectTotal);
+
sqlite3_close_v2(db);
}
if( forceFail ){
testMethods.add(m);
}
- }else if( m.isAnnotationPresent( RequiresNioBuffer.class )
+ }else if( m.isAnnotationPresent( RequiresJniNio.class )
&& !sqlite3_jni_supports_nio() ){
- outln("Skipping test for lack JNI nio.Buffer support: ",name,"()\n");
+ outln("Skipping test for lack of JNI java.nio.ByteBuffer support: ",
+ name,"()\n");
++nSkipped;
}else if( !m.isAnnotationPresent( ManualTest.class ) ){
if( nThread>1 && m.isAnnotationPresent( SingleThreadOnly.class ) ){
-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
+C JNI:\sadd\ssqlite3_result_nio_buffer()\sand\stests.\sDiscover\sthat\swe\scannot\screate\ssensible\ssqlite3_column_nio_buffer()\sor\ssqlite3_value_nio_buffer()\scounterparts\sbecause\sof\sByteBuffer\sinterface\slimitations.
+D 2023-11-13T18:35:37.387
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 a04d0d77e7391a69f7f8ca4b38e24de59b3a8f61610f2e91698c190c07283850
-F ext/jni/src/c/sqlite3-jni.h 2848299f845d36b4b6123d360e7a4eb960d040637a10158079af49f4ded16453
+F ext/jni/src/c/sqlite3-jni.c 56ddb35bbae71123bedd003ae2bf42adae7d683717bb15ac3ed51d16e2b6c3ff
+F ext/jni/src/c/sqlite3-jni.h 1f28a6a2beec13a49efdb42804926d2139f6a786558ea52c121ddf0bf39af59a
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 5ef54290c17dca46d7f24001ac3b689559e1b37ee40d06b88fa5315d64863789
+F ext/jni/src/org/sqlite/jni/capi/CApi.java 1517e1f5fc53001b2cb7229a427d33747163f8f37348368fe7c3e14edbb9c512
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 4ec21172917f641787767443f418854329bf9b9779807b644e000dac1ec77013
+F ext/jni/src/org/sqlite/jni/capi/Tester1.java 4d460ccce50e7ef4ce8e1ab5d46ceca70834a798d8a2233c93f4bede9deb6683
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 0832f9a8e9f574b157c791c5cddc73aff7b2ff403509f5d78f310494d4a7f93d
-R 7292f27854539ba720bc8a8fcd0c273f
+P b10ce1ef82d84726fbf6a8f624d6530f84fefb505f7868b4a0ea910fed7a877f
+R 2f05bfa531dff8515ddabab7cc0cd5f1
U stephan
-Z 1881d5bf6b59348a7807616e897efc0f
+Z acc87a8d47162ab6fa5eb71b4d2c3712
# Remove this line to create a well-formed Fossil manifest.
-b10ce1ef82d84726fbf6a8f624d6530f84fefb505f7868b4a0ea910fed7a877f
\ No newline at end of file
+44b4df01ff86841fb85b6295cbada422c6ba8a32a420a2e840e2d607b6c90164
\ No newline at end of file