@ohos-ports/lmdb 2.5.3-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 +21 -0
- package/README.md +536 -0
- package/SECURITY.md +12 -0
- package/binding.gyp +111 -0
- package/build/Release/lmdb.node +0 -0
- package/caching.js +156 -0
- package/dependencies/lmdb/libraries/liblmdb/COPYRIGHT +20 -0
- package/dependencies/lmdb/libraries/liblmdb/Doxyfile +1631 -0
- package/dependencies/lmdb/libraries/liblmdb/LICENSE +47 -0
- package/dependencies/lmdb/libraries/liblmdb/Makefile +136 -0
- package/dependencies/lmdb/libraries/liblmdb/chacha8.c +183 -0
- package/dependencies/lmdb/libraries/liblmdb/chacha8.h +14 -0
- package/dependencies/lmdb/libraries/liblmdb/crypto.c +121 -0
- package/dependencies/lmdb/libraries/liblmdb/intro.doc +192 -0
- package/dependencies/lmdb/libraries/liblmdb/lmdb.h +1792 -0
- package/dependencies/lmdb/libraries/liblmdb/mdb.c +12429 -0
- package/dependencies/lmdb/libraries/liblmdb/mdb_copy.1 +74 -0
- package/dependencies/lmdb/libraries/liblmdb/mdb_copy.c +106 -0
- package/dependencies/lmdb/libraries/liblmdb/mdb_drop.1 +53 -0
- package/dependencies/lmdb/libraries/liblmdb/mdb_drop.c +154 -0
- package/dependencies/lmdb/libraries/liblmdb/mdb_dump.1 +94 -0
- package/dependencies/lmdb/libraries/liblmdb/mdb_dump.c +333 -0
- package/dependencies/lmdb/libraries/liblmdb/mdb_load.1 +97 -0
- package/dependencies/lmdb/libraries/liblmdb/mdb_load.c +530 -0
- package/dependencies/lmdb/libraries/liblmdb/mdb_stat.1 +83 -0
- package/dependencies/lmdb/libraries/liblmdb/mdb_stat.c +276 -0
- package/dependencies/lmdb/libraries/liblmdb/midl.c +452 -0
- package/dependencies/lmdb/libraries/liblmdb/midl.h +208 -0
- package/dependencies/lmdb/libraries/liblmdb/module.c +101 -0
- package/dependencies/lmdb/libraries/liblmdb/module.h +16 -0
- package/dependencies/lmdb/libraries/liblmdb/mtest.c +178 -0
- package/dependencies/lmdb/libraries/liblmdb/mtest2.c +124 -0
- package/dependencies/lmdb/libraries/liblmdb/mtest3.c +133 -0
- package/dependencies/lmdb/libraries/liblmdb/mtest4.c +168 -0
- package/dependencies/lmdb/libraries/liblmdb/mtest5.c +135 -0
- package/dependencies/lmdb/libraries/liblmdb/mtest6.c +141 -0
- package/dependencies/lmdb/libraries/liblmdb/mtest_enc.c +190 -0
- package/dependencies/lmdb/libraries/liblmdb/mtest_enc2.c +189 -0
- package/dependencies/lmdb/libraries/liblmdb/mtest_remap.c +177 -0
- package/dependencies/lmdb/libraries/liblmdb/sample-bdb.txt +73 -0
- package/dependencies/lmdb/libraries/liblmdb/sample-mdb.txt +62 -0
- package/dependencies/lmdb/libraries/liblmdb/tooltag +27 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/COPYRIGHT +20 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/Doxyfile +1631 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/LICENSE +47 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/Makefile +118 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/intro.doc +192 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/lmdb.h +1653 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb.c +11359 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_copy.1 +61 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_copy.c +84 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_drop.1 +40 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_drop.c +135 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_dump.1 +81 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_dump.c +319 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_load.1 +84 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_load.c +492 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_stat.1 +70 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mdb_stat.c +264 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/midl.c +421 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/midl.h +200 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest.c +177 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest2.c +124 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest3.c +133 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest4.c +168 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest5.c +135 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/mtest6.c +141 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/sample-bdb.txt +73 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/sample-mdb.txt +62 -0
- package/dependencies/lmdb-data-v1/libraries/liblmdb/tooltag +27 -0
- package/dependencies/lz4/LICENSE +11 -0
- package/dependencies/lz4/lib/LICENSE +24 -0
- package/dependencies/lz4/lib/README.md +137 -0
- package/dependencies/lz4/lib/dll/example/README.md +69 -0
- package/dependencies/lz4/lib/dll/example/fullbench-dll.sln +25 -0
- package/dependencies/lz4/lib/dll/example/fullbench-dll.vcxproj +182 -0
- package/dependencies/lz4/lib/dll/liblz4.def +62 -0
- package/dependencies/lz4/lib/liblz4-dll.rc.in +35 -0
- package/dependencies/lz4/lib/liblz4.pc.in +14 -0
- package/dependencies/lz4/lib/lz4.c +2495 -0
- package/dependencies/lz4/lib/lz4.h +774 -0
- package/dependencies/lz4/lib/lz4frame.c +1899 -0
- package/dependencies/lz4/lib/lz4frame.h +623 -0
- package/dependencies/lz4/lib/lz4frame_static.h +47 -0
- package/dependencies/lz4/lib/lz4hc.c +1615 -0
- package/dependencies/lz4/lib/lz4hc.h +413 -0
- package/dependencies/lz4/lib/xxhash.c +1030 -0
- package/dependencies/lz4/lib/xxhash.h +328 -0
- package/dependencies/v8/v8-fast-api-calls-v16.h +791 -0
- package/dependencies/v8/v8-fast-api-calls.h +888 -0
- package/dict/dict.txt +1 -0
- package/dict/dict2.txt +1 -0
- package/dist/index.cjs +2478 -0
- package/dist/index.cjs.map +1 -0
- package/index.d.ts +385 -0
- package/index.js +37 -0
- package/keys.js +102 -0
- package/level.js +27 -0
- package/native.js +82 -0
- package/node-index.js +28 -0
- package/open.js +414 -0
- package/package.json +70 -0
- package/read.js +590 -0
- package/rollup.config.js +12 -0
- package/src/compression.cpp +222 -0
- package/src/cursor.cpp +344 -0
- package/src/dbi.cpp +344 -0
- package/src/env.cpp +762 -0
- package/src/lmdb-js.cpp +64 -0
- package/src/lmdb-js.h +599 -0
- package/src/misc.cpp +324 -0
- package/src/ordered-binary.cpp +46 -0
- package/src/txn.cpp +257 -0
- package/src/v8-functions.cpp +166 -0
- package/src/windows.c +35 -0
- package/src/writer.cpp +452 -0
- package/util/RangeIterable.js +141 -0
- package/util/set-optional-deps.cjs +10 -0
- package/util/when.js +8 -0
- package/write.js +882 -0
package/read.js
ADDED
|
@@ -0,0 +1,590 @@
|
|
|
1
|
+
import { RangeIterable } from './util/RangeIterable.js';
|
|
2
|
+
import { getAddress, Cursor, Txn, orderedBinary, lmdbError, getByBinary, detachBuffer, setGlobalBuffer, prefetch, iterate, position as doPosition, resetTxn, getCurrentValue, getCurrentShared, getStringByBinary, getSharedByBinary } from './native.js';
|
|
3
|
+
import { saveKey } from './keys.js';
|
|
4
|
+
const ITERATOR_DONE = { done: true, value: undefined };
|
|
5
|
+
const Uint8ArraySlice = Uint8Array.prototype.slice;
|
|
6
|
+
const Uint8A = typeof Buffer != 'undefined' ? Buffer.allocUnsafeSlow : Uint8Array
|
|
7
|
+
let getValueBytes = makeReusableBuffer(0);
|
|
8
|
+
const START_ADDRESS_POSITION = 4064;
|
|
9
|
+
const NEW_BUFFER_THRESHOLD = 0x8000;
|
|
10
|
+
|
|
11
|
+
export function addReadMethods(LMDBStore, {
|
|
12
|
+
maxKeySize, env, keyBytes, keyBytesView, getLastVersion
|
|
13
|
+
}) {
|
|
14
|
+
let readTxn, readTxnRenewed, asSafeBuffer = false;
|
|
15
|
+
let renewId = 1;
|
|
16
|
+
Object.assign(LMDBStore.prototype, {
|
|
17
|
+
getString(id) {
|
|
18
|
+
(env.writeTxn || (readTxnRenewed ? readTxn : renewReadTxn(this)));
|
|
19
|
+
let string = getStringByBinary(this.dbAddress, this.writeKey(id, keyBytes, 0));
|
|
20
|
+
if (typeof string === 'number') { // indicates the buffer wasn't large enough
|
|
21
|
+
this._allocateGetBuffer(string);
|
|
22
|
+
// and then try again
|
|
23
|
+
string = getStringByBinary(this.dbAddress, this.writeKey(id, keyBytes, 0));
|
|
24
|
+
}
|
|
25
|
+
if (string)
|
|
26
|
+
this.lastSize = string.length;
|
|
27
|
+
return string;
|
|
28
|
+
},
|
|
29
|
+
getBinaryFast(id) {
|
|
30
|
+
(env.writeTxn || (readTxnRenewed ? readTxn : renewReadTxn(this)));
|
|
31
|
+
let rc = this.lastSize = getByBinary(this.dbAddress, this.writeKey(id, keyBytes, 0));
|
|
32
|
+
if (rc < 0) {
|
|
33
|
+
if (rc == -30798) // MDB_NOTFOUND
|
|
34
|
+
return; // undefined
|
|
35
|
+
if (rc == -30781 /*MDB_BAD_VALSIZE*/ && this.writeKey(id, keyBytes, 0) == 0)
|
|
36
|
+
throw new Error(id === undefined ?
|
|
37
|
+
'A key is required for get, but is undefined' :
|
|
38
|
+
'Zero length key is not allowed in LMDB');
|
|
39
|
+
if (rc == -30000) // int32 overflow, read uint32
|
|
40
|
+
rc = this.lastSize = keyBytesView.getUint32(0, true);
|
|
41
|
+
else
|
|
42
|
+
throw lmdbError(rc);
|
|
43
|
+
}
|
|
44
|
+
let compression = this.compression;
|
|
45
|
+
let bytes = compression ? compression.getValueBytes : getValueBytes;
|
|
46
|
+
if (rc > bytes.maxLength) {
|
|
47
|
+
// 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
|
|
48
|
+
return this._returnLargeBuffer(
|
|
49
|
+
() => getByBinary(this.dbAddress, this.writeKey(id, keyBytes, 0)),
|
|
50
|
+
() => getSharedByBinary(this.dbAddress, this.writeKey(id, keyBytes, 0)));
|
|
51
|
+
}
|
|
52
|
+
bytes.length = this.lastSize;
|
|
53
|
+
return bytes;
|
|
54
|
+
},
|
|
55
|
+
_returnLargeBuffer(getFast, getShared) {
|
|
56
|
+
let bytes;
|
|
57
|
+
let compression = this.compression;
|
|
58
|
+
if (asSafeBuffer && this.lastSize > NEW_BUFFER_THRESHOLD) {
|
|
59
|
+
// used by getBinary to indicate it should create a dedicated buffer to receive this
|
|
60
|
+
let bytesToRestore
|
|
61
|
+
try {
|
|
62
|
+
if (compression) {
|
|
63
|
+
bytesToRestore = compression.getValueBytes;
|
|
64
|
+
let dictionary = compression.dictionary || [];
|
|
65
|
+
let dictLength = (dictionary.length >> 3) << 3;// make sure it is word-aligned
|
|
66
|
+
bytes = makeReusableBuffer(this.lastSize);
|
|
67
|
+
compression.setBuffer(bytes, this.lastSize, dictionary, dictLength);
|
|
68
|
+
compression.getValueBytes = bytes;
|
|
69
|
+
} else {
|
|
70
|
+
bytesToRestore = getValueBytes;
|
|
71
|
+
setGlobalBuffer(bytes = getValueBytes = makeReusableBuffer(this.lastSize));
|
|
72
|
+
}
|
|
73
|
+
getFast();
|
|
74
|
+
} finally {
|
|
75
|
+
if (compression) {
|
|
76
|
+
let dictLength = (compression.dictionary.length >> 3) << 3;
|
|
77
|
+
compression.setBuffer(bytesToRestore, bytesToRestore.maxLength, compression.dictionary, dictLength);
|
|
78
|
+
compression.getValueBytes = bytesToRestore;
|
|
79
|
+
} else {
|
|
80
|
+
setGlobalBuffer(bytesToRestore);
|
|
81
|
+
getValueBytes = bytesToRestore;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return bytes;
|
|
85
|
+
}
|
|
86
|
+
if (this.lastSize > NEW_BUFFER_THRESHOLD && !compression) {
|
|
87
|
+
// 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
|
|
88
|
+
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
|
|
89
|
+
detachBuffer(globalThis.__lmdb_last_shared__.buffer);
|
|
90
|
+
return globalThis.__lmdb_last_shared__ = getShared();
|
|
91
|
+
}
|
|
92
|
+
// grow our shared/static buffer to accomodate the size of the data
|
|
93
|
+
bytes = this._allocateGetBuffer(this.lastSize);
|
|
94
|
+
// and try again
|
|
95
|
+
getFast();
|
|
96
|
+
bytes.length = this.lastSize;
|
|
97
|
+
return bytes;
|
|
98
|
+
},
|
|
99
|
+
_allocateGetBuffer(lastSize) {
|
|
100
|
+
let newLength = Math.min(Math.max(lastSize * 2, 0x1000), 0xfffffff8);
|
|
101
|
+
let bytes;
|
|
102
|
+
if (this.compression) {
|
|
103
|
+
let dictionary = this.compression.dictionary || new Uint8A(0);
|
|
104
|
+
let dictLength = (dictionary.length >> 3) << 3;// make sure it is word-aligned
|
|
105
|
+
bytes = new Uint8A(newLength + dictLength);
|
|
106
|
+
bytes.set(dictionary) // copy dictionary into start
|
|
107
|
+
// the section after the dictionary is the target area for get values
|
|
108
|
+
bytes = bytes.subarray(dictLength);
|
|
109
|
+
this.compression.setBuffer(bytes, newLength, dictionary, dictLength);
|
|
110
|
+
bytes.maxLength = newLength;
|
|
111
|
+
Object.defineProperty(bytes, 'length', { value: newLength, writable: true, configurable: true });
|
|
112
|
+
this.compression.getValueBytes = bytes;
|
|
113
|
+
} else {
|
|
114
|
+
bytes = makeReusableBuffer(newLength);
|
|
115
|
+
setGlobalBuffer(getValueBytes = bytes);
|
|
116
|
+
}
|
|
117
|
+
bytes.isShared = true;
|
|
118
|
+
return bytes;
|
|
119
|
+
},
|
|
120
|
+
getBinary(id) {
|
|
121
|
+
try {
|
|
122
|
+
asSafeBuffer = true;
|
|
123
|
+
let fastBuffer = this.getBinaryFast(id);
|
|
124
|
+
return fastBuffer && (fastBuffer.isShared ? Uint8ArraySlice.call(fastBuffer, 0, this.lastSize) : fastBuffer);
|
|
125
|
+
} finally {
|
|
126
|
+
asSafeBuffer = false;
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
get(id) {
|
|
130
|
+
if (this.decoderCopies) {
|
|
131
|
+
// the decoder copies any data, so we can use the fast binary retrieval that overwrites the same buffer space
|
|
132
|
+
let bytes = this.getBinaryFast(id);
|
|
133
|
+
return bytes && this.decoder.decode(bytes);
|
|
134
|
+
}
|
|
135
|
+
if (this.encoding == 'binary')
|
|
136
|
+
return this.getBinary(id);
|
|
137
|
+
if (this.decoder) {
|
|
138
|
+
// the decoder potentially uses the data from the buffer in the future and needs a stable buffer
|
|
139
|
+
let bytes = this.getBinary(id);
|
|
140
|
+
return bytes && this.decoder.decode(bytes);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
let result = this.getString(id);
|
|
144
|
+
if (result) {
|
|
145
|
+
if (this.encoding == 'json')
|
|
146
|
+
return JSON.parse(result);
|
|
147
|
+
}
|
|
148
|
+
return result;
|
|
149
|
+
},
|
|
150
|
+
getEntry(id) {
|
|
151
|
+
let value = this.get(id);
|
|
152
|
+
if (value !== undefined) {
|
|
153
|
+
if (this.useVersions)
|
|
154
|
+
return {
|
|
155
|
+
value,
|
|
156
|
+
version: getLastVersion(),
|
|
157
|
+
//size: this.lastSize
|
|
158
|
+
};
|
|
159
|
+
else
|
|
160
|
+
return {
|
|
161
|
+
value,
|
|
162
|
+
//size: this.lastSize
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
resetReadTxn() {
|
|
167
|
+
resetReadTxn();
|
|
168
|
+
},
|
|
169
|
+
_commitReadTxn() {
|
|
170
|
+
if (readTxn)
|
|
171
|
+
readTxn.commit();
|
|
172
|
+
readTxnRenewed = null;
|
|
173
|
+
readTxn = null;
|
|
174
|
+
},
|
|
175
|
+
ensureReadTxn() {
|
|
176
|
+
if (!env.writeTxn && !readTxnRenewed)
|
|
177
|
+
renewReadTxn(this);
|
|
178
|
+
},
|
|
179
|
+
doesExist(key, versionOrValue) {
|
|
180
|
+
if (!env.writeTxn)
|
|
181
|
+
readTxnRenewed ? readTxn : renewReadTxn(this);
|
|
182
|
+
if (versionOrValue == null) {
|
|
183
|
+
// undefined means the entry exists, null is used specifically to check for the entry *not* existing
|
|
184
|
+
return (this.getBinaryFast(key) === undefined) == (versionOrValue === null);
|
|
185
|
+
}
|
|
186
|
+
else if (this.useVersions) {
|
|
187
|
+
this.getBinaryFast(key);
|
|
188
|
+
return this.getBinaryFast(key) !== undefined && getLastVersion() === versionOrValue;
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
if (versionOrValue && versionOrValue['\x10binary-data\x02'])
|
|
192
|
+
versionOrValue = versionOrValue['\x10binary-data\x02'];
|
|
193
|
+
else if (this.encoder)
|
|
194
|
+
versionOrValue = this.encoder.encode(versionOrValue);
|
|
195
|
+
if (typeof versionOrValue == 'string')
|
|
196
|
+
versionOrValue = Buffer.from(versionOrValue);
|
|
197
|
+
return this.getValuesCount(key, { start: versionOrValue, exactMatch: true}) > 0;
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
getValues(key, options) {
|
|
201
|
+
let defaultOptions = {
|
|
202
|
+
key,
|
|
203
|
+
valuesForKey: true
|
|
204
|
+
};
|
|
205
|
+
if (options && options.snapshot === false)
|
|
206
|
+
throw new Error('Can not disable snapshots for getValues');
|
|
207
|
+
return this.getRange(options ? Object.assign(defaultOptions, options) : defaultOptions);
|
|
208
|
+
},
|
|
209
|
+
getKeys(options) {
|
|
210
|
+
if (!options)
|
|
211
|
+
options = {};
|
|
212
|
+
options.values = false;
|
|
213
|
+
return this.getRange(options);
|
|
214
|
+
},
|
|
215
|
+
getCount(options) {
|
|
216
|
+
if (!options)
|
|
217
|
+
options = {};
|
|
218
|
+
options.onlyCount = true;
|
|
219
|
+
return this.getRange(options).iterate();
|
|
220
|
+
},
|
|
221
|
+
getKeysCount(options) {
|
|
222
|
+
if (!options)
|
|
223
|
+
options = {};
|
|
224
|
+
options.onlyCount = true;
|
|
225
|
+
options.values = false;
|
|
226
|
+
return this.getRange(options).iterate();
|
|
227
|
+
},
|
|
228
|
+
getValuesCount(key, options) {
|
|
229
|
+
if (!options)
|
|
230
|
+
options = {};
|
|
231
|
+
options.key = key;
|
|
232
|
+
options.valuesForKey = true;
|
|
233
|
+
options.onlyCount = true;
|
|
234
|
+
return this.getRange(options).iterate();
|
|
235
|
+
},
|
|
236
|
+
getRange(options) {
|
|
237
|
+
let iterable = new RangeIterable();
|
|
238
|
+
if (!options)
|
|
239
|
+
options = {};
|
|
240
|
+
let includeValues = options.values !== false;
|
|
241
|
+
let includeVersions = options.versions;
|
|
242
|
+
let valuesForKey = options.valuesForKey;
|
|
243
|
+
let limit = options.limit;
|
|
244
|
+
let db = this.db;
|
|
245
|
+
let snapshot = options.snapshot;
|
|
246
|
+
let compression = this.compression;
|
|
247
|
+
iterable.iterate = () => {
|
|
248
|
+
let currentKey = valuesForKey ? options.key : options.start;
|
|
249
|
+
const reverse = options.reverse;
|
|
250
|
+
let count = 0;
|
|
251
|
+
let cursor, cursorRenewId, cursorAddress;
|
|
252
|
+
let txn;
|
|
253
|
+
let flags = (includeValues ? 0x100 : 0) | (reverse ? 0x400 : 0) |
|
|
254
|
+
(valuesForKey ? 0x800 : 0) | (options.exactMatch ? 0x4000 : 0);
|
|
255
|
+
let store = this;
|
|
256
|
+
function resetCursor() {
|
|
257
|
+
try {
|
|
258
|
+
if (cursor)
|
|
259
|
+
finishCursor();
|
|
260
|
+
let writeTxn = env.writeTxn;
|
|
261
|
+
if (writeTxn)
|
|
262
|
+
snapshot = false;
|
|
263
|
+
txn = writeTxn || (readTxnRenewed ? readTxn : renewReadTxn(store));
|
|
264
|
+
cursor = !writeTxn && db.availableCursor;
|
|
265
|
+
if (cursor) {
|
|
266
|
+
db.availableCursor = null;
|
|
267
|
+
flags |= 0x2000;
|
|
268
|
+
} else {
|
|
269
|
+
cursor = new Cursor(db);
|
|
270
|
+
}
|
|
271
|
+
cursorAddress = cursor.address;
|
|
272
|
+
txn.cursorCount = (txn.cursorCount || 0) + 1; // track transaction so we always use the same one
|
|
273
|
+
if (snapshot === false) {
|
|
274
|
+
cursorRenewId = renewId; // use shared read transaction
|
|
275
|
+
txn.renewingCursorCount = (txn.renewingCursorCount || 0) + 1; // need to know how many are renewing cursors
|
|
276
|
+
}
|
|
277
|
+
} catch(error) {
|
|
278
|
+
if (cursor) {
|
|
279
|
+
try {
|
|
280
|
+
cursor.close();
|
|
281
|
+
} catch(error) { }
|
|
282
|
+
}
|
|
283
|
+
throw error;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
resetCursor();
|
|
287
|
+
if (options.onlyCount) {
|
|
288
|
+
flags |= 0x1000;
|
|
289
|
+
let count = position(options.offset);
|
|
290
|
+
if (count < 0)
|
|
291
|
+
lmdbError(count);
|
|
292
|
+
finishCursor();
|
|
293
|
+
return count;
|
|
294
|
+
}
|
|
295
|
+
function position(offset) {
|
|
296
|
+
let keySize = currentKey === undefined ? 0 : store.writeKey(currentKey, keyBytes, 0);
|
|
297
|
+
let endAddress;
|
|
298
|
+
if (valuesForKey) {
|
|
299
|
+
if (options.start === undefined && options.end === undefined)
|
|
300
|
+
endAddress = 0;
|
|
301
|
+
else {
|
|
302
|
+
let startAddress;
|
|
303
|
+
if (store.encoder.writeKey) {
|
|
304
|
+
startAddress = saveKey(options.start, store.encoder.writeKey, iterable, maxKeySize);
|
|
305
|
+
keyBytesView.setFloat64(START_ADDRESS_POSITION, startAddress, true);
|
|
306
|
+
endAddress = saveKey(options.end, store.encoder.writeKey, iterable, maxKeySize);
|
|
307
|
+
} else if ((!options.start || options.start instanceof Uint8Array) && (!options.end || options.end instanceof Uint8Array)) {
|
|
308
|
+
startAddress = saveKey(options.start, orderedBinary.writeKey, iterable, maxKeySize);
|
|
309
|
+
keyBytesView.setFloat64(START_ADDRESS_POSITION, startAddress, true);
|
|
310
|
+
endAddress = saveKey(options.end, orderedBinary.writeKey, iterable, maxKeySize);
|
|
311
|
+
} else {
|
|
312
|
+
throw new Error('Only key-based encoding is supported for start/end values');
|
|
313
|
+
let encoded = store.encoder.encode(options.start);
|
|
314
|
+
let bufferAddress = encoded.buffer.address || (encoded.buffer.address = getAddress(encoded) - encoded.byteOffset);
|
|
315
|
+
startAddress = bufferAddress + encoded.byteOffset;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
} else
|
|
319
|
+
endAddress = saveKey(options.end, store.writeKey, iterable, maxKeySize);
|
|
320
|
+
return doPosition(cursorAddress, flags, offset || 0, keySize, endAddress);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function finishCursor() {
|
|
324
|
+
if (txn.isDone)
|
|
325
|
+
return;
|
|
326
|
+
if (cursorRenewId)
|
|
327
|
+
txn.renewingCursorCount--;
|
|
328
|
+
if (--txn.cursorCount <= 0 && txn.onlyCursor) {
|
|
329
|
+
cursor.close();
|
|
330
|
+
txn.abort(); // this is no longer main read txn, abort it now that we are done
|
|
331
|
+
txn.isDone = true;
|
|
332
|
+
} else {
|
|
333
|
+
if (db.availableCursor || txn != readTxn) {
|
|
334
|
+
cursor.close();
|
|
335
|
+
} else { // try to reuse it
|
|
336
|
+
db.availableCursor = cursor;
|
|
337
|
+
db.cursorTxn = txn;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return {
|
|
342
|
+
next() {
|
|
343
|
+
let keySize, lastSize;
|
|
344
|
+
if (cursorRenewId && (cursorRenewId != renewId || txn.isDone)) {
|
|
345
|
+
resetCursor();
|
|
346
|
+
keySize = position(0);
|
|
347
|
+
}
|
|
348
|
+
if (count === 0) { // && includeValues) // on first entry, get current value if we need to
|
|
349
|
+
keySize = position(options.offset);
|
|
350
|
+
} else
|
|
351
|
+
keySize = iterate(cursorAddress);
|
|
352
|
+
if (keySize <= 0 ||
|
|
353
|
+
(count++ >= limit)) {
|
|
354
|
+
if (count < 0)
|
|
355
|
+
lmdbError(count);
|
|
356
|
+
finishCursor();
|
|
357
|
+
return ITERATOR_DONE;
|
|
358
|
+
}
|
|
359
|
+
if (!valuesForKey || snapshot === false) {
|
|
360
|
+
if (keySize > 20000) {
|
|
361
|
+
if (keySize > 0x1000000)
|
|
362
|
+
lmdbError(keySize - 0x100000000)
|
|
363
|
+
throw new Error('Invalid key size ' + keySize.toString(16))
|
|
364
|
+
}
|
|
365
|
+
currentKey = store.readKey(keyBytes, 32, keySize + 32);
|
|
366
|
+
}
|
|
367
|
+
if (includeValues) {
|
|
368
|
+
let value;
|
|
369
|
+
lastSize = keyBytesView.getUint32(0, true);
|
|
370
|
+
let bytes = compression ? compression.getValueBytes : getValueBytes;
|
|
371
|
+
if (lastSize > bytes.maxLength) {
|
|
372
|
+
store.lastSize = lastSize;
|
|
373
|
+
asSafeBuffer = store.encoding == 'binary';
|
|
374
|
+
try {
|
|
375
|
+
bytes = store._returnLargeBuffer(
|
|
376
|
+
() => getCurrentValue(cursorAddress),
|
|
377
|
+
() => getCurrentShared(cursorAddress)
|
|
378
|
+
);
|
|
379
|
+
} finally {
|
|
380
|
+
asSafeBuffer = false;
|
|
381
|
+
}
|
|
382
|
+
} else
|
|
383
|
+
bytes.length = lastSize;
|
|
384
|
+
if (store.decoder) {
|
|
385
|
+
value = store.decoder.decode(bytes, lastSize);
|
|
386
|
+
} else if (store.encoding == 'binary')
|
|
387
|
+
value = bytes.isShared ? Uint8ArraySlice.call(bytes, 0, lastSize) : bytes;
|
|
388
|
+
else {
|
|
389
|
+
value = bytes.toString('utf8', 0, lastSize);
|
|
390
|
+
if (store.encoding == 'json' && value)
|
|
391
|
+
value = JSON.parse(value);
|
|
392
|
+
}
|
|
393
|
+
if (includeVersions)
|
|
394
|
+
return {
|
|
395
|
+
value: {
|
|
396
|
+
key: currentKey,
|
|
397
|
+
value,
|
|
398
|
+
version: getLastVersion()
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
else if (valuesForKey)
|
|
402
|
+
return {
|
|
403
|
+
value
|
|
404
|
+
};
|
|
405
|
+
else
|
|
406
|
+
return {
|
|
407
|
+
value: {
|
|
408
|
+
key: currentKey,
|
|
409
|
+
value,
|
|
410
|
+
}
|
|
411
|
+
};
|
|
412
|
+
} else if (includeVersions) {
|
|
413
|
+
return {
|
|
414
|
+
value: {
|
|
415
|
+
key: currentKey,
|
|
416
|
+
version: getLastVersion()
|
|
417
|
+
}
|
|
418
|
+
};
|
|
419
|
+
} else {
|
|
420
|
+
return {
|
|
421
|
+
value: currentKey
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
},
|
|
425
|
+
return() {
|
|
426
|
+
finishCursor();
|
|
427
|
+
return ITERATOR_DONE;
|
|
428
|
+
},
|
|
429
|
+
throw() {
|
|
430
|
+
finishCursor();
|
|
431
|
+
return ITERATOR_DONE;
|
|
432
|
+
}
|
|
433
|
+
};
|
|
434
|
+
};
|
|
435
|
+
return iterable;
|
|
436
|
+
},
|
|
437
|
+
|
|
438
|
+
getMany(keys, callback) {
|
|
439
|
+
// this is an asynchronous get for multiple keys. It actually works by prefetching asynchronously,
|
|
440
|
+
// allowing a separate to absorb the potentially largest cost: hard page faults (and disk I/O).
|
|
441
|
+
// And then we just do standard sync gets (to deserialized data) to fulfil the callback/promise
|
|
442
|
+
// once the prefetch occurs
|
|
443
|
+
let promise = callback ? undefined : new Promise(resolve => callback = (error, results) => resolve(results));
|
|
444
|
+
this.prefetch(keys, () => {
|
|
445
|
+
let results = new Array(keys.length);
|
|
446
|
+
for (let i = 0, l = keys.length; i < l; i++) {
|
|
447
|
+
results[i] = get.call(this, keys[i]);
|
|
448
|
+
}
|
|
449
|
+
callback(null, results);
|
|
450
|
+
});
|
|
451
|
+
return promise;
|
|
452
|
+
},
|
|
453
|
+
getSharedBufferForGet(id) {
|
|
454
|
+
let txn = (env.writeTxn || (readTxnRenewed ? readTxn : renewReadTxn(this)));
|
|
455
|
+
this.lastSize = this.keyIsCompatibility ? txn.getBinaryShared(id) : this.db.get(this.writeKey(id, keyBytes, 0));
|
|
456
|
+
if (this.lastSize === -30798) { // not found code
|
|
457
|
+
return; //undefined
|
|
458
|
+
}
|
|
459
|
+
return this.lastSize;
|
|
460
|
+
this.lastSize = keyBytesView.getUint32(0, true);
|
|
461
|
+
let bufferIndex = keyBytesView.getUint32(12, true);
|
|
462
|
+
lastOffset = keyBytesView.getUint32(8, true);
|
|
463
|
+
let buffer = buffers[bufferIndex];
|
|
464
|
+
let startOffset;
|
|
465
|
+
if (!buffer || lastOffset < (startOffset = buffer.startOffset) || (lastOffset + this.lastSize > startOffset + 0x100000000)) {
|
|
466
|
+
if (buffer)
|
|
467
|
+
env.detachBuffer(buffer.buffer);
|
|
468
|
+
startOffset = (lastOffset >>> 16) * 0x10000;
|
|
469
|
+
console.log('make buffer for address', bufferIndex * 0x100000000 + startOffset);
|
|
470
|
+
buffer = buffers[bufferIndex] = Buffer.from(getBufferForAddress(bufferIndex * 0x100000000 + startOffset));
|
|
471
|
+
buffer.startOffset = startOffset;
|
|
472
|
+
}
|
|
473
|
+
lastOffset -= startOffset;
|
|
474
|
+
return buffer;
|
|
475
|
+
return buffer.slice(lastOffset, lastOffset + this.lastSize);/*Uint8ArraySlice.call(buffer, lastOffset, lastOffset + this.lastSize)*/
|
|
476
|
+
},
|
|
477
|
+
prefetch(keys, callback) {
|
|
478
|
+
if (!keys)
|
|
479
|
+
throw new Error('An array of keys must be provided');
|
|
480
|
+
if (!keys.length) {
|
|
481
|
+
if (callback) {
|
|
482
|
+
callback(null);
|
|
483
|
+
return;
|
|
484
|
+
} else
|
|
485
|
+
return Promise.resolve();
|
|
486
|
+
}
|
|
487
|
+
let buffers = [];
|
|
488
|
+
let startPosition;
|
|
489
|
+
let bufferHolder = {};
|
|
490
|
+
let lastBuffer;
|
|
491
|
+
for (let key of keys) {
|
|
492
|
+
let position = saveKey(key, this.writeKey, bufferHolder, maxKeySize);
|
|
493
|
+
if (!startPosition)
|
|
494
|
+
startPosition = position;
|
|
495
|
+
if (bufferHolder.saveBuffer != lastBuffer) {
|
|
496
|
+
buffers.push(bufferHolder);
|
|
497
|
+
lastBuffer = bufferHolder.saveBuffer;
|
|
498
|
+
bufferHolder = { saveBuffer: lastBuffer };
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
saveKey(undefined, this.writeKey, bufferHolder, maxKeySize);
|
|
502
|
+
prefetch(this.dbAddress, startPosition, (error) => {
|
|
503
|
+
if (error)
|
|
504
|
+
console.error('Error with prefetch', buffers, bufferHolder); // partly exists to keep the buffers pinned in memory
|
|
505
|
+
else
|
|
506
|
+
callback(null);
|
|
507
|
+
});
|
|
508
|
+
if (!callback)
|
|
509
|
+
return new Promise(resolve => callback = resolve);
|
|
510
|
+
},
|
|
511
|
+
close(callback) {
|
|
512
|
+
this.status = 'closing';
|
|
513
|
+
if (this.isRoot) {
|
|
514
|
+
if (readTxn) {
|
|
515
|
+
try {
|
|
516
|
+
readTxn.abort();
|
|
517
|
+
} catch(error) {}
|
|
518
|
+
}
|
|
519
|
+
readTxn = {
|
|
520
|
+
renew() {
|
|
521
|
+
throw new Error('Can not read from a closed database');
|
|
522
|
+
}
|
|
523
|
+
};
|
|
524
|
+
readTxnRenewed = null;
|
|
525
|
+
}
|
|
526
|
+
let txnPromise = this._endWrites();
|
|
527
|
+
const doClose = () => {
|
|
528
|
+
this.db.close();
|
|
529
|
+
if (this.isRoot) {
|
|
530
|
+
env.close();
|
|
531
|
+
}
|
|
532
|
+
this.status = 'closed';
|
|
533
|
+
if (callback)
|
|
534
|
+
callback();
|
|
535
|
+
}
|
|
536
|
+
if (txnPromise)
|
|
537
|
+
return txnPromise.then(doClose);
|
|
538
|
+
else {
|
|
539
|
+
doClose();
|
|
540
|
+
return Promise.resolve();
|
|
541
|
+
}
|
|
542
|
+
},
|
|
543
|
+
getStats() {
|
|
544
|
+
readTxnRenewed ? readTxn : renewReadTxn(this);
|
|
545
|
+
return this.db.stat();
|
|
546
|
+
}
|
|
547
|
+
});
|
|
548
|
+
let get = LMDBStore.prototype.get;
|
|
549
|
+
function renewReadTxn(store) {
|
|
550
|
+
if (!readTxn) {
|
|
551
|
+
let retries = 0;
|
|
552
|
+
let waitArray;
|
|
553
|
+
do {
|
|
554
|
+
try {
|
|
555
|
+
readTxn = new Txn(env, 0x20000);
|
|
556
|
+
break;
|
|
557
|
+
} catch (error) {
|
|
558
|
+
if (error.message.includes('temporarily')) {
|
|
559
|
+
if (!waitArray)
|
|
560
|
+
waitArray = new Int32Array(new SharedArrayBuffer(4), 0, 1);
|
|
561
|
+
Atomics.wait(waitArray, 0, 0, retries * 2);
|
|
562
|
+
} else
|
|
563
|
+
throw error;
|
|
564
|
+
}
|
|
565
|
+
} while (retries++ < 100);
|
|
566
|
+
}
|
|
567
|
+
// we actually don't renew here, we let the renew take place in the next
|
|
568
|
+
// lmdb native read/call so as to avoid an extra native call
|
|
569
|
+
readTxnRenewed = setTimeout(resetReadTxn, 0);
|
|
570
|
+
store.emit('begin-transaction');
|
|
571
|
+
return readTxn;
|
|
572
|
+
}
|
|
573
|
+
function resetReadTxn(hardReset) {
|
|
574
|
+
renewId++;
|
|
575
|
+
if (readTxnRenewed) {
|
|
576
|
+
readTxnRenewed = null;
|
|
577
|
+
if (readTxn.cursorCount - (readTxn.renewingCursorCount || 0) > 0) {
|
|
578
|
+
readTxn.onlyCursor = true;
|
|
579
|
+
readTxn = null;
|
|
580
|
+
} else
|
|
581
|
+
resetTxn(readTxn.address);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
export function makeReusableBuffer(size) {
|
|
586
|
+
let bytes = typeof Buffer != 'undefined' ? Buffer.alloc(size) : new Uint8Array(size);
|
|
587
|
+
bytes.maxLength = size;
|
|
588
|
+
Object.defineProperty(bytes, 'length', { value: size, writable: true, configurable: true });
|
|
589
|
+
return bytes;
|
|
590
|
+
}
|