@ohos-ports/lmdb 2.5.3 → 2.8.5-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +20 -20
- package/README.md +72 -28
- package/SECURITY.md +12 -12
- package/bin/download-prebuilds.js +15 -0
- package/binding.gyp +115 -111
- package/build/Release/lmdb.node +0 -0
- package/caching.js +212 -156
- package/dependencies/lmdb/libraries/liblmdb/COPYRIGHT +20 -20
- package/dependencies/lmdb/libraries/liblmdb/Doxyfile +1631 -1631
- package/dependencies/lmdb/libraries/liblmdb/LICENSE +47 -47
- package/dependencies/lmdb/libraries/liblmdb/Makefile +136 -136
- package/dependencies/lmdb/libraries/liblmdb/chacha8.c +183 -183
- package/dependencies/lmdb/libraries/liblmdb/chacha8.h +14 -14
- package/dependencies/lmdb/libraries/liblmdb/crypto.c +121 -121
- package/dependencies/lmdb/libraries/liblmdb/intro.doc +192 -192
- package/dependencies/lmdb/libraries/liblmdb/lmdb.h +28 -3
- package/dependencies/lmdb/libraries/liblmdb/mdb.c +157 -31
- package/dependencies/lmdb/libraries/liblmdb/mdb_copy.1 +74 -74
- package/dependencies/lmdb/libraries/liblmdb/mdb_copy.c +106 -106
- package/dependencies/lmdb/libraries/liblmdb/mdb_drop.1 +53 -53
- package/dependencies/lmdb/libraries/liblmdb/mdb_drop.c +154 -154
- package/dependencies/lmdb/libraries/liblmdb/mdb_dump.1 +94 -94
- package/dependencies/lmdb/libraries/liblmdb/mdb_dump.c +333 -333
- package/dependencies/lmdb/libraries/liblmdb/mdb_load.1 +97 -97
- package/dependencies/lmdb/libraries/liblmdb/mdb_load.c +530 -530
- package/dependencies/lmdb/libraries/liblmdb/mdb_stat.1 +83 -83
- package/dependencies/lmdb/libraries/liblmdb/mdb_stat.c +276 -276
- package/dependencies/lmdb/libraries/liblmdb/midl-sparse.c +452 -0
- package/dependencies/lmdb/libraries/liblmdb/midl.c +452 -452
- package/dependencies/lmdb/libraries/liblmdb/midl.h +208 -208
- package/dependencies/lmdb/libraries/liblmdb/module.c +101 -101
- package/dependencies/lmdb/libraries/liblmdb/module.h +16 -16
- package/dependencies/lmdb/libraries/liblmdb/mtest.c +178 -178
- package/dependencies/lmdb/libraries/liblmdb/mtest2.c +124 -124
- package/dependencies/lmdb/libraries/liblmdb/mtest3.c +133 -133
- package/dependencies/lmdb/libraries/liblmdb/mtest4.c +168 -168
- package/dependencies/lmdb/libraries/liblmdb/mtest5.c +135 -135
- package/dependencies/lmdb/libraries/liblmdb/mtest6.c +141 -141
- package/dependencies/lmdb/libraries/liblmdb/mtest_enc.c +190 -190
- package/dependencies/lmdb/libraries/liblmdb/mtest_enc2.c +189 -189
- package/dependencies/lmdb/libraries/liblmdb/mtest_remap.c +177 -177
- package/dependencies/lmdb/libraries/liblmdb/sample-bdb.txt +73 -73
- package/dependencies/lmdb/libraries/liblmdb/sample-mdb.txt +62 -62
- package/dependencies/lmdb/libraries/liblmdb/tooltag +27 -27
- package/dependencies/lmdb-data-v1/libraries/liblmdb/CHANGES +266 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/COPYRIGHT +20 -20
- package/dependencies/lmdb-data-v1/libraries/liblmdb/Doxyfile +1631 -1631
- package/dependencies/lmdb-data-v1/libraries/liblmdb/LICENSE +47 -47
- package/dependencies/lmdb-data-v1/libraries/liblmdb/intro.doc +192 -192
- package/dependencies/lmdb-data-v1/libraries/liblmdb/lmdb.h +1608 -1653
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb.c +10320 -11359
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_copy.1 +55 -61
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_copy.c +82 -84
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_dump.1 +75 -81
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_dump.c +319 -319
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_load.1 +84 -84
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_load.c +496 -492
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_stat.1 +64 -70
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_stat.c +263 -264
- package/dependencies/lmdb-data-v1/libraries/liblmdb/midl.c +359 -421
- package/dependencies/lmdb-data-v1/libraries/liblmdb/midl.h +186 -200
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest.c +177 -177
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest2.c +124 -124
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest3.c +133 -133
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest4.c +168 -168
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest5.c +135 -135
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest6.c +141 -141
- package/dependencies/lmdb-data-v1/libraries/liblmdb/sample-bdb.txt +73 -73
- package/dependencies/lmdb-data-v1/libraries/liblmdb/sample-mdb.txt +62 -62
- package/dependencies/lmdb-data-v1/libraries/liblmdb/tooltag +22 -27
- package/dependencies/lz4/LICENSE +11 -11
- package/dependencies/lz4/lib/LICENSE +24 -24
- package/dependencies/lz4/lib/Makefile +225 -0
- package/dependencies/lz4/lib/README.md +169 -137
- package/dependencies/lz4/lib/dll/example/Makefile +63 -0
- package/dependencies/lz4/lib/dll/example/README.md +69 -69
- package/dependencies/lz4/lib/dll/liblz4.def +62 -62
- package/dependencies/lz4/lib/liblz4-dll.rc.in +35 -35
- package/dependencies/lz4/lib/liblz4.pc.in +14 -14
- package/dependencies/lz4/lib/lz4.c +2722 -2495
- package/dependencies/lz4/lib/lz4.h +842 -774
- package/dependencies/lz4/lib/lz4file.c +311 -0
- package/dependencies/lz4/lib/lz4file.h +93 -0
- package/dependencies/lz4/lib/lz4frame.c +2078 -1899
- package/dependencies/lz4/lib/lz4frame.h +692 -623
- package/dependencies/lz4/lib/lz4frame_static.h +47 -47
- package/dependencies/lz4/lib/lz4hc.c +1631 -1615
- package/dependencies/lz4/lib/lz4hc.h +413 -413
- package/dependencies/lz4/lib/xxhash.c +1030 -1030
- package/dependencies/lz4/lib/xxhash.h +328 -328
- package/dependencies/v8/v8-fast-api-calls-v16.h +790 -790
- package/dependencies/v8/v8-fast-api-calls.h +784 -888
- package/dist/index.cjs +1050 -584
- package/dist/index.cjs.map +1 -1
- package/index.d.cts +420 -0
- package/index.d.ts +450 -385
- package/index.js +38 -37
- package/keys.js +116 -102
- package/level.js +26 -26
- package/native.js +8 -5
- package/node-index.js +21 -17
- package/open.js +51 -17
- package/package.json +63 -21
- package/read.js +338 -95
- package/src/compression.cpp +12 -9
- package/src/cursor.cpp +82 -31
- package/src/dbi.cpp +78 -40
- package/src/env.cpp +336 -68
- package/src/lmdb-js.cpp +64 -64
- package/src/lmdb-js.h +83 -49
- package/src/misc.cpp +297 -28
- package/src/ordered-binary.cpp +46 -46
- package/src/txn.cpp +210 -257
- package/src/v8-functions.cpp +170 -166
- package/src/windows.c +34 -34
- package/src/writer.cpp +169 -84
- package/util/RangeIterable.js +112 -17
- package/util/set-optional-deps.cjs +14 -10
- package/util/when.js +8 -8
- package/write.js +145 -121
- package/dependencies/lmdb-data-v1/libraries/liblmdb/Makefile +0 -118
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_drop.1 +0 -40
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_drop.c +0 -135
- package/rollup.config.js +0 -12
package/dist/index.cjs
CHANGED
|
@@ -6,7 +6,6 @@ var module$1 = require('module');
|
|
|
6
6
|
var pathModule = require('path');
|
|
7
7
|
var url = require('url');
|
|
8
8
|
var loadNAPI = require('node-gyp-build-optional-packages');
|
|
9
|
-
var v8 = require('v8');
|
|
10
9
|
var events = require('events');
|
|
11
10
|
var os$1 = require('os');
|
|
12
11
|
var fs$1 = require('fs');
|
|
@@ -39,14 +38,12 @@ var loadNAPI__default = /*#__PURE__*/_interopDefaultLegacy(loadNAPI);
|
|
|
39
38
|
var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs$1);
|
|
40
39
|
var orderedBinary__namespace = /*#__PURE__*/_interopNamespace(orderedBinary$1);
|
|
41
40
|
|
|
42
|
-
let Env, Txn, Dbi, Compression, Cursor, getAddress; exports.clearKeptObjects = void 0; let setGlobalBuffer, arch, fs, os, tmpdir, lmdbError, path, EventEmitter, orderedBinary, MsgpackrEncoder, WeakLRUCache, getByBinary,
|
|
43
|
-
|
|
41
|
+
let Env, Txn, Dbi, Compression, Cursor, getAddress, getBufferAddress; exports.clearKeptObjects = void 0; let globalBuffer, setGlobalBuffer, arch, fs, os, tmpdir, lmdbError, path, EventEmitter, orderedBinary, MsgpackrEncoder, WeakLRUCache, getByBinary, startRead, setReadCallback, write, position, iterate, prefetch, resetTxn, getCurrentValue, getStringByBinary, getSharedBuffer, compress;
|
|
44
42
|
path = pathModule__default["default"];
|
|
45
|
-
let dirName = (typeof
|
|
46
|
-
pathModule.dirname(url.fileURLToPath((typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.tagName.toUpperCase() === 'SCRIPT' && document.currentScript.src || new URL('index.cjs', document.baseURI).href))))).replace(/dist$/, ''); // for node, which doesn't have __dirname in ESM
|
|
43
|
+
let dirName = pathModule.dirname(url.fileURLToPath((typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.src || new URL('index.cjs', document.baseURI).href)))).replace(/dist$/, '');
|
|
47
44
|
let nativeAddon = loadNAPI__default["default"](dirName);
|
|
48
45
|
|
|
49
|
-
if (process.isBun) {
|
|
46
|
+
if (process.isBun && false) {
|
|
50
47
|
const { linkSymbols, FFIType } = require('bun:ffi');
|
|
51
48
|
let lmdbLib = linkSymbols({
|
|
52
49
|
getByBinary: {
|
|
@@ -87,19 +84,24 @@ function setNativeFunctions(externals) {
|
|
|
87
84
|
Dbi = externals.Dbi;
|
|
88
85
|
Compression = externals.Compression;
|
|
89
86
|
getAddress = externals.getAddress;
|
|
87
|
+
getBufferAddress = externals.getBufferAddress;
|
|
90
88
|
externals.createBufferForAddress;
|
|
91
89
|
exports.clearKeptObjects = externals.clearKeptObjects || function() {};
|
|
92
90
|
getByBinary = externals.getByBinary;
|
|
93
|
-
|
|
91
|
+
externals.detachBuffer;
|
|
92
|
+
startRead = externals.startRead;
|
|
93
|
+
setReadCallback = externals.setReadCallback;
|
|
94
94
|
setGlobalBuffer = externals.setGlobalBuffer;
|
|
95
|
+
globalBuffer = externals.globalBuffer;
|
|
96
|
+
getSharedBuffer = externals.getSharedBuffer;
|
|
95
97
|
prefetch = externals.prefetch;
|
|
96
98
|
iterate = externals.iterate;
|
|
97
99
|
position = externals.position;
|
|
98
100
|
resetTxn = externals.resetTxn;
|
|
99
101
|
getCurrentValue = externals.getCurrentValue;
|
|
100
|
-
|
|
102
|
+
externals.getCurrentShared;
|
|
101
103
|
getStringByBinary = externals.getStringByBinary;
|
|
102
|
-
|
|
104
|
+
externals.getSharedByBinary;
|
|
103
105
|
write = externals.write;
|
|
104
106
|
compress = externals.compress;
|
|
105
107
|
Cursor = externals.Cursor;
|
|
@@ -119,36 +121,38 @@ function setExternals(externals) {
|
|
|
119
121
|
externals.onExit;
|
|
120
122
|
}
|
|
121
123
|
|
|
122
|
-
function when(promise, callback, errback) {
|
|
123
|
-
if (promise && promise.then) {
|
|
124
|
-
return errback ?
|
|
125
|
-
promise.then(callback, errback) :
|
|
126
|
-
promise.then(callback);
|
|
127
|
-
}
|
|
128
|
-
return callback(promise);
|
|
124
|
+
function when(promise, callback, errback) {
|
|
125
|
+
if (promise && promise.then) {
|
|
126
|
+
return errback ?
|
|
127
|
+
promise.then(callback, errback) :
|
|
128
|
+
promise.then(callback);
|
|
129
|
+
}
|
|
130
|
+
return callback(promise);
|
|
129
131
|
}
|
|
130
132
|
|
|
131
133
|
var backpressureArray;
|
|
132
134
|
|
|
133
135
|
const WAITING_OPERATION = 0x2000000;
|
|
134
|
-
const BACKPRESSURE_THRESHOLD =
|
|
136
|
+
const BACKPRESSURE_THRESHOLD = 300000;
|
|
135
137
|
const TXN_DELIMITER = 0x8000000;
|
|
136
138
|
const TXN_COMMITTED = 0x10000000;
|
|
137
139
|
const TXN_FLUSHED = 0x20000000;
|
|
138
140
|
const TXN_FAILED = 0x40000000;
|
|
139
141
|
const FAILED_CONDITION = 0x4000000;
|
|
140
|
-
const REUSE_BUFFER_MODE = 512;
|
|
142
|
+
const REUSE_BUFFER_MODE$1 = 512;
|
|
141
143
|
const RESET_BUFFER_MODE = 1024;
|
|
142
144
|
const NO_RESOLVE = 16;
|
|
143
145
|
const HAS_TXN = 8;
|
|
146
|
+
const CONDITIONAL_VERSION_LESS_THAN = 0x800;
|
|
147
|
+
const CONDITIONAL_ALLOW_NOTFOUND = 0x800;
|
|
144
148
|
|
|
145
149
|
const SYNC_PROMISE_SUCCESS = Promise.resolve(true);
|
|
146
150
|
const SYNC_PROMISE_FAIL = Promise.resolve(false);
|
|
147
151
|
SYNC_PROMISE_SUCCESS.isSync = true;
|
|
148
152
|
SYNC_PROMISE_FAIL.isSync = true;
|
|
149
153
|
const PROMISE_SUCCESS = Promise.resolve(true);
|
|
150
|
-
const ABORT =
|
|
151
|
-
const IF_EXISTS = 3.542694326329068e-103;
|
|
154
|
+
const ABORT = 4.452694326329068e-106; // random/unguessable numbers, which work across module/versions and native
|
|
155
|
+
const IF_EXISTS$1 = 3.542694326329068e-103;
|
|
152
156
|
const CALLBACK_THREW = {};
|
|
153
157
|
const LocalSharedArrayBuffer = typeof Deno != 'undefined' ? ArrayBuffer : SharedArrayBuffer; // Deno can't handle SharedArrayBuffer as an FFI argument due to https://github.com/denoland/deno/issues/12678
|
|
154
158
|
const ByteArray = typeof Buffer != 'undefined' ? function(buffer) { return Buffer.from(buffer) } : Uint8Array;
|
|
@@ -159,18 +163,23 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
|
|
|
159
163
|
eventTurnBatching, txnStartThreshold, batchStartThreshold, overlappingSync, commitDelay, separateFlushed, maxFlushDelay }) {
|
|
160
164
|
// stands for write instructions
|
|
161
165
|
var dynamicBytes;
|
|
162
|
-
function allocateInstructionBuffer() {
|
|
166
|
+
function allocateInstructionBuffer(lastPosition) {
|
|
163
167
|
// Must use a shared buffer on older node in order to use Atomics, and it is also more correct since we are
|
|
164
168
|
// indeed accessing and modifying it from another thread (in C). However, Deno can't handle it for
|
|
165
169
|
// FFI so aliased above
|
|
166
170
|
let buffer = new LocalSharedArrayBuffer(WRITE_BUFFER_SIZE);
|
|
171
|
+
let lastBytes = dynamicBytes;
|
|
167
172
|
dynamicBytes = new ByteArray(buffer);
|
|
168
173
|
let uint32 = dynamicBytes.uint32 = new Uint32Array(buffer, 0, WRITE_BUFFER_SIZE >> 2);
|
|
169
|
-
uint32[
|
|
174
|
+
uint32[2] = 0;
|
|
170
175
|
dynamicBytes.float64 = new Float64Array(buffer, 0, WRITE_BUFFER_SIZE >> 3);
|
|
171
|
-
buffer.address =
|
|
176
|
+
buffer.address = getBufferAddress(dynamicBytes);
|
|
172
177
|
uint32.address = buffer.address + uint32.byteOffset;
|
|
173
|
-
dynamicBytes.position =
|
|
178
|
+
dynamicBytes.position = 1; // we start at position 1 to save space for writing the txn id before the txn delimiter
|
|
179
|
+
if (lastPosition) {
|
|
180
|
+
lastBytes.float64[lastPosition + 1] = dynamicBytes.uint32.address + (dynamicBytes.position << 3);
|
|
181
|
+
lastBytes.uint32[lastPosition << 1] = 3; // pointer instruction
|
|
182
|
+
}
|
|
174
183
|
return dynamicBytes;
|
|
175
184
|
}
|
|
176
185
|
var newBufferThreshold = (WRITE_BUFFER_SIZE - maxKeySize - 64) >> 3; // need to reserve more room if we do inline values
|
|
@@ -180,7 +189,7 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
|
|
|
180
189
|
var committed;
|
|
181
190
|
var abortedNonChildTransactionWarn;
|
|
182
191
|
var nextTxnCallbacks = [];
|
|
183
|
-
var commitPromise, flushPromise, flushResolvers = [];
|
|
192
|
+
var commitPromise, flushPromise, flushResolvers = [], batchFlushResolvers = [];
|
|
184
193
|
commitDelay = commitDelay || 0;
|
|
185
194
|
eventTurnBatching = eventTurnBatching === false ? false : true;
|
|
186
195
|
var enqueuedCommit;
|
|
@@ -189,23 +198,25 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
|
|
|
189
198
|
var enqueuedEventTurnBatch;
|
|
190
199
|
var batchDepth = 0;
|
|
191
200
|
var lastWritePromise;
|
|
192
|
-
var writeBatchStart, outstandingBatchCount, lastSyncTxnFlush
|
|
201
|
+
var writeBatchStart, outstandingBatchCount, lastSyncTxnFlush;
|
|
202
|
+
var hasUnresolvedTxns;
|
|
193
203
|
txnStartThreshold = txnStartThreshold || 5;
|
|
194
204
|
batchStartThreshold = batchStartThreshold || 1000;
|
|
195
205
|
maxFlushDelay = maxFlushDelay || 500;
|
|
196
206
|
|
|
197
207
|
allocateInstructionBuffer();
|
|
198
|
-
dynamicBytes.uint32[
|
|
199
|
-
var txnResolution,
|
|
200
|
-
uint32: dynamicBytes.uint32, flagPosition:
|
|
208
|
+
dynamicBytes.uint32[2] = TXN_DELIMITER | TXN_COMMITTED | TXN_FLUSHED;
|
|
209
|
+
var txnResolution, nextResolution = {
|
|
210
|
+
uint32: dynamicBytes.uint32, flagPosition: 2, flag: 0, valueBuffer: null, next: null, meta: null };
|
|
201
211
|
var uncommittedResolution = {
|
|
202
|
-
uint32: null, flagPosition:
|
|
212
|
+
uint32: null, flagPosition: 2, flag: 0, valueBuffer: null, next: nextResolution, meta: null };
|
|
203
213
|
var unwrittenResolution = nextResolution;
|
|
204
214
|
var lastPromisedResolution = uncommittedResolution;
|
|
215
|
+
var lastQueuedResolution = uncommittedResolution;
|
|
205
216
|
function writeInstructions(flags, store, key, value, version, ifVersion) {
|
|
206
217
|
let writeStatus;
|
|
207
218
|
let targetBytes, position, encoder;
|
|
208
|
-
let
|
|
219
|
+
let valueSize, valueBuffer, valueBufferStart;
|
|
209
220
|
if (flags & 2) {
|
|
210
221
|
// encode first in case we have to write a shared structure
|
|
211
222
|
encoder = store.encoder;
|
|
@@ -213,7 +224,7 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
|
|
|
213
224
|
valueBuffer = value['\x10binary-data\x02'];
|
|
214
225
|
else if (encoder) {
|
|
215
226
|
if (encoder.copyBuffers) // use this as indicator for support buffer reuse for now
|
|
216
|
-
valueBuffer = encoder.encode(value, REUSE_BUFFER_MODE | (writeTxn ? RESET_BUFFER_MODE : 0)); // in addition, if we are writing sync, after using, we can immediately reset the encoder's position to reuse that space, which can improve performance
|
|
227
|
+
valueBuffer = encoder.encode(value, REUSE_BUFFER_MODE$1 | (writeTxn ? RESET_BUFFER_MODE : 0)); // in addition, if we are writing sync, after using, we can immediately reset the encoder's position to reuse that space, which can improve performance
|
|
217
228
|
else { // various other encoders, including JSON.stringify, that might serialize to a string
|
|
218
229
|
valueBuffer = encoder.encode(value);
|
|
219
230
|
if (typeof valueBuffer == 'string')
|
|
@@ -242,7 +253,11 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
|
|
|
242
253
|
enqueuedEventTurnBatch = queueTask(() => {
|
|
243
254
|
try {
|
|
244
255
|
for (let i = 0, l = beforeCommitCallbacks.length; i < l; i++) {
|
|
245
|
-
|
|
256
|
+
try {
|
|
257
|
+
beforeCommitCallbacks[i]();
|
|
258
|
+
} catch(error) {
|
|
259
|
+
console.error('In beforecommit callback', error);
|
|
260
|
+
}
|
|
246
261
|
}
|
|
247
262
|
} catch(error) {
|
|
248
263
|
console.error(error);
|
|
@@ -276,7 +291,7 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
|
|
|
276
291
|
try {
|
|
277
292
|
endPosition = store.writeKey(key, targetBytes, keyStartPosition);
|
|
278
293
|
if (!(keyStartPosition < endPosition) && (flags & 0xf) != 12)
|
|
279
|
-
throw new Error('Invalid key or zero length key is not allowed in LMDB')
|
|
294
|
+
throw new Error('Invalid key or zero length key is not allowed in LMDB ' + key)
|
|
280
295
|
} catch(error) {
|
|
281
296
|
targetBytes.fill(0, keyStartPosition);
|
|
282
297
|
if (error.name == 'RangeError')
|
|
@@ -295,14 +310,14 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
|
|
|
295
310
|
if (valueBufferStart > -1) { // if we have buffers with start/end position
|
|
296
311
|
// record pointer to value buffer
|
|
297
312
|
float64[position] = (valueBuffer.address ||
|
|
298
|
-
(valueBuffer.address = getAddress(valueBuffer))) + valueBufferStart;
|
|
313
|
+
(valueBuffer.address = getAddress(valueBuffer.buffer))) + valueBufferStart;
|
|
299
314
|
mustCompress = valueBuffer[valueBufferStart] >= 250; // this is the compression indicator, so we must compress
|
|
300
315
|
} else {
|
|
301
316
|
let valueArrayBuffer = valueBuffer.buffer;
|
|
302
317
|
// record pointer to value buffer
|
|
303
318
|
let address = (valueArrayBuffer.address ||
|
|
304
319
|
(valueBuffer.length === 0 ? 0 : // externally allocated buffers of zero-length with the same non-null-pointer can crash node, #161
|
|
305
|
-
|
|
320
|
+
valueArrayBuffer.address = getAddress(valueArrayBuffer)))
|
|
306
321
|
+ valueBuffer.byteOffset;
|
|
307
322
|
if (address <= 0 && valueBuffer.length > 0)
|
|
308
323
|
console.error('Supplied buffer had an invalid address', address);
|
|
@@ -347,12 +362,8 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
|
|
|
347
362
|
// uint32[position << 1] = 0 // clear out the next slot
|
|
348
363
|
let nextUint32;
|
|
349
364
|
if (position > newBufferThreshold) {
|
|
350
|
-
|
|
351
|
-
let lastPosition = position;
|
|
352
|
-
targetBytes = allocateInstructionBuffer();
|
|
365
|
+
targetBytes = allocateInstructionBuffer(position);
|
|
353
366
|
position = targetBytes.position;
|
|
354
|
-
float64[lastPosition + 1] = targetBytes.uint32.address + position;
|
|
355
|
-
uint32[lastPosition << 1] = 3; // pointer instruction
|
|
356
367
|
nextUint32 = targetBytes.uint32;
|
|
357
368
|
} else
|
|
358
369
|
nextUint32 = uint32;
|
|
@@ -366,6 +377,8 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
|
|
|
366
377
|
next: null,
|
|
367
378
|
meta: null,
|
|
368
379
|
};
|
|
380
|
+
lastQueuedResolution = resolution;
|
|
381
|
+
|
|
369
382
|
let writtenBatchDepth = batchDepth;
|
|
370
383
|
|
|
371
384
|
return (callback) => {
|
|
@@ -393,13 +406,25 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
|
|
|
393
406
|
if (writeStatus & TXN_DELIMITER) {
|
|
394
407
|
commitPromise = null; // TODO: Don't reset these if this comes from the batch start operation on an event turn batch
|
|
395
408
|
flushPromise = null;
|
|
409
|
+
flushResolvers = [];
|
|
396
410
|
queueCommitResolution(resolution);
|
|
397
411
|
if (!startAddress) {
|
|
398
412
|
startAddress = uint32.address + (flagPosition << 2);
|
|
399
413
|
}
|
|
400
414
|
}
|
|
401
|
-
if (!
|
|
402
|
-
|
|
415
|
+
if (!writtenBatchDepth && batchFlushResolvers.length > 0) {
|
|
416
|
+
flushResolvers.push(...batchFlushResolvers);
|
|
417
|
+
batchFlushResolvers = [];
|
|
418
|
+
}
|
|
419
|
+
if (!flushPromise && overlappingSync) {
|
|
420
|
+
flushPromise = new Promise(resolve => {
|
|
421
|
+
if (writtenBatchDepth) {
|
|
422
|
+
batchFlushResolvers.push(resolve);
|
|
423
|
+
} else {
|
|
424
|
+
flushResolvers.push(resolve);
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
}
|
|
403
428
|
if (writeStatus & WAITING_OPERATION) { // write thread is waiting
|
|
404
429
|
write(env.address, 0);
|
|
405
430
|
}
|
|
@@ -428,11 +453,10 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
|
|
|
428
453
|
};
|
|
429
454
|
}
|
|
430
455
|
resolution.valueBuffer = valueBuffer;
|
|
431
|
-
lastQueuedResolution = resolution;
|
|
432
456
|
|
|
433
457
|
if (callback) {
|
|
434
|
-
if (callback === IF_EXISTS)
|
|
435
|
-
ifVersion = IF_EXISTS;
|
|
458
|
+
if (callback === IF_EXISTS$1)
|
|
459
|
+
ifVersion = IF_EXISTS$1;
|
|
436
460
|
else {
|
|
437
461
|
let meta = resolution.meta || (resolution.meta = {});
|
|
438
462
|
meta.reject = callback;
|
|
@@ -471,15 +495,13 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
|
|
|
471
495
|
return lastWritePromise;
|
|
472
496
|
};
|
|
473
497
|
}
|
|
474
|
-
|
|
498
|
+
Promise.resolve();
|
|
475
499
|
function startWriting() {
|
|
476
500
|
if (enqueuedCommit) {
|
|
477
501
|
clearImmediate(enqueuedCommit);
|
|
478
502
|
enqueuedCommit = null;
|
|
479
503
|
}
|
|
480
504
|
let resolvers = flushResolvers;
|
|
481
|
-
flushResolvers = [];
|
|
482
|
-
let start = Date.now();
|
|
483
505
|
env.startWriting(startAddress, (status) => {
|
|
484
506
|
if (dynamicBytes.uint32[dynamicBytes.position << 1] & TXN_DELIMITER)
|
|
485
507
|
queueCommitResolution(nextResolution);
|
|
@@ -487,52 +509,30 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
|
|
|
487
509
|
resolveWrites(true);
|
|
488
510
|
switch (status) {
|
|
489
511
|
case 0:
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
scheduleFlush(resolvers, Math.min((flushPromise && flushPromise.hasCallbacks ? delay >> 1 : delay) + 1, maxFlushDelay));
|
|
512
|
+
for (let resolver of resolvers) {
|
|
513
|
+
resolver();
|
|
493
514
|
}
|
|
515
|
+
break;
|
|
494
516
|
case 1:
|
|
495
|
-
|
|
517
|
+
break;
|
|
496
518
|
case 2:
|
|
519
|
+
hasUnresolvedTxns = false;
|
|
497
520
|
executeTxnCallbacks();
|
|
498
|
-
|
|
521
|
+
return hasUnresolvedTxns;
|
|
499
522
|
default:
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
523
|
+
try {
|
|
524
|
+
lmdbError(status);
|
|
525
|
+
} catch(error) {
|
|
526
|
+
console.error(error);
|
|
527
|
+
if (commitRejectPromise) {
|
|
528
|
+
commitRejectPromise.reject(error);
|
|
529
|
+
commitRejectPromise = null;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
505
532
|
}
|
|
506
533
|
});
|
|
507
534
|
startAddress = 0;
|
|
508
535
|
}
|
|
509
|
-
function scheduleFlush(resolvers, delay) {
|
|
510
|
-
if (committedFlushResolvers)
|
|
511
|
-
committedFlushResolvers.push(...resolvers);
|
|
512
|
-
else {
|
|
513
|
-
committedFlushResolvers = resolvers;
|
|
514
|
-
lastFlushTimeout = setTimeout(lastFlushCallback = () => {
|
|
515
|
-
lastFlushTimeout = null;
|
|
516
|
-
lastSync.then(() => {
|
|
517
|
-
let resolvers = committedFlushResolvers || [];
|
|
518
|
-
committedFlushResolvers = null;
|
|
519
|
-
lastSync = new Promise((resolve) => {
|
|
520
|
-
env.sync(() => {
|
|
521
|
-
for (let i = 0; i < resolvers.length; i++)
|
|
522
|
-
resolvers[i]();
|
|
523
|
-
resolve();
|
|
524
|
-
});
|
|
525
|
-
});
|
|
526
|
-
});
|
|
527
|
-
}, delay || 0);
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
function expediteFlush() {
|
|
531
|
-
if (lastFlushTimeout) {
|
|
532
|
-
clearTimeout(lastFlushTimeout);
|
|
533
|
-
lastFlushCallback();
|
|
534
|
-
}
|
|
535
|
-
}
|
|
536
536
|
|
|
537
537
|
function queueCommitResolution(resolution) {
|
|
538
538
|
if (!(resolution.flag & HAS_TXN)) {
|
|
@@ -557,7 +557,7 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
|
|
|
557
557
|
}
|
|
558
558
|
outstandingWriteCount--;
|
|
559
559
|
if (unwrittenResolution.flag !== HAS_TXN) {
|
|
560
|
-
if (unwrittenResolution.flag === NO_RESOLVE && !unwrittenResolution.
|
|
560
|
+
if (unwrittenResolution.flag === NO_RESOLVE && !unwrittenResolution.meta) {
|
|
561
561
|
// in this case we can completely remove from the linked list, clearing more memory
|
|
562
562
|
lastPromisedResolution.next = unwrittenResolution = unwrittenResolution.next;
|
|
563
563
|
continue;
|
|
@@ -579,7 +579,7 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
|
|
|
579
579
|
}
|
|
580
580
|
|
|
581
581
|
function resolveCommit(async) {
|
|
582
|
-
afterCommit();
|
|
582
|
+
afterCommit(txnResolution.uint32[txnResolution.flagPosition - 1]);
|
|
583
583
|
if (async)
|
|
584
584
|
resetReadTxn();
|
|
585
585
|
else
|
|
@@ -625,35 +625,38 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
|
|
|
625
625
|
} else // otherwise the transaction could end at any time and we need to know the
|
|
626
626
|
// deterministically if it is ending, so we can reset the commit promise
|
|
627
627
|
// so we use the slower atomic operation
|
|
628
|
-
|
|
628
|
+
try {
|
|
629
|
+
return Atomics.or(uint32, flagPosition, newStatus);
|
|
630
|
+
} catch(error) {
|
|
631
|
+
console.error(error);
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
629
634
|
}
|
|
630
|
-
function afterCommit() {
|
|
635
|
+
function afterCommit(txnId) {
|
|
631
636
|
for (let i = 0, l = afterCommitCallbacks.length; i < l; i++) {
|
|
632
|
-
|
|
637
|
+
try {
|
|
638
|
+
afterCommitCallbacks[i]({next: uncommittedResolution, last: txnResolution, txnId});
|
|
639
|
+
} catch(error) {
|
|
640
|
+
console.error('In aftercommit callback', error);
|
|
641
|
+
}
|
|
633
642
|
}
|
|
634
643
|
}
|
|
635
644
|
async function executeTxnCallbacks() {
|
|
636
645
|
env.writeTxn = writeTxn = { write: true };
|
|
637
|
-
|
|
638
|
-
let
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
for (let i = 0, l = txnCallbacks.length; i < l; i++) {
|
|
643
|
-
let userTxnCallback = txnCallbacks[i];
|
|
646
|
+
nextTxnCallbacks.isExecuting = true;
|
|
647
|
+
for (let i = 0; i < nextTxnCallbacks.length; i++) {
|
|
648
|
+
let txnCallbacks = nextTxnCallbacks[i];
|
|
649
|
+
for (let j = 0, l = txnCallbacks.length; j < l; j++) {
|
|
650
|
+
let userTxnCallback = txnCallbacks[j];
|
|
644
651
|
let asChild = userTxnCallback.asChild;
|
|
645
652
|
if (asChild) {
|
|
646
|
-
if (promises) {
|
|
647
|
-
// must complete any outstanding transactions before proceeding
|
|
648
|
-
await Promise.all(promises);
|
|
649
|
-
promises = null;
|
|
650
|
-
}
|
|
651
653
|
env.beginTxn(1); // abortable
|
|
652
654
|
let parentTxn = writeTxn;
|
|
653
655
|
env.writeTxn = writeTxn = { write: true };
|
|
654
656
|
try {
|
|
655
657
|
let result = userTxnCallback.callback();
|
|
656
658
|
if (result && result.then) {
|
|
659
|
+
hasUnresolvedTxns = true;
|
|
657
660
|
await result;
|
|
658
661
|
}
|
|
659
662
|
if (result === ABORT)
|
|
@@ -661,33 +664,32 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
|
|
|
661
664
|
else
|
|
662
665
|
env.commitTxn();
|
|
663
666
|
clearWriteTxn(parentTxn);
|
|
664
|
-
txnCallbacks[
|
|
667
|
+
txnCallbacks[j] = result;
|
|
665
668
|
} catch(error) {
|
|
666
669
|
clearWriteTxn(parentTxn);
|
|
667
670
|
env.abortTxn();
|
|
668
|
-
txnError(error,
|
|
671
|
+
txnError(error, txnCallbacks, j);
|
|
669
672
|
}
|
|
670
673
|
} else {
|
|
671
674
|
try {
|
|
672
675
|
let result = userTxnCallback();
|
|
673
|
-
txnCallbacks[
|
|
676
|
+
txnCallbacks[j] = result;
|
|
674
677
|
if (result && result.then) {
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
promises.push(result.catch(() => {}));
|
|
678
|
+
hasUnresolvedTxns = true;
|
|
679
|
+
await result;
|
|
678
680
|
}
|
|
679
681
|
} catch(error) {
|
|
680
|
-
txnError(error,
|
|
682
|
+
txnError(error, txnCallbacks, j);
|
|
681
683
|
}
|
|
682
684
|
}
|
|
683
685
|
}
|
|
684
686
|
}
|
|
685
687
|
nextTxnCallbacks = [];
|
|
686
|
-
if (promises) { // finish any outstanding commit functions
|
|
687
|
-
await Promise.all(promises);
|
|
688
|
-
}
|
|
689
688
|
clearWriteTxn(null);
|
|
690
|
-
|
|
689
|
+
if (hasUnresolvedTxns) {
|
|
690
|
+
env.resumeWriting();
|
|
691
|
+
}
|
|
692
|
+
function txnError(error, txnCallbacks, i) {
|
|
691
693
|
(txnCallbacks.errors || (txnCallbacks.errors = []))[i] = error;
|
|
692
694
|
txnCallbacks[i] = CALLBACK_THREW;
|
|
693
695
|
}
|
|
@@ -699,18 +701,23 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
|
|
|
699
701
|
if (writeStatus & WAITING_OPERATION) {
|
|
700
702
|
write(env.address, 0);
|
|
701
703
|
}
|
|
704
|
+
if (dynamicBytes.position > newBufferThreshold) {
|
|
705
|
+
allocateInstructionBuffer(dynamicBytes.position);
|
|
706
|
+
nextResolution.flagPosition = dynamicBytes.position << 1;
|
|
707
|
+
nextResolution.uint32 = dynamicBytes.uint32;
|
|
708
|
+
}
|
|
702
709
|
}
|
|
703
710
|
function clearWriteTxn(parentTxn) {
|
|
704
711
|
// TODO: We might actually want to track cursors in a write txn and manually
|
|
705
712
|
// close them.
|
|
706
|
-
if (writeTxn.
|
|
713
|
+
if (writeTxn && writeTxn.refCount > 0)
|
|
707
714
|
writeTxn.isDone = true;
|
|
708
715
|
env.writeTxn = writeTxn = parentTxn || null;
|
|
709
716
|
}
|
|
710
717
|
Object.assign(LMDBStore.prototype, {
|
|
711
718
|
put(key, value, versionOrOptions, ifVersion) {
|
|
712
719
|
let callback, flags = 15, type = typeof versionOrOptions;
|
|
713
|
-
if (type == 'object') {
|
|
720
|
+
if (type == 'object' && versionOrOptions) {
|
|
714
721
|
if (versionOrOptions.noOverwrite)
|
|
715
722
|
flags |= 0x10;
|
|
716
723
|
if (versionOrOptions.noDupData)
|
|
@@ -718,7 +725,7 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
|
|
|
718
725
|
if (versionOrOptions.append)
|
|
719
726
|
flags |= 0x20000;
|
|
720
727
|
if (versionOrOptions.ifVersion != undefined)
|
|
721
|
-
ifVersion =
|
|
728
|
+
ifVersion = versionOrOptions.ifVersion;
|
|
722
729
|
versionOrOptions = versionOrOptions.version;
|
|
723
730
|
if (typeof ifVersion == 'function')
|
|
724
731
|
callback = ifVersion;
|
|
@@ -733,7 +740,7 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
|
|
|
733
740
|
if (ifVersionOrValue !== undefined) {
|
|
734
741
|
if (typeof ifVersionOrValue == 'function')
|
|
735
742
|
callback = ifVersionOrValue;
|
|
736
|
-
else if (ifVersionOrValue === IF_EXISTS && !callback)
|
|
743
|
+
else if (ifVersionOrValue === IF_EXISTS$1 && !callback)
|
|
737
744
|
// we have a handler for IF_EXISTS in the callback handler for remove
|
|
738
745
|
callback = ifVersionOrValue;
|
|
739
746
|
else if (this.useVersions)
|
|
@@ -751,11 +758,10 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
|
|
|
751
758
|
ifNoExists(key, callback) {
|
|
752
759
|
return this.ifVersion(key, null, callback);
|
|
753
760
|
},
|
|
754
|
-
|
|
755
|
-
ifVersion(key, version, callback) {
|
|
761
|
+
ifVersion(key, version, callback, options) {
|
|
756
762
|
if (!callback) {
|
|
757
763
|
return new Batch((operations, callback) => {
|
|
758
|
-
let promise = this.ifVersion(key, version, operations);
|
|
764
|
+
let promise = this.ifVersion(key, version, operations, options);
|
|
759
765
|
if (callback)
|
|
760
766
|
promise.then(callback);
|
|
761
767
|
return promise;
|
|
@@ -768,7 +774,12 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
|
|
|
768
774
|
}
|
|
769
775
|
return SYNC_PROMISE_FAIL;
|
|
770
776
|
}
|
|
771
|
-
let
|
|
777
|
+
let flags = key === undefined || version === undefined ? 1 : 4;
|
|
778
|
+
if (options?.ifLessThan)
|
|
779
|
+
flags |= CONDITIONAL_VERSION_LESS_THAN;
|
|
780
|
+
if (options?.allowNotFound)
|
|
781
|
+
flags |= CONDITIONAL_ALLOW_NOTFOUND;
|
|
782
|
+
let finishStartWrite = writeInstructions(flags, this, key, undefined, undefined, version);
|
|
772
783
|
let promise;
|
|
773
784
|
batchDepth += 2;
|
|
774
785
|
if (batchDepth > 2)
|
|
@@ -805,7 +816,7 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
|
|
|
805
816
|
return this.ifVersion(undefined, undefined, callbackOrOperations);
|
|
806
817
|
},
|
|
807
818
|
drop(callback) {
|
|
808
|
-
return writeInstructions(1024 + 12, this,
|
|
819
|
+
return writeInstructions(1024 + 12, this, Buffer.from([]), undefined, undefined, undefined)(callback);
|
|
809
820
|
},
|
|
810
821
|
clearAsync(callback) {
|
|
811
822
|
if (this.encoder) {
|
|
@@ -814,7 +825,7 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
|
|
|
814
825
|
else if (this.encoder.structures)
|
|
815
826
|
this.encoder.structures = [];
|
|
816
827
|
}
|
|
817
|
-
return writeInstructions(12, this,
|
|
828
|
+
return writeInstructions(12, this, Buffer.from([]), undefined, undefined, undefined)(callback);
|
|
818
829
|
},
|
|
819
830
|
_triggerError() {
|
|
820
831
|
finishBatch();
|
|
@@ -822,20 +833,20 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
|
|
|
822
833
|
|
|
823
834
|
putSync(key, value, versionOrOptions, ifVersion) {
|
|
824
835
|
if (writeTxn)
|
|
825
|
-
return this.put(key, value, versionOrOptions, ifVersion);
|
|
836
|
+
return this.put(key, value, versionOrOptions, ifVersion) === SYNC_PROMISE_SUCCESS;
|
|
826
837
|
else
|
|
827
838
|
return this.transactionSync(() =>
|
|
828
|
-
this.put(key, value, versionOrOptions, ifVersion)
|
|
839
|
+
this.put(key, value, versionOrOptions, ifVersion) === SYNC_PROMISE_SUCCESS, overlappingSync? 0x10002 : 2); // non-abortable, async flush
|
|
829
840
|
},
|
|
830
841
|
removeSync(key, ifVersionOrValue) {
|
|
831
842
|
if (writeTxn)
|
|
832
|
-
return this.remove(key, ifVersionOrValue);
|
|
843
|
+
return this.remove(key, ifVersionOrValue) === SYNC_PROMISE_SUCCESS;
|
|
833
844
|
else
|
|
834
845
|
return this.transactionSync(() =>
|
|
835
|
-
this.remove(key, ifVersionOrValue)
|
|
846
|
+
this.remove(key, ifVersionOrValue) === SYNC_PROMISE_SUCCESS, overlappingSync? 0x10002 : 2); // non-abortable, async flush
|
|
836
847
|
},
|
|
837
848
|
transaction(callback) {
|
|
838
|
-
if (writeTxn) {
|
|
849
|
+
if (writeTxn && !nextTxnCallbacks.isExecuting) {
|
|
839
850
|
// already nested in a transaction, just execute and return
|
|
840
851
|
return callback();
|
|
841
852
|
}
|
|
@@ -846,11 +857,13 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
|
|
|
846
857
|
throw new Error('Child transactions are not supported in writemap mode');
|
|
847
858
|
if (writeTxn) {
|
|
848
859
|
let parentTxn = writeTxn;
|
|
849
|
-
env.writeTxn = writeTxn = { write: true };
|
|
860
|
+
let thisTxn = env.writeTxn = writeTxn = { write: true };
|
|
850
861
|
env.beginTxn(1); // abortable
|
|
851
|
-
let callbackDone;
|
|
862
|
+
let callbackDone, finishTxn;
|
|
852
863
|
try {
|
|
853
|
-
return when(callback(), (result) => {
|
|
864
|
+
return writeTxn.childResults = when(callback(), finishTxn = (result) => {
|
|
865
|
+
if (writeTxn !== thisTxn) // need to wait for child txn to finish asynchronously
|
|
866
|
+
return writeTxn.childResults.then(() => finishTxn(result));
|
|
854
867
|
callbackDone = true;
|
|
855
868
|
if (result === ABORT)
|
|
856
869
|
env.abortTxn();
|
|
@@ -875,14 +888,23 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
|
|
|
875
888
|
transactionAsync(callback, asChild) {
|
|
876
889
|
let txnIndex;
|
|
877
890
|
let txnCallbacks;
|
|
878
|
-
if (
|
|
891
|
+
if (lastQueuedResolution.callbacks) {
|
|
892
|
+
txnCallbacks = lastQueuedResolution.callbacks;
|
|
893
|
+
txnIndex = txnCallbacks.push(asChild ? { callback, asChild } : callback) - 1;
|
|
894
|
+
} else if (nextTxnCallbacks.isExecuting) {
|
|
879
895
|
txnCallbacks = [asChild ? { callback, asChild } : callback];
|
|
880
|
-
|
|
881
|
-
|
|
896
|
+
txnCallbacks.results = commitPromise;
|
|
897
|
+
nextTxnCallbacks.push(txnCallbacks);
|
|
882
898
|
txnIndex = 0;
|
|
883
899
|
} else {
|
|
884
|
-
|
|
885
|
-
|
|
900
|
+
if (writeTxn)
|
|
901
|
+
throw new Error('Can not enqueue transaction during write txn');
|
|
902
|
+
let finishWrite = writeInstructions(8 | (this.strictAsyncOrder ? 0x100000 : 0), this);
|
|
903
|
+
txnCallbacks = [asChild ? { callback, asChild } : callback];
|
|
904
|
+
lastQueuedResolution.callbacks = txnCallbacks;
|
|
905
|
+
lastQueuedResolution.id = Math.random();
|
|
906
|
+
txnCallbacks.results = finishWrite();
|
|
907
|
+
txnIndex = 0;
|
|
886
908
|
}
|
|
887
909
|
return txnCallbacks.results.then((results) => {
|
|
888
910
|
let result = txnCallbacks[txnIndex];
|
|
@@ -893,7 +915,7 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
|
|
|
893
915
|
},
|
|
894
916
|
transactionSync(callback, flags) {
|
|
895
917
|
if (writeTxn) {
|
|
896
|
-
if (!useWritemap &&
|
|
918
|
+
if (!useWritemap && (flags == undefined || (flags & 1))) // can't use child transactions in write maps
|
|
897
919
|
// already nested in a transaction, execute as child transaction (if possible) and return
|
|
898
920
|
return this.childTransaction(callback);
|
|
899
921
|
let result = callback(); // else just run in current transaction
|
|
@@ -903,13 +925,17 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
|
|
|
903
925
|
}
|
|
904
926
|
return result;
|
|
905
927
|
}
|
|
906
|
-
let callbackDone;
|
|
928
|
+
let callbackDone, finishTxn;
|
|
929
|
+
this.transactions++;
|
|
930
|
+
if (!env.address)
|
|
931
|
+
throw new Error('The database has been closed and you can not transact on it');
|
|
932
|
+
env.beginTxn(flags == undefined ? 3 : flags);
|
|
933
|
+
let thisTxn = writeTxn = env.writeTxn = { write: true };
|
|
907
934
|
try {
|
|
908
|
-
this.transactions++;
|
|
909
|
-
env.beginTxn(flags == undefined ? 3 : flags);
|
|
910
|
-
writeTxn = env.writeTxn = { write: true };
|
|
911
935
|
this.emit('begin-transaction');
|
|
912
|
-
return when(callback(), (result) => {
|
|
936
|
+
return writeTxn.childResults = when(callback(), finishTxn = (result) => {
|
|
937
|
+
if (writeTxn !== thisTxn) // need to wait for child txn to finish asynchronously
|
|
938
|
+
return writeTxn.childResults.then(() => finishTxn(result));
|
|
913
939
|
try {
|
|
914
940
|
callbackDone = true;
|
|
915
941
|
if (result === ABORT)
|
|
@@ -917,8 +943,6 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
|
|
|
917
943
|
else {
|
|
918
944
|
env.commitTxn();
|
|
919
945
|
resetReadTxn();
|
|
920
|
-
if ((flags & 0x10000) && overlappingSync) // if it is no-sync in overlapping-sync mode, need to schedule flush for it to be marked as persisted
|
|
921
|
-
lastSyncTxnFlush = new Promise(resolve => scheduleFlush([resolve]));
|
|
922
946
|
}
|
|
923
947
|
return result;
|
|
924
948
|
} finally {
|
|
@@ -963,11 +987,9 @@ function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritema
|
|
|
963
987
|
let finalPromise = flushPromise || commitPromise || lastWritePromise;
|
|
964
988
|
if (flushPromise)
|
|
965
989
|
flushPromise.hasCallbacks = true;
|
|
966
|
-
if (lastFlushTimeout)
|
|
967
|
-
expediteFlush();
|
|
968
990
|
let finalSyncPromise = lastSyncTxnFlush;
|
|
969
991
|
if (finalPromise && resolvedPromise != finalPromise ||
|
|
970
|
-
finalSyncPromise
|
|
992
|
+
finalSyncPromise ) {
|
|
971
993
|
return Promise.all([finalPromise, finalSyncPromise]).then(() => this._endWrites(finalPromise, finalSyncPromise), () => this._endWrites(finalPromise, finalSyncPromise));
|
|
972
994
|
}
|
|
973
995
|
Object.defineProperty(env, 'sync', { value: null });
|
|
@@ -1008,160 +1030,11 @@ function asBinary(buffer) {
|
|
|
1008
1030
|
};
|
|
1009
1031
|
}
|
|
1010
1032
|
|
|
1011
|
-
let getLastVersion$1;
|
|
1012
|
-
const mapGet = Map.prototype.get;
|
|
1013
|
-
const CachingStore = (Store, env) => {
|
|
1014
|
-
let childTxnChanges;
|
|
1015
|
-
return class extends Store {
|
|
1016
|
-
constructor(dbName, options) {
|
|
1017
|
-
super(dbName, options);
|
|
1018
|
-
if (!env.cacheCommitter) {
|
|
1019
|
-
env.cacheCommitter = true;
|
|
1020
|
-
this.on('aftercommit', ({ next, last }) => {
|
|
1021
|
-
do {
|
|
1022
|
-
let meta = next.meta;
|
|
1023
|
-
let store = meta && meta.store;
|
|
1024
|
-
if (store) {
|
|
1025
|
-
if (next.flag & FAILED_CONDITION)
|
|
1026
|
-
store.cache.delete(meta.key); // just delete it from the map
|
|
1027
|
-
else {
|
|
1028
|
-
let expirationPriority = meta.valueSize >> 10;
|
|
1029
|
-
let cache = store.cache;
|
|
1030
|
-
let entry = mapGet.call(cache, meta.key);
|
|
1031
|
-
if (entry)
|
|
1032
|
-
cache.used(entry, expirationPriority + 4); // this will enter it into the LRFU (with a little lower priority than a read)
|
|
1033
|
-
}
|
|
1034
|
-
}
|
|
1035
|
-
} while (next != last && (next = next.next))
|
|
1036
|
-
});
|
|
1037
|
-
}
|
|
1038
|
-
this.db.cachingDb = this;
|
|
1039
|
-
if (options.cache.clearKeptInterval)
|
|
1040
|
-
options.cache.clearKeptObjects = exports.clearKeptObjects;
|
|
1041
|
-
this.cache = new WeakLRUCache(options.cache);
|
|
1042
|
-
}
|
|
1043
|
-
get isCaching() {
|
|
1044
|
-
return true
|
|
1045
|
-
}
|
|
1046
|
-
get(id, cacheMode) {
|
|
1047
|
-
let value = this.cache.getValue(id);
|
|
1048
|
-
if (value !== undefined)
|
|
1049
|
-
return value;
|
|
1050
|
-
value = super.get(id);
|
|
1051
|
-
if (value && typeof value === 'object' && !cacheMode && typeof id !== 'object') {
|
|
1052
|
-
let entry = this.cache.setValue(id, value, this.lastSize >> 10);
|
|
1053
|
-
if (this.useVersions) {
|
|
1054
|
-
entry.version = getLastVersion$1();
|
|
1055
|
-
}
|
|
1056
|
-
}
|
|
1057
|
-
return value;
|
|
1058
|
-
}
|
|
1059
|
-
getEntry(id, cacheMode) {
|
|
1060
|
-
let entry = this.cache.get(id);
|
|
1061
|
-
if (entry)
|
|
1062
|
-
return entry;
|
|
1063
|
-
let value = super.get(id);
|
|
1064
|
-
if (value === undefined)
|
|
1065
|
-
return;
|
|
1066
|
-
if (value && typeof value === 'object' && !cacheMode && typeof id !== 'object') {
|
|
1067
|
-
entry = this.cache.setValue(id, value, this.lastSize >> 10);
|
|
1068
|
-
} else {
|
|
1069
|
-
entry = { value };
|
|
1070
|
-
}
|
|
1071
|
-
if (this.useVersions) {
|
|
1072
|
-
entry.version = getLastVersion$1();
|
|
1073
|
-
}
|
|
1074
|
-
return entry;
|
|
1075
|
-
}
|
|
1076
|
-
putEntry(id, entry, ifVersion) {
|
|
1077
|
-
let result = super.put(id, entry.value, entry.version, ifVersion);
|
|
1078
|
-
if (typeof id === 'object')
|
|
1079
|
-
return result;
|
|
1080
|
-
if (result && result.then)
|
|
1081
|
-
this.cache.setManually(id, entry); // set manually so we can keep it pinned in memory until it is committed
|
|
1082
|
-
else // sync operation, immediately add to cache
|
|
1083
|
-
this.cache.set(id, entry);
|
|
1084
|
-
}
|
|
1085
|
-
put(id, value, version, ifVersion) {
|
|
1086
|
-
let result = super.put(id, value, version, ifVersion);
|
|
1087
|
-
if (typeof id !== 'object') {
|
|
1088
|
-
if (value && value['\x10binary-data\x02']) {
|
|
1089
|
-
// don't cache binary data, since it will be decoded on get
|
|
1090
|
-
this.cache.delete(id);
|
|
1091
|
-
return result;
|
|
1092
|
-
}
|
|
1093
|
-
// sync operation, immediately add to cache, otherwise keep it pinned in memory until it is committed
|
|
1094
|
-
let entry = this.cache.setValue(id, value, !result || result.isSync ? 0 : -1);
|
|
1095
|
-
if (childTxnChanges)
|
|
1096
|
-
childTxnChanges.add(id);
|
|
1097
|
-
if (version !== undefined)
|
|
1098
|
-
entry.version = typeof version === 'object' ? version.version : version;
|
|
1099
|
-
}
|
|
1100
|
-
return result;
|
|
1101
|
-
}
|
|
1102
|
-
putSync(id, value, version, ifVersion) {
|
|
1103
|
-
if (id !== 'object') {
|
|
1104
|
-
// sync operation, immediately add to cache, otherwise keep it pinned in memory until it is committed
|
|
1105
|
-
if (value && typeof value === 'object') {
|
|
1106
|
-
let entry = this.cache.setValue(id, value);
|
|
1107
|
-
if (childTxnChanges)
|
|
1108
|
-
childTxnChanges.add(id);
|
|
1109
|
-
if (version !== undefined) {
|
|
1110
|
-
entry.version = typeof version === 'object' ? version.version : version;
|
|
1111
|
-
}
|
|
1112
|
-
} else // it is possible that a value used to exist here
|
|
1113
|
-
this.cache.delete(id);
|
|
1114
|
-
}
|
|
1115
|
-
return super.putSync(id, value, version, ifVersion);
|
|
1116
|
-
}
|
|
1117
|
-
remove(id, ifVersion) {
|
|
1118
|
-
this.cache.delete(id);
|
|
1119
|
-
return super.remove(id, ifVersion);
|
|
1120
|
-
}
|
|
1121
|
-
removeSync(id, ifVersion) {
|
|
1122
|
-
this.cache.delete(id);
|
|
1123
|
-
return super.removeSync(id, ifVersion);
|
|
1124
|
-
}
|
|
1125
|
-
clearAsync(callback) {
|
|
1126
|
-
this.cache.clear();
|
|
1127
|
-
return super.clearAsync(callback);
|
|
1128
|
-
}
|
|
1129
|
-
clearSync() {
|
|
1130
|
-
this.cache.clear();
|
|
1131
|
-
super.clearSync();
|
|
1132
|
-
}
|
|
1133
|
-
childTransaction(callback) {
|
|
1134
|
-
return super.childTransaction(() => {
|
|
1135
|
-
let cache = this.cache;
|
|
1136
|
-
let previousChanges = childTxnChanges;
|
|
1137
|
-
try {
|
|
1138
|
-
childTxnChanges = new Set();
|
|
1139
|
-
return when(callback(), (result) => {
|
|
1140
|
-
if (result === ABORT)
|
|
1141
|
-
return abort();
|
|
1142
|
-
childTxnChanges = previousChanges;
|
|
1143
|
-
return result;
|
|
1144
|
-
}, abort);
|
|
1145
|
-
} catch(error) {
|
|
1146
|
-
abort(error);
|
|
1147
|
-
}
|
|
1148
|
-
function abort(error) {
|
|
1149
|
-
// if the transaction was aborted, remove all affected entries from cache
|
|
1150
|
-
for (let id of childTxnChanges)
|
|
1151
|
-
cache.delete(id);
|
|
1152
|
-
childTxnChanges = previousChanges;
|
|
1153
|
-
if (error)
|
|
1154
|
-
throw error;
|
|
1155
|
-
}
|
|
1156
|
-
});
|
|
1157
|
-
}
|
|
1158
|
-
};
|
|
1159
|
-
};
|
|
1160
|
-
function setGetLastVersion(get) {
|
|
1161
|
-
getLastVersion$1 = get;
|
|
1162
|
-
}
|
|
1163
|
-
|
|
1164
1033
|
const SKIP = {};
|
|
1034
|
+
const DONE = {
|
|
1035
|
+
value: null,
|
|
1036
|
+
done: true,
|
|
1037
|
+
};
|
|
1165
1038
|
if (!Symbol.asyncIterator) {
|
|
1166
1039
|
Symbol.asyncIterator = Symbol.for('Symbol.asyncIterator');
|
|
1167
1040
|
}
|
|
@@ -1174,9 +1047,10 @@ class RangeIterable {
|
|
|
1174
1047
|
}
|
|
1175
1048
|
map(func) {
|
|
1176
1049
|
let source = this;
|
|
1177
|
-
let
|
|
1178
|
-
|
|
1050
|
+
let iterable = new RangeIterable();
|
|
1051
|
+
iterable.iterate = (async) => {
|
|
1179
1052
|
let iterator = source[Symbol.iterator](async);
|
|
1053
|
+
let i = 0;
|
|
1180
1054
|
return {
|
|
1181
1055
|
next(resolvedResult) {
|
|
1182
1056
|
let result;
|
|
@@ -1193,31 +1067,38 @@ class RangeIterable {
|
|
|
1193
1067
|
}
|
|
1194
1068
|
if (iteratorResult.done === true) {
|
|
1195
1069
|
this.done = true;
|
|
1070
|
+
if (iterable.onDone) iterable.onDone();
|
|
1196
1071
|
return iteratorResult;
|
|
1197
1072
|
}
|
|
1198
|
-
result = func(iteratorResult.value);
|
|
1073
|
+
result = func(iteratorResult.value, i++);
|
|
1199
1074
|
if (result && result.then) {
|
|
1200
1075
|
return result.then(result =>
|
|
1201
|
-
result
|
|
1076
|
+
result === SKIP ?
|
|
1202
1077
|
this.next() :
|
|
1203
1078
|
{
|
|
1204
1079
|
value: result
|
|
1205
1080
|
});
|
|
1206
1081
|
}
|
|
1207
|
-
} while(result
|
|
1082
|
+
} while(result === SKIP);
|
|
1083
|
+
if (result === DONE) {
|
|
1084
|
+
if (iterable.onDone) iterable.onDone();
|
|
1085
|
+
return result;
|
|
1086
|
+
}
|
|
1208
1087
|
return {
|
|
1209
1088
|
value: result
|
|
1210
1089
|
};
|
|
1211
1090
|
},
|
|
1212
1091
|
return() {
|
|
1092
|
+
if (iterable.onDone) iterable.onDone();
|
|
1213
1093
|
return iterator.return();
|
|
1214
1094
|
},
|
|
1215
1095
|
throw() {
|
|
1096
|
+
if (iterable.onDone) iterable.onDone();
|
|
1216
1097
|
return iterator.throw();
|
|
1217
1098
|
}
|
|
1218
1099
|
};
|
|
1219
1100
|
};
|
|
1220
|
-
return
|
|
1101
|
+
return iterable;
|
|
1221
1102
|
}
|
|
1222
1103
|
[Symbol.asyncIterator]() {
|
|
1223
1104
|
return this.iterator = this.iterate();
|
|
@@ -1226,7 +1107,12 @@ class RangeIterable {
|
|
|
1226
1107
|
return this.iterator = this.iterate();
|
|
1227
1108
|
}
|
|
1228
1109
|
filter(func) {
|
|
1229
|
-
return this.map(element =>
|
|
1110
|
+
return this.map(element => {
|
|
1111
|
+
let result = func(element);
|
|
1112
|
+
// handle promise
|
|
1113
|
+
if (result?.then) return result.then((result) => result ? element : SKIP);
|
|
1114
|
+
else return result ? element : SKIP;
|
|
1115
|
+
});
|
|
1230
1116
|
}
|
|
1231
1117
|
|
|
1232
1118
|
forEach(callback) {
|
|
@@ -1241,27 +1127,103 @@ class RangeIterable {
|
|
|
1241
1127
|
concatIterable.iterate = (async) => {
|
|
1242
1128
|
let iterator = this.iterator = this.iterate();
|
|
1243
1129
|
let isFirst = true;
|
|
1244
|
-
|
|
1130
|
+
function iteratorDone(result) {
|
|
1131
|
+
if (isFirst) {
|
|
1132
|
+
isFirst = false;
|
|
1133
|
+
iterator = secondIterable[Symbol.iterator](async);
|
|
1134
|
+
result = iterator.next();
|
|
1135
|
+
if (concatIterable.onDone) {
|
|
1136
|
+
if (result.then)
|
|
1137
|
+
result.then((result) => {
|
|
1138
|
+
if (result.done()) concatIterable.onDone();
|
|
1139
|
+
});
|
|
1140
|
+
else if (result.done) concatIterable.onDone();
|
|
1141
|
+
}
|
|
1142
|
+
} else {
|
|
1143
|
+
if (concatIterable.onDone) concatIterable.onDone();
|
|
1144
|
+
}
|
|
1145
|
+
return result;
|
|
1146
|
+
}
|
|
1147
|
+
return {
|
|
1245
1148
|
next() {
|
|
1246
1149
|
let result = iterator.next();
|
|
1247
|
-
if (
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1150
|
+
if (result.then)
|
|
1151
|
+
return result.then((result) => {
|
|
1152
|
+
if (result.done) return iteratorDone(result);
|
|
1153
|
+
return result;
|
|
1154
|
+
});
|
|
1155
|
+
if (result.done) return iteratorDone(result);
|
|
1252
1156
|
return result;
|
|
1253
1157
|
},
|
|
1254
1158
|
return() {
|
|
1159
|
+
if (concatIterable.onDone) concatIterable.onDone();
|
|
1255
1160
|
return iterator.return();
|
|
1256
1161
|
},
|
|
1257
1162
|
throw() {
|
|
1163
|
+
if (concatIterable.onDone) concatIterable.onDone();
|
|
1258
1164
|
return iterator.throw();
|
|
1259
1165
|
}
|
|
1260
1166
|
};
|
|
1261
|
-
return concatIterator;
|
|
1262
1167
|
};
|
|
1263
1168
|
return concatIterable;
|
|
1264
1169
|
}
|
|
1170
|
+
|
|
1171
|
+
flatMap(callback) {
|
|
1172
|
+
let mappedIterable = new RangeIterable();
|
|
1173
|
+
mappedIterable.iterate = (async) => {
|
|
1174
|
+
let iterator = this.iterator = this.iterate(async);
|
|
1175
|
+
let currentSubIterator;
|
|
1176
|
+
return {
|
|
1177
|
+
next() {
|
|
1178
|
+
do {
|
|
1179
|
+
if (currentSubIterator) {
|
|
1180
|
+
let result = currentSubIterator.next();
|
|
1181
|
+
if (!result.done) {
|
|
1182
|
+
return result;
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
let result = iterator.next();
|
|
1186
|
+
if (result.done) {
|
|
1187
|
+
if (mappedIterable.onDone) mappedIterable.onDone();
|
|
1188
|
+
return result;
|
|
1189
|
+
}
|
|
1190
|
+
let value = callback(result.value);
|
|
1191
|
+
if (Array.isArray(value) || value instanceof RangeIterable)
|
|
1192
|
+
currentSubIterator = value[Symbol.iterator]();
|
|
1193
|
+
else {
|
|
1194
|
+
currentSubIterator = null;
|
|
1195
|
+
return { value };
|
|
1196
|
+
}
|
|
1197
|
+
} while(true);
|
|
1198
|
+
},
|
|
1199
|
+
return() {
|
|
1200
|
+
if (mappedIterable.onDone) mappedIterable.onDone();
|
|
1201
|
+
if (currentSubIterator)
|
|
1202
|
+
currentSubIterator.return();
|
|
1203
|
+
return iterator.return();
|
|
1204
|
+
},
|
|
1205
|
+
throw() {
|
|
1206
|
+
if (mappedIterable.onDone) mappedIterable.onDone();
|
|
1207
|
+
if (currentSubIterator)
|
|
1208
|
+
currentSubIterator.throw();
|
|
1209
|
+
return iterator.throw();
|
|
1210
|
+
}
|
|
1211
|
+
};
|
|
1212
|
+
};
|
|
1213
|
+
return mappedIterable;
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
slice(start, end) {
|
|
1217
|
+
return this.map((element, i) => {
|
|
1218
|
+
if (i < start)
|
|
1219
|
+
return SKIP;
|
|
1220
|
+
if (i >= end) {
|
|
1221
|
+
DONE.value = element;
|
|
1222
|
+
return DONE;
|
|
1223
|
+
}
|
|
1224
|
+
return element;
|
|
1225
|
+
});
|
|
1226
|
+
}
|
|
1265
1227
|
next() {
|
|
1266
1228
|
if (!this.iterator)
|
|
1267
1229
|
this.iterator = this.iterate();
|
|
@@ -1271,7 +1233,7 @@ class RangeIterable {
|
|
|
1271
1233
|
if (this.asArray && this.asArray.forEach) {
|
|
1272
1234
|
return this.asArray;
|
|
1273
1235
|
}
|
|
1274
|
-
throw new Error('Can not serialize async
|
|
1236
|
+
throw new Error('Can not serialize async iterables without first calling resolveJSON');
|
|
1275
1237
|
//return Array.from(this)
|
|
1276
1238
|
}
|
|
1277
1239
|
get asArray() {
|
|
@@ -1281,6 +1243,7 @@ class RangeIterable {
|
|
|
1281
1243
|
let iterator = this.iterate();
|
|
1282
1244
|
let array = [];
|
|
1283
1245
|
let iterable = this;
|
|
1246
|
+
Object.defineProperty(array, 'iterable', { value: iterable });
|
|
1284
1247
|
function next(result) {
|
|
1285
1248
|
while (result.done !== true) {
|
|
1286
1249
|
if (result.then) {
|
|
@@ -1290,7 +1253,6 @@ class RangeIterable {
|
|
|
1290
1253
|
}
|
|
1291
1254
|
result = iterator.next();
|
|
1292
1255
|
}
|
|
1293
|
-
array.iterable = iterable;
|
|
1294
1256
|
resolve(iterable._asArray = array);
|
|
1295
1257
|
}
|
|
1296
1258
|
next(iterator.next());
|
|
@@ -1302,146 +1264,173 @@ class RangeIterable {
|
|
|
1302
1264
|
return this.asArray;
|
|
1303
1265
|
}
|
|
1304
1266
|
}
|
|
1267
|
+
RangeIterable.prototype.DONE = DONE;
|
|
1268
|
+
|
|
1269
|
+
const REUSE_BUFFER_MODE = 512;
|
|
1270
|
+
const writeUint32Key = (key, target, start) => {
|
|
1271
|
+
(target.dataView || (target.dataView = new DataView(target.buffer, 0, target.length))).setUint32(start, key, true);
|
|
1272
|
+
return start + 4;
|
|
1273
|
+
};
|
|
1274
|
+
const readUint32Key = (target, start) => {
|
|
1275
|
+
return (target.dataView || (target.dataView = new DataView(target.buffer, 0, target.length))).getUint32(start, true);
|
|
1276
|
+
};
|
|
1277
|
+
const writeBufferKey = (key, target, start) => {
|
|
1278
|
+
target.set(key, start);
|
|
1279
|
+
return key.length + start;
|
|
1280
|
+
};
|
|
1281
|
+
const Uint8ArraySlice$1 = Uint8Array.prototype.slice;
|
|
1282
|
+
const readBufferKey = (target, start, end) => {
|
|
1283
|
+
return Uint8ArraySlice$1.call(target, start, end);
|
|
1284
|
+
};
|
|
1285
|
+
|
|
1286
|
+
let lastEncodedValue, bytes;
|
|
1287
|
+
function applyKeyHandling(store) {
|
|
1288
|
+
if (store.encoding == 'ordered-binary') {
|
|
1289
|
+
store.encoder = store.decoder = {
|
|
1290
|
+
writeKey: orderedBinary.writeKey,
|
|
1291
|
+
readKey: orderedBinary.readKey,
|
|
1292
|
+
};
|
|
1293
|
+
}
|
|
1294
|
+
if (store.encoder && store.encoder.writeKey && !store.encoder.encode) {
|
|
1295
|
+
store.encoder.encode = function(value, mode) {
|
|
1296
|
+
if (typeof value !== 'object' && value && value === lastEncodedValue) ; else {
|
|
1297
|
+
lastEncodedValue = value;
|
|
1298
|
+
bytes = saveKey(value, this.writeKey, false, store.maxKeySize);
|
|
1299
|
+
}
|
|
1300
|
+
if (bytes.end > 0 && !(REUSE_BUFFER_MODE & mode)) {
|
|
1301
|
+
return bytes.subarray(bytes.start, bytes.end);
|
|
1302
|
+
}
|
|
1303
|
+
return bytes;
|
|
1304
|
+
};
|
|
1305
|
+
store.encoder.copyBuffers = true; // just an indicator for the buffer reuse in write.js
|
|
1306
|
+
}
|
|
1307
|
+
if (store.decoder && store.decoder.readKey && !store.decoder.decode) {
|
|
1308
|
+
store.decoder.decode = function(buffer) { return this.readKey(buffer, 0, buffer.length); };
|
|
1309
|
+
store.decoderCopies = true;
|
|
1310
|
+
}
|
|
1311
|
+
if (store.keyIsUint32 || store.keyEncoding == 'uint32') {
|
|
1312
|
+
store.writeKey = writeUint32Key;
|
|
1313
|
+
store.readKey = readUint32Key;
|
|
1314
|
+
} else if (store.keyIsBuffer || store.keyEncoding == 'binary') {
|
|
1315
|
+
store.writeKey = writeBufferKey;
|
|
1316
|
+
store.readKey = readBufferKey;
|
|
1317
|
+
} else if (store.keyEncoder) {
|
|
1318
|
+
store.writeKey = store.keyEncoder.writeKey;
|
|
1319
|
+
store.readKey = store.keyEncoder.readKey;
|
|
1320
|
+
} else {
|
|
1321
|
+
store.writeKey = orderedBinary.writeKey;
|
|
1322
|
+
store.readKey = orderedBinary.readKey;
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1305
1325
|
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
}
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
}
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
saveBuffer.buffer.address = getAddress(saveBuffer);
|
|
1359
|
-
saveDataAddress = saveBuffer.buffer.address;
|
|
1360
|
-
// TODO: Conditionally only do this for key sequences?
|
|
1361
|
-
saveDataView.setUint32(savePosition, 0xffffffff);
|
|
1362
|
-
saveDataView.setFloat64(savePosition + 4, saveDataAddress, true); // save a pointer from the old buffer to the new address for the sake of the prefetch sequences
|
|
1363
|
-
saveBuffer.dataView = saveDataView = new DataView(saveBuffer.buffer, saveBuffer.byteOffset, saveBuffer.byteLength);
|
|
1364
|
-
savePosition = 0;
|
|
1365
|
-
}
|
|
1366
|
-
function saveKey(key, writeKey, saveTo, maxKeySize) {
|
|
1367
|
-
if (savePosition > 7800) {
|
|
1368
|
-
allocateSaveBuffer();
|
|
1369
|
-
}
|
|
1370
|
-
let start = savePosition;
|
|
1371
|
-
try {
|
|
1372
|
-
savePosition = key === undefined ? start + 4 :
|
|
1373
|
-
writeKey(key, saveBuffer, start + 4);
|
|
1374
|
-
} catch (error) {
|
|
1375
|
-
saveBuffer.fill(0, start + 4); // restore zeros
|
|
1376
|
-
if (error.name == 'RangeError') {
|
|
1377
|
-
if (8180 - start < maxKeySize) {
|
|
1378
|
-
allocateSaveBuffer(); // try again:
|
|
1379
|
-
return saveKey(key, writeKey, saveTo, maxKeySize);
|
|
1380
|
-
}
|
|
1381
|
-
throw new Error('Key was too large, max key size is ' + maxKeySize);
|
|
1382
|
-
} else
|
|
1383
|
-
throw error;
|
|
1384
|
-
}
|
|
1385
|
-
let length = savePosition - start - 4;
|
|
1386
|
-
if (length > maxKeySize) {
|
|
1387
|
-
throw new Error('Key of size ' + length + ' was too large, max key size is ' + maxKeySize);
|
|
1388
|
-
}
|
|
1389
|
-
if (savePosition >= 8160) { // need to reserve enough room at the end for pointers
|
|
1390
|
-
savePosition = start; // reset position
|
|
1391
|
-
allocateSaveBuffer(); // try again:
|
|
1392
|
-
return saveKey(key, writeKey, saveTo, maxKeySize);
|
|
1393
|
-
}
|
|
1394
|
-
if (saveTo) {
|
|
1395
|
-
saveDataView.setUint32(start, length, true); // save the length
|
|
1396
|
-
saveTo.saveBuffer = saveBuffer;
|
|
1397
|
-
savePosition = (savePosition + 12) & 0xfffffc;
|
|
1398
|
-
return start + saveDataAddress;
|
|
1399
|
-
} else {
|
|
1400
|
-
saveBuffer.start = start + 4;
|
|
1401
|
-
saveBuffer.end = savePosition;
|
|
1402
|
-
savePosition = (savePosition + 7) & 0xfffff8; // full 64-bit word alignment since these are usually copied
|
|
1403
|
-
return saveBuffer;
|
|
1404
|
-
}
|
|
1326
|
+
let saveBuffer, saveDataView = { setFloat64() {}, setUint32() {} }, saveDataAddress;
|
|
1327
|
+
let savePosition$1 = 8000;
|
|
1328
|
+
let DYNAMIC_KEY_BUFFER_SIZE$1 = 8192;
|
|
1329
|
+
function allocateSaveBuffer() {
|
|
1330
|
+
saveBuffer = typeof Buffer != 'undefined' ? Buffer.alloc(DYNAMIC_KEY_BUFFER_SIZE$1) : new Uint8Array(DYNAMIC_KEY_BUFFER_SIZE$1);
|
|
1331
|
+
saveBuffer.buffer.address = getAddress(saveBuffer.buffer);
|
|
1332
|
+
saveDataAddress = saveBuffer.buffer.address;
|
|
1333
|
+
// TODO: Conditionally only do this for key sequences?
|
|
1334
|
+
saveDataView.setUint32(savePosition$1, 0xffffffff);
|
|
1335
|
+
saveDataView.setFloat64(savePosition$1 + 4, saveDataAddress, true); // save a pointer from the old buffer to the new address for the sake of the prefetch sequences
|
|
1336
|
+
saveDataView = saveBuffer.dataView || (saveBuffer.dataView = new DataView(saveBuffer.buffer, saveBuffer.byteOffset, saveBuffer.byteLength));
|
|
1337
|
+
savePosition$1 = 0;
|
|
1338
|
+
}
|
|
1339
|
+
function saveKey(key, writeKey, saveTo, maxKeySize, flags) {
|
|
1340
|
+
if (savePosition$1 > 7800) {
|
|
1341
|
+
allocateSaveBuffer();
|
|
1342
|
+
}
|
|
1343
|
+
let start = savePosition$1;
|
|
1344
|
+
try {
|
|
1345
|
+
savePosition$1 = key === undefined ? start + 4 :
|
|
1346
|
+
writeKey(key, saveBuffer, start + 4);
|
|
1347
|
+
} catch (error) {
|
|
1348
|
+
saveBuffer.fill(0, start + 4); // restore zeros
|
|
1349
|
+
if (error.name == 'RangeError') {
|
|
1350
|
+
if (8180 - start < maxKeySize) {
|
|
1351
|
+
allocateSaveBuffer(); // try again:
|
|
1352
|
+
return saveKey(key, writeKey, saveTo, maxKeySize);
|
|
1353
|
+
}
|
|
1354
|
+
throw new Error('Key was too large, max key size is ' + maxKeySize);
|
|
1355
|
+
} else
|
|
1356
|
+
throw error;
|
|
1357
|
+
}
|
|
1358
|
+
let length = savePosition$1 - start - 4;
|
|
1359
|
+
if (length > maxKeySize) {
|
|
1360
|
+
throw new Error('Key of size ' + length + ' was too large, max key size is ' + maxKeySize);
|
|
1361
|
+
}
|
|
1362
|
+
if (savePosition$1 >= 8160) { // need to reserve enough room at the end for pointers
|
|
1363
|
+
savePosition$1 = start; // reset position
|
|
1364
|
+
allocateSaveBuffer(); // try again:
|
|
1365
|
+
return saveKey(key, writeKey, saveTo, maxKeySize);
|
|
1366
|
+
}
|
|
1367
|
+
if (saveTo) {
|
|
1368
|
+
saveDataView.setUint32(start, flags ? length | flags : length, true); // save the length
|
|
1369
|
+
saveTo.saveBuffer = saveBuffer;
|
|
1370
|
+
savePosition$1 = (savePosition$1 + 12) & 0xfffffc;
|
|
1371
|
+
return start + saveDataAddress;
|
|
1372
|
+
} else {
|
|
1373
|
+
saveBuffer.start = start + 4;
|
|
1374
|
+
saveBuffer.end = savePosition$1;
|
|
1375
|
+
savePosition$1 = (savePosition$1 + 7) & 0xfffff8; // full 64-bit word alignment since these are usually copied
|
|
1376
|
+
return saveBuffer;
|
|
1377
|
+
}
|
|
1405
1378
|
}
|
|
1406
1379
|
|
|
1380
|
+
const IF_EXISTS = 3.542694326329068e-103;
|
|
1407
1381
|
const ITERATOR_DONE = { done: true, value: undefined };
|
|
1408
1382
|
const Uint8ArraySlice = Uint8Array.prototype.slice;
|
|
1409
|
-
|
|
1410
|
-
|
|
1383
|
+
let getValueBytes = globalBuffer;
|
|
1384
|
+
if (!getValueBytes.maxLength) {
|
|
1385
|
+
getValueBytes.maxLength = getValueBytes.length;
|
|
1386
|
+
getValueBytes.isGlobal = true;
|
|
1387
|
+
Object.defineProperty(getValueBytes, 'length', { value: getValueBytes.length, writable: true, configurable: true });
|
|
1388
|
+
}
|
|
1411
1389
|
const START_ADDRESS_POSITION = 4064;
|
|
1412
1390
|
const NEW_BUFFER_THRESHOLD = 0x8000;
|
|
1391
|
+
const SOURCE_SYMBOL = Symbol.for('source');
|
|
1392
|
+
const UNMODIFIED = {};
|
|
1393
|
+
let mmaps = [];
|
|
1413
1394
|
|
|
1414
1395
|
function addReadMethods(LMDBStore, {
|
|
1415
|
-
maxKeySize, env, keyBytes, keyBytesView, getLastVersion
|
|
1396
|
+
maxKeySize, env, keyBytes, keyBytesView, getLastVersion, getLastTxnId
|
|
1416
1397
|
}) {
|
|
1417
1398
|
let readTxn, readTxnRenewed, asSafeBuffer = false;
|
|
1418
1399
|
let renewId = 1;
|
|
1400
|
+
let outstandingReads = 0;
|
|
1419
1401
|
Object.assign(LMDBStore.prototype, {
|
|
1420
|
-
getString(id) {
|
|
1421
|
-
|
|
1422
|
-
let string = getStringByBinary(this.dbAddress, this.writeKey(id, keyBytes, 0));
|
|
1402
|
+
getString(id, options) {
|
|
1403
|
+
let txn = env.writeTxn || (options && options.transaction) || (readTxnRenewed ? readTxn : renewReadTxn(this));
|
|
1404
|
+
let string = getStringByBinary(this.dbAddress, this.writeKey(id, keyBytes, 0), txn.address || 0);
|
|
1423
1405
|
if (typeof string === 'number') { // indicates the buffer wasn't large enough
|
|
1424
1406
|
this._allocateGetBuffer(string);
|
|
1425
1407
|
// and then try again
|
|
1426
|
-
string = getStringByBinary(this.dbAddress, this.writeKey(id, keyBytes, 0));
|
|
1408
|
+
string = getStringByBinary(this.dbAddress, this.writeKey(id, keyBytes, 0), txn.address || 0);
|
|
1427
1409
|
}
|
|
1428
1410
|
if (string)
|
|
1429
1411
|
this.lastSize = string.length;
|
|
1430
1412
|
return string;
|
|
1431
1413
|
},
|
|
1432
|
-
getBinaryFast(id) {
|
|
1433
|
-
|
|
1434
|
-
let
|
|
1414
|
+
getBinaryFast(id, options) {
|
|
1415
|
+
let rc;
|
|
1416
|
+
let txn = env.writeTxn || (options && options.transaction) || (readTxnRenewed ? readTxn : renewReadTxn(this));
|
|
1417
|
+
rc = this.lastSize = getByBinary(this.dbAddress, this.writeKey(id, keyBytes, 0), (options && options.ifNotTxnId) || 0, txn.address || 0);
|
|
1435
1418
|
if (rc < 0) {
|
|
1436
1419
|
if (rc == -30798) // MDB_NOTFOUND
|
|
1437
1420
|
return; // undefined
|
|
1421
|
+
if (rc == -30004) // txn id matched
|
|
1422
|
+
return UNMODIFIED;
|
|
1438
1423
|
if (rc == -30781 /*MDB_BAD_VALSIZE*/ && this.writeKey(id, keyBytes, 0) == 0)
|
|
1439
1424
|
throw new Error(id === undefined ?
|
|
1440
1425
|
'A key is required for get, but is undefined' :
|
|
1441
1426
|
'Zero length key is not allowed in LMDB');
|
|
1442
1427
|
if (rc == -30000) // int32 overflow, read uint32
|
|
1443
1428
|
rc = this.lastSize = keyBytesView.getUint32(0, true);
|
|
1444
|
-
else
|
|
1429
|
+
else if (rc == -30001) {// shared buffer
|
|
1430
|
+
this.lastSize = keyBytesView.getUint32(0, true);
|
|
1431
|
+
let bufferId = keyBytesView.getUint32(4, true);
|
|
1432
|
+
return getMMapBuffer(bufferId, this.lastSize);
|
|
1433
|
+
} else
|
|
1445
1434
|
throw lmdbError(rc);
|
|
1446
1435
|
}
|
|
1447
1436
|
let compression = this.compression;
|
|
@@ -1449,13 +1438,89 @@ function addReadMethods(LMDBStore, {
|
|
|
1449
1438
|
if (rc > bytes.maxLength) {
|
|
1450
1439
|
// this means the target buffer wasn't big enough, so the get failed to copy all the data from the database, need to either grow or use special buffer
|
|
1451
1440
|
return this._returnLargeBuffer(
|
|
1452
|
-
() => getByBinary(this.dbAddress, this.writeKey(id, keyBytes, 0))
|
|
1453
|
-
() => getSharedByBinary(this.dbAddress, this.writeKey(id, keyBytes, 0)));
|
|
1441
|
+
() => getByBinary(this.dbAddress, this.writeKey(id, keyBytes, 0), 0, txn.address || 0));
|
|
1454
1442
|
}
|
|
1455
1443
|
bytes.length = this.lastSize;
|
|
1456
1444
|
return bytes;
|
|
1457
1445
|
},
|
|
1458
|
-
|
|
1446
|
+
getBFAsync(id, options, callback) {
|
|
1447
|
+
let txn = env.writeTxn || (options && options.transaction) || (readTxnRenewed ? readTxn : renewReadTxn(this));
|
|
1448
|
+
txn.refCount = (txn.refCount || 0) + 1;
|
|
1449
|
+
outstandingReads++;
|
|
1450
|
+
let address = recordReadInstruction(txn.address, this.db.dbi, id, this.writeKey, maxKeySize, ( rc, bufferId, offset, size ) => {
|
|
1451
|
+
if (rc && rc !== 1)
|
|
1452
|
+
callback(lmdbError(rc));
|
|
1453
|
+
outstandingReads--;
|
|
1454
|
+
let buffer = mmaps[bufferId];
|
|
1455
|
+
if (!buffer) {
|
|
1456
|
+
buffer = mmaps[bufferId] = getSharedBuffer(bufferId, env.address);
|
|
1457
|
+
}
|
|
1458
|
+
//console.log({bufferId, offset, size})
|
|
1459
|
+
if (buffer.isSharedMap) {
|
|
1460
|
+
// using LMDB shared memory
|
|
1461
|
+
// TODO: We may want explicit support for clearing aborting the transaction on the next event turn,
|
|
1462
|
+
// but for now we are relying on the GC to cleanup transaction for larger blocks of memory
|
|
1463
|
+
let bytes = new Uint8Array(buffer, offset, size);
|
|
1464
|
+
bytes.txn = txn;
|
|
1465
|
+
callback(bytes, 0, size);
|
|
1466
|
+
} else {
|
|
1467
|
+
// using copied memory
|
|
1468
|
+
txn.done(); // decrement and possibly abort
|
|
1469
|
+
callback(buffer, offset, size);
|
|
1470
|
+
}
|
|
1471
|
+
});
|
|
1472
|
+
if (address) {
|
|
1473
|
+
startRead(address, () => {
|
|
1474
|
+
resolveReads();
|
|
1475
|
+
});
|
|
1476
|
+
}
|
|
1477
|
+
},
|
|
1478
|
+
getAsync(id, options, callback) {
|
|
1479
|
+
let promise;
|
|
1480
|
+
if (!callback)
|
|
1481
|
+
promise = new Promise(resolve => callback = resolve);
|
|
1482
|
+
this.getBFAsync(id, options, (buffer, offset, size) => {
|
|
1483
|
+
if (this.useVersions) {
|
|
1484
|
+
// TODO: And get the version
|
|
1485
|
+
offset += 8;
|
|
1486
|
+
size -= 8;
|
|
1487
|
+
}
|
|
1488
|
+
let bytes = new Uint8Array(buffer, offset, size);
|
|
1489
|
+
let value;
|
|
1490
|
+
if (this.decoder) {
|
|
1491
|
+
// the decoder potentially uses the data from the buffer in the future and needs a stable buffer
|
|
1492
|
+
value = bytes && this.decoder.decode(bytes);
|
|
1493
|
+
} else if (this.encoding == 'binary') {
|
|
1494
|
+
value = bytes;
|
|
1495
|
+
} else {
|
|
1496
|
+
value = Buffer.prototype.utf8Slice.call(bytes, 0, size);
|
|
1497
|
+
if (this.encoding == 'json' && value)
|
|
1498
|
+
value = JSON.parse(value);
|
|
1499
|
+
}
|
|
1500
|
+
callback(value);
|
|
1501
|
+
});
|
|
1502
|
+
return promise;
|
|
1503
|
+
},
|
|
1504
|
+
retain(data, options) {
|
|
1505
|
+
if (!data)
|
|
1506
|
+
return
|
|
1507
|
+
let source = data[SOURCE_SYMBOL];
|
|
1508
|
+
let buffer = source ? source.bytes : data;
|
|
1509
|
+
if (!buffer.isGlobal && !env.writeTxn) {
|
|
1510
|
+
let txn = options?.transaction || (readTxnRenewed ? readTxn : renewReadTxn(this));
|
|
1511
|
+
buffer.txn = txn;
|
|
1512
|
+
txn.refCount = (txn.refCount || 0) + 1;
|
|
1513
|
+
return data;
|
|
1514
|
+
} else {
|
|
1515
|
+
buffer = Uint8ArraySlice.call(buffer, 0, this.lastSize);
|
|
1516
|
+
if (source) {
|
|
1517
|
+
source.bytes = buffer;
|
|
1518
|
+
return data;
|
|
1519
|
+
} else
|
|
1520
|
+
return buffer;
|
|
1521
|
+
}
|
|
1522
|
+
},
|
|
1523
|
+
_returnLargeBuffer(getFast) {
|
|
1459
1524
|
let bytes;
|
|
1460
1525
|
let compression = this.compression;
|
|
1461
1526
|
if (asSafeBuffer && this.lastSize > NEW_BUFFER_THRESHOLD) {
|
|
@@ -1467,7 +1532,7 @@ function addReadMethods(LMDBStore, {
|
|
|
1467
1532
|
let dictionary = compression.dictionary || [];
|
|
1468
1533
|
let dictLength = (dictionary.length >> 3) << 3;// make sure it is word-aligned
|
|
1469
1534
|
bytes = makeReusableBuffer(this.lastSize);
|
|
1470
|
-
compression.setBuffer(bytes, this.lastSize, dictionary, dictLength);
|
|
1535
|
+
compression.setBuffer(bytes.buffer, bytes.byteOffset, this.lastSize, dictionary, dictLength);
|
|
1471
1536
|
compression.getValueBytes = bytes;
|
|
1472
1537
|
} else {
|
|
1473
1538
|
bytesToRestore = getValueBytes;
|
|
@@ -1477,7 +1542,7 @@ function addReadMethods(LMDBStore, {
|
|
|
1477
1542
|
} finally {
|
|
1478
1543
|
if (compression) {
|
|
1479
1544
|
let dictLength = (compression.dictionary.length >> 3) << 3;
|
|
1480
|
-
compression.setBuffer(bytesToRestore, bytesToRestore.maxLength, compression.dictionary, dictLength);
|
|
1545
|
+
compression.setBuffer(bytesToRestore.buffer, bytesToRestore.byteOffset, bytesToRestore.maxLength, compression.dictionary, dictLength);
|
|
1481
1546
|
compression.getValueBytes = bytesToRestore;
|
|
1482
1547
|
} else {
|
|
1483
1548
|
setGlobalBuffer(bytesToRestore);
|
|
@@ -1486,12 +1551,6 @@ function addReadMethods(LMDBStore, {
|
|
|
1486
1551
|
}
|
|
1487
1552
|
return bytes;
|
|
1488
1553
|
}
|
|
1489
|
-
if (this.lastSize > NEW_BUFFER_THRESHOLD && !compression) {
|
|
1490
|
-
// for large binary objects, cheaper to make a buffer that directly points at the shared LMDB memory to avoid copying a large amount of memory, but only for large data since there is significant overhead to instantiating the buffer
|
|
1491
|
-
if (globalThis.__lmdb_last_shared__ && detachBuffer) // we have to detach the last one, or else could crash due to two buffers pointing at same location
|
|
1492
|
-
detachBuffer(globalThis.__lmdb_last_shared__.buffer);
|
|
1493
|
-
return globalThis.__lmdb_last_shared__ = getShared();
|
|
1494
|
-
}
|
|
1495
1554
|
// grow our shared/static buffer to accomodate the size of the data
|
|
1496
1555
|
bytes = this._allocateGetBuffer(this.lastSize);
|
|
1497
1556
|
// and try again
|
|
@@ -1503,13 +1562,13 @@ function addReadMethods(LMDBStore, {
|
|
|
1503
1562
|
let newLength = Math.min(Math.max(lastSize * 2, 0x1000), 0xfffffff8);
|
|
1504
1563
|
let bytes;
|
|
1505
1564
|
if (this.compression) {
|
|
1506
|
-
let dictionary = this.compression.dictionary ||
|
|
1565
|
+
let dictionary = this.compression.dictionary || Buffer.allocUnsafeSlow(0);
|
|
1507
1566
|
let dictLength = (dictionary.length >> 3) << 3;// make sure it is word-aligned
|
|
1508
|
-
bytes =
|
|
1567
|
+
bytes = Buffer.allocUnsafeSlow(newLength + dictLength);
|
|
1509
1568
|
bytes.set(dictionary); // copy dictionary into start
|
|
1510
1569
|
// the section after the dictionary is the target area for get values
|
|
1511
1570
|
bytes = bytes.subarray(dictLength);
|
|
1512
|
-
this.compression.setBuffer(bytes, newLength, dictionary, dictLength);
|
|
1571
|
+
this.compression.setBuffer(bytes.buffer, bytes.byteOffset, newLength, dictionary, dictLength);
|
|
1513
1572
|
bytes.maxLength = newLength;
|
|
1514
1573
|
Object.defineProperty(bytes, 'length', { value: newLength, writable: true, configurable: true });
|
|
1515
1574
|
this.compression.getValueBytes = bytes;
|
|
@@ -1517,41 +1576,51 @@ function addReadMethods(LMDBStore, {
|
|
|
1517
1576
|
bytes = makeReusableBuffer(newLength);
|
|
1518
1577
|
setGlobalBuffer(getValueBytes = bytes);
|
|
1519
1578
|
}
|
|
1520
|
-
bytes.
|
|
1579
|
+
bytes.isGlobal = true;
|
|
1521
1580
|
return bytes;
|
|
1522
1581
|
},
|
|
1523
|
-
getBinary(id) {
|
|
1582
|
+
getBinary(id, options) {
|
|
1524
1583
|
try {
|
|
1525
1584
|
asSafeBuffer = true;
|
|
1526
|
-
let fastBuffer = this.getBinaryFast(id);
|
|
1527
|
-
return fastBuffer && (fastBuffer.
|
|
1585
|
+
let fastBuffer = this.getBinaryFast(id, options);
|
|
1586
|
+
return fastBuffer && (fastBuffer.isGlobal ? Uint8ArraySlice.call(fastBuffer, 0, this.lastSize) : fastBuffer);
|
|
1528
1587
|
} finally {
|
|
1529
1588
|
asSafeBuffer = false;
|
|
1530
1589
|
}
|
|
1531
1590
|
},
|
|
1532
|
-
|
|
1591
|
+
getSharedBinary(id, options) {
|
|
1592
|
+
let fastBuffer = this.getBinaryFast(id, options);
|
|
1593
|
+
if (fastBuffer) {
|
|
1594
|
+
if (fastBuffer.isGlobal || writeTxn)
|
|
1595
|
+
return Uint8ArraySlice.call(fastBuffer, 0, this.lastSize)
|
|
1596
|
+
fastBuffer.txn = (options && options.transaction);
|
|
1597
|
+
options.transaction.refCount = (options.transaction.refCount || 0) + 1;
|
|
1598
|
+
return fastBuffer;
|
|
1599
|
+
}
|
|
1600
|
+
},
|
|
1601
|
+
get(id, options) {
|
|
1533
1602
|
if (this.decoderCopies) {
|
|
1534
1603
|
// the decoder copies any data, so we can use the fast binary retrieval that overwrites the same buffer space
|
|
1535
|
-
let bytes = this.getBinaryFast(id);
|
|
1536
|
-
return bytes && this.decoder.decode(bytes);
|
|
1604
|
+
let bytes = this.getBinaryFast(id, options);
|
|
1605
|
+
return bytes && (bytes == UNMODIFIED ? UNMODIFIED : this.decoder.decode(bytes, options));
|
|
1537
1606
|
}
|
|
1538
1607
|
if (this.encoding == 'binary')
|
|
1539
|
-
return this.getBinary(id);
|
|
1608
|
+
return this.getBinary(id, options);
|
|
1540
1609
|
if (this.decoder) {
|
|
1541
1610
|
// the decoder potentially uses the data from the buffer in the future and needs a stable buffer
|
|
1542
|
-
let bytes = this.getBinary(id);
|
|
1543
|
-
return bytes && this.decoder.decode(bytes);
|
|
1611
|
+
let bytes = this.getBinary(id, options);
|
|
1612
|
+
return bytes && (bytes == UNMODIFIED ? UNMODIFIED : this.decoder.decode(bytes));
|
|
1544
1613
|
}
|
|
1545
1614
|
|
|
1546
|
-
let result = this.getString(id);
|
|
1615
|
+
let result = this.getString(id, options);
|
|
1547
1616
|
if (result) {
|
|
1548
1617
|
if (this.encoding == 'json')
|
|
1549
1618
|
return JSON.parse(result);
|
|
1550
1619
|
}
|
|
1551
1620
|
return result;
|
|
1552
1621
|
},
|
|
1553
|
-
getEntry(id) {
|
|
1554
|
-
let value = this.get(id);
|
|
1622
|
+
getEntry(id, options) {
|
|
1623
|
+
let value = this.get(id, options);
|
|
1555
1624
|
if (value !== undefined) {
|
|
1556
1625
|
if (this.useVersions)
|
|
1557
1626
|
return {
|
|
@@ -1570,8 +1639,11 @@ function addReadMethods(LMDBStore, {
|
|
|
1570
1639
|
resetReadTxn();
|
|
1571
1640
|
},
|
|
1572
1641
|
_commitReadTxn() {
|
|
1573
|
-
if (readTxn)
|
|
1642
|
+
if (readTxn) {
|
|
1643
|
+
readTxn.isCommitted = true;
|
|
1574
1644
|
readTxn.commit();
|
|
1645
|
+
}
|
|
1646
|
+
lastReadTxnRef = null;
|
|
1575
1647
|
readTxnRenewed = null;
|
|
1576
1648
|
readTxn = null;
|
|
1577
1649
|
},
|
|
@@ -1580,15 +1652,12 @@ function addReadMethods(LMDBStore, {
|
|
|
1580
1652
|
renewReadTxn(this);
|
|
1581
1653
|
},
|
|
1582
1654
|
doesExist(key, versionOrValue) {
|
|
1583
|
-
if (!env.writeTxn)
|
|
1584
|
-
readTxnRenewed ? readTxn : renewReadTxn(this);
|
|
1585
1655
|
if (versionOrValue == null) {
|
|
1586
1656
|
// undefined means the entry exists, null is used specifically to check for the entry *not* existing
|
|
1587
1657
|
return (this.getBinaryFast(key) === undefined) == (versionOrValue === null);
|
|
1588
1658
|
}
|
|
1589
1659
|
else if (this.useVersions) {
|
|
1590
|
-
this.getBinaryFast(key);
|
|
1591
|
-
return this.getBinaryFast(key) !== undefined && getLastVersion() === versionOrValue;
|
|
1660
|
+
return this.getBinaryFast(key) !== undefined && (versionOrValue === IF_EXISTS || getLastVersion() === versionOrValue);
|
|
1592
1661
|
}
|
|
1593
1662
|
else {
|
|
1594
1663
|
if (versionOrValue && versionOrValue['\x10binary-data\x02'])
|
|
@@ -1654,28 +1723,39 @@ function addReadMethods(LMDBStore, {
|
|
|
1654
1723
|
let cursor, cursorRenewId, cursorAddress;
|
|
1655
1724
|
let txn;
|
|
1656
1725
|
let flags = (includeValues ? 0x100 : 0) | (reverse ? 0x400 : 0) |
|
|
1657
|
-
(valuesForKey ? 0x800 : 0) | (options.exactMatch ? 0x4000 : 0)
|
|
1726
|
+
(valuesForKey ? 0x800 : 0) | (options.exactMatch ? 0x4000 : 0) |
|
|
1727
|
+
(options.inclusiveEnd ? 0x8000 : 0) |
|
|
1728
|
+
(options.exclusiveStart ? 0x10000 : 0);
|
|
1658
1729
|
let store = this;
|
|
1659
1730
|
function resetCursor() {
|
|
1660
1731
|
try {
|
|
1661
1732
|
if (cursor)
|
|
1662
1733
|
finishCursor();
|
|
1663
|
-
let
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1734
|
+
let txnAddress;
|
|
1735
|
+
txn = options.transaction;
|
|
1736
|
+
if (txn) {
|
|
1737
|
+
if (txn.isDone) throw new Error('Can not iterate on range with transaction that is already' +
|
|
1738
|
+
' done');
|
|
1739
|
+
txnAddress = txn.address;
|
|
1740
|
+
cursor = null;
|
|
1741
|
+
} else {
|
|
1742
|
+
let writeTxn = env.writeTxn;
|
|
1743
|
+
if (writeTxn)
|
|
1744
|
+
snapshot = false;
|
|
1745
|
+
txn = env.writeTxn || options.transaction || (readTxnRenewed ? readTxn : renewReadTxn(store));
|
|
1746
|
+
cursor = !writeTxn && db.availableCursor;
|
|
1747
|
+
}
|
|
1668
1748
|
if (cursor) {
|
|
1669
1749
|
db.availableCursor = null;
|
|
1670
1750
|
flags |= 0x2000;
|
|
1671
1751
|
} else {
|
|
1672
|
-
cursor = new Cursor(db);
|
|
1752
|
+
cursor = new Cursor(db, txnAddress || 0);
|
|
1673
1753
|
}
|
|
1674
1754
|
cursorAddress = cursor.address;
|
|
1675
|
-
txn.
|
|
1755
|
+
txn.refCount = (txn.refCount || 0) + 1; // track transaction so we always use the same one
|
|
1676
1756
|
if (snapshot === false) {
|
|
1677
1757
|
cursorRenewId = renewId; // use shared read transaction
|
|
1678
|
-
txn.
|
|
1758
|
+
txn.renewingRefCount = (txn.renewingRefCount || 0) + 1; // need to know how many are renewing cursors
|
|
1679
1759
|
}
|
|
1680
1760
|
} catch(error) {
|
|
1681
1761
|
if (cursor) {
|
|
@@ -1696,6 +1776,9 @@ function addReadMethods(LMDBStore, {
|
|
|
1696
1776
|
return count;
|
|
1697
1777
|
}
|
|
1698
1778
|
function position$1(offset) {
|
|
1779
|
+
if (!env.address) {
|
|
1780
|
+
throw new Error('Can not iterate on a closed database');
|
|
1781
|
+
}
|
|
1699
1782
|
let keySize = currentKey === undefined ? 0 : store.writeKey(currentKey, keyBytes, 0);
|
|
1700
1783
|
let endAddress;
|
|
1701
1784
|
if (valuesForKey) {
|
|
@@ -1723,9 +1806,11 @@ function addReadMethods(LMDBStore, {
|
|
|
1723
1806
|
function finishCursor() {
|
|
1724
1807
|
if (txn.isDone)
|
|
1725
1808
|
return;
|
|
1809
|
+
if (iterable.onDone)
|
|
1810
|
+
iterable.onDone();
|
|
1726
1811
|
if (cursorRenewId)
|
|
1727
|
-
txn.
|
|
1728
|
-
if (--txn.
|
|
1812
|
+
txn.renewingRefCount--;
|
|
1813
|
+
if (--txn.refCount <= 0 && txn.notCurrent) {
|
|
1729
1814
|
cursor.close();
|
|
1730
1815
|
txn.abort(); // this is no longer main read txn, abort it now that we are done
|
|
1731
1816
|
txn.isDone = true;
|
|
@@ -1752,7 +1837,7 @@ function addReadMethods(LMDBStore, {
|
|
|
1752
1837
|
if (keySize <= 0 ||
|
|
1753
1838
|
(count++ >= limit)) {
|
|
1754
1839
|
if (count < 0)
|
|
1755
|
-
lmdbError(count);
|
|
1840
|
+
lmdbError(count);
|
|
1756
1841
|
finishCursor();
|
|
1757
1842
|
return ITERATOR_DONE;
|
|
1758
1843
|
}
|
|
@@ -1767,24 +1852,27 @@ function addReadMethods(LMDBStore, {
|
|
|
1767
1852
|
if (includeValues) {
|
|
1768
1853
|
let value;
|
|
1769
1854
|
lastSize = keyBytesView.getUint32(0, true);
|
|
1770
|
-
let
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1855
|
+
let bufferId = keyBytesView.getUint32(4, true);
|
|
1856
|
+
let bytes;
|
|
1857
|
+
if (bufferId) {
|
|
1858
|
+
bytes = getMMapBuffer(bufferId, lastSize);
|
|
1859
|
+
} else {
|
|
1860
|
+
bytes = compression ? compression.getValueBytes : getValueBytes;
|
|
1861
|
+
if (lastSize > bytes.maxLength) {
|
|
1862
|
+
store.lastSize = lastSize;
|
|
1863
|
+
asSafeBuffer = store.encoding == 'binary';
|
|
1864
|
+
try {
|
|
1865
|
+
bytes = store._returnLargeBuffer(() => getCurrentValue(cursorAddress));
|
|
1866
|
+
} finally {
|
|
1867
|
+
asSafeBuffer = false;
|
|
1868
|
+
}
|
|
1869
|
+
} else
|
|
1870
|
+
bytes.length = lastSize;
|
|
1871
|
+
}
|
|
1784
1872
|
if (store.decoder) {
|
|
1785
1873
|
value = store.decoder.decode(bytes, lastSize);
|
|
1786
1874
|
} else if (store.encoding == 'binary')
|
|
1787
|
-
value = bytes.
|
|
1875
|
+
value = bytes.isGlobal ? Uint8ArraySlice.call(bytes, 0, lastSize) : bytes;
|
|
1788
1876
|
else {
|
|
1789
1877
|
value = bytes.toString('utf8', 0, lastSize);
|
|
1790
1878
|
if (store.encoding == 'json' && value)
|
|
@@ -1850,8 +1938,8 @@ function addReadMethods(LMDBStore, {
|
|
|
1850
1938
|
});
|
|
1851
1939
|
return promise;
|
|
1852
1940
|
},
|
|
1853
|
-
getSharedBufferForGet(id) {
|
|
1854
|
-
let txn =
|
|
1941
|
+
getSharedBufferForGet(id, options) {
|
|
1942
|
+
let txn = env.writeTxn || (options && options.transaction) || (readTxnRenewed ? readTxn : renewReadTxn(this));
|
|
1855
1943
|
this.lastSize = this.keyIsCompatibility ? txn.getBinaryShared(id) : this.db.get(this.writeKey(id, keyBytes, 0));
|
|
1856
1944
|
if (this.lastSize === -30798) { // not found code
|
|
1857
1945
|
return; //undefined
|
|
@@ -1873,7 +1961,13 @@ function addReadMethods(LMDBStore, {
|
|
|
1873
1961
|
let bufferHolder = {};
|
|
1874
1962
|
let lastBuffer;
|
|
1875
1963
|
for (let key of keys) {
|
|
1876
|
-
let position
|
|
1964
|
+
let position;
|
|
1965
|
+
if (key && key.key !== undefined && key.value !== undefined) {
|
|
1966
|
+
position = saveKey(key.value, this.writeKey, bufferHolder, maxKeySize, 0x80000000);
|
|
1967
|
+
saveKey(key.key, this.writeKey, bufferHolder, maxKeySize);
|
|
1968
|
+
} else {
|
|
1969
|
+
position = saveKey(key, this.writeKey, bufferHolder, maxKeySize);
|
|
1970
|
+
}
|
|
1877
1971
|
if (!startPosition)
|
|
1878
1972
|
startPosition = position;
|
|
1879
1973
|
if (bufferHolder.saveBuffer != lastBuffer) {
|
|
@@ -1883,7 +1977,9 @@ function addReadMethods(LMDBStore, {
|
|
|
1883
1977
|
}
|
|
1884
1978
|
}
|
|
1885
1979
|
saveKey(undefined, this.writeKey, bufferHolder, maxKeySize);
|
|
1980
|
+
outstandingReads++;
|
|
1886
1981
|
prefetch(this.dbAddress, startPosition, (error) => {
|
|
1982
|
+
outstandingReads--;
|
|
1887
1983
|
if (error)
|
|
1888
1984
|
console.error('Error with prefetch', buffers, bufferHolder); // partly exists to keep the buffers pinned in memory
|
|
1889
1985
|
else
|
|
@@ -1892,27 +1988,44 @@ function addReadMethods(LMDBStore, {
|
|
|
1892
1988
|
if (!callback)
|
|
1893
1989
|
return new Promise(resolve => callback = resolve);
|
|
1894
1990
|
},
|
|
1991
|
+
useReadTransaction() {
|
|
1992
|
+
let txn = readTxnRenewed ? readTxn : renewReadTxn(this);
|
|
1993
|
+
if (!txn.use) {
|
|
1994
|
+
throw new Error('Can not use read transaction from a closed database');
|
|
1995
|
+
}
|
|
1996
|
+
txn.use();
|
|
1997
|
+
return txn;
|
|
1998
|
+
},
|
|
1895
1999
|
close(callback) {
|
|
1896
2000
|
this.status = 'closing';
|
|
2001
|
+
let txnPromise;
|
|
1897
2002
|
if (this.isRoot) {
|
|
1898
|
-
if
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
renew() {
|
|
2003
|
+
// if it is root, we need to abort and/or wait for transactions to finish
|
|
2004
|
+
if (readTxn) readTxn.abort();
|
|
2005
|
+
else readTxn = {};
|
|
2006
|
+
readTxn.isDone = true;
|
|
2007
|
+
Object.defineProperty(readTxn,'renew', {
|
|
2008
|
+
value: () => {
|
|
1905
2009
|
throw new Error('Can not read from a closed database');
|
|
1906
|
-
}
|
|
1907
|
-
};
|
|
2010
|
+
}, configurable: true
|
|
2011
|
+
});
|
|
2012
|
+
Object.defineProperty(readTxn,'use', {
|
|
2013
|
+
value: () => {
|
|
2014
|
+
throw new Error('Can not read from a closed database');
|
|
2015
|
+
}, configurable: true
|
|
2016
|
+
});
|
|
1908
2017
|
readTxnRenewed = null;
|
|
2018
|
+
txnPromise = this._endWrites && this._endWrites();
|
|
1909
2019
|
}
|
|
1910
|
-
let txnPromise = this._endWrites();
|
|
1911
2020
|
const doClose = () => {
|
|
1912
|
-
this.db.close();
|
|
1913
2021
|
if (this.isRoot) {
|
|
2022
|
+
if (outstandingReads > 0) {
|
|
2023
|
+
return new Promise(resolve => setTimeout(() => resolve(doClose()), 1));
|
|
2024
|
+
}
|
|
2025
|
+
env.address = 0;
|
|
1914
2026
|
env.close();
|
|
1915
|
-
}
|
|
2027
|
+
} else
|
|
2028
|
+
this.db.close();
|
|
1916
2029
|
this.status = 'closed';
|
|
1917
2030
|
if (callback)
|
|
1918
2031
|
callback();
|
|
@@ -1925,18 +2038,40 @@ function addReadMethods(LMDBStore, {
|
|
|
1925
2038
|
}
|
|
1926
2039
|
},
|
|
1927
2040
|
getStats() {
|
|
1928
|
-
readTxnRenewed ? readTxn : renewReadTxn(this);
|
|
1929
|
-
|
|
1930
|
-
|
|
2041
|
+
env.writeTxn || (readTxnRenewed ? readTxn : renewReadTxn(this));
|
|
2042
|
+
let dbStats = this.db.stat();
|
|
2043
|
+
dbStats.root = env.stat();
|
|
2044
|
+
Object.assign(dbStats, env.info());
|
|
2045
|
+
dbStats.free = env.freeStat();
|
|
2046
|
+
return dbStats;
|
|
2047
|
+
},
|
|
1931
2048
|
});
|
|
1932
2049
|
let get = LMDBStore.prototype.get;
|
|
2050
|
+
let lastReadTxnRef;
|
|
2051
|
+
function getMMapBuffer(bufferId, size) {
|
|
2052
|
+
let buffer = mmaps[bufferId];
|
|
2053
|
+
if (!buffer) {
|
|
2054
|
+
buffer = mmaps[bufferId] = getSharedBuffer(bufferId, env.address);
|
|
2055
|
+
}
|
|
2056
|
+
let offset = keyBytesView.getUint32(8, true);
|
|
2057
|
+
return new Uint8Array(buffer, offset, size);
|
|
2058
|
+
}
|
|
1933
2059
|
function renewReadTxn(store) {
|
|
2060
|
+
if (!env.address) {
|
|
2061
|
+
throw new Error('Can not renew a transaction from a closed database');
|
|
2062
|
+
}
|
|
1934
2063
|
if (!readTxn) {
|
|
1935
2064
|
let retries = 0;
|
|
1936
2065
|
let waitArray;
|
|
1937
2066
|
do {
|
|
1938
2067
|
try {
|
|
1939
|
-
|
|
2068
|
+
let lastReadTxn = lastReadTxnRef && lastReadTxnRef.deref();
|
|
2069
|
+
readTxn = new Txn(env, 0x20000, lastReadTxn && !lastReadTxn.isDone && lastReadTxn);
|
|
2070
|
+
if (readTxn.address == 0) {
|
|
2071
|
+
readTxn = lastReadTxn;
|
|
2072
|
+
if (readTxn.notCurrent)
|
|
2073
|
+
readTxn.notCurrent = false;
|
|
2074
|
+
}
|
|
1940
2075
|
break;
|
|
1941
2076
|
} catch (error) {
|
|
1942
2077
|
if (error.message.includes('temporarily')) {
|
|
@@ -1954,15 +2089,20 @@ function addReadMethods(LMDBStore, {
|
|
|
1954
2089
|
store.emit('begin-transaction');
|
|
1955
2090
|
return readTxn;
|
|
1956
2091
|
}
|
|
1957
|
-
function resetReadTxn(
|
|
2092
|
+
function resetReadTxn() {
|
|
1958
2093
|
renewId++;
|
|
1959
2094
|
if (readTxnRenewed) {
|
|
1960
2095
|
readTxnRenewed = null;
|
|
1961
|
-
if (readTxn.
|
|
1962
|
-
readTxn.
|
|
2096
|
+
if (readTxn.refCount - (readTxn.renewingRefCount || 0) > 0) {
|
|
2097
|
+
readTxn.notCurrent = true;
|
|
2098
|
+
lastReadTxnRef = new WeakRef(readTxn);
|
|
1963
2099
|
readTxn = null;
|
|
1964
|
-
} else
|
|
2100
|
+
} else if (readTxn.address && !readTxn.isDone) {
|
|
1965
2101
|
resetTxn(readTxn.address);
|
|
2102
|
+
} else {
|
|
2103
|
+
console.warn('Attempt to reset an invalid read txn', readTxn);
|
|
2104
|
+
throw new Error('Attempt to reset an invalid read txn');
|
|
2105
|
+
}
|
|
1966
2106
|
}
|
|
1967
2107
|
}
|
|
1968
2108
|
}
|
|
@@ -1973,12 +2113,296 @@ function makeReusableBuffer(size) {
|
|
|
1973
2113
|
return bytes;
|
|
1974
2114
|
}
|
|
1975
2115
|
|
|
2116
|
+
Txn.prototype.done = function() {
|
|
2117
|
+
this.refCount--;
|
|
2118
|
+
if (this.refCount === 0 && this.notCurrent) {
|
|
2119
|
+
this.abort();
|
|
2120
|
+
this.isDone = true;
|
|
2121
|
+
}
|
|
2122
|
+
};
|
|
2123
|
+
Txn.prototype.use = function() {
|
|
2124
|
+
this.refCount = (this.refCount || 0) + 1;
|
|
2125
|
+
};
|
|
2126
|
+
|
|
2127
|
+
|
|
2128
|
+
let readInstructions, readCallbacks = new Map(), uint32Instructions, instructionsDataView = { setFloat64() {}, setUint32() {} }, instructionsAddress;
|
|
2129
|
+
let savePosition = 8000;
|
|
2130
|
+
let DYNAMIC_KEY_BUFFER_SIZE = 8192;
|
|
2131
|
+
function allocateInstructionsBuffer() {
|
|
2132
|
+
readInstructions = typeof Buffer != 'undefined' ? Buffer.alloc(DYNAMIC_KEY_BUFFER_SIZE) : new Uint8Array(DYNAMIC_KEY_BUFFER_SIZE);
|
|
2133
|
+
uint32Instructions = new Int32Array(readInstructions.buffer, 0, readInstructions.buffer.byteLength >> 2);
|
|
2134
|
+
uint32Instructions[2] = 0xf0000000; // indicates a new read task must be started
|
|
2135
|
+
instructionsAddress = readInstructions.buffer.address = getAddress(readInstructions.buffer);
|
|
2136
|
+
readInstructions.dataView = instructionsDataView = new DataView(readInstructions.buffer, readInstructions.byteOffset, readInstructions.byteLength);
|
|
2137
|
+
savePosition = 0;
|
|
2138
|
+
}
|
|
2139
|
+
function recordReadInstruction(txnAddress, dbi, key, writeKey, maxKeySize, callback) {
|
|
2140
|
+
if (savePosition > 7800) {
|
|
2141
|
+
allocateInstructionsBuffer();
|
|
2142
|
+
}
|
|
2143
|
+
let start = savePosition;
|
|
2144
|
+
let keyPosition = savePosition + 16;
|
|
2145
|
+
try {
|
|
2146
|
+
savePosition = key === undefined ? keyPosition :
|
|
2147
|
+
writeKey(key, readInstructions, keyPosition);
|
|
2148
|
+
} catch (error) {
|
|
2149
|
+
if (error.name == 'RangeError') {
|
|
2150
|
+
if (8180 - start < maxKeySize) {
|
|
2151
|
+
allocateInstructionsBuffer(); // try again:
|
|
2152
|
+
return recordReadInstruction(txnAddress, dbi, key, writeKey, maxKeySize, callback);
|
|
2153
|
+
}
|
|
2154
|
+
throw new Error('Key was too large, max key size is ' + maxKeySize);
|
|
2155
|
+
} else
|
|
2156
|
+
throw error;
|
|
2157
|
+
}
|
|
2158
|
+
let length = savePosition - keyPosition;
|
|
2159
|
+
if (length > maxKeySize) {
|
|
2160
|
+
savePosition = start;
|
|
2161
|
+
throw new Error('Key of size ' + length + ' was too large, max key size is ' + maxKeySize);
|
|
2162
|
+
}
|
|
2163
|
+
uint32Instructions[(start >> 2) + 3] = length; // save the length
|
|
2164
|
+
uint32Instructions[(start >> 2) + 2] = dbi;
|
|
2165
|
+
savePosition = (savePosition + 12) & 0xfffffc;
|
|
2166
|
+
instructionsDataView.setFloat64(start, txnAddress, true);
|
|
2167
|
+
let callbackId = addReadCallback(() => {
|
|
2168
|
+
let position = start >> 2;
|
|
2169
|
+
let rc = thisInstructions[position];
|
|
2170
|
+
callback(rc, thisInstructions[position + 1], thisInstructions[position + 2], thisInstructions[position + 3]);
|
|
2171
|
+
});
|
|
2172
|
+
let thisInstructions = uint32Instructions;
|
|
2173
|
+
//if (start === 0)
|
|
2174
|
+
return startRead(instructionsAddress + start, callbackId, {}, 'read');
|
|
2175
|
+
//else
|
|
2176
|
+
//nextRead(start);
|
|
2177
|
+
}
|
|
2178
|
+
let nextCallbackId = 0;
|
|
2179
|
+
let addReadCallback = globalThis.__lmdb_read_callback;
|
|
2180
|
+
if (!addReadCallback) {
|
|
2181
|
+
addReadCallback = globalThis.__lmdb_read_callback = function(callback) {
|
|
2182
|
+
let callbackId = nextCallbackId++;
|
|
2183
|
+
readCallbacks.set(callbackId, callback);
|
|
2184
|
+
return callbackId;
|
|
2185
|
+
};
|
|
2186
|
+
setReadCallback(function(callbackId) {
|
|
2187
|
+
readCallbacks.get(callbackId)();
|
|
2188
|
+
readCallbacks.delete(callbackId);
|
|
2189
|
+
});
|
|
2190
|
+
}
|
|
2191
|
+
|
|
2192
|
+
let getLastVersion$1, getLastTxnId$1;
|
|
2193
|
+
const mapGet = Map.prototype.get;
|
|
2194
|
+
const CachingStore = (Store, env) => {
|
|
2195
|
+
let childTxnChanges;
|
|
2196
|
+
return class LMDBStore extends Store {
|
|
2197
|
+
constructor(dbName, options) {
|
|
2198
|
+
super(dbName, options);
|
|
2199
|
+
if (!env.cacheCommitter) {
|
|
2200
|
+
env.cacheCommitter = true;
|
|
2201
|
+
this.on('aftercommit', ({ next, last, txnId }) => {
|
|
2202
|
+
do {
|
|
2203
|
+
let meta = next.meta;
|
|
2204
|
+
let store = meta && meta.store;
|
|
2205
|
+
if (store) {
|
|
2206
|
+
if (next.flag & FAILED_CONDITION)
|
|
2207
|
+
store.cache.delete(meta.key); // just delete it from the map
|
|
2208
|
+
else {
|
|
2209
|
+
let expirationPriority = meta.valueSize >> 10;
|
|
2210
|
+
let cache = store.cache;
|
|
2211
|
+
let entry = mapGet.call(cache, meta.key);
|
|
2212
|
+
if (entry) {
|
|
2213
|
+
entry.txnId = txnId;
|
|
2214
|
+
cache.used(entry, expirationPriority + 4); // this will enter it into the LRFU (with a little lower priority than a read)
|
|
2215
|
+
}
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
} while (next != last && (next = next.next))
|
|
2219
|
+
});
|
|
2220
|
+
}
|
|
2221
|
+
this.db.cachingDb = this;
|
|
2222
|
+
if (options.cache.clearKeptInterval)
|
|
2223
|
+
options.cache.clearKeptObjects = exports.clearKeptObjects;
|
|
2224
|
+
this.cache = new WeakLRUCache(options.cache);
|
|
2225
|
+
if (options.cache.validated)
|
|
2226
|
+
this.cache.validated = true;
|
|
2227
|
+
}
|
|
2228
|
+
get isCaching() {
|
|
2229
|
+
return true
|
|
2230
|
+
}
|
|
2231
|
+
get(id, options) {
|
|
2232
|
+
let value;
|
|
2233
|
+
if (this.cache.validated) {
|
|
2234
|
+
let entry = this.cache.get(id);
|
|
2235
|
+
if (entry) {
|
|
2236
|
+
let cachedValue = entry.value;
|
|
2237
|
+
if (entry.txnId != null) {
|
|
2238
|
+
value = super.get(id, { ifNotTxnId: entry.txnId, transaction: options && options.transaction });
|
|
2239
|
+
if (value === UNMODIFIED)
|
|
2240
|
+
return cachedValue;
|
|
2241
|
+
} else // with no txn id we do not validate; this is the state of a cached value after a write before it transacts
|
|
2242
|
+
return cachedValue;
|
|
2243
|
+
} else
|
|
2244
|
+
value = super.get(id, options);
|
|
2245
|
+
} else if (options && options.transaction) {
|
|
2246
|
+
return super.get(id, options);
|
|
2247
|
+
} else {
|
|
2248
|
+
value = this.cache.getValue(id);
|
|
2249
|
+
if (value !== undefined) {
|
|
2250
|
+
return value;
|
|
2251
|
+
}
|
|
2252
|
+
value = super.get(id);
|
|
2253
|
+
}
|
|
2254
|
+
if (value && typeof value === 'object' && !options && typeof id !== 'object') {
|
|
2255
|
+
let entry = this.cache.setValue(id, value, this.lastSize >> 10);
|
|
2256
|
+
if (this.useVersions) {
|
|
2257
|
+
entry.version = getLastVersion$1();
|
|
2258
|
+
}
|
|
2259
|
+
if (this.cache.validated)
|
|
2260
|
+
entry.txnId = getLastTxnId$1();
|
|
2261
|
+
}
|
|
2262
|
+
return value;
|
|
2263
|
+
}
|
|
2264
|
+
getEntry(id, options) {
|
|
2265
|
+
let entry, value;
|
|
2266
|
+
if (this.cache.validated) {
|
|
2267
|
+
entry = this.cache.get(id);
|
|
2268
|
+
if (entry) {
|
|
2269
|
+
if (entry.txnId != null) {
|
|
2270
|
+
value = super.get(id, { ifNotTxnId: entry.txnId, transaction: options && options.transaction });
|
|
2271
|
+
if (value === UNMODIFIED)
|
|
2272
|
+
return entry;
|
|
2273
|
+
} else // with no txn id we do not validate; this is the state of a cached value after a write before it transacts
|
|
2274
|
+
return entry;
|
|
2275
|
+
} else
|
|
2276
|
+
value = super.get(id, options);
|
|
2277
|
+
} else if (options && options.transaction) {
|
|
2278
|
+
return super.getEntry(id, options);
|
|
2279
|
+
} else {
|
|
2280
|
+
entry = this.cache.get(id);
|
|
2281
|
+
if (entry !== undefined) {
|
|
2282
|
+
return entry;
|
|
2283
|
+
}
|
|
2284
|
+
value = super.get(id);
|
|
2285
|
+
}
|
|
2286
|
+
if (value === undefined)
|
|
2287
|
+
return;
|
|
2288
|
+
if (value && typeof value === 'object' && !options && typeof id !== 'object') {
|
|
2289
|
+
entry = this.cache.setValue(id, value, this.lastSize >> 10);
|
|
2290
|
+
} else
|
|
2291
|
+
entry = { value };
|
|
2292
|
+
if (this.useVersions)
|
|
2293
|
+
entry.version = getLastVersion$1();
|
|
2294
|
+
if (this.cache.validated)
|
|
2295
|
+
entry.txnId = getLastTxnId$1();
|
|
2296
|
+
return entry;
|
|
2297
|
+
}
|
|
2298
|
+
putEntry(id, entry, ifVersion) {
|
|
2299
|
+
let result = super.put(id, entry.value, entry.version, ifVersion);
|
|
2300
|
+
if (typeof id === 'object')
|
|
2301
|
+
return result;
|
|
2302
|
+
if (result && result.then)
|
|
2303
|
+
this.cache.setManually(id, entry); // set manually so we can keep it pinned in memory until it is committed
|
|
2304
|
+
else // sync operation, immediately add to cache
|
|
2305
|
+
this.cache.set(id, entry);
|
|
2306
|
+
}
|
|
2307
|
+
put(id, value, version, ifVersion) {
|
|
2308
|
+
let result = super.put(id, value, version, ifVersion);
|
|
2309
|
+
if (typeof id !== 'object') {
|
|
2310
|
+
if (value && value['\x10binary-data\x02']) {
|
|
2311
|
+
// don't cache binary data, since it will be decoded on get
|
|
2312
|
+
this.cache.delete(id);
|
|
2313
|
+
return result;
|
|
2314
|
+
}
|
|
2315
|
+
// sync operation, immediately add to cache, otherwise keep it pinned in memory until it is committed
|
|
2316
|
+
let entry = this.cache.setValue(id, value, !result || result.isSync ? 0 : -1);
|
|
2317
|
+
if (childTxnChanges)
|
|
2318
|
+
childTxnChanges.add(id);
|
|
2319
|
+
if (version !== undefined)
|
|
2320
|
+
entry.version = typeof version === 'object' ? version.version : version;
|
|
2321
|
+
}
|
|
2322
|
+
return result;
|
|
2323
|
+
}
|
|
2324
|
+
putSync(id, value, version, ifVersion) {
|
|
2325
|
+
if (id !== 'object') {
|
|
2326
|
+
// sync operation, immediately add to cache, otherwise keep it pinned in memory until it is committed
|
|
2327
|
+
if (value && typeof value === 'object') {
|
|
2328
|
+
let entry = this.cache.setValue(id, value);
|
|
2329
|
+
if (childTxnChanges)
|
|
2330
|
+
childTxnChanges.add(id);
|
|
2331
|
+
if (version !== undefined) {
|
|
2332
|
+
entry.version = typeof version === 'object' ? version.version : version;
|
|
2333
|
+
}
|
|
2334
|
+
} else // it is possible that a value used to exist here
|
|
2335
|
+
this.cache.delete(id);
|
|
2336
|
+
}
|
|
2337
|
+
return super.putSync(id, value, version, ifVersion);
|
|
2338
|
+
}
|
|
2339
|
+
remove(id, ifVersion) {
|
|
2340
|
+
this.cache.delete(id);
|
|
2341
|
+
return super.remove(id, ifVersion);
|
|
2342
|
+
}
|
|
2343
|
+
removeSync(id, ifVersion) {
|
|
2344
|
+
this.cache.delete(id);
|
|
2345
|
+
return super.removeSync(id, ifVersion);
|
|
2346
|
+
}
|
|
2347
|
+
clearAsync(callback) {
|
|
2348
|
+
this.cache.clear();
|
|
2349
|
+
return super.clearAsync(callback);
|
|
2350
|
+
}
|
|
2351
|
+
clearSync() {
|
|
2352
|
+
this.cache.clear();
|
|
2353
|
+
super.clearSync();
|
|
2354
|
+
}
|
|
2355
|
+
childTransaction(callback) {
|
|
2356
|
+
return super.childTransaction(() => {
|
|
2357
|
+
let cache = this.cache;
|
|
2358
|
+
let previousChanges = childTxnChanges;
|
|
2359
|
+
try {
|
|
2360
|
+
childTxnChanges = new Set();
|
|
2361
|
+
return when(callback(), (result) => {
|
|
2362
|
+
if (result === ABORT)
|
|
2363
|
+
return abort();
|
|
2364
|
+
childTxnChanges = previousChanges;
|
|
2365
|
+
return result;
|
|
2366
|
+
}, abort);
|
|
2367
|
+
} catch(error) {
|
|
2368
|
+
abort(error);
|
|
2369
|
+
}
|
|
2370
|
+
function abort(error) {
|
|
2371
|
+
// if the transaction was aborted, remove all affected entries from cache
|
|
2372
|
+
for (let id of childTxnChanges)
|
|
2373
|
+
cache.delete(id);
|
|
2374
|
+
childTxnChanges = previousChanges;
|
|
2375
|
+
if (error)
|
|
2376
|
+
throw error;
|
|
2377
|
+
else
|
|
2378
|
+
return ABORT;
|
|
2379
|
+
}
|
|
2380
|
+
});
|
|
2381
|
+
}
|
|
2382
|
+
doesExist(key, versionOrValue) {
|
|
2383
|
+
let entry = this.cache.get(key);
|
|
2384
|
+
if (entry) {
|
|
2385
|
+
if (versionOrValue == null) {
|
|
2386
|
+
return versionOrValue !== null;
|
|
2387
|
+
} else if (this.useVersions) {
|
|
2388
|
+
return versionOrValue === IF_EXISTS$1 || entry.version === versionOrValue;
|
|
2389
|
+
}
|
|
2390
|
+
}
|
|
2391
|
+
return super.doesExist(key, versionOrValue);
|
|
2392
|
+
}
|
|
2393
|
+
};
|
|
2394
|
+
};
|
|
2395
|
+
function setGetLastVersion(get, getTxnId) {
|
|
2396
|
+
getLastVersion$1 = get;
|
|
2397
|
+
getLastTxnId$1 = getTxnId;
|
|
2398
|
+
}
|
|
2399
|
+
|
|
1976
2400
|
let moduleRequire = typeof require == 'function' && require;
|
|
1977
2401
|
function setRequire(require) {
|
|
1978
2402
|
moduleRequire = require;
|
|
1979
2403
|
}
|
|
1980
2404
|
|
|
1981
|
-
setGetLastVersion(getLastVersion);
|
|
2405
|
+
setGetLastVersion(getLastVersion, getLastTxnId);
|
|
1982
2406
|
let keyBytes, keyBytesView;
|
|
1983
2407
|
const { onExit, getEnvsPointer, setEnvsPointer, getEnvFlags, setJSFlags } = nativeAddon;
|
|
1984
2408
|
if (globalThis.__lmdb_envs__)
|
|
@@ -1989,12 +2413,29 @@ else
|
|
|
1989
2413
|
// this is hard coded as an upper limit because it is important assumption of the fixed buffers in writing instructions
|
|
1990
2414
|
// this corresponds to the max key size for 8KB pages
|
|
1991
2415
|
const MAX_KEY_SIZE = 4026;
|
|
2416
|
+
// this is used as the key size by default because default page size is OS page size, which is usually
|
|
2417
|
+
// 4KB (but is 16KB on M-series MacOS), and this keeps a consistent max key size when no page size specified.
|
|
2418
|
+
const DEFAULT_MAX_KEY_SIZE = 1978;
|
|
1992
2419
|
const DEFAULT_COMMIT_DELAY = 0;
|
|
1993
2420
|
|
|
1994
2421
|
const allDbs = new Map();
|
|
1995
2422
|
let defaultCompression;
|
|
1996
2423
|
let hasRegisteredOnExit;
|
|
1997
2424
|
function open(path$1, options) {
|
|
2425
|
+
if (nativeAddon.open) {
|
|
2426
|
+
if (nativeAddon.open !== open) {
|
|
2427
|
+
// this is the case when lmdb-js has been opened in both ESM and CJS mode, which means that there are two
|
|
2428
|
+
// separate JS modules, but they are both using the same native module.
|
|
2429
|
+
getLastVersion = nativeAddon.getLastVersion;
|
|
2430
|
+
getLastTxnId = nativeAddon.getLastTxnId;
|
|
2431
|
+
setGetLastVersion(getLastVersion, getLastTxnId);
|
|
2432
|
+
return nativeAddon.open(path$1, options);
|
|
2433
|
+
}
|
|
2434
|
+
} else {
|
|
2435
|
+
nativeAddon.open = open;
|
|
2436
|
+
nativeAddon.getLastVersion = getLastVersion;
|
|
2437
|
+
nativeAddon.getLastTxnId = getLastTxnId;
|
|
2438
|
+
}
|
|
1998
2439
|
if (!keyBytes) // TODO: Consolidate get buffer and key buffer (don't think we need both)
|
|
1999
2440
|
allocateFixedBuffer();
|
|
2000
2441
|
if (typeof path$1 == 'object' && !options) {
|
|
@@ -2002,8 +2443,9 @@ function open(path$1, options) {
|
|
|
2002
2443
|
path$1 = options.path;
|
|
2003
2444
|
}
|
|
2004
2445
|
options = options || {};
|
|
2446
|
+
let noFSAccess = options.noFSAccess; // this can only be configured on open, can't let users change it
|
|
2005
2447
|
let userOptions = options;
|
|
2006
|
-
if (
|
|
2448
|
+
if (path$1 == null) {
|
|
2007
2449
|
options = Object.assign({
|
|
2008
2450
|
deleteOnClose: true,
|
|
2009
2451
|
noSync: true,
|
|
@@ -2014,17 +2456,16 @@ function open(path$1, options) {
|
|
|
2014
2456
|
let extension = path.extname(path$1);
|
|
2015
2457
|
let name = path.basename(path$1, extension);
|
|
2016
2458
|
let is32Bit = arch().endsWith('32');
|
|
2017
|
-
let remapChunks = options.remapChunks || (options.mapSize ?
|
|
2459
|
+
let remapChunks = options.remapChunks || options.encryptionKey || (options.mapSize ?
|
|
2018
2460
|
(is32Bit && options.mapSize > 0x100000000) : // larger than fits in address space, must use dynamic maps
|
|
2019
2461
|
is32Bit); // without a known map size, we default to being able to handle large data correctly/well*/
|
|
2462
|
+
let userMapSize = options.mapSize;
|
|
2020
2463
|
options = Object.assign({
|
|
2021
|
-
path: path$1,
|
|
2022
2464
|
noSubdir: Boolean(extension),
|
|
2023
2465
|
isRoot: true,
|
|
2024
2466
|
maxDbs: 12,
|
|
2025
2467
|
remapChunks,
|
|
2026
2468
|
keyBytes,
|
|
2027
|
-
pageSize: 4096,
|
|
2028
2469
|
overlappingSync: (options.noSync || options.readOnly) ? false : (os != 'win32'),
|
|
2029
2470
|
// default map size limit of 4 exabytes when using remapChunks, since it is not preallocated and we can
|
|
2030
2471
|
// make it super huge.
|
|
@@ -2032,9 +2473,18 @@ function open(path$1, options) {
|
|
|
2032
2473
|
0x20000, // Otherwise we start small with 128KB
|
|
2033
2474
|
safeRestore: process.env.LMDB_RESTORE == 'safe',
|
|
2034
2475
|
}, options);
|
|
2476
|
+
options.path = path$1;
|
|
2035
2477
|
if (options.asyncTransactionOrder == 'strict') {
|
|
2036
2478
|
options.strictAsyncOrder = true;
|
|
2037
2479
|
}
|
|
2480
|
+
if (nativeAddon.version.major + nativeAddon.version.minor / 100 + nativeAddon.version.patch / 10000 < 0.0980) {
|
|
2481
|
+
options.overlappingSync = false; // not support on older versions
|
|
2482
|
+
options.trackMetrics = false;
|
|
2483
|
+
options.usePreviousSnapshot = false;
|
|
2484
|
+
options.safeRestore = false;
|
|
2485
|
+
options.remapChunks = false;
|
|
2486
|
+
if (!userMapSize) options.mapSize = 0x40000000; // 1 GB
|
|
2487
|
+
}
|
|
2038
2488
|
|
|
2039
2489
|
if (!exists(options.noSubdir ? path.dirname(path$1) : path$1))
|
|
2040
2490
|
fs.mkdirSync(options.noSubdir ? path.dirname(path$1) : path$1, { recursive: true }
|
|
@@ -2047,7 +2497,7 @@ function open(path$1, options) {
|
|
|
2047
2497
|
return defaultCompression;
|
|
2048
2498
|
compressionOptions = Object.assign({
|
|
2049
2499
|
threshold: 1000,
|
|
2050
|
-
dictionary: fs.readFileSync(new URL('./dict/dict.txt', (typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.
|
|
2500
|
+
dictionary: fs.readFileSync(new URL('./dict/dict.txt', (typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.src || new URL('index.cjs', document.baseURI).href)).replace(/dist[\\\/]index.cjs$/, ''))),
|
|
2051
2501
|
getValueBytes: makeReusableBuffer(0),
|
|
2052
2502
|
}, compressionOptions);
|
|
2053
2503
|
let compression = Object.assign(new Compression(compressionOptions), compressionOptions);
|
|
@@ -2070,17 +2520,20 @@ function open(path$1, options) {
|
|
|
2070
2520
|
(options.noMemInit ? 0x1000000 : 0) |
|
|
2071
2521
|
(options.usePreviousSnapshot ? 0x2000000 : 0) |
|
|
2072
2522
|
(options.remapChunks ? 0x4000000 : 0) |
|
|
2073
|
-
(options.safeRestore ?
|
|
2523
|
+
(options.safeRestore ? 0x800 : 0) |
|
|
2524
|
+
(options.trackMetrics ? 0x400 : 0);
|
|
2074
2525
|
|
|
2075
2526
|
let env = new Env();
|
|
2076
2527
|
let jsFlags = (options.overlappingSync ? 0x1000 : 0) |
|
|
2077
2528
|
(options.separateFlushed ? 1 : 0) |
|
|
2078
2529
|
(options.deleteOnClose ? 2 : 0);
|
|
2079
2530
|
let rc = env.open(options, flags, jsFlags);
|
|
2531
|
+
env.path = path$1;
|
|
2080
2532
|
if (rc)
|
|
2081
2533
|
lmdbError(rc);
|
|
2534
|
+
delete options.keyBytes; // no longer needed, don't copy to stores
|
|
2082
2535
|
let maxKeySize = env.getMaxKeySize();
|
|
2083
|
-
maxKeySize = Math.min(maxKeySize, MAX_KEY_SIZE);
|
|
2536
|
+
maxKeySize = Math.min(maxKeySize, options.pageSize ? MAX_KEY_SIZE : DEFAULT_MAX_KEY_SIZE);
|
|
2084
2537
|
flags = getEnvFlags(env.address); // re-retrieve them, they are not necessarily the same if we are connecting to an existing env
|
|
2085
2538
|
if (flags & 0x1000) {
|
|
2086
2539
|
if (userOptions.noSync) {
|
|
@@ -2134,14 +2587,14 @@ function open(path$1, options) {
|
|
|
2134
2587
|
(dbOptions.integerDup ? 0x20 : 0) |
|
|
2135
2588
|
(dbOptions.reverseDup ? 0x40 : 0) |
|
|
2136
2589
|
(!options.readOnly && dbOptions.create !== false ? 0x40000 : 0) |
|
|
2137
|
-
(dbOptions.useVersions ?
|
|
2590
|
+
(dbOptions.useVersions ? 0x100 : 0);
|
|
2138
2591
|
let keyType = (dbOptions.keyIsUint32 || dbOptions.keyEncoding == 'uint32') ? 2 : keyIsBuffer ? 3 : 0;
|
|
2139
2592
|
if (keyType == 2)
|
|
2140
2593
|
flags |= 0x08; // integer key
|
|
2141
2594
|
|
|
2142
2595
|
if (options.readOnly) {
|
|
2143
2596
|
// in read-only mode we use a read-only txn to open the database
|
|
2144
|
-
// TODO: LMDB is actually not
|
|
2597
|
+
// TODO: LMDB is actually not entirely thread-safe when it comes to opening databases with
|
|
2145
2598
|
// read-only transactions since there is a race condition on setting the update dbis that
|
|
2146
2599
|
// occurs outside the lock
|
|
2147
2600
|
// make sure we are using a fresh read txn, so we don't want to share with a cursor txn
|
|
@@ -2186,7 +2639,7 @@ function open(path$1, options) {
|
|
|
2186
2639
|
}
|
|
2187
2640
|
if (Encoder) {
|
|
2188
2641
|
this.encoder = new Encoder(Object.assign(
|
|
2189
|
-
assignConstrainedProperties(['copyBuffers', 'getStructures', 'saveStructures', 'useFloat32', 'useRecords', 'structuredClone', 'variableMapSize', 'useTimestamp32', 'largeBigIntToFloat', 'encodeUndefinedAsNil', 'int64AsNumber', 'onInvalidDate', 'mapsAsObjects', 'useTag259ForMaps', 'pack', 'maxSharedStructures', 'shouldShareStructure'],
|
|
2642
|
+
assignConstrainedProperties(['copyBuffers', 'getStructures', 'saveStructures', 'useFloat32', 'useRecords', 'structuredClone', 'variableMapSize', 'useTimestamp32', 'largeBigIntToFloat', 'encodeUndefinedAsNil', 'int64AsNumber', 'onInvalidDate', 'mapsAsObjects', 'useTag259ForMaps', 'pack', 'maxSharedStructures', 'shouldShareStructure', 'randomAccessStructure', 'freezeData'],
|
|
2190
2643
|
this.sharedStructuresKey ? this.setupSharedStructures() : {
|
|
2191
2644
|
copyBuffers: true, // need to copy any embedded buffers that are found since we use unsafe buffers
|
|
2192
2645
|
}, options, dbOptions), this.encoder));
|
|
@@ -2230,8 +2683,11 @@ function open(path$1, options) {
|
|
|
2230
2683
|
callback(null, db);
|
|
2231
2684
|
return db;
|
|
2232
2685
|
}
|
|
2233
|
-
backup(path) {
|
|
2234
|
-
|
|
2686
|
+
backup(path$1, compact) {
|
|
2687
|
+
if (noFSAccess)
|
|
2688
|
+
return;
|
|
2689
|
+
fs.mkdirSync(path.dirname(path$1), { recursive: true });
|
|
2690
|
+
return new Promise((resolve, reject) => env.copy(path$1, false, (error) => {
|
|
2235
2691
|
if (error) {
|
|
2236
2692
|
reject(error);
|
|
2237
2693
|
} else {
|
|
@@ -2343,11 +2799,14 @@ function openAsClass(path, options) {
|
|
|
2343
2799
|
function getLastVersion() {
|
|
2344
2800
|
return keyBytesView.getFloat64(16, true);
|
|
2345
2801
|
}
|
|
2346
|
-
|
|
2347
2802
|
function setLastVersion(version) {
|
|
2348
2803
|
return keyBytesView.setFloat64(16, version, true);
|
|
2349
2804
|
}
|
|
2350
2805
|
|
|
2806
|
+
function getLastTxnId() {
|
|
2807
|
+
return keyBytesView.getUint32(32, true);
|
|
2808
|
+
}
|
|
2809
|
+
|
|
2351
2810
|
const KEY_BUFFER_SIZE = 4096;
|
|
2352
2811
|
function allocateFixedBuffer() {
|
|
2353
2812
|
keyBytes = typeof Buffer != 'undefined' ? Buffer.allocUnsafeSlow(KEY_BUFFER_SIZE) : new Uint8Array(KEY_BUFFER_SIZE);
|
|
@@ -2355,7 +2814,7 @@ function allocateFixedBuffer() {
|
|
|
2355
2814
|
keyBytesView = keyBytes.dataView || (keyBytes.dataView = new DataView(keyBytes.buffer, 0, KEY_BUFFER_SIZE)); // max key size is actually 4026
|
|
2356
2815
|
keyBytes.uint32 = new Uint32Array(keyBuffer, 0, KEY_BUFFER_SIZE >> 2);
|
|
2357
2816
|
keyBytes.float64 = new Float64Array(keyBuffer, 0, KEY_BUFFER_SIZE >> 3);
|
|
2358
|
-
keyBytes.uint32.address = keyBytes.address = keyBuffer.address = getAddress(
|
|
2817
|
+
keyBytes.uint32.address = keyBytes.address = keyBuffer.address = getAddress(keyBuffer);
|
|
2359
2818
|
}
|
|
2360
2819
|
|
|
2361
2820
|
function exists(path) {
|
|
@@ -2379,72 +2838,76 @@ function assignConstrainedProperties(allowedProperties, target) {
|
|
|
2379
2838
|
return target;
|
|
2380
2839
|
}
|
|
2381
2840
|
|
|
2382
|
-
function levelup(store) {
|
|
2383
|
-
return Object.assign(Object.create(store), {
|
|
2384
|
-
get(key, options, callback) {
|
|
2385
|
-
let result = store.get(key);
|
|
2386
|
-
if (typeof options == 'function')
|
|
2387
|
-
callback = options;
|
|
2388
|
-
if (callback) {
|
|
2389
|
-
if (result === undefined)
|
|
2390
|
-
callback(new NotFoundError());
|
|
2391
|
-
else
|
|
2392
|
-
callback(null, result);
|
|
2393
|
-
} else {
|
|
2394
|
-
if (result === undefined)
|
|
2395
|
-
return Promise.reject(new NotFoundError());
|
|
2396
|
-
else
|
|
2397
|
-
return Promise.resolve(result);
|
|
2398
|
-
}
|
|
2399
|
-
},
|
|
2400
|
-
});
|
|
2401
|
-
}
|
|
2402
|
-
class NotFoundError extends Error {
|
|
2403
|
-
constructor(message) {
|
|
2404
|
-
super(message);
|
|
2405
|
-
this.name = 'NotFoundError';
|
|
2406
|
-
this.notFound = true;
|
|
2407
|
-
}
|
|
2841
|
+
function levelup(store) {
|
|
2842
|
+
return Object.assign(Object.create(store), {
|
|
2843
|
+
get(key, options, callback) {
|
|
2844
|
+
let result = store.get(key);
|
|
2845
|
+
if (typeof options == 'function')
|
|
2846
|
+
callback = options;
|
|
2847
|
+
if (callback) {
|
|
2848
|
+
if (result === undefined)
|
|
2849
|
+
callback(new NotFoundError());
|
|
2850
|
+
else
|
|
2851
|
+
callback(null, result);
|
|
2852
|
+
} else {
|
|
2853
|
+
if (result === undefined)
|
|
2854
|
+
return Promise.reject(new NotFoundError());
|
|
2855
|
+
else
|
|
2856
|
+
return Promise.resolve(result);
|
|
2857
|
+
}
|
|
2858
|
+
},
|
|
2859
|
+
});
|
|
2860
|
+
}
|
|
2861
|
+
class NotFoundError extends Error {
|
|
2862
|
+
constructor(message) {
|
|
2863
|
+
super(message);
|
|
2864
|
+
this.name = 'NotFoundError';
|
|
2865
|
+
this.notFound = true;
|
|
2866
|
+
}
|
|
2408
2867
|
}
|
|
2409
2868
|
|
|
2410
|
-
orderedBinary__namespace.enableNullTermination();
|
|
2411
|
-
setExternals({
|
|
2412
|
-
arch: os$1.arch, fs: fs__default["default"], tmpdir: os$1.tmpdir, MsgpackrEncoder: msgpackr.Encoder, WeakLRUCache: weakLruCache.WeakLRUCache, orderedBinary: orderedBinary__namespace,
|
|
2413
|
-
EventEmitter: events.EventEmitter, os: os$1.platform(), onExit(callback) {
|
|
2414
|
-
if (process.getMaxListeners() < process.listenerCount('exit') + 8)
|
|
2415
|
-
process.setMaxListeners(process.listenerCount('exit') + 8);
|
|
2416
|
-
process.on('exit', callback);
|
|
2417
|
-
},
|
|
2418
|
-
});
|
|
2419
|
-
let { noop } = nativeAddon;
|
|
2420
|
-
const TransactionFlags = {
|
|
2421
|
-
ABORTABLE: 1,
|
|
2422
|
-
SYNCHRONOUS_COMMIT: 2,
|
|
2423
|
-
NO_SYNC_FLUSH: 0x10000,
|
|
2424
|
-
};
|
|
2425
|
-
var index = {
|
|
2426
|
-
open, openAsClass, getLastVersion, compareKey: orderedBinary$1.compareKeys, keyValueToBuffer: orderedBinary$1.toBufferKey, bufferToKeyValue: orderedBinary$1.fromBufferKey, ABORT, IF_EXISTS, asBinary, levelup, TransactionFlags
|
|
2869
|
+
orderedBinary__namespace.enableNullTermination();
|
|
2870
|
+
setExternals({
|
|
2871
|
+
arch: os$1.arch, fs: fs__default["default"], tmpdir: os$1.tmpdir, MsgpackrEncoder: msgpackr.Encoder, WeakLRUCache: weakLruCache.WeakLRUCache, orderedBinary: orderedBinary__namespace,
|
|
2872
|
+
EventEmitter: events.EventEmitter, os: os$1.platform(), onExit(callback) {
|
|
2873
|
+
if (process.getMaxListeners() < process.listenerCount('exit') + 8)
|
|
2874
|
+
process.setMaxListeners(process.listenerCount('exit') + 8);
|
|
2875
|
+
process.on('exit', callback);
|
|
2876
|
+
},
|
|
2877
|
+
});
|
|
2878
|
+
let { noop } = nativeAddon;
|
|
2879
|
+
const TransactionFlags = {
|
|
2880
|
+
ABORTABLE: 1,
|
|
2881
|
+
SYNCHRONOUS_COMMIT: 2,
|
|
2882
|
+
NO_SYNC_FLUSH: 0x10000,
|
|
2883
|
+
};
|
|
2884
|
+
var index = {
|
|
2885
|
+
open, openAsClass, getLastVersion, compareKey: orderedBinary$1.compareKeys, keyValueToBuffer: orderedBinary$1.toBufferKey, bufferToKeyValue: orderedBinary$1.fromBufferKey, ABORT, IF_EXISTS: IF_EXISTS$1, asBinary, levelup, TransactionFlags
|
|
2427
2886
|
};
|
|
2428
2887
|
|
|
2429
|
-
|
|
2888
|
+
let require$1 = module$1.createRequire((typeof document === 'undefined' ? new (require('u' + 'rl').URL)('file:' + __filename).href : (document.currentScript && document.currentScript.src || new URL('index.cjs', document.baseURI).href)));
|
|
2889
|
+
setRequire(require$1);
|
|
2430
2890
|
exports.v8AccelerationEnabled = false;
|
|
2431
2891
|
|
|
2432
2892
|
let versions = process.versions;
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
if (versions.v8 && +majorVersion == nativeAddon.version.nodeCompiledVersion) {
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2893
|
+
if (!versions.deno && !process.isBun) {
|
|
2894
|
+
let [ majorVersion, minorVersion ] = versions.node.split('.');
|
|
2895
|
+
if (versions.v8 && +majorVersion == nativeAddon.version.nodeCompiledVersion) {
|
|
2896
|
+
let v8Funcs = {};
|
|
2897
|
+
let fastApiCalls = (majorVersion == 17 || majorVersion == 18 || majorVersion == 16 && minorVersion > 8) && !process.env.DISABLE_TURBO_CALLS;
|
|
2898
|
+
if (fastApiCalls) {
|
|
2899
|
+
require$1('v8').setFlagsFromString('--turbo-fast-api-calls');
|
|
2900
|
+
}
|
|
2901
|
+
nativeAddon.enableDirectV8(v8Funcs, fastApiCalls);
|
|
2902
|
+
Object.assign(nativeAddon, v8Funcs);
|
|
2903
|
+
exports.v8AccelerationEnabled = true;
|
|
2904
|
+
} else if (majorVersion == 14) {
|
|
2905
|
+
// node v14 only has ABI compatibility with node v16 for zero-arg clearKeptObjects
|
|
2906
|
+
let v8Funcs = {};
|
|
2907
|
+
nativeAddon.enableDirectV8(v8Funcs, false);
|
|
2908
|
+
nativeAddon.clearKeptObjects = v8Funcs.clearKeptObjects;
|
|
2909
|
+
}
|
|
2910
|
+
nativeAddon.enableThreadSafeCalls();
|
|
2448
2911
|
}
|
|
2449
2912
|
setNativeFunctions(nativeAddon);
|
|
2450
2913
|
|
|
@@ -2465,13 +2928,16 @@ Object.defineProperty(exports, 'keyValueToBuffer', {
|
|
|
2465
2928
|
get: function () { return orderedBinary$1.toBufferKey; }
|
|
2466
2929
|
});
|
|
2467
2930
|
exports.ABORT = ABORT;
|
|
2468
|
-
exports.IF_EXISTS = IF_EXISTS;
|
|
2931
|
+
exports.IF_EXISTS = IF_EXISTS$1;
|
|
2932
|
+
exports.SKIP = SKIP;
|
|
2469
2933
|
exports.TransactionFlags = TransactionFlags;
|
|
2470
2934
|
exports.allDbs = allDbs;
|
|
2471
2935
|
exports.asBinary = asBinary;
|
|
2472
2936
|
exports["default"] = index;
|
|
2937
|
+
exports.getLastTxnId = getLastTxnId;
|
|
2473
2938
|
exports.getLastVersion = getLastVersion;
|
|
2474
2939
|
exports.levelup = levelup;
|
|
2940
|
+
exports.nativeAddon = nativeAddon;
|
|
2475
2941
|
exports.noop = noop;
|
|
2476
2942
|
exports.open = open;
|
|
2477
2943
|
exports.openAsClass = openAsClass;
|