lmdb
Advanced tools
Comparing version 3.0.4 to 3.0.5
import { dirname, join, default as pathModule } from 'path'; | ||
import { fileURLToPath } from 'url'; | ||
import loadNAPI from 'node-gyp-build-optional-packages'; | ||
export let Env, Txn, Dbi, Compression, Cursor, getAddress, getBufferAddress, createBufferForAddress, clearKeptObjects, globalBuffer, setGlobalBuffer, arch, fs, os, onExit, tmpdir, lmdbError, path, EventEmitter, orderedBinary, MsgpackrEncoder, WeakLRUCache, setEnvMap, getEnvMap, getByBinary, detachBuffer, startRead, setReadCallback, write, position, iterate, prefetch, resetTxn, getCurrentValue, getCurrentShared, getStringByBinary, getSharedByBinary, getSharedBuffer, compress, directWrite, attemptLock, unlock; | ||
export let Env, | ||
Txn, | ||
Dbi, | ||
Compression, | ||
Cursor, | ||
getAddress, | ||
getBufferAddress, | ||
createBufferForAddress, | ||
clearKeptObjects, | ||
globalBuffer, | ||
setGlobalBuffer, | ||
arch, | ||
fs, | ||
os, | ||
onExit, | ||
tmpdir, | ||
lmdbError, | ||
path, | ||
EventEmitter, | ||
orderedBinary, | ||
MsgpackrEncoder, | ||
WeakLRUCache, | ||
setEnvMap, | ||
getEnvMap, | ||
getByBinary, | ||
detachBuffer, | ||
startRead, | ||
setReadCallback, | ||
write, | ||
position, | ||
iterate, | ||
prefetch, | ||
resetTxn, | ||
getCurrentValue, | ||
getCurrentShared, | ||
getStringByBinary, | ||
getSharedByBinary, | ||
getSharedBuffer, | ||
compress, | ||
directWrite, | ||
getIncrementer, | ||
attemptLock, | ||
unlock; | ||
path = pathModule; | ||
@@ -15,3 +57,3 @@ let dirName = dirname(fileURLToPath(import.meta.url)).replace(/dist$/, ''); | ||
returns: FFIType.u32, | ||
ptr: nativeAddon.getByBinaryPtr | ||
ptr: nativeAddon.getByBinaryPtr, | ||
}, | ||
@@ -37,3 +79,3 @@ iterate: { | ||
ptr: nativeAddon.resetTxnPtr, | ||
} | ||
}, | ||
}); | ||
@@ -45,3 +87,3 @@ for (let key in lmdbLib.symbols) { | ||
setNativeFunctions(nativeAddon); | ||
export function setNativeFunctions(externals) { | ||
@@ -55,5 +97,5 @@ Env = externals.Env; | ||
createBufferForAddress = externals.createBufferForAddress; | ||
clearKeptObjects = externals.clearKeptObjects || function() {}; | ||
clearKeptObjects = externals.clearKeptObjects || function () {}; | ||
getByBinary = externals.getByBinary; | ||
detachBuffer = externals.detachBuffer; | ||
detachBuffer = externals.detachBuffer; | ||
startRead = externals.startRead; | ||
@@ -69,2 +111,3 @@ setReadCallback = externals.setReadCallback; | ||
directWrite = externals.directWrite; | ||
getIncrementer = externals.getIncrementer; | ||
attemptLock = externals.attemptLock; | ||
@@ -80,4 +123,3 @@ unlock = externals.unlock; | ||
lmdbError = externals.lmdbError; | ||
if (externals.tmpdir) | ||
tmpdir = externals.tmpdir | ||
if (externals.tmpdir) tmpdir = externals.tmpdir; | ||
} | ||
@@ -92,4 +134,4 @@ export function setExternals(externals) { | ||
tmpdir = externals.tmpdir; | ||
os = externals.os; | ||
os = externals.os; | ||
onExit = externals.onExit; | ||
} |
{ | ||
"name": "lmdb", | ||
"author": "Kris Zyp", | ||
"version": "3.0.4", | ||
"version": "3.0.5", | ||
"description": "Simple, efficient, scalable, high-performance LMDB interface", | ||
@@ -113,9 +113,9 @@ "license": "MIT", | ||
"optionalDependencies": { | ||
"@lmdb/lmdb-darwin-arm64": "3.0.4", | ||
"@lmdb/lmdb-darwin-x64": "3.0.4", | ||
"@lmdb/lmdb-linux-arm": "3.0.4", | ||
"@lmdb/lmdb-linux-arm64": "3.0.4", | ||
"@lmdb/lmdb-linux-x64": "3.0.4", | ||
"@lmdb/lmdb-win32-x64": "3.0.4" | ||
"@lmdb/lmdb-darwin-arm64": "3.0.5", | ||
"@lmdb/lmdb-darwin-x64": "3.0.5", | ||
"@lmdb/lmdb-linux-arm": "3.0.5", | ||
"@lmdb/lmdb-linux-arm64": "3.0.5", | ||
"@lmdb/lmdb-linux-x64": "3.0.5", | ||
"@lmdb/lmdb-win32-x64": "3.0.5" | ||
} | ||
} |
694
read.js
@@ -1,2 +0,2 @@ | ||
import { RangeIterable } from './util/RangeIterable.js'; | ||
import { RangeIterable } from './util/RangeIterable.js'; | ||
import { | ||
@@ -23,5 +23,5 @@ getAddress, | ||
attemptLock, | ||
unlock | ||
unlock, | ||
} from './native.js'; | ||
import { saveKey } from './keys.js'; | ||
import { saveKey } from './keys.js'; | ||
const IF_EXISTS = 3.542694326329068e-103; | ||
@@ -35,3 +35,7 @@ const DEFAULT_BEGINNING_KEY = Buffer.from([5]); // the default starting key for iteration, which excludes symbols/metadata | ||
getValueBytes.isGlobal = true; | ||
Object.defineProperty(getValueBytes, 'length', { value: getValueBytes.length, writable: true, configurable: true }); | ||
Object.defineProperty(getValueBytes, 'length', { | ||
value: getValueBytes.length, | ||
writable: true, | ||
configurable: true, | ||
}); | ||
} | ||
@@ -44,6 +48,9 @@ const START_ADDRESS_POSITION = 4064; | ||
export function addReadMethods(LMDBStore, { | ||
maxKeySize, env, keyBytes, keyBytesView, getLastVersion, getLastTxnId | ||
}) { | ||
let readTxn, readTxnRenewed, asSafeBuffer = false; | ||
export function addReadMethods( | ||
LMDBStore, | ||
{ maxKeySize, env, keyBytes, keyBytesView, getLastVersion, getLastTxnId }, | ||
) { | ||
let readTxn, | ||
readTxnRenewed, | ||
asSafeBuffer = false; | ||
let renewId = 1; | ||
@@ -53,11 +60,22 @@ let outstandingReads = 0; | ||
getString(id, options) { | ||
let txn = env.writeTxn || (options && options.transaction) || (readTxnRenewed ? readTxn : renewReadTxn(this)); | ||
let string = getStringByBinary(this.dbAddress, this.writeKey(id, keyBytes, 0), txn.address || 0); | ||
if (typeof string === 'number') { // indicates the buffer wasn't large enough | ||
let txn = | ||
env.writeTxn || | ||
(options && options.transaction) || | ||
(readTxnRenewed ? readTxn : renewReadTxn(this)); | ||
let string = getStringByBinary( | ||
this.dbAddress, | ||
this.writeKey(id, keyBytes, 0), | ||
txn.address || 0, | ||
); | ||
if (typeof string === 'number') { | ||
// indicates the buffer wasn't large enough | ||
this._allocateGetBuffer(string); | ||
// and then try again | ||
string = getStringByBinary(this.dbAddress, this.writeKey(id, keyBytes, 0), txn.address || 0); | ||
string = getStringByBinary( | ||
this.dbAddress, | ||
this.writeKey(id, keyBytes, 0), | ||
txn.address || 0, | ||
); | ||
} | ||
if (string) | ||
this.lastSize = string.length; | ||
if (string) this.lastSize = string.length; | ||
return string; | ||
@@ -67,16 +85,33 @@ }, | ||
let rc; | ||
let txn = env.writeTxn || (options && options.transaction) || (readTxnRenewed ? readTxn : renewReadTxn(this)); | ||
rc = this.lastSize = getByBinary(this.dbAddress, this.writeKey(id, keyBytes, 0), (options && options.ifNotTxnId) || 0, txn.address || 0); | ||
let txn = | ||
env.writeTxn || | ||
(options && options.transaction) || | ||
(readTxnRenewed ? readTxn : renewReadTxn(this)); | ||
rc = this.lastSize = getByBinary( | ||
this.dbAddress, | ||
this.writeKey(id, keyBytes, 0), | ||
(options && options.ifNotTxnId) || 0, | ||
txn.address || 0, | ||
); | ||
if (rc < 0) { | ||
if (rc == -30798) // MDB_NOTFOUND | ||
if (rc == -30798) | ||
// MDB_NOTFOUND | ||
return; // undefined | ||
if (rc == -30004) // txn id matched | ||
if (rc == -30004) | ||
// txn id matched | ||
return UNMODIFIED; | ||
if (rc == -30781 /*MDB_BAD_VALSIZE*/ && this.writeKey(id, keyBytes, 0) == 0) | ||
throw new Error(id === undefined ? | ||
'A key is required for get, but is undefined' : | ||
'Zero length key is not allowed in LMDB'); | ||
if (rc == -30000) // int32 overflow, read uint32 | ||
if ( | ||
rc == -30781 /*MDB_BAD_VALSIZE*/ && | ||
this.writeKey(id, keyBytes, 0) == 0 | ||
) | ||
throw new Error( | ||
id === undefined | ||
? 'A key is required for get, but is undefined' | ||
: 'Zero length key is not allowed in LMDB', | ||
); | ||
if (rc == -30000) | ||
// int32 overflow, read uint32 | ||
rc = this.lastSize = keyBytesView.getUint32(0, true); | ||
else if (rc == -30001) {// shared buffer | ||
else if (rc == -30001) { | ||
// shared buffer | ||
this.lastSize = keyBytesView.getUint32(0, true); | ||
@@ -86,4 +121,3 @@ let bufferId = keyBytesView.getUint32(4, true); | ||
return asSafeBuffer ? Buffer.from(bytes) : bytes; | ||
} else | ||
throw lmdbError(rc); | ||
} else throw lmdbError(rc); | ||
} | ||
@@ -94,4 +128,10 @@ let compression = this.compression; | ||
// 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 | ||
return this._returnLargeBuffer( | ||
() => getByBinary(this.dbAddress, this.writeKey(id, keyBytes, 0), 0, txn.address || 0)); | ||
return this._returnLargeBuffer(() => | ||
getByBinary( | ||
this.dbAddress, | ||
this.writeKey(id, keyBytes, 0), | ||
0, | ||
txn.address || 0, | ||
), | ||
); | ||
} | ||
@@ -102,3 +142,6 @@ bytes.length = this.lastSize; | ||
getBFAsync(id, options, callback) { | ||
let txn = env.writeTxn || (options && options.transaction) || (readTxnRenewed ? readTxn : renewReadTxn(this)); | ||
let txn = | ||
env.writeTxn || | ||
(options && options.transaction) || | ||
(readTxnRenewed ? readTxn : renewReadTxn(this)); | ||
txn.refCount = (txn.refCount || 0) + 1; | ||
@@ -109,24 +152,30 @@ outstandingReads++; | ||
} | ||
let address = recordReadInstruction(txn.address, this.db.dbi, id, this.writeKey, maxKeySize, ( rc, bufferId, offset, size ) => { | ||
if (rc && rc !== 1) | ||
callback(lmdbError(rc)); | ||
outstandingReads--; | ||
let buffer = mmaps[bufferId]; | ||
if (!buffer) { | ||
buffer = mmaps[bufferId] = getSharedBuffer(bufferId, env.address); | ||
} | ||
//console.log({bufferId, offset, size}) | ||
if (buffer.isSharedMap) { | ||
// using LMDB shared memory | ||
// TODO: We may want explicit support for clearing aborting the transaction on the next event turn, | ||
// but for now we are relying on the GC to cleanup transaction for larger blocks of memory | ||
let bytes = new Uint8Array(buffer, offset, size); | ||
bytes.txn = txn; | ||
callback(bytes, 0, size); | ||
} else { | ||
// using copied memory | ||
txn.done(); // decrement and possibly abort | ||
callback(buffer, offset, size); | ||
} | ||
}); | ||
let address = recordReadInstruction( | ||
txn.address, | ||
this.db.dbi, | ||
id, | ||
this.writeKey, | ||
maxKeySize, | ||
(rc, bufferId, offset, size) => { | ||
if (rc && rc !== 1) callback(lmdbError(rc)); | ||
outstandingReads--; | ||
let buffer = mmaps[bufferId]; | ||
if (!buffer) { | ||
buffer = mmaps[bufferId] = getSharedBuffer(bufferId, env.address); | ||
} | ||
//console.log({bufferId, offset, size}) | ||
if (buffer.isSharedMap) { | ||
// using LMDB shared memory | ||
// TODO: We may want explicit support for clearing aborting the transaction on the next event turn, | ||
// but for now we are relying on the GC to cleanup transaction for larger blocks of memory | ||
let bytes = new Uint8Array(buffer, offset, size); | ||
bytes.txn = txn; | ||
callback(bytes, 0, size); | ||
} else { | ||
// using copied memory | ||
txn.done(); // decrement and possibly abort | ||
callback(buffer, offset, size); | ||
} | ||
}, | ||
); | ||
if (address) { | ||
@@ -140,4 +189,3 @@ startRead(address, () => { | ||
let promise; | ||
if (!callback) | ||
promise = new Promise(resolve => callback = resolve); | ||
if (!callback) promise = new Promise((resolve) => (callback = resolve)); | ||
this.getBFAsync(id, options, (buffer, offset, size) => { | ||
@@ -158,4 +206,3 @@ if (this.useVersions) { | ||
value = Buffer.prototype.utf8Slice.call(bytes, 0, size); | ||
if (this.encoding == 'json' && value) | ||
value = JSON.parse(value); | ||
if (this.encoding == 'json' && value) value = JSON.parse(value); | ||
} | ||
@@ -167,8 +214,9 @@ callback(value); | ||
retain(data, options) { | ||
if (!data) | ||
return | ||
if (!data) return; | ||
let source = data[SOURCE_SYMBOL]; | ||
let buffer = source ? source.bytes : data; | ||
if (!buffer.isGlobal && !env.writeTxn) { | ||
let txn = options?.transaction || (readTxnRenewed ? readTxn : renewReadTxn(this)); | ||
let txn = | ||
options?.transaction || | ||
(readTxnRenewed ? readTxn : renewReadTxn(this)); | ||
buffer.txn = txn; | ||
@@ -183,4 +231,3 @@ | ||
return data; | ||
} else | ||
return buffer; | ||
} else return buffer; | ||
} | ||
@@ -193,3 +240,3 @@ }, | ||
// used by getBinary to indicate it should create a dedicated buffer to receive this | ||
let bytesToRestore | ||
let bytesToRestore; | ||
try { | ||
@@ -199,9 +246,17 @@ if (compression) { | ||
let dictionary = compression.dictionary || []; | ||
let dictLength = (dictionary.length >> 3) << 3;// make sure it is word-aligned | ||
let dictLength = (dictionary.length >> 3) << 3; // make sure it is word-aligned | ||
bytes = makeReusableBuffer(this.lastSize); | ||
compression.setBuffer(bytes.buffer, bytes.byteOffset, this.lastSize, dictionary, dictLength); | ||
compression.setBuffer( | ||
bytes.buffer, | ||
bytes.byteOffset, | ||
this.lastSize, | ||
dictionary, | ||
dictLength, | ||
); | ||
compression.getValueBytes = bytes; | ||
} else { | ||
bytesToRestore = getValueBytes; | ||
setGlobalBuffer(bytes = getValueBytes = makeReusableBuffer(this.lastSize)); | ||
setGlobalBuffer( | ||
(bytes = getValueBytes = makeReusableBuffer(this.lastSize)), | ||
); | ||
} | ||
@@ -212,3 +267,9 @@ getFast(); | ||
let dictLength = (compression.dictionary.length >> 3) << 3; | ||
compression.setBuffer(bytesToRestore.buffer, bytesToRestore.byteOffset, bytesToRestore.maxLength, compression.dictionary, dictLength); | ||
compression.setBuffer( | ||
bytesToRestore.buffer, | ||
bytesToRestore.byteOffset, | ||
bytesToRestore.maxLength, | ||
compression.dictionary, | ||
dictLength, | ||
); | ||
compression.getValueBytes = bytesToRestore; | ||
@@ -233,15 +294,26 @@ } else { | ||
if (this.compression) { | ||
let dictionary = this.compression.dictionary || Buffer.allocUnsafeSlow(0); | ||
let dictLength = (dictionary.length >> 3) << 3;// make sure it is word-aligned | ||
let dictionary = | ||
this.compression.dictionary || Buffer.allocUnsafeSlow(0); | ||
let dictLength = (dictionary.length >> 3) << 3; // make sure it is word-aligned | ||
bytes = Buffer.allocUnsafeSlow(newLength + dictLength); | ||
bytes.set(dictionary) // copy dictionary into start | ||
bytes.set(dictionary); // copy dictionary into start | ||
// the section after the dictionary is the target area for get values | ||
bytes = bytes.subarray(dictLength); | ||
this.compression.setBuffer(bytes.buffer, bytes.byteOffset, newLength, dictionary, dictLength); | ||
this.compression.setBuffer( | ||
bytes.buffer, | ||
bytes.byteOffset, | ||
newLength, | ||
dictionary, | ||
dictLength, | ||
); | ||
bytes.maxLength = newLength; | ||
Object.defineProperty(bytes, 'length', { value: newLength, writable: true, configurable: true }); | ||
Object.defineProperty(bytes, 'length', { | ||
value: newLength, | ||
writable: true, | ||
configurable: true, | ||
}); | ||
this.compression.getValueBytes = bytes; | ||
} else { | ||
bytes = makeReusableBuffer(newLength); | ||
setGlobalBuffer(getValueBytes = bytes); | ||
setGlobalBuffer((getValueBytes = bytes)); | ||
} | ||
@@ -255,3 +327,8 @@ bytes.isGlobal = true; | ||
let fastBuffer = this.getBinaryFast(id, options); | ||
return fastBuffer && (fastBuffer.isGlobal ? Uint8ArraySlice.call(fastBuffer, 0, this.lastSize) : fastBuffer); | ||
return ( | ||
fastBuffer && | ||
(fastBuffer.isGlobal | ||
? Uint8ArraySlice.call(fastBuffer, 0, this.lastSize) | ||
: fastBuffer) | ||
); | ||
} finally { | ||
@@ -265,4 +342,4 @@ asSafeBuffer = false; | ||
if (fastBuffer.isGlobal || writeTxn) | ||
return Uint8ArraySlice.call(fastBuffer, 0, this.lastSize) | ||
fastBuffer.txn = (options && options.transaction); | ||
return Uint8ArraySlice.call(fastBuffer, 0, this.lastSize); | ||
fastBuffer.txn = options && options.transaction; | ||
options.transaction.refCount = (options.transaction.refCount || 0) + 1; | ||
@@ -276,10 +353,17 @@ return fastBuffer; | ||
let bytes = this.getBinaryFast(id, options); | ||
return bytes && (bytes == UNMODIFIED ? UNMODIFIED : this.decoder.decode(bytes, options)); | ||
return ( | ||
bytes && | ||
(bytes == UNMODIFIED | ||
? UNMODIFIED | ||
: this.decoder.decode(bytes, options)) | ||
); | ||
} | ||
if (this.encoding == 'binary') | ||
return this.getBinary(id, options); | ||
if (this.encoding == 'binary') return this.getBinary(id, options); | ||
if (this.decoder) { | ||
// the decoder potentially uses the data from the buffer in the future and needs a stable buffer | ||
let bytes = this.getBinary(id, options); | ||
return bytes && (bytes == UNMODIFIED ? UNMODIFIED : this.decoder.decode(bytes)); | ||
return ( | ||
bytes && | ||
(bytes == UNMODIFIED ? UNMODIFIED : this.decoder.decode(bytes)) | ||
); | ||
} | ||
@@ -289,4 +373,3 @@ | ||
if (result) { | ||
if (this.encoding == 'json') | ||
return JSON.parse(result); | ||
if (this.encoding == 'json') return JSON.parse(result); | ||
} | ||
@@ -314,10 +397,25 @@ return result; | ||
let rc; | ||
let txn = env.writeTxn || (options && options.transaction) || (readTxnRenewed ? readTxn : renewReadTxn(this)); | ||
let txn = | ||
env.writeTxn || | ||
(options && options.transaction) || | ||
(readTxnRenewed ? readTxn : renewReadTxn(this)); | ||
let keySize = this.writeKey(id, keyBytes, 0); | ||
let dataOffset = (((keySize >> 3) + 1) << 3); | ||
keyBytes.set(options.bytes, dataOffset) | ||
rc = directWrite(this.dbAddress, keySize, options.offset, options.bytes.length, txn.address || 0); | ||
let dataOffset = ((keySize >> 3) + 1) << 3; | ||
keyBytes.set(options.bytes, dataOffset); | ||
rc = directWrite( | ||
this.dbAddress, | ||
keySize, | ||
options.offset, | ||
options.bytes.length, | ||
txn.address || 0, | ||
); | ||
if (rc < 0) lmdbError(rc); | ||
}, | ||
getIncrementer(id, startingValue) { | ||
keyBytes.dataView.setUint32(0, this.db.dbi); | ||
let keySize = this.writeKey(id, keyBytes, 4); | ||
return getIncrementer(env.address, keySize, startingValue); | ||
}, | ||
attemptLock(id, version, callback) { | ||
@@ -345,3 +443,3 @@ keyBytes.dataView.setUint32(0, this.db.dbi); | ||
if (readTxn) { | ||
readTxn.isCommitted = true | ||
readTxn.isCommitted = true; | ||
readTxn.commit(); | ||
@@ -354,4 +452,3 @@ } | ||
ensureReadTxn() { | ||
if (!env.writeTxn && !readTxnRenewed) | ||
renewReadTxn(this); | ||
if (!env.writeTxn && !readTxnRenewed) renewReadTxn(this); | ||
}, | ||
@@ -361,8 +458,12 @@ doesExist(key, versionOrValue, options) { | ||
// undefined means the entry exists, null is used specifically to check for the entry *not* existing | ||
return (this.getBinaryFast(key, options) === undefined) == (versionOrValue === null); | ||
} | ||
else if (this.useVersions) { | ||
return this.getBinaryFast(key, options) !== undefined && (versionOrValue === IF_EXISTS || getLastVersion() === versionOrValue); | ||
} | ||
else { | ||
return ( | ||
(this.getBinaryFast(key, options) === undefined) == | ||
(versionOrValue === null) | ||
); | ||
} else if (this.useVersions) { | ||
return ( | ||
this.getBinaryFast(key, options) !== undefined && | ||
(versionOrValue === IF_EXISTS || getLastVersion() === versionOrValue) | ||
); | ||
} else { | ||
if (versionOrValue && versionOrValue['\x10binary-data\x02']) | ||
@@ -375,3 +476,8 @@ versionOrValue = versionOrValue['\x10binary-data\x02']; | ||
let defaultOptions = { start: versionOrValue, exactMatch: true }; | ||
return this.getValuesCount(key, options ? Object.assign(defaultOptions, options) : defaultOptions) > 0; | ||
return ( | ||
this.getValuesCount( | ||
key, | ||
options ? Object.assign(defaultOptions, options) : defaultOptions, | ||
) > 0 | ||
); | ||
} | ||
@@ -382,11 +488,12 @@ }, | ||
key, | ||
valuesForKey: true | ||
valuesForKey: true, | ||
}; | ||
if (options && options.snapshot === false) | ||
throw new Error('Can not disable snapshots for getValues'); | ||
return this.getRange(options ? Object.assign(defaultOptions, options) : defaultOptions); | ||
return this.getRange( | ||
options ? Object.assign(defaultOptions, options) : defaultOptions, | ||
); | ||
}, | ||
getKeys(options) { | ||
if (!options) | ||
options = {}; | ||
if (!options) options = {}; | ||
options.values = false; | ||
@@ -396,4 +503,3 @@ return this.getRange(options); | ||
getCount(options) { | ||
if (!options) | ||
options = {}; | ||
if (!options) options = {}; | ||
options.onlyCount = true; | ||
@@ -403,4 +509,3 @@ return this.getRange(options).iterate(); | ||
getKeysCount(options) { | ||
if (!options) | ||
options = {}; | ||
if (!options) options = {}; | ||
options.onlyCount = true; | ||
@@ -411,4 +516,3 @@ options.values = false; | ||
getValuesCount(key, options) { | ||
if (!options) | ||
options = {}; | ||
if (!options) options = {}; | ||
options.key = key; | ||
@@ -421,4 +525,3 @@ options.valuesForKey = true; | ||
let iterable = new RangeIterable(); | ||
if (!options) | ||
options = {}; | ||
if (!options) options = {}; | ||
let includeValues = options.values !== false; | ||
@@ -430,13 +533,22 @@ let includeVersions = options.versions; | ||
let snapshot = options.snapshot; | ||
if (snapshot === false && this.dupSort && includeValues) throw new Error('Can not disable snapshot on a' + | ||
' dupSort data store'); | ||
if (snapshot === false && this.dupSort && includeValues) | ||
throw new Error( | ||
'Can not disable snapshot on a' + ' dupSort data store', | ||
); | ||
let compression = this.compression; | ||
iterable.iterate = () => { | ||
const reverse = options.reverse; | ||
let currentKey = valuesForKey ? options.key : (reverse || 'start' in options) ? options.start : DEFAULT_BEGINNING_KEY; | ||
let currentKey = valuesForKey | ||
? options.key | ||
: reverse || 'start' in options | ||
? options.start | ||
: DEFAULT_BEGINNING_KEY; | ||
let count = 0; | ||
let cursor, cursorRenewId, cursorAddress; | ||
let txn; | ||
let flags = (includeValues ? 0x100 : 0) | (reverse ? 0x400 : 0) | | ||
(valuesForKey ? 0x800 : 0) | (options.exactMatch ? 0x4000 : 0) | | ||
let flags = | ||
(includeValues ? 0x100 : 0) | | ||
(reverse ? 0x400 : 0) | | ||
(valuesForKey ? 0x800 : 0) | | ||
(options.exactMatch ? 0x4000 : 0) | | ||
(options.inclusiveEnd ? 0x8000 : 0) | | ||
@@ -447,9 +559,11 @@ (options.exclusiveStart ? 0x10000 : 0); | ||
try { | ||
if (cursor) | ||
finishCursor(); | ||
if (cursor) finishCursor(); | ||
let txnAddress; | ||
txn = options.transaction | ||
txn = options.transaction; | ||
if (txn) { | ||
if (txn.isDone) throw new Error('Can not iterate on range with transaction that is already' + | ||
' done'); | ||
if (txn.isDone) | ||
throw new Error( | ||
'Can not iterate on range with transaction that is already' + | ||
' done', | ||
); | ||
txnAddress = txn.address; | ||
@@ -462,5 +576,7 @@ if (!txnAddress) { | ||
let writeTxn = env.writeTxn; | ||
if (writeTxn) | ||
snapshot = false; | ||
txn = env.writeTxn || options.transaction || (readTxnRenewed ? readTxn : renewReadTxn(store)); | ||
if (writeTxn) snapshot = false; | ||
txn = | ||
env.writeTxn || | ||
options.transaction || | ||
(readTxnRenewed ? readTxn : renewReadTxn(store)); | ||
cursor = !writeTxn && db.availableCursor; | ||
@@ -475,3 +591,4 @@ } | ||
cursorAddress = cursor.address; | ||
if (txn.use) txn.use(); // track transaction so we always use the same one | ||
if (txn.use) | ||
txn.use(); // track transaction so we always use the same one | ||
else txn.refCount = (txn.refCount || 0) + 1; | ||
@@ -482,7 +599,7 @@ if (snapshot === false) { | ||
} | ||
} catch(error) { | ||
} catch (error) { | ||
if (cursor) { | ||
try { | ||
cursor.close(); | ||
} catch(error) { } | ||
} catch (error) {} | ||
} | ||
@@ -496,4 +613,3 @@ throw error; | ||
let count = position(options.offset); | ||
if (count < 0) | ||
lmdbError(count); | ||
if (count < 0) lmdbError(count); | ||
finishCursor(); | ||
@@ -506,3 +622,6 @@ return count; | ||
} | ||
let keySize = currentKey === undefined ? 0 : store.writeKey(currentKey, keyBytes, 0); | ||
let keySize = | ||
currentKey === undefined | ||
? 0 | ||
: store.writeKey(currentKey, keyBytes, 0); | ||
let endAddress; | ||
@@ -515,13 +634,49 @@ if (valuesForKey) { | ||
if (store.encoder.writeKey) { | ||
startAddress = saveKey(options.start, store.encoder.writeKey, iterable, maxKeySize); | ||
keyBytesView.setFloat64(START_ADDRESS_POSITION, startAddress, true); | ||
endAddress = saveKey(options.end, store.encoder.writeKey, iterable, maxKeySize); | ||
} else if ((!options.start || options.start instanceof Uint8Array) && (!options.end || options.end instanceof Uint8Array)) { | ||
startAddress = saveKey(options.start, orderedBinary.writeKey, iterable, maxKeySize); | ||
keyBytesView.setFloat64(START_ADDRESS_POSITION, startAddress, true); | ||
endAddress = saveKey(options.end, orderedBinary.writeKey, iterable, maxKeySize); | ||
startAddress = saveKey( | ||
options.start, | ||
store.encoder.writeKey, | ||
iterable, | ||
maxKeySize, | ||
); | ||
keyBytesView.setFloat64( | ||
START_ADDRESS_POSITION, | ||
startAddress, | ||
true, | ||
); | ||
endAddress = saveKey( | ||
options.end, | ||
store.encoder.writeKey, | ||
iterable, | ||
maxKeySize, | ||
); | ||
} else if ( | ||
(!options.start || options.start instanceof Uint8Array) && | ||
(!options.end || options.end instanceof Uint8Array) | ||
) { | ||
startAddress = saveKey( | ||
options.start, | ||
orderedBinary.writeKey, | ||
iterable, | ||
maxKeySize, | ||
); | ||
keyBytesView.setFloat64( | ||
START_ADDRESS_POSITION, | ||
startAddress, | ||
true, | ||
); | ||
endAddress = saveKey( | ||
options.end, | ||
orderedBinary.writeKey, | ||
iterable, | ||
maxKeySize, | ||
); | ||
} else { | ||
throw new Error('Only key-based encoding is supported for start/end values'); | ||
throw new Error( | ||
'Only key-based encoding is supported for start/end values', | ||
); | ||
let encoded = store.encoder.encode(options.start); | ||
let bufferAddress = encoded.buffer.address || (encoded.buffer.address = getAddress(encoded.buffer) - encoded.byteOffset); | ||
let bufferAddress = | ||
encoded.buffer.address || | ||
(encoded.buffer.address = | ||
getAddress(encoded.buffer) - encoded.byteOffset); | ||
startAddress = bufferAddress + encoded.byteOffset; | ||
@@ -531,13 +686,23 @@ } | ||
} else | ||
endAddress = saveKey((reverse && !('end' in options)) ? DEFAULT_BEGINNING_KEY : options.end, store.writeKey, iterable, maxKeySize); | ||
return doPosition(cursorAddress, flags, offset || 0, keySize, endAddress); | ||
endAddress = saveKey( | ||
reverse && !('end' in options) | ||
? DEFAULT_BEGINNING_KEY | ||
: options.end, | ||
store.writeKey, | ||
iterable, | ||
maxKeySize, | ||
); | ||
return doPosition( | ||
cursorAddress, | ||
flags, | ||
offset || 0, | ||
keySize, | ||
endAddress, | ||
); | ||
} | ||
function finishCursor() { | ||
if (!cursor || txn.isDone) | ||
return; | ||
if (iterable.onDone) | ||
iterable.onDone() | ||
if (cursorRenewId) | ||
txn.renewingRefCount--; | ||
if (!cursor || txn.isDone) return; | ||
if (iterable.onDone) iterable.onDone(); | ||
if (cursorRenewId) txn.renewingRefCount--; | ||
if (txn.refCount <= 1 && txn.notCurrent) { | ||
@@ -555,3 +720,4 @@ cursor.close(); // this must be closed before the transaction is aborted or it can cause a | ||
cursor.close(); | ||
} else { // try to reuse it | ||
} else { | ||
// try to reuse it | ||
db.availableCursor = cursor; | ||
@@ -573,10 +739,8 @@ db.cursorTxn = txn; | ||
} | ||
if (count === 0) { // && includeValues) // on first entry, get current value if we need to | ||
if (count === 0) { | ||
// && includeValues) // on first entry, get current value if we need to | ||
keySize = position(options.offset); | ||
} else | ||
keySize = iterate(cursorAddress); | ||
if (keySize <= 0 || | ||
(count++ >= limit)) { | ||
if (keySize < -30700 && keySize !== -30798) | ||
lmdbError(keySize); | ||
} else keySize = iterate(cursorAddress); | ||
if (keySize <= 0 || count++ >= limit) { | ||
if (keySize < -30700 && keySize !== -30798) lmdbError(keySize); | ||
finishCursor(); | ||
@@ -587,5 +751,4 @@ return ITERATOR_DONE; | ||
if (keySize > 20000) { | ||
if (keySize > 0x1000000) | ||
lmdbError(keySize - 0x100000000) | ||
throw new Error('Invalid key size ' + keySize.toString(16)) | ||
if (keySize > 0x1000000) lmdbError(keySize - 0x100000000); | ||
throw new Error('Invalid key size ' + keySize.toString(16)); | ||
} | ||
@@ -608,8 +771,9 @@ currentKey = store.readKey(keyBytes, 32, keySize + 32); | ||
try { | ||
bytes = store._returnLargeBuffer(() => getCurrentValue(cursorAddress)); | ||
bytes = store._returnLargeBuffer(() => | ||
getCurrentValue(cursorAddress), | ||
); | ||
} finally { | ||
asSafeBuffer = false; | ||
} | ||
} else | ||
bytes.length = lastSize; | ||
} else bytes.length = lastSize; | ||
} | ||
@@ -619,3 +783,5 @@ if (store.decoder) { | ||
} else if (store.encoding == 'binary') | ||
value = bytes.isGlobal ? Uint8ArraySlice.call(bytes, 0, lastSize) : bytes; | ||
value = bytes.isGlobal | ||
? Uint8ArraySlice.call(bytes, 0, lastSize) | ||
: bytes; | ||
else { | ||
@@ -631,8 +797,8 @@ value = bytes.toString('utf8', 0, lastSize); | ||
value, | ||
version: getLastVersion() | ||
} | ||
version: getLastVersion(), | ||
}, | ||
}; | ||
else if (valuesForKey) | ||
else if (valuesForKey) | ||
return { | ||
value | ||
value, | ||
}; | ||
@@ -644,3 +810,3 @@ else | ||
value, | ||
} | ||
}, | ||
}; | ||
@@ -651,8 +817,8 @@ } else if (includeVersions) { | ||
key: currentKey, | ||
version: getLastVersion() | ||
} | ||
version: getLastVersion(), | ||
}, | ||
}; | ||
} else { | ||
return { | ||
value: currentKey | ||
value: currentKey, | ||
}; | ||
@@ -668,3 +834,3 @@ } | ||
return ITERATOR_DONE; | ||
} | ||
}, | ||
}; | ||
@@ -680,3 +846,7 @@ }; | ||
// once the prefetch occurs | ||
let promise = callback ? undefined : new Promise(resolve => callback = (error, results) => resolve(results)); | ||
let promise = callback | ||
? undefined | ||
: new Promise( | ||
(resolve) => (callback = (error, results) => resolve(results)), | ||
); | ||
this.prefetch(keys, () => { | ||
@@ -692,5 +862,11 @@ let results = new Array(keys.length); | ||
getSharedBufferForGet(id, options) { | ||
let txn = env.writeTxn || (options && options.transaction) || (readTxnRenewed ? readTxn : renewReadTxn(this)); | ||
this.lastSize = this.keyIsCompatibility ? txn.getBinaryShared(id) : this.db.get(this.writeKey(id, keyBytes, 0)); | ||
if (this.lastSize === -30798) { // not found code | ||
let txn = | ||
env.writeTxn || | ||
(options && options.transaction) || | ||
(readTxnRenewed ? readTxn : renewReadTxn(this)); | ||
this.lastSize = this.keyIsCompatibility | ||
? txn.getBinaryShared(id) | ||
: this.db.get(this.writeKey(id, keyBytes, 0)); | ||
if (this.lastSize === -30798) { | ||
// not found code | ||
return; //undefined | ||
@@ -704,8 +880,16 @@ } | ||
let startOffset; | ||
if (!buffer || lastOffset < (startOffset = buffer.startOffset) || (lastOffset + this.lastSize > startOffset + 0x100000000)) { | ||
if (buffer) | ||
env.detachBuffer(buffer.buffer); | ||
if ( | ||
!buffer || | ||
lastOffset < (startOffset = buffer.startOffset) || | ||
lastOffset + this.lastSize > startOffset + 0x100000000 | ||
) { | ||
if (buffer) env.detachBuffer(buffer.buffer); | ||
startOffset = (lastOffset >>> 16) * 0x10000; | ||
console.log('make buffer for address', bufferIndex * 0x100000000 + startOffset); | ||
buffer = buffers[bufferIndex] = Buffer.from(getBufferForAddress(bufferIndex * 0x100000000 + startOffset)); | ||
console.log( | ||
'make buffer for address', | ||
bufferIndex * 0x100000000 + startOffset, | ||
); | ||
buffer = buffers[bufferIndex] = Buffer.from( | ||
getBufferForAddress(bufferIndex * 0x100000000 + startOffset), | ||
); | ||
buffer.startOffset = startOffset; | ||
@@ -715,7 +899,9 @@ } | ||
return buffer; | ||
return buffer.slice(lastOffset, lastOffset + this.lastSize);/*Uint8ArraySlice.call(buffer, lastOffset, lastOffset + this.lastSize)*/ | ||
return buffer.slice( | ||
lastOffset, | ||
lastOffset + this.lastSize, | ||
); /*Uint8ArraySlice.call(buffer, lastOffset, lastOffset + this.lastSize)*/ | ||
}, | ||
prefetch(keys, callback) { | ||
if (!keys) | ||
throw new Error('An array of keys must be provided'); | ||
if (!keys) throw new Error('An array of keys must be provided'); | ||
if (!keys.length) { | ||
@@ -725,4 +911,3 @@ if (callback) { | ||
return; | ||
} else | ||
return Promise.resolve(); | ||
} else return Promise.resolve(); | ||
} | ||
@@ -734,5 +919,11 @@ let buffers = []; | ||
for (let key of keys) { | ||
let position | ||
let position; | ||
if (key && key.key !== undefined && key.value !== undefined) { | ||
position = saveKey(key.value, this.writeKey, bufferHolder, maxKeySize, 0x80000000); | ||
position = saveKey( | ||
key.value, | ||
this.writeKey, | ||
bufferHolder, | ||
maxKeySize, | ||
0x80000000, | ||
); | ||
saveReferenceToBuffer(); | ||
@@ -743,4 +934,3 @@ saveKey(key.key, this.writeKey, bufferHolder, maxKeySize); | ||
} | ||
if (!startPosition) | ||
startPosition = position; | ||
if (!startPosition) startPosition = position; | ||
saveReferenceToBuffer(); | ||
@@ -761,7 +951,5 @@ } | ||
console.error('Error with prefetch', buffers); // partly exists to keep the buffers pinned in memory | ||
else | ||
callback(null); | ||
else callback(null); | ||
}); | ||
if (!callback) | ||
return new Promise(resolve => callback = resolve); | ||
if (!callback) return new Promise((resolve) => (callback = resolve)); | ||
}, | ||
@@ -789,11 +977,13 @@ useReadTransaction() { | ||
readTxn.isDone = true; | ||
Object.defineProperty(readTxn,'renew', { | ||
Object.defineProperty(readTxn, 'renew', { | ||
value: () => { | ||
throw new Error('Can not read from a closed database'); | ||
}, configurable: true | ||
}, | ||
configurable: true, | ||
}); | ||
Object.defineProperty(readTxn,'use', { | ||
Object.defineProperty(readTxn, 'use', { | ||
value: () => { | ||
throw new Error('Can not read from a closed database'); | ||
}, configurable: true | ||
}, | ||
configurable: true, | ||
}); | ||
@@ -806,14 +996,13 @@ readTxnRenewed = null; | ||
if (outstandingReads > 0) { | ||
return new Promise(resolve => setTimeout(() => resolve(doClose()), 1)); | ||
return new Promise((resolve) => | ||
setTimeout(() => resolve(doClose()), 1), | ||
); | ||
} | ||
env.address = 0; | ||
env.close(); | ||
} else | ||
this.db.close(); | ||
} else this.db.close(); | ||
this.status = 'closed'; | ||
if (callback) | ||
callback(); | ||
} | ||
if (txnPromise) | ||
return txnPromise.then(doClose); | ||
if (callback) callback(); | ||
}; | ||
if (txnPromise) return txnPromise.then(doClose); | ||
else { | ||
@@ -853,7 +1042,10 @@ doClose(); | ||
let lastReadTxn = lastReadTxnRef && lastReadTxnRef.deref(); | ||
readTxn = new Txn(env, 0x20000, lastReadTxn && !lastReadTxn.isDone && lastReadTxn); | ||
readTxn = new Txn( | ||
env, | ||
0x20000, | ||
lastReadTxn && !lastReadTxn.isDone && lastReadTxn, | ||
); | ||
if (readTxn.address == 0) { | ||
readTxn = lastReadTxn; | ||
if (readTxn.notCurrent) | ||
readTxn.notCurrent = false; | ||
if (readTxn.notCurrent) readTxn.notCurrent = false; | ||
} | ||
@@ -866,4 +1058,3 @@ break; | ||
Atomics.wait(waitArray, 0, 0, retries * 2); | ||
} else | ||
throw error; | ||
} else throw error; | ||
} | ||
@@ -896,9 +1087,14 @@ } while (retries++ < 100); | ||
export function makeReusableBuffer(size) { | ||
let bytes = typeof Buffer != 'undefined' ? Buffer.alloc(size) : new Uint8Array(size); | ||
let bytes = | ||
typeof Buffer != 'undefined' ? Buffer.alloc(size) : new Uint8Array(size); | ||
bytes.maxLength = size; | ||
Object.defineProperty(bytes, 'length', { value: size, writable: true, configurable: true }); | ||
Object.defineProperty(bytes, 'length', { | ||
value: size, | ||
writable: true, | ||
configurable: true, | ||
}); | ||
return bytes; | ||
} | ||
Txn.prototype.done = function() { | ||
Txn.prototype.done = function () { | ||
this.refCount--; | ||
@@ -910,20 +1106,43 @@ if (this.refCount === 0 && this.notCurrent) { | ||
throw new Error('Can not finish a transaction more times than it was used'); | ||
} | ||
Txn.prototype.use = function() { | ||
}; | ||
Txn.prototype.use = function () { | ||
this.refCount = (this.refCount || 0) + 1; | ||
} | ||
}; | ||
let readInstructions, readCallbacks = new Map(), uint32Instructions, instructionsDataView = { setFloat64() {}, setUint32() {} }, instructionsAddress; | ||
let readInstructions, | ||
readCallbacks = new Map(), | ||
uint32Instructions, | ||
instructionsDataView = { setFloat64() {}, setUint32() {} }, | ||
instructionsAddress; | ||
let savePosition = 8000; | ||
let DYNAMIC_KEY_BUFFER_SIZE = 8192; | ||
function allocateInstructionsBuffer() { | ||
readInstructions = typeof Buffer != 'undefined' ? Buffer.alloc(DYNAMIC_KEY_BUFFER_SIZE) : new Uint8Array(DYNAMIC_KEY_BUFFER_SIZE); | ||
uint32Instructions = new Int32Array(readInstructions.buffer, 0, readInstructions.buffer.byteLength >> 2); | ||
readInstructions = | ||
typeof Buffer != 'undefined' | ||
? Buffer.alloc(DYNAMIC_KEY_BUFFER_SIZE) | ||
: new Uint8Array(DYNAMIC_KEY_BUFFER_SIZE); | ||
uint32Instructions = new Int32Array( | ||
readInstructions.buffer, | ||
0, | ||
readInstructions.buffer.byteLength >> 2, | ||
); | ||
uint32Instructions[2] = 0xf0000000; // indicates a new read task must be started | ||
instructionsAddress = readInstructions.buffer.address = getAddress(readInstructions.buffer); | ||
readInstructions.dataView = instructionsDataView = new DataView(readInstructions.buffer, readInstructions.byteOffset, readInstructions.byteLength); | ||
instructionsAddress = readInstructions.buffer.address = getAddress( | ||
readInstructions.buffer, | ||
); | ||
readInstructions.dataView = instructionsDataView = new DataView( | ||
readInstructions.buffer, | ||
readInstructions.byteOffset, | ||
readInstructions.byteLength, | ||
); | ||
savePosition = 0; | ||
} | ||
export function recordReadInstruction(txnAddress, dbi, key, writeKey, maxKeySize, callback) { | ||
export function recordReadInstruction( | ||
txnAddress, | ||
dbi, | ||
key, | ||
writeKey, | ||
maxKeySize, | ||
callback, | ||
) { | ||
if (savePosition > 7800) { | ||
@@ -935,4 +1154,6 @@ allocateInstructionsBuffer(); | ||
try { | ||
savePosition = key === undefined ? keyPosition : | ||
writeKey(key, readInstructions, keyPosition); | ||
savePosition = | ||
key === undefined | ||
? keyPosition | ||
: writeKey(key, readInstructions, keyPosition); | ||
} catch (error) { | ||
@@ -942,7 +1163,13 @@ if (error.name == 'RangeError') { | ||
allocateInstructionsBuffer(); // try again: | ||
return recordReadInstruction(txnAddress, dbi, key, writeKey, maxKeySize, callback); | ||
return recordReadInstruction( | ||
txnAddress, | ||
dbi, | ||
key, | ||
writeKey, | ||
maxKeySize, | ||
callback, | ||
); | ||
} | ||
throw new Error('Key was too large, max key size is ' + maxKeySize); | ||
} else | ||
throw error; | ||
} else throw error; | ||
} | ||
@@ -952,3 +1179,5 @@ let length = savePosition - keyPosition; | ||
savePosition = start; | ||
throw new Error('Key of size ' + length + ' was too large, max key size is ' + maxKeySize); | ||
throw new Error( | ||
'Key of size ' + length + ' was too large, max key size is ' + maxKeySize, | ||
); | ||
} | ||
@@ -962,9 +1191,14 @@ uint32Instructions[(start >> 2) + 3] = length; // save the length | ||
let rc = thisInstructions[position]; | ||
callback(rc, thisInstructions[position + 1], thisInstructions[position + 2], thisInstructions[position + 3]); | ||
callback( | ||
rc, | ||
thisInstructions[position + 1], | ||
thisInstructions[position + 2], | ||
thisInstructions[position + 3], | ||
); | ||
}); | ||
let thisInstructions = uint32Instructions; | ||
//if (start === 0) | ||
return startRead(instructionsAddress + start, callbackId, {}, 'read'); | ||
return startRead(instructionsAddress + start, callbackId, {}, 'read'); | ||
//else | ||
//nextRead(start); | ||
//nextRead(start); | ||
} | ||
@@ -974,3 +1208,3 @@ let nextCallbackId = 0; | ||
if (!addReadCallback) { | ||
addReadCallback = globalThis.__lmdb_read_callback = function(callback) { | ||
addReadCallback = globalThis.__lmdb_read_callback = function (callback) { | ||
let callbackId = nextCallbackId++; | ||
@@ -980,6 +1214,6 @@ readCallbacks.set(callbackId, callback); | ||
}; | ||
setReadCallback(function(callbackId) { | ||
setReadCallback(function (callbackId) { | ||
readCallbacks.get(callbackId)(); | ||
readCallbacks.delete(callbackId); | ||
}) | ||
}); | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
2496882
7231