Skip to content

Commit

Permalink
Expose module schema string allocation.
Browse files Browse the repository at this point in the history
  • Loading branch information
shoestringresearch committed May 23, 2022
1 parent 7f4084b commit 29a38da
Show file tree
Hide file tree
Showing 2 changed files with 30 additions and 26 deletions.
11 changes: 9 additions & 2 deletions src/examples/ArrayAsyncModule.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export class ArrayAsyncModule extends ArrayModule {
* @param {*} appData Application data passed to `SQLiteAPI.create_module`.
* @param {Array<string>} argv
* @param {number} pVTab
* @param {{ set: function(string): void}} pzString
* @param {{ allocate: function(number): number, set: function(string): void }} pzString
* @returns {number|Promise<number>}
*/
xCreate(db, appData, argv, pVTab, pzString) {
Expand All @@ -20,9 +20,16 @@ export class ArrayAsyncModule extends ArrayModule {
* @param {*} appData Application data passed to `SQLiteAPI.create_module`.
* @param {Array<string>} argv
* @param {number} pVTab
* @param {{ set: function(string): void}} pzString
* @param {{ allocate: function(number): number, set: function(string): void }} pzString
*/
xConnect(db, appData, argv, pVTab, pzString) {
// Allocate space for CREATE TABLE string. SQLite functions cannot be
// called within handleAsync, so this allocation must be outside the
// asynchronous call. It must be larger than the UTF8 representation
// of the string passed to pzString.set() or undefined behavior will
// result.
pzString.allocate(1024);

return this.handleAsync(async () => {
return super.xConnect(db, appData, argv, pVTab, pzString);
});
Expand Down
45 changes: 21 additions & 24 deletions src/libmodule.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,35 +12,32 @@ const mod_methods = {
const closedVTabs = hasAsyncify ? new Set() : null;
const closedCursors = hasAsyncify ? new Set() : null;

// Ideally this would be a static constant of the Value class, but
// the Emscripten optimizer doesn't like that.
const MAX_VALUE_STRING_BYTES = 2 ** 17;

class Value {
constructor(ptr, type) {
this.ptr = ptr;
this.type = type;

if (type === 's' && getValue(ptr, 'i32') === 0) {
// Preallocate string buffer for virtual table declaration.
// This preallocation is necessary for asynchronous xCreate/xConnect.
const p = ccall('sqlite3_malloc', 'number', ['number'], [MAX_VALUE_STRING_BYTES]);
setValue(this.ptr, p, 'i32');
}
}

set(v) {
switch (this.type) {
case 's':
const length = lengthBytesUTF8(v);
if (length + 1 > MAX_VALUE_STRING_BYTES) throw new Error('string overflow');
const p = getValue(this.ptr, 'i32');
stringToUTF8(v, p, MAX_VALUE_STRING_BYTES);
break;
default:
setValue(this.ptr, v, this.type);
break;
}
}
Value.prototype['allocate'] = function(size) {
// Preallocate string buffer for virtual table declaration.
// This preallocation is necessary for asynchronous xCreate/xConnect.
let p = getValue(this.ptr, 'i32');
if (!p) {
p = ccall('sqlite3_malloc', 'number', ['number'], [size]);
setValue(this.ptr, p, 'i32');
}
return p;
}
Value.prototype['set'] = function(v) {
switch (this.type) {
case 's':
const length = lengthBytesUTF8(v);
const p = this['allocate'](length + 1);
stringToUTF8(v, p, length + 1);
break;
default:
setValue(this.ptr, v, this.type);
break;
}
}

Expand Down

0 comments on commit 29a38da

Please sign in to comment.