@ohos-ports/lmdb 2.8.6-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.
Files changed (125) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +580 -0
  3. package/SECURITY.md +12 -0
  4. package/bin/download-prebuilds.js +15 -0
  5. package/binding.gyp +113 -0
  6. package/build/Release/lmdb.node +0 -0
  7. package/caching.js +212 -0
  8. package/dependencies/lmdb/libraries/liblmdb/COPYRIGHT +20 -0
  9. package/dependencies/lmdb/libraries/liblmdb/Doxyfile +1631 -0
  10. package/dependencies/lmdb/libraries/liblmdb/LICENSE +47 -0
  11. package/dependencies/lmdb/libraries/liblmdb/Makefile +136 -0
  12. package/dependencies/lmdb/libraries/liblmdb/chacha8.c +183 -0
  13. package/dependencies/lmdb/libraries/liblmdb/chacha8.h +14 -0
  14. package/dependencies/lmdb/libraries/liblmdb/crypto.c +121 -0
  15. package/dependencies/lmdb/libraries/liblmdb/intro.doc +192 -0
  16. package/dependencies/lmdb/libraries/liblmdb/lmdb.h +1817 -0
  17. package/dependencies/lmdb/libraries/liblmdb/mdb.c +12555 -0
  18. package/dependencies/lmdb/libraries/liblmdb/mdb_copy.1 +74 -0
  19. package/dependencies/lmdb/libraries/liblmdb/mdb_copy.c +106 -0
  20. package/dependencies/lmdb/libraries/liblmdb/mdb_drop.1 +53 -0
  21. package/dependencies/lmdb/libraries/liblmdb/mdb_drop.c +154 -0
  22. package/dependencies/lmdb/libraries/liblmdb/mdb_dump.1 +94 -0
  23. package/dependencies/lmdb/libraries/liblmdb/mdb_dump.c +333 -0
  24. package/dependencies/lmdb/libraries/liblmdb/mdb_load.1 +97 -0
  25. package/dependencies/lmdb/libraries/liblmdb/mdb_load.c +530 -0
  26. package/dependencies/lmdb/libraries/liblmdb/mdb_stat.1 +83 -0
  27. package/dependencies/lmdb/libraries/liblmdb/mdb_stat.c +276 -0
  28. package/dependencies/lmdb/libraries/liblmdb/midl-sparse.c +452 -0
  29. package/dependencies/lmdb/libraries/liblmdb/midl.c +452 -0
  30. package/dependencies/lmdb/libraries/liblmdb/midl.h +208 -0
  31. package/dependencies/lmdb/libraries/liblmdb/module.c +101 -0
  32. package/dependencies/lmdb/libraries/liblmdb/module.h +16 -0
  33. package/dependencies/lmdb/libraries/liblmdb/mtest.c +178 -0
  34. package/dependencies/lmdb/libraries/liblmdb/mtest2.c +124 -0
  35. package/dependencies/lmdb/libraries/liblmdb/mtest3.c +133 -0
  36. package/dependencies/lmdb/libraries/liblmdb/mtest4.c +168 -0
  37. package/dependencies/lmdb/libraries/liblmdb/mtest5.c +135 -0
  38. package/dependencies/lmdb/libraries/liblmdb/mtest6.c +141 -0
  39. package/dependencies/lmdb/libraries/liblmdb/mtest_enc.c +190 -0
  40. package/dependencies/lmdb/libraries/liblmdb/mtest_enc2.c +189 -0
  41. package/dependencies/lmdb/libraries/liblmdb/mtest_remap.c +177 -0
  42. package/dependencies/lmdb/libraries/liblmdb/sample-bdb.txt +73 -0
  43. package/dependencies/lmdb/libraries/liblmdb/sample-mdb.txt +62 -0
  44. package/dependencies/lmdb/libraries/liblmdb/tooltag +27 -0
  45. package/dependencies/lmdb-data-v1/libraries/liblmdb/CHANGES +266 -0
  46. package/dependencies/lmdb-data-v1/libraries/liblmdb/COPYRIGHT +20 -0
  47. package/dependencies/lmdb-data-v1/libraries/liblmdb/Doxyfile +1631 -0
  48. package/dependencies/lmdb-data-v1/libraries/liblmdb/LICENSE +47 -0
  49. package/dependencies/lmdb-data-v1/libraries/liblmdb/intro.doc +192 -0
  50. package/dependencies/lmdb-data-v1/libraries/liblmdb/lmdb.h +1608 -0
  51. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb.c +10320 -0
  52. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_copy.1 +55 -0
  53. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_copy.c +82 -0
  54. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_dump.1 +75 -0
  55. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_dump.c +319 -0
  56. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_load.1 +84 -0
  57. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_load.c +496 -0
  58. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_stat.1 +64 -0
  59. package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_stat.c +263 -0
  60. package/dependencies/lmdb-data-v1/libraries/liblmdb/midl.c +359 -0
  61. package/dependencies/lmdb-data-v1/libraries/liblmdb/midl.h +186 -0
  62. package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest.c +177 -0
  63. package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest2.c +124 -0
  64. package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest3.c +133 -0
  65. package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest4.c +168 -0
  66. package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest5.c +135 -0
  67. package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest6.c +141 -0
  68. package/dependencies/lmdb-data-v1/libraries/liblmdb/sample-bdb.txt +73 -0
  69. package/dependencies/lmdb-data-v1/libraries/liblmdb/sample-mdb.txt +62 -0
  70. package/dependencies/lmdb-data-v1/libraries/liblmdb/tooltag +22 -0
  71. package/dependencies/lz4/LICENSE +11 -0
  72. package/dependencies/lz4/lib/LICENSE +24 -0
  73. package/dependencies/lz4/lib/Makefile +225 -0
  74. package/dependencies/lz4/lib/README.md +169 -0
  75. package/dependencies/lz4/lib/dll/example/Makefile +63 -0
  76. package/dependencies/lz4/lib/dll/example/README.md +69 -0
  77. package/dependencies/lz4/lib/dll/example/fullbench-dll.sln +25 -0
  78. package/dependencies/lz4/lib/dll/example/fullbench-dll.vcxproj +182 -0
  79. package/dependencies/lz4/lib/dll/liblz4.def +62 -0
  80. package/dependencies/lz4/lib/liblz4-dll.rc.in +35 -0
  81. package/dependencies/lz4/lib/liblz4.pc.in +14 -0
  82. package/dependencies/lz4/lib/lz4.c +2722 -0
  83. package/dependencies/lz4/lib/lz4.h +842 -0
  84. package/dependencies/lz4/lib/lz4file.c +311 -0
  85. package/dependencies/lz4/lib/lz4file.h +93 -0
  86. package/dependencies/lz4/lib/lz4frame.c +2078 -0
  87. package/dependencies/lz4/lib/lz4frame.h +692 -0
  88. package/dependencies/lz4/lib/lz4frame_static.h +47 -0
  89. package/dependencies/lz4/lib/lz4hc.c +1631 -0
  90. package/dependencies/lz4/lib/lz4hc.h +413 -0
  91. package/dependencies/lz4/lib/xxhash.c +1030 -0
  92. package/dependencies/lz4/lib/xxhash.h +328 -0
  93. package/dependencies/v8/v8-fast-api-calls-v16.h +791 -0
  94. package/dependencies/v8/v8-fast-api-calls.h +888 -0
  95. package/dict/dict.txt +1 -0
  96. package/dict/dict2.txt +1 -0
  97. package/dist/index.cjs +2950 -0
  98. package/dist/index.cjs.map +1 -0
  99. package/index.d.cts +420 -0
  100. package/index.d.ts +450 -0
  101. package/index.js +38 -0
  102. package/keys.js +116 -0
  103. package/level.js +27 -0
  104. package/native.js +85 -0
  105. package/node-index.js +32 -0
  106. package/open.js +448 -0
  107. package/package.json +124 -0
  108. package/read.js +833 -0
  109. package/rollup.config.js +12 -0
  110. package/src/compression.cpp +225 -0
  111. package/src/cursor.cpp +395 -0
  112. package/src/dbi.cpp +382 -0
  113. package/src/env.cpp +1030 -0
  114. package/src/lmdb-js.cpp +64 -0
  115. package/src/lmdb-js.h +633 -0
  116. package/src/misc.cpp +593 -0
  117. package/src/ordered-binary.cpp +46 -0
  118. package/src/txn.cpp +210 -0
  119. package/src/v8-functions.cpp +170 -0
  120. package/src/windows.c +35 -0
  121. package/src/writer.cpp +537 -0
  122. package/util/RangeIterable.js +236 -0
  123. package/util/set-optional-deps.cjs +14 -0
  124. package/util/when.js +8 -0
  125. package/write.js +912 -0
