wasm: eliminated the dependency on the deprecated emcc-provided allocate() function. Adjacent cleanups in blob binding.

FossilOrigin-Name: 140618b212e3aa9ff2df20f22af846a1c4c5ffaeefd84330446f61362b39a8f1
This commit is contained in:
stephan 2022-06-25 06:46:22 +00:00
parent 766ba7917e
commit f170b0868d
5 changed files with 136 additions and 62 deletions

View File

@ -1,12 +1,11 @@
ALLOC_NORMAL
FS
UTF8ToString
addFunction
allocate
allocateUTF8OnStack
ccall
cwrap
getValue
intArrayFromString
lengthBytesUTF8
removeFunction
setValue
stackAlloc

View File

@ -122,6 +122,27 @@ Module.postRun.push(function(namespace/*the module object, the target for
const SQM = namespace/*the sqlite module object */;
/** Throws a new Error, the message of which is the concatenation
all args with a space between each. */
const toss = function(){
throw new Error(Array.prototype.join.call(arguments, ' '));
};
/**
Returns true if v appears to be one of our supported TypedArray types:
Uint8Array or Int8Array.
*/
const isSupportedTypedArray = function(v){
return v && (undefined!==v.byteLength) && (v.byteLength === v.length);
};
/** Returns true if isSupportedTypedArray(v) does, else throws with a message
that v is not a supported TypedArray value. */
const affirmSupportedTypedArray = function(v){
return isSupportedTypedArray(v)
|| toss("Value is not of a supported TypedArray type.");
};
/**
Set up the main sqlite3 binding API here, mimicking the C API as
closely as we can.
@ -181,15 +202,38 @@ Module.postRun.push(function(namespace/*the module object, the target for
*/
wasm: {
/**
Proxy for SQM.allocate(). TODO: remove deprecated allocate(),
use _malloc(). The kicker is that allocate() uses
module-init-internal state which isn't normally visible to
us.
api.wasm._malloc()'s srcTypedArray.byteLength bytes,
populates them with the values from the source array,
and returns the pointer to that memory. The pointer
must eventually be passed to api.wasm._free() to clean
it up.
As a special case, to avoid further special cases where
this is used, if srcTypedArray.byteLength is 0, it
allocates a single byte and sets it to the value 0.
ACHTUNG: this currently only works for Uint8Array and
Int8Array types.
*/
allocate: (slab, allocator=SQM.ALLOC_NORMAL)=>SQM.allocate(slab, allocator),
mallocFromTypedArray: function(srcTypedArray){
affirmSupportedTypedArray(srcTypedArray);
const pRet = api.wasm._malloc(srcTypedArray.byteLength || 1);
if(srcTypedArray.byteLength){
api.wasm._malloc.HEAP.set(srcTypedArray, pRet);
/* That unfortunately does not behave intuitively
when copying, e.g., the contents of a
Uint16Array, copying only 1 byte of each
entry instead of blitting the whole array
contents over the destination array. A potential TODO
is handle that copying here so that we can support a wider
array (haha) of bindable-as-blob types. */
}
else api.wasm._malloc.HEAP[pRet] = 0;
return pRet;
},
/**
The buffer which holds the heap memory managed by the
emscripten-installed allocator.
The TypedArray buffer which holds the heap memory
managed by the emscripten-installed _malloc().
*/
HEAP8: SQM.HEAP8
}
@ -360,12 +404,20 @@ Module.postRun.push(function(namespace/*the module object, the target for
/** Populate api.wasm with several members of the module object... */
['getValue','setValue', 'stackSave', 'stackRestore', 'stackAlloc',
'allocateUTF8OnStack', '_malloc', '_free',
'addFunction', 'removeFunction'
'addFunction', 'removeFunction',
'intArrayFromString'
].forEach(function(m){
if(undefined === (api.wasm[m] = SQM[m])){
toss("Internal init error: Module."+m+" not found.");
}
});
/**
The array object which holds the raw bytes managed by the
_malloc() binding. Side note: why on earth _malloc() manages
HEAP8 (an Int8Array), rather than HEAPU8 (a Uint8Array), is a
mystery.
*/
api.wasm._malloc.HEAP = api.wasm.HEAP8;
/* What follows is colloquially known as "OO API #1". It is a
binding of the sqlite3 API which is designed to be run within
@ -374,12 +426,6 @@ Module.postRun.push(function(namespace/*the module object, the target for
the sqlite3 binding if, e.g., the wrapper is in the main thread
and the sqlite3 API is in a worker. */
/** Throws a new error, concatenating all args with a space between
each. */
const toss = function(){
throw new Error(Array.prototype.join.call(arguments, ' '));
};
/**
The DB class wraps a sqlite3 db handle.
@ -954,10 +1000,9 @@ Module.postRun.push(function(namespace/*the module object, the target for
if(null===val) {
api.sqlite3_result_null(pCx);
break;
}else if(undefined!==val.length){
const pBlob =
api.wasm.allocate(val);
api.sqlite3_result_blob(pCx, pBlob, val.length,
}else if(isSupportedTypedArray(val)){
const pBlob = api.wasm.mallocFromTypedArray(val);
api.sqlite3_result_blob(pCx, pBlob, val.byteLength,
api.SQLITE_TRANSIENT);
api.wasm._free(pBlob);
break;
@ -1068,8 +1113,8 @@ Module.postRun.push(function(namespace/*the module object, the target for
case BindTypes.string:
return t;
default:
if(v instanceof Uint8Array) return BindTypes.blob;
return undefined;
//console.log("isSupportedBindType",t,v);
return isSupportedTypedArray(v) ? BindTypes.blob : undefined;
}
};
@ -1078,7 +1123,8 @@ Module.postRun.push(function(namespace/*the module object, the target for
function returns that value, else it throws.
*/
const affirmSupportedBindType = function(v){
return isSupportedBindType(v) || toss("Unsupport bind() argument type.");
//console.log('affirmSupportedBindType',v);
return isSupportedBindType(v) || toss("Unsupported bind() argument type:",typeof v);
};
/**
@ -1140,11 +1186,12 @@ Module.postRun.push(function(namespace/*the module object, the target for
if(!f._){
f._ = {
string: function(stmt, ndx, val, asBlob){
const bytes = intArrayFromString(val,true);
const pStr = api.wasm.allocate(bytes);
const bytes = api.wasm.intArrayFromString(val,true);
const pStr = api.wasm._malloc(bytes.length || 1);
stmt._allocs.push(pStr);
api.wasm._malloc.HEAP.set(bytes, pStr);
const func = asBlob ? api.sqlite3_bind_blob : api.sqlite3_bind_text;
return func(stmt._pStmt, ndx, pStr, bytes.length, 0);
return func(stmt._pStmt, ndx, pStr, bytes.length, api.SQLITE_STATIC);
}
};
}
@ -1175,17 +1222,21 @@ Module.postRun.push(function(namespace/*the module object, the target for
if('string'===typeof val){
rc = f._.string(stmt, ndx, val, true);
}else{
const len = val.length;
if(undefined===len){
if(!isSupportedTypedArray(val)){
toss("Binding a value as a blob requires",
"that it have a length member.");
"that it be a string, Uint8Array, or Int8Array.");
}
const pBlob = api.wasm.allocate(val);
//console.debug("Binding blob",len,val);
const pBlob = api.wasm.mallocFromTypedArray(val);
stmt._allocs.push(pBlob);
rc = api.sqlite3_bind_blob(stmt._pStmt, ndx, pBlob, len, 0);
rc = api.sqlite3_bind_blob(stmt._pStmt, ndx, pBlob, val.byteLength,
api.SQLITE_STATIC);
}
break;
}
default: toss("Unsupported bind() argument type.");
default:
console.warn("Unsupported bind() argument type:",val);
toss("Unsupported bind() argument type.");
}
if(rc) stmt.db.checkRc(rc);
return stmt;
@ -1286,7 +1337,9 @@ Module.postRun.push(function(namespace/*the module object, the target for
- Strings are bound as strings (use bindAsBlob() to force
blob binding).
- Uint8Array instances are bound as blobs.
- Uint8Array and Int8Array instances are bound as blobs.
(TODO: support binding other TypedArray types with larger
int sizes.)
If passed an array, each element of the array is bound at
the parameter index equal to the array index plus 1
@ -1297,7 +1350,7 @@ Module.postRun.push(function(namespace/*the module object, the target for
bindable parameter names, including any `$`, `@`, or `:`
prefix. Because `$` is a legal identifier chararacter in
JavaScript, that is the suggested prefix for bindable
parameters.
parameters: `stmt.bind({$a: 1, $b: 2})`.
It returns this object on success and throws on
error. Errors include:
@ -1342,8 +1395,9 @@ Module.postRun.push(function(namespace/*the module object, the target for
arg.forEach((v,i)=>bindOne(this, i+1, affirmSupportedBindType(v), v));
return this;
}
else if('object'===typeof arg/*null was checked above*/){
/* bind by name */
else if('object'===typeof arg/*null was checked above*/
&& !isSupportedTypedArray(arg)){
/* Treat each property of arg as a named bound parameter. */
if(1!==arguments.length){
toss("When binding an object, an index argument is not permitted.");
}
@ -1353,25 +1407,25 @@ Module.postRun.push(function(namespace/*the module object, the target for
arg[k]));
return this;
}else{
return bindOne(this, ndx,
affirmSupportedBindType(arg), arg);
return bindOne(this, ndx, affirmSupportedBindType(arg), arg);
}
toss("Should not reach this point.");
},
/**
Special case of bind() which binds the given value
using the BLOB binding mechanism instead of the default
selected one for the value. The ndx may be a numbered
or named bind index. The value must be of type string,
Uint8Array, or null/undefined (both treated as null).
Special case of bind() which binds the given value using
the BLOB binding mechanism instead of the default selected
one for the value. The ndx may be a numbered or named bind
index. The value must be of type string, null/undefined
(both treated as null), or a TypedArray of a type supported
by the bind() API.
If passed a single argument, a bind index of 1 is assumed.
*/
bindAsBlob: function(ndx,arg){
affirmStmtOpen(this);
if(1===arguments.length){
arg = ndx;
ndx = 1;
arg = arguments[0];
}
const t = affirmSupportedBindType(arg);
if(BindTypes.string !== t && BindTypes.blob !== t

View File

@ -77,6 +77,12 @@
});
T.assert(2 === list.length);
//log("Exec'd SQL:", list);
let blob = db.selectValue("select b from t where a='blob'");
T.assert(blob instanceof Uint8Array).
assert(0x68===blob[0] && 0x69===blob[1]);
blob = null;
let counter = 0, colNames = [];
list.length = 0;
db.exec(new TextEncoder('utf-8').encode("SELECT a a, b b FROM t"),{
@ -121,9 +127,7 @@
}
}).createFunction({
name: "asis",
callback: function(arg){
return arg;
}
callback: (arg)=>arg
});
log("Testing DB::selectValue() w/ UDF...");
@ -132,11 +136,7 @@
assert(3===db.selectValue("select bar(1,2)")).
assert(-1===db.selectValue("select bar(1,2,-4)")).
assert('hi'===db.selectValue("select asis('hi')"));
let blob = db.selectValue("select asis(X'6869')");
T.assert(blob instanceof Uint8Array).
assert(2 === blob.length).
assert(0x68==blob[0] && 0x69==blob[1]);
const eqApprox = function(v1,v2,factor=0.05){
//log('eqApprox',v1, v2);
return v1>=(v2-factor) && v1<=(v2+factor);
@ -150,6 +150,27 @@
assert(eqApprox(3.1,db.selectValue("select 3.0 + 0.1"))).
assert(eqApprox(1.3,db.selectValue("select asis(1 + 0.3)")))
;
log("Testing binding and UDF propagation of blobs...");
let blobArg = new Uint8Array(2);
blobArg.set([0x68, 0x69], 0);
let blobRc = db.selectValue("select asis(?1)", blobArg);
T.assert(blobRc instanceof Uint8Array).
assert(2 === blobRc.length).
assert(0x68==blobRc[0] && 0x69==blobRc[1]);
blobRc = db.selectValue("select asis(X'6869')");
T.assert(blobRc instanceof Uint8Array).
assert(2 === blobRc.length).
assert(0x68==blobRc[0] && 0x69==blobRc[1]);
blobArg = new Int8Array(2);
blobArg.set([0x68, 0x69]);
console.debug("blobArg=",blobArg);
blobRc = db.selectValue("select asis(?1)", blobArg);
T.assert(blobRc instanceof Uint8Array).
assert(2 === blobRc.length);
console.debug("blobRc=",blobRc);
T.assert(0x68==blobRc[0] && 0x69==blobRc[1]);
};
const testAttach = function(db){

View File

@ -1,5 +1,5 @@
C wasm:\scorrected\sthe\spropagation\sof\stext/blob\svalues\svia\sUDFs.\sDB.exec()'s\ssql\smay\snow\sbe\san\sarray\sof\sstrings\swhich\sget\sconcatenated\stogether\sbefore\spassing\sit\son\sto\ssqlite3_prepare_v2().\sDB.exec()'s\scallback\snow\sapplies\sto\sthe\sfirst\sstatement\swhich\shas\sresult\scolumns\sinstead\sof\sonly\sthe\sfirst\sstatement.\sFixed\sa\sprecedence\sbut\swhich\scaused\sisInt32()\sto\sreport\sfalse\spositives.
D 2022-06-25T03:53:43.503
C wasm:\seliminated\sthe\sdependency\son\sthe\sdeprecated\semcc-provided\sallocate()\sfunction.\sAdjacent\scleanups\sin\sblob\sbinding.
D 2022-06-25T06:46:22.284
F .fossil-settings/empty-dirs dbb81e8fc0401ac46a1491ab34a7f2c7c0452f2f06b54ebb845d024ca8283ef1
F .fossil-settings/ignore-glob 35175cdfcf539b2318cb04a9901442804be81cd677d8b889fcc9149c21f239ea
F LICENSE.md df5091916dbb40e6e9686186587125e1b2ff51f022cc334e886c19a0e9982724
@ -57,7 +57,7 @@ F ext/expert/sqlite3expert.h ca81efc2679a92373a13a3e76a6138d0310e32be53d6c3bfaed
F ext/expert/test_expert.c d56c194b769bdc90cf829a14c9ecbc1edca9c850b837a4d0b13be14095c32a72
F ext/fiddle/EXPORTED_FUNCTIONS.fiddle 7fb73f7150ab79d83bb45a67d257553c905c78cd3d693101699243f36c5ae6c3
F ext/fiddle/EXPORTED_FUNCTIONS.sqlite3-api 540b9dec63a3a62a256e2f030827848a92e9b9d9b6fa5c0188295a4a1c5382cd
F ext/fiddle/EXPORTED_RUNTIME_METHODS b831017ba67ba993b34a27400cef2f6095bd6789c0fc4eba7e7a251c207be31c
F ext/fiddle/EXPORTED_RUNTIME_METHODS e499bbb5201bf671850da8dcd47de31b0db47c1183f0c669016801b4d2035534
F ext/fiddle/Makefile e25d34a0e1324f771d64c09c592601b97219282011587e6ce410fa8acdedb913
F ext/fiddle/SqliteTestUtil.js 559731c3e8e0de330ec7d292e6c1846566408caee6637acc8a119ac338a8781c
F ext/fiddle/emscripten.css 3d253a6fdb8983a2ac983855bfbdd4b6fa1ff267c28d69513dd6ef1f289ada3f
@ -65,11 +65,11 @@ F ext/fiddle/fiddle-worker.js 88bc2193a6cb6a3f04d8911bed50a4401fe6f277de7a71ba83
F ext/fiddle/fiddle.html 550c5aafce40bd218de9bf26192749f69f9b10bc379423ecd2e162bcef885c08
F ext/fiddle/fiddle.js 812f9954cc7c4b191884ad171f36fcf2d0112d0a7ecfdf6087896833a0c079a8
F ext/fiddle/index.md d9c1c308d8074341bc3b11d1d39073cd77754cb3ca9aeb949f23fdd8323d81cf
F ext/fiddle/sqlite3-api.js b706be1f777a4508f9e3e237301e11c108933ae651b3fa599bf047cac34b5bdb
F ext/fiddle/sqlite3-api.js 706bb55b3901a90ec3a0e588f00d4c260d61d5b5ac916b894f48cdd6203ff0ba
F ext/fiddle/sqlite3-worker.js a9c2b614beca187dbdd8c053ec2770cc61ec1ac9c0ec6398ceb49a79f705a421
F ext/fiddle/testing.css 750572dded671d2cf142bbcb27af5542522ac08db128245d0b9fe410aa1d7f2a
F ext/fiddle/testing1.html ea1f3be727f78e420007f823912c1a03b337ecbb8e79449abc2244ad4fe15d9a
F ext/fiddle/testing1.js 3e5f1fb22764ec735396db518b85ea6b3070f6c2da479cba69c8d8e89f8299f8
F ext/fiddle/testing1.js f3a5bd125154a51dfe20b699cb90c7cf2a23fbb5eaf9129cd4fb06e262de2647
F ext/fiddle/testing2.html 9063b2430ade2fe9da4e711addd1b51a2741cf0c7ebf6926472a5e5dd63c0bc4
F ext/fiddle/testing2.js 7b45b4e7fddbd51dbaf89b6722c02758051b34bac5a98c11b569a7e7572f88ee
F ext/fts1/README.txt 20ac73b006a70bcfd80069bdaf59214b6cf1db5e
@ -1978,8 +1978,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 42dc500819bfc1308a9542aa2cae4f6dfd98a29237c59cec82e0e6f9e0bf3779
R a152bf70e7d16ac14f437dceeb13aaae
P 37a8fecb56793fbd3763a2240a0bd62b639c811934d3af2ef0e5ff579073d632
R 4c023b8516cd5b1336a52ada20a79c7d
U stephan
Z 385f37f8ee9972f16da6e6bace96df52
Z 05e90a0a470a56e05a8109a17f4f47da
# Remove this line to create a well-formed Fossil manifest.

View File

@ -1 +1 @@
37a8fecb56793fbd3763a2240a0bd62b639c811934d3af2ef0e5ff579073d632
140618b212e3aa9ff2df20f22af846a1c4c5ffaeefd84330446f61362b39a8f1