package/read.js ADDED
@@ -0,0 +1,833 @@
1
+ import { RangeIterable } from './util/RangeIterable.js';
2
+ import { getAddress, Cursor, Txn, orderedBinary, lmdbError, getByBinary, setGlobalBuffer, prefetch, iterate, position as doPosition, resetTxn, getCurrentValue, getCurrentShared, getStringByBinary, globalBuffer, getSharedBuffer, startRead, setReadCallback } from './native.js';
3
+ import { saveKey } from './keys.js';
4
+ const IF_EXISTS = 3.542694326329068e-103;
5
+ const ITERATOR_DONE = { done: true, value: undefined };
6
+ const Uint8ArraySlice = Uint8Array.prototype.slice;
7
+ let getValueBytes = globalBuffer;
8
+ if (!getValueBytes.maxLength) {
9
+ getValueBytes.maxLength = getValueBytes.length;
10
+ getValueBytes.isGlobal = true;
11
+ Object.defineProperty(getValueBytes, 'length', { value: getValueBytes.length, writable: true, configurable: true });
12
+ }
13
+ const START_ADDRESS_POSITION = 4064;
14
+ const NEW_BUFFER_THRESHOLD = 0x8000;
15
+ const SOURCE_SYMBOL = Symbol.for('source');
16
+ export const UNMODIFIED = {};
17
+ let mmaps = [];
18
+
19
+ export function addReadMethods(LMDBStore, {
20
+ maxKeySize, env, keyBytes, keyBytesView, getLastVersion, getLastTxnId
21
+ }) {
22
+ let readTxn, readTxnRenewed, asSafeBuffer = false;
23
+ let renewId = 1;
24
+ let outstandingReads = 0;
25
+ Object.assign(LMDBStore.prototype, {
26
+ getString(id, options) {
27
+ let txn = env.writeTxn || (options && options.transaction) || (readTxnRenewed ? readTxn : renewReadTxn(this));
28
+ let string = getStringByBinary(this.dbAddress, this.writeKey(id, keyBytes, 0), txn.address || 0);
29
+ if (typeof string === 'number') { // indicates the buffer wasn't large enough
30
+ this._allocateGetBuffer(string);
31
+ // and then try again
32
+ string = getStringByBinary(this.dbAddress, this.writeKey(id, keyBytes, 0), txn.address || 0);
33
+ }
34
+ if (string)
35
+ this.lastSize = string.length;
36
+ return string;
37
+ },
38
+ getBinaryFast(id, options) {
39
+ let rc;
40
+ let txn = env.writeTxn || (options && options.transaction) || (readTxnRenewed ? readTxn : renewReadTxn(this));
41
+ rc = this.lastSize = getByBinary(this.dbAddress, this.writeKey(id, keyBytes, 0), (options && options.ifNotTxnId) || 0, txn.address || 0);
42
+ if (rc < 0) {
43
+ if (rc == -30798) // MDB_NOTFOUND
44
+ return; // undefined
45
+ if (rc == -30004) // txn id matched
46
+ return UNMODIFIED;
47
+ if (rc == -30781 /*MDB_BAD_VALSIZE*/ && this.writeKey(id, keyBytes, 0) == 0)
48
+ throw new Error(id === undefined ?
49
+ 'A key is required for get, but is undefined' :
50
+ 'Zero length key is not allowed in LMDB');
51
+ if (rc == -30000) // int32 overflow, read uint32
52
+ rc = this.lastSize = keyBytesView.getUint32(0, true);
53
+ else if (rc == -30001) {// shared buffer
54
+ this.lastSize = keyBytesView.getUint32(0, true);
55
+ let bufferId = keyBytesView.getUint32(4, true);
56
+ return getMMapBuffer(bufferId, this.lastSize);
57
+ } else
58
+ throw lmdbError(rc);
59
+ }
60
+ let compression = this.compression;
61
+ let bytes = compression ? compression.getValueBytes : getValueBytes;
62
+ if (rc > bytes.maxLength) {
63
+ // 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
64
+ return this._returnLargeBuffer(
65
+ () => getByBinary(this.dbAddress, this.writeKey(id, keyBytes, 0), 0, txn.address || 0));
66
+ }
67
+ bytes.length = this.lastSize;
68
+ return bytes;
69
+ },
70
+ getBFAsync(id, options, callback) {
71
+ let txn = env.writeTxn || (options && options.transaction) || (readTxnRenewed ? readTxn : renewReadTxn(this));
72
+ txn.refCount = (txn.refCount || 0) + 1;
73
+ outstandingReads++;
74
+ let address = recordReadInstruction(txn.address, this.db.dbi, id, this.writeKey, maxKeySize, ( rc, bufferId, offset, size ) => {
75
+ if (rc && rc !== 1)
76
+ callback(lmdbError(rc));
77
+ outstandingReads--;
78
+ let buffer = mmaps[bufferId];
79
+ if (!buffer) {
80
+ buffer = mmaps[bufferId] = getSharedBuffer(bufferId, env.address);
81
+ }
82
+ //console.log({bufferId, offset, size})
83
+ if (buffer.isSharedMap) {
84
+ // using LMDB shared memory
85
+ // TODO: We may want explicit support for clearing aborting the transaction on the next event turn,
86
+ // but for now we are relying on the GC to cleanup transaction for larger blocks of memory
87
+ let bytes = new Uint8Array(buffer, offset, size);
88
+ bytes.txn = txn;
89
+ callback(bytes, 0, size);
90
+ } else {
91
+ // using copied memory
92
+ txn.done(); // decrement and possibly abort
93
+ callback(buffer, offset, size);
94
+ }
95
+ });
96
+ if (address) {
97
+ startRead(address, () => {
98
+ resolveReads();
99
+ });
100
+ }
101
+ },
102
+ getAsync(id, options, callback) {
103
+ let promise;
104
+ if (!callback)
105
+ promise = new Promise(resolve => callback = resolve);
106
+ this.getBFAsync(id, options, (buffer, offset, size) => {
107
+ if (this.useVersions) {
108
+ // TODO: And get the version
109
+ offset += 8;
110
+ size -= 8;
111
+ }
112
+ let bytes = new Uint8Array(buffer, offset, size);
113
+ let value;
114
+ if (this.decoder) {
115
+ // the decoder potentially uses the data from the buffer in the future and needs a stable buffer
116
+ value = bytes && this.decoder.decode(bytes);
117
+ } else if (this.encoding == 'binary') {
118
+ value = bytes;
119
+ } else {
120
+ value = Buffer.prototype.utf8Slice.call(bytes, 0, size);
121
+ if (this.encoding == 'json' && value)
122
+ value = JSON.parse(value);
123
+ }
124
+ callback(value);
125
+ });
126
+ return promise;
127
+ },
128
+ retain(data, options) {
129
+ if (!data)
130
+ return
131
+ let source = data[SOURCE_SYMBOL];
132
+ let buffer = source ? source.bytes : data;
133
+ if (!buffer.isGlobal && !env.writeTxn) {
134
+ let txn = options?.transaction || (readTxnRenewed ? readTxn : renewReadTxn(this));
135
+ buffer.txn = txn;
136
+ txn.refCount = (txn.refCount || 0) + 1;
137
+ return data;
138
+ } else {
139
+ buffer = Uint8ArraySlice.call(buffer, 0, this.lastSize);
140
+ if (source) {
141
+ source.bytes = buffer;
142
+ return data;
143
+ } else
144
+ return buffer;
145
+ }
146
+ },
147
+ _returnLargeBuffer(getFast) {
148
+ let bytes;
149
+ let compression = this.compression;
150
+ if (asSafeBuffer && this.lastSize > NEW_BUFFER_THRESHOLD) {
151
+ // used by getBinary to indicate it should create a dedicated buffer to receive this
152
+ let bytesToRestore
153
+ try {
154
+ if (compression) {
155
+ bytesToRestore = compression.getValueBytes;
156
+ let dictionary = compression.dictionary || [];
157
+ let dictLength = (dictionary.length >> 3) << 3;// make sure it is word-aligned
158
+ bytes = makeReusableBuffer(this.lastSize);
159
+ compression.setBuffer(bytes.buffer, bytes.byteOffset, this.lastSize, dictionary, dictLength);
160
+ compression.getValueBytes = bytes;
161
+ } else {
162
+ bytesToRestore = getValueBytes;
163
+ setGlobalBuffer(bytes = getValueBytes = makeReusableBuffer(this.lastSize));
164
+ }
165
+ getFast();
166
+ } finally {
167
+ if (compression) {
168
+ let dictLength = (compression.dictionary.length >> 3) << 3;
169
+ compression.setBuffer(bytesToRestore.buffer, bytesToRestore.byteOffset, bytesToRestore.maxLength, compression.dictionary, dictLength);
170
+ compression.getValueBytes = bytesToRestore;
171
+ } else {
172
+ setGlobalBuffer(bytesToRestore);
173
+ getValueBytes = bytesToRestore;
174
+ }
175
+ }
176
+ return bytes;
177
+ }
178
+ // grow our shared/static buffer to accomodate the size of the data
179
+ bytes = this._allocateGetBuffer(this.lastSize);
180
+ // and try again
181
+ getFast();
182
+ bytes.length = this.lastSize;
183
+ return bytes;
184
+ },
185
+ _allocateGetBuffer(lastSize) {
186
+ let newLength = Math.min(Math.max(lastSize * 2, 0x1000), 0xfffffff8);
187
+ let bytes;
188
+ if (this.compression) {
189
+ let dictionary = this.compression.dictionary || Buffer.allocUnsafeSlow(0);
190
+ let dictLength = (dictionary.length >> 3) << 3;// make sure it is word-aligned
191
+ bytes = Buffer.allocUnsafeSlow(newLength + dictLength);
192
+ bytes.set(dictionary) // copy dictionary into start
193
+ // the section after the dictionary is the target area for get values
194
+ bytes = bytes.subarray(dictLength);
195
+ this.compression.setBuffer(bytes.buffer, bytes.byteOffset, newLength, dictionary, dictLength);
196
+ bytes.maxLength = newLength;
197
+ Object.defineProperty(bytes, 'length', { value: newLength, writable: true, configurable: true });
198
+ this.compression.getValueBytes = bytes;
199
+ } else {
200
+ bytes = makeReusableBuffer(newLength);
201
+ setGlobalBuffer(getValueBytes = bytes);
202
+ }
203
+ bytes.isGlobal = true;
204
+ return bytes;
205
+ },
206
+ getBinary(id, options) {
207
+ try {
208
+ asSafeBuffer = true;
209
+ let fastBuffer = this.getBinaryFast(id, options);
210
+ return fastBuffer && (fastBuffer.isGlobal ? Uint8ArraySlice.call(fastBuffer, 0, this.lastSize) : fastBuffer);
211
+ } finally {
212
+ asSafeBuffer = false;
213
+ }
214
+ },
215
+ getSharedBinary(id, options) {
216
+ let fastBuffer = this.getBinaryFast(id, options);
217
+ if (fastBuffer) {
218
+ if (fastBuffer.isGlobal || writeTxn)
219
+ return Uint8ArraySlice.call(fastBuffer, 0, this.lastSize)
220
+ fastBuffer.txn = (options && options.transaction);
221
+ options.transaction.refCount = (options.transaction.refCount || 0) + 1;
222
+ return fastBuffer;
223
+ }
224
+ },
225
+ get(id, options) {
226
+ if (this.decoderCopies) {
227
+ // the decoder copies any data, so we can use the fast binary retrieval that overwrites the same buffer space
228
+ let bytes = this.getBinaryFast(id, options);
229
+ return bytes && (bytes == UNMODIFIED ? UNMODIFIED : this.decoder.decode(bytes, options));
230
+ }
231
+ if (this.encoding == 'binary')
232
+ return this.getBinary(id, options);
233
+ if (this.decoder) {
234
+ // the decoder potentially uses the data from the buffer in the future and needs a stable buffer
235
+ let bytes = this.getBinary(id, options);
236
+ return bytes && (bytes == UNMODIFIED ? UNMODIFIED : this.decoder.decode(bytes));
237
+ }
238
+
239
+ let result = this.getString(id, options);
240
+ if (result) {
241
+ if (this.encoding == 'json')
242
+ return JSON.parse(result);
243
+ }
244
+ return result;
245
+ },
246
+ getEntry(id, options) {
247
+ let value = this.get(id, options);
248
+ if (value !== undefined) {
249
+ if (this.useVersions)
250
+ return {
251
+ value,
252
+ version: getLastVersion(),
253
+ //size: this.lastSize
254
+ };
255
+ else
256
+ return {
257
+ value,
258
+ //size: this.lastSize
259
+ };
260
+ }
261
+ },
262
+ resetReadTxn() {
263
+ resetReadTxn();
264
+ },
265
+ _commitReadTxn() {
266
+ if (readTxn) {
267
+ readTxn.isCommitted = true
268
+ readTxn.commit();
269
+ }
270
+ lastReadTxnRef = null;
271
+ readTxnRenewed = null;
272
+ readTxn = null;
273
+ },
274
+ ensureReadTxn() {
275
+ if (!env.writeTxn && !readTxnRenewed)
276
+ renewReadTxn(this);
277
+ },
278
+ doesExist(key, versionOrValue) {
279
+ if (versionOrValue == null) {
280
+ // undefined means the entry exists, null is used specifically to check for the entry *not* existing
281
+ return (this.getBinaryFast(key) === undefined) == (versionOrValue === null);
282
+ }
283
+ else if (this.useVersions) {
284
+ return this.getBinaryFast(key) !== undefined && (versionOrValue === IF_EXISTS || getLastVersion() === versionOrValue);
285
+ }
286
+ else {
287
+ if (versionOrValue && versionOrValue['\x10binary-data\x02'])
288
+ versionOrValue = versionOrValue['\x10binary-data\x02'];
289
+ else if (this.encoder)
290
+ versionOrValue = this.encoder.encode(versionOrValue);
291
+ if (typeof versionOrValue == 'string')
292
+ versionOrValue = Buffer.from(versionOrValue);
293
+ return this.getValuesCount(key, { start: versionOrValue, exactMatch: true}) > 0;
294
+ }
295
+ },
296
+ getValues(key, options) {
297
+ let defaultOptions = {
298
+ key,
299
+ valuesForKey: true
300
+ };
301
+ if (options && options.snapshot === false)
302
+ throw new Error('Can not disable snapshots for getValues');
303
+ return this.getRange(options ? Object.assign(defaultOptions, options) : defaultOptions);
304
+ },
305
+ getKeys(options) {
306
+ if (!options)
307
+ options = {};
308
+ options.values = false;
309
+ return this.getRange(options);
310
+ },
311
+ getCount(options) {
312
+ if (!options)
313
+ options = {};
314
+ options.onlyCount = true;
315
+ return this.getRange(options).iterate();
316
+ },
317
+ getKeysCount(options) {
318
+ if (!options)
319
+ options = {};
320
+ options.onlyCount = true;
321
+ options.values = false;
322
+ return this.getRange(options).iterate();
323
+ },
324
+ getValuesCount(key, options) {
325
+ if (!options)
326
+ options = {};
327
+ options.key = key;
328
+ options.valuesForKey = true;
329
+ options.onlyCount = true;
330
+ return this.getRange(options).iterate();
331
+ },
332
+ getRange(options) {
333
+ let iterable = new RangeIterable();
334
+ if (!options)
335
+ options = {};
336
+ let includeValues = options.values !== false;
337
+ let includeVersions = options.versions;
338
+ let valuesForKey = options.valuesForKey;
339
+ let limit = options.limit;
340
+ let db = this.db;
341
+ let snapshot = options.snapshot;
342
+ let compression = this.compression;
343
+ iterable.iterate = () => {
344
+ let currentKey = valuesForKey ? options.key : options.start;
345
+ const reverse = options.reverse;
346
+ let count = 0;
347
+ let cursor, cursorRenewId, cursorAddress;
348
+ let txn;
349
+ let flags = (includeValues ? 0x100 : 0) | (reverse ? 0x400 : 0) |
350
+ (valuesForKey ? 0x800 : 0) | (options.exactMatch ? 0x4000 : 0) |
351
+ (options.inclusiveEnd ? 0x8000 : 0) |
352
+ (options.exclusiveStart ? 0x10000 : 0);
353
+ let store = this;
354
+ function resetCursor() {
355
+ try {
356
+ if (cursor)
357
+ finishCursor();
358
+ let txnAddress;
359
+ txn = options.transaction
360
+ if (txn) {
361
+ if (txn.isDone) throw new Error('Can not iterate on range with transaction that is already' +
362
+ ' done');
363
+ txnAddress = txn.address;
364
+ cursor = null;
365
+ } else {
366
+ let writeTxn = env.writeTxn;
367
+ if (writeTxn)
368
+ snapshot = false;
369
+ txn = env.writeTxn || options.transaction || (readTxnRenewed ? readTxn : renewReadTxn(store));
370
+ cursor = !writeTxn && db.availableCursor;
371
+ }
372
+ if (cursor) {
373
+ db.availableCursor = null;
374
+ flags |= 0x2000;
375
+ } else {
376
+ cursor = new Cursor(db, txnAddress || 0);
377
+ }
378
+ cursorAddress = cursor.address;
379
+ txn.refCount = (txn.refCount || 0) + 1; // track transaction so we always use the same one
380
+ if (snapshot === false) {
381
+ cursorRenewId = renewId; // use shared read transaction
382
+ txn.renewingRefCount = (txn.renewingRefCount || 0) + 1; // need to know how many are renewing cursors
383
+ }
384
+ } catch(error) {
385
+ if (cursor) {
386
+ try {
387
+ cursor.close();
388
+ } catch(error) { }
389
+ }
390
+ throw error;
391
+ }
392
+ }
393
+ resetCursor();
394
+ if (options.onlyCount) {
395
+ flags |= 0x1000;
396
+ let count = position(options.offset);
397
+ if (count < 0)
398
+ lmdbError(count);
399
+ finishCursor();
400
+ return count;
401
+ }
402
+ function position(offset) {
403
+ if (!env.address) {
404
+ throw new Error('Can not iterate on a closed database');
405
+ }
406
+ let keySize = currentKey === undefined ? 0 : store.writeKey(currentKey, keyBytes, 0);
407
+ let endAddress;
408
+ if (valuesForKey) {
409
+ if (options.start === undefined && options.end === undefined)
410
+ endAddress = 0;
411
+ else {
412
+ let startAddress;
413
+ if (store.encoder.writeKey) {
414
+ startAddress = saveKey(options.start, store.encoder.writeKey, iterable, maxKeySize);
415
+ keyBytesView.setFloat64(START_ADDRESS_POSITION, startAddress, true);
416
+ endAddress = saveKey(options.end, store.encoder.writeKey, iterable, maxKeySize);
417
+ } else if ((!options.start || options.start instanceof Uint8Array) && (!options.end || options.end instanceof Uint8Array)) {
418
+ startAddress = saveKey(options.start, orderedBinary.writeKey, iterable, maxKeySize);
419
+ keyBytesView.setFloat64(START_ADDRESS_POSITION, startAddress, true);
420
+ endAddress = saveKey(options.end, orderedBinary.writeKey, iterable, maxKeySize);
421
+ } else {
422
+ throw new Error('Only key-based encoding is supported for start/end values');
423
+ let encoded = store.encoder.encode(options.start);
424
+ let bufferAddress = encoded.buffer.address || (encoded.buffer.address = getAddress(encoded.buffer) - encoded.byteOffset);
425
+ startAddress = bufferAddress + encoded.byteOffset;
426
+ }
427
+ }
428
+ } else
429
+ endAddress = saveKey(options.end, store.writeKey, iterable, maxKeySize);
430
+ return doPosition(cursorAddress, flags, offset || 0, keySize, endAddress);
431
+ }
432
+
433
+ function finishCursor() {
434
+ if (txn.isDone)
435
+ return;
436
+ if (iterable.onDone)
437
+ iterable.onDone()
438
+ if (cursorRenewId)
439
+ txn.renewingRefCount--;
440
+ if (--txn.refCount <= 0 && txn.notCurrent) {
441
+ cursor.close();
442
+ txn.abort(); // this is no longer main read txn, abort it now that we are done
443
+ txn.isDone = true;
444
+ } else {
445
+ if (db.availableCursor || txn != readTxn) {
446
+ cursor.close();
447
+ } else { // try to reuse it
448
+ db.availableCursor = cursor;
449
+ db.cursorTxn = txn;
450
+ }
451
+ }
452
+ }
453
+ return {
454
+ next() {
455
+ let keySize, lastSize;
456
+ if (cursorRenewId && (cursorRenewId != renewId || txn.isDone)) {
457
+ resetCursor();
458
+ keySize = position(0);
459
+ }
460
+ if (count === 0) { // && includeValues) // on first entry, get current value if we need to
461
+ keySize = position(options.offset);
462
+ } else
463
+ keySize = iterate(cursorAddress);
464
+ if (keySize <= 0 ||
465
+ (count++ >= limit)) {
466
+ if (count < 0)
467
+ lmdbError(count);
468
+ finishCursor();
469
+ return ITERATOR_DONE;
470
+ }
471
+ if (!valuesForKey || snapshot === false) {
472
+ if (keySize > 20000) {
473
+ if (keySize > 0x1000000)
474
+ lmdbError(keySize - 0x100000000)
475
+ throw new Error('Invalid key size ' + keySize.toString(16))
476
+ }
477
+ currentKey = store.readKey(keyBytes, 32, keySize + 32);
478
+ }
479
+ if (includeValues) {
480
+ let value;
481
+ lastSize = keyBytesView.getUint32(0, true);
482
+ let bufferId = keyBytesView.getUint32(4, true);
483
+ let bytes;
484
+ if (bufferId) {
485
+ bytes = getMMapBuffer(bufferId, lastSize);
486
+ } else {
487
+ bytes = compression ? compression.getValueBytes : getValueBytes;
488
+ if (lastSize > bytes.maxLength) {
489
+ store.lastSize = lastSize;
490
+ asSafeBuffer = store.encoding == 'binary';
491
+ try {
492
+ bytes = store._returnLargeBuffer(() => getCurrentValue(cursorAddress));
493
+ } finally {
494
+ asSafeBuffer = false;
495
+ }
496
+ } else
497
+ bytes.length = lastSize;
498
+ }
499
+ if (store.decoder) {
500
+ value = store.decoder.decode(bytes, lastSize);
501
+ } else if (store.encoding == 'binary')
502
+ value = bytes.isGlobal ? Uint8ArraySlice.call(bytes, 0, lastSize) : bytes;
503
+ else {
504
+ value = bytes.toString('utf8', 0, lastSize);
505
+ if (store.encoding == 'json' && value)
506
+ value = JSON.parse(value);
507
+ }
508
+ if (includeVersions)
509
+ return {
510
+ value: {
511
+ key: currentKey,
512
+ value,
513
+ version: getLastVersion()
514
+ }
515
+ };
516
+ else if (valuesForKey)
517
+ return {
518
+ value
519
+ };
520
+ else
521
+ return {
522
+ value: {
523
+ key: currentKey,
524
+ value,
525
+ }
526
+ };
527
+ } else if (includeVersions) {
528
+ return {
529
+ value: {
530
+ key: currentKey,
531
+ version: getLastVersion()
532
+ }
533
+ };
534
+ } else {
535
+ return {
536
+ value: currentKey
537
+ };
538
+ }
539
+ },
540
+ return() {
541
+ finishCursor();
542
+ return ITERATOR_DONE;
543
+ },
544
+ throw() {
545
+ finishCursor();
546
+ return ITERATOR_DONE;
547
+ }
548
+ };
549
+ };
550
+ return iterable;
551
+ },
552
+
553
+ getMany(keys, callback) {
554
+ // this is an asynchronous get for multiple keys. It actually works by prefetching asynchronously,
555
+ // allowing a separate to absorb the potentially largest cost: hard page faults (and disk I/O).
556
+ // And then we just do standard sync gets (to deserialized data) to fulfil the callback/promise
557
+ // once the prefetch occurs
558
+ let promise = callback ? undefined : new Promise(resolve => callback = (error, results) => resolve(results));
559
+ this.prefetch(keys, () => {
560
+ let results = new Array(keys.length);
561
+ for (let i = 0, l = keys.length; i < l; i++) {
562
+ results[i] = get.call(this, keys[i]);
563
+ }
564
+ callback(null, results);
565
+ });
566
+ return promise;
567
+ },
568
+ getSharedBufferForGet(id, options) {
569
+ let txn = env.writeTxn || (options && options.transaction) || (readTxnRenewed ? readTxn : renewReadTxn(this));
570
+ this.lastSize = this.keyIsCompatibility ? txn.getBinaryShared(id) : this.db.get(this.writeKey(id, keyBytes, 0));
571
+ if (this.lastSize === -30798) { // not found code
572
+ return; //undefined
573
+ }
574
+ return this.lastSize;
575
+ this.lastSize = keyBytesView.getUint32(0, true);
576
+ let bufferIndex = keyBytesView.getUint32(12, true);
577
+ lastOffset = keyBytesView.getUint32(8, true);
578
+ let buffer = buffers[bufferIndex];
579
+ let startOffset;
580
+ if (!buffer || lastOffset < (startOffset = buffer.startOffset) || (lastOffset + this.lastSize > startOffset + 0x100000000)) {
581
+ if (buffer)
582
+ env.detachBuffer(buffer.buffer);
583
+ startOffset = (lastOffset >>> 16) * 0x10000;
584
+ console.log('make buffer for address', bufferIndex * 0x100000000 + startOffset);
585
+ buffer = buffers[bufferIndex] = Buffer.from(getBufferForAddress(bufferIndex * 0x100000000 + startOffset));
586
+ buffer.startOffset = startOffset;
587
+ }
588
+ lastOffset -= startOffset;
589
+ return buffer;
590
+ return buffer.slice(lastOffset, lastOffset + this.lastSize);/*Uint8ArraySlice.call(buffer, lastOffset, lastOffset + this.lastSize)*/
591
+ },
592
+ prefetch(keys, callback) {
593
+ if (!keys)
594
+ throw new Error('An array of keys must be provided');
595
+ if (!keys.length) {
596
+ if (callback) {
597
+ callback(null);
598
+ return;
599
+ } else
600
+ return Promise.resolve();
601
+ }
602
+ let buffers = [];
603
+ let startPosition;
604
+ let bufferHolder = {};
605
+ let lastBuffer;
606
+ for (let key of keys) {
607
+ let position
608
+ if (key && key.key !== undefined && key.value !== undefined) {
609
+ position = saveKey(key.value, this.writeKey, bufferHolder, maxKeySize, 0x80000000);
610
+ saveKey(key.key, this.writeKey, bufferHolder, maxKeySize);
611
+ } else {
612
+ position = saveKey(key, this.writeKey, bufferHolder, maxKeySize);
613
+ }
614
+ if (!startPosition)
615
+ startPosition = position;
616
+ if (bufferHolder.saveBuffer != lastBuffer) {
617
+ buffers.push(bufferHolder);
618
+ lastBuffer = bufferHolder.saveBuffer;
619
+ bufferHolder = { saveBuffer: lastBuffer };
620
+ }
621
+ }
622
+ saveKey(undefined, this.writeKey, bufferHolder, maxKeySize);
623
+ outstandingReads++;
624
+ prefetch(this.dbAddress, startPosition, (error) => {
625
+ outstandingReads--;
626
+ if (error)
627
+ console.error('Error with prefetch', buffers, bufferHolder); // partly exists to keep the buffers pinned in memory
628
+ else
629
+ callback(null);
630
+ });
631
+ if (!callback)
632
+ return new Promise(resolve => callback = resolve);
633
+ },
634
+ useReadTransaction() {
635
+ let txn = readTxnRenewed ? readTxn : renewReadTxn(this);
636
+ if (!txn.use) {
637
+ throw new Error('Can not use read transaction from a closed database');
638
+ }
639
+ txn.use();
640
+ return txn;
641
+ },
642
+ close(callback) {
643
+ this.status = 'closing';
644
+ let txnPromise;
645
+ if (this.isRoot) {
646
+ // if it is root, we need to abort and/or wait for transactions to finish
647
+ if (readTxn) readTxn.abort();
648
+ else readTxn = {};
649
+ readTxn.isDone = true;
650
+ Object.defineProperty(readTxn,'renew', {
651
+ value: () => {
652
+ throw new Error('Can not read from a closed database');
653
+ }, configurable: true
654
+ });
655
+ Object.defineProperty(readTxn,'use', {
656
+ value: () => {
657
+ throw new Error('Can not read from a closed database');
658
+ }, configurable: true
659
+ });
660
+ readTxnRenewed = null;
661
+ txnPromise = this._endWrites && this._endWrites();
662
+ }
663
+ const doClose = () => {
664
+ if (this.isRoot) {
665
+ if (outstandingReads > 0) {
666
+ return new Promise(resolve => setTimeout(() => resolve(doClose()), 1));
667
+ }
668
+ env.address = 0;
669
+ env.close();
670
+ } else
671
+ this.db.close();
672
+ this.status = 'closed';
673
+ if (callback)
674
+ callback();
675
+ }
676
+ if (txnPromise)
677
+ return txnPromise.then(doClose);
678
+ else {
679
+ doClose();
680
+ return Promise.resolve();
681
+ }
682
+ },
683
+ getStats() {
684
+ let txn = env.writeTxn || (readTxnRenewed ? readTxn : renewReadTxn(this));
685
+ let dbStats = this.db.stat();
686
+ dbStats.root = env.stat();
687
+ Object.assign(dbStats, env.info());
688
+ dbStats.free = env.freeStat();
689
+ return dbStats;
690
+ },
691
+ });
692
+ let get = LMDBStore.prototype.get;
693
+ let lastReadTxnRef;
694
+ function getMMapBuffer(bufferId, size) {
695
+ let buffer = mmaps[bufferId];
696
+ if (!buffer) {
697
+ buffer = mmaps[bufferId] = getSharedBuffer(bufferId, env.address);
698
+ }
699
+ let offset = keyBytesView.getUint32(8, true);
700
+ return new Uint8Array(buffer, offset, size);
701
+ }
702
+ function renewReadTxn(store) {
703
+ if (!env.address) {
704
+ throw new Error('Can not renew a transaction from a closed database');
705
+ }
706
+ if (!readTxn) {
707
+ let retries = 0;
708
+ let waitArray;
709
+ do {
710
+ try {
711
+ let lastReadTxn = lastReadTxnRef && lastReadTxnRef.deref();
712
+ readTxn = new Txn(env, 0x20000, lastReadTxn && !lastReadTxn.isDone && lastReadTxn);
713
+ if (readTxn.address == 0) {
714
+ readTxn = lastReadTxn;
715
+ if (readTxn.notCurrent)
716
+ readTxn.notCurrent = false;
717
+ }
718
+ break;
719
+ } catch (error) {
720
+ if (error.message.includes('temporarily')) {
721
+ if (!waitArray)
722
+ waitArray = new Int32Array(new SharedArrayBuffer(4), 0, 1);
723
+ Atomics.wait(waitArray, 0, 0, retries * 2);
724
+ } else
725
+ throw error;
726
+ }
727
+ } while (retries++ < 100);
728
+ }
729
+ // we actually don't renew here, we let the renew take place in the next
730
+ // lmdb native read/call so as to avoid an extra native call
731
+ readTxnRenewed = setTimeout(resetReadTxn, 0);
732
+ store.emit('begin-transaction');
733
+ return readTxn;
734
+ }
735
+ function resetReadTxn() {
736
+ renewId++;
737
+ if (readTxnRenewed) {
738
+ readTxnRenewed = null;
739
+ if (readTxn.refCount - (readTxn.renewingRefCount || 0) > 0) {
740
+ readTxn.notCurrent = true;
741
+ lastReadTxnRef = new WeakRef(readTxn);
742
+ readTxn = null;
743
+ } else if (readTxn.address && !readTxn.isDone) {
744
+ resetTxn(readTxn.address);
745
+ } else {
746
+ console.warn('Attempt to reset an invalid read txn', readTxn);
747
+ throw new Error('Attempt to reset an invalid read txn');
748
+ }
749
+ }
750
+ }
751
+ }
752
+ export function makeReusableBuffer(size) {
753
+ let bytes = typeof Buffer != 'undefined' ? Buffer.alloc(size) : new Uint8Array(size);
754
+ bytes.maxLength = size;
755
+ Object.defineProperty(bytes, 'length', { value: size, writable: true, configurable: true });
756
+ return bytes;
757
+ }
758
+
759
+ Txn.prototype.done = function() {
760
+ this.refCount--;
761
+ if (this.refCount === 0 && this.notCurrent) {
762
+ this.abort();
763
+ this.isDone = true;
764
+ }
765
+ }
766
+ Txn.prototype.use = function() {
767
+ this.refCount = (this.refCount || 0) + 1;
768
+ }
769
+
770
+
771
+ let readInstructions, readCallbacks = new Map(), uint32Instructions, instructionsDataView = { setFloat64() {}, setUint32() {} }, instructionsAddress;
772
+ let savePosition = 8000;
773
+ let DYNAMIC_KEY_BUFFER_SIZE = 8192;
774
+ function allocateInstructionsBuffer() {
775
+ readInstructions = typeof Buffer != 'undefined' ? Buffer.alloc(DYNAMIC_KEY_BUFFER_SIZE) : new Uint8Array(DYNAMIC_KEY_BUFFER_SIZE);
776
+ uint32Instructions = new Int32Array(readInstructions.buffer, 0, readInstructions.buffer.byteLength >> 2);
777
+ uint32Instructions[2] = 0xf0000000; // indicates a new read task must be started
778
+ instructionsAddress = readInstructions.buffer.address = getAddress(readInstructions.buffer);
779
+ readInstructions.dataView = instructionsDataView = new DataView(readInstructions.buffer, readInstructions.byteOffset, readInstructions.byteLength);
780
+ savePosition = 0;
781
+ }
782
+ export function recordReadInstruction(txnAddress, dbi, key, writeKey, maxKeySize, callback) {
783
+ if (savePosition > 7800) {
784
+ allocateInstructionsBuffer();
785
+ }
786
+ let start = savePosition;
787
+ let keyPosition = savePosition + 16;
788
+ try {
789
+ savePosition = key === undefined ? keyPosition :
790
+ writeKey(key, readInstructions, keyPosition);
791
+ } catch (error) {
792
+ if (error.name == 'RangeError') {
793
+ if (8180 - start < maxKeySize) {
794
+ allocateInstructionsBuffer(); // try again:
795
+ return recordReadInstruction(txnAddress, dbi, key, writeKey, maxKeySize, callback);
796
+ }
797
+ throw new Error('Key was too large, max key size is ' + maxKeySize);
798
+ } else
799
+ throw error;
800
+ }
801
+ let length = savePosition - keyPosition;
802
+ if (length > maxKeySize) {
803
+ savePosition = start;
804
+ throw new Error('Key of size ' + length + ' was too large, max key size is ' + maxKeySize);
805
+ }
806
+ uint32Instructions[(start >> 2) + 3] = length; // save the length
807
+ uint32Instructions[(start >> 2) + 2] = dbi;
808
+ savePosition = (savePosition + 12) & 0xfffffc;
809
+ instructionsDataView.setFloat64(start, txnAddress, true);
810
+ let callbackId = addReadCallback(() => {
811
+ let position = start >> 2;
812
+ let rc = thisInstructions[position];
813
+ callback(rc, thisInstructions[position + 1], thisInstructions[position + 2], thisInstructions[position + 3]);
814
+ });
815
+ let thisInstructions = uint32Instructions;
816
+ //if (start === 0)
817
+ return startRead(instructionsAddress + start, callbackId, {}, 'read');
818
+ //else
819
+ //nextRead(start);
820
+ }
821
+ let nextCallbackId = 0;
822
+ let addReadCallback = globalThis.__lmdb_read_callback;
823
+ if (!addReadCallback) {
824
+ addReadCallback = globalThis.__lmdb_read_callback = function(callback) {
825
+ let callbackId = nextCallbackId++;
826
+ readCallbacks.set(callbackId, callback);
827
+ return callbackId;
828
+ };
829
+ setReadCallback(function(callbackId) {
830
+ readCallbacks.get(callbackId)();
831
+ readCallbacks.delete(callbackId);
832
+ })
833
+ }