lmdbx
Version:
Simple, efficient, scalable data store wrapper for libmdbx
1 lines • 169 kB
Source Map (JSON)
{"version":3,"file":"index.cjs","sources":["../external.js","../util/when.js","../write.js","../level.js","../caching.js","../util/RangeIterable.js","../keys.js","../read.js","../open.js","../node-index.js"],"sourcesContent":["export let Env, Compression, Cursor, getAddress, clearKeptObjects, setGlobalBuffer,\r\n require, arch, fs, lmdbxError, path, EventEmitter, orderedBinary, MsgpackrEncoder, WeakLRUCache;\r\nexport function setNativeFunctions(externals) {\r\n\tEnv = externals.Env;\r\n\tCompression = externals.Compression;\r\n\tgetAddress = externals.getAddress;\r\n clearKeptObjects = externals.clearKeptObjects;\r\n setGlobalBuffer = externals.setGlobalBuffer;\r\n Cursor = externals.Cursor;\r\n lmdbxError = externals.lmdbxError;\r\n}\r\nexport function setExternals(externals) {\r\n require = externals.require;\r\n arch = externals.arch;\r\n fs = externals.fs;\r\n path = externals.path;\r\n EventEmitter = externals.EventEmitter;\r\n orderedBinary = externals.orderedBinary;\r\n MsgpackrEncoder = externals.MsgpackrEncoder;\r\n WeakLRUCache = externals.WeakLRUCache;\r\n}\r\n","export function when(promise, callback, errback) {\r\n if (promise && promise.then) {\r\n return errback ?\r\n promise.then(callback, errback) :\r\n promise.then(callback);\r\n }\r\n return callback(promise);\r\n}\r\n","import { getAddress } from './external.js';\r\nimport { when } from './util/when.js';\r\nvar backpressureArray;\r\n\r\nconst WAITING_OPERATION = 0x2000000;\r\nconst BACKPRESSURE_THRESHOLD = 100000;\r\nconst TXN_DELIMITER = 0x8000000;\r\nconst TXN_COMMITTED = 0x10000000;\r\nconst TXN_FLUSHED = 0x20000000;\r\nconst TXN_FAILED = 0x40000000;\r\nexport const FAILED_CONDITION = 0x4000000;\r\nconst REUSE_BUFFER_MODE = 512;\r\nconst RESET_BUFFER_MODE = 1024;\r\n\r\nconst SYNC_PROMISE_SUCCESS = Promise.resolve(true);\r\nconst SYNC_PROMISE_FAIL = Promise.resolve(false);\r\nSYNC_PROMISE_SUCCESS.isSync = true;\r\nSYNC_PROMISE_FAIL.isSync = true;\r\nconst PROMISE_SUCCESS = Promise.resolve(true);\r\nexport const ABORT = {};\r\nexport const IF_EXISTS = 3.542694326329068e-103;\r\nconst CALLBACK_THREW = {};\r\nconst LocalSharedArrayBuffer = typeof Deno != 'undefined' ? ArrayBuffer : SharedArrayBuffer; // Deno can't handle SharedArrayBuffer as an FFI argument due to https://github.com/denoland/deno/issues/12678\r\nconst ByteArray = typeof Buffer != 'undefined' ? Buffer.from : Uint8Array;\r\nconst queueTask = typeof setImmediate != 'undefined' ? setImmediate : setTimeout; // TODO: Or queueMicrotask?\r\n//let debugLog = []\r\nconst WRITE_BUFFER_SIZE = 0x10000;\r\nvar log = [];\r\nexport function addWriteMethods(LMDBStore, { env, fixedBuffer, resetReadTxn, useWritemap, maxKeySize,\r\n\teventTurnBatching, txnStartThreshold, batchStartThreshold, overlappingSync, commitDelay, separateFlushed }) {\r\n\t// stands for write instructions\r\n\tvar dynamicBytes;\r\n\tfunction allocateInstructionBuffer() {\r\n\t\t// Must use a shared buffer on older node in order to use Atomics, and it is also more correct since we are \r\n\t\t// indeed accessing and modifying it from another thread (in C). However, Deno can't handle it for\r\n\t\t// FFI so aliased above\r\n\t\tlet buffer = new LocalSharedArrayBuffer(WRITE_BUFFER_SIZE);\r\n\t\tdynamicBytes = new ByteArray(buffer);\r\n\t\tlet uint32 = dynamicBytes.uint32 = new Uint32Array(buffer, 0, WRITE_BUFFER_SIZE >> 2);\r\n\t\tuint32[0] = 0;\r\n\t\tdynamicBytes.float64 = new Float64Array(buffer, 0, WRITE_BUFFER_SIZE >> 3);\r\n\t\tbuffer.address = getAddress(dynamicBytes);\r\n\t\tuint32.address = buffer.address + uint32.byteOffset;\r\n\t\tdynamicBytes.position = 0;\r\n\t\treturn dynamicBytes;\r\n\t}\r\n\tvar newBufferThreshold = (WRITE_BUFFER_SIZE - maxKeySize - 64) >> 3; // need to reserve more room if we do inline values\r\n\tvar outstandingWriteCount = 0;\r\n\tvar startAddress = 0;\r\n\tvar writeTxn = null;\r\n\tvar committed;\r\n\tvar abortedNonChildTransactionWarn;\r\n\tvar nextTxnCallbacks = [];\r\n\tvar commitPromise, flushPromise, flushResolvers = [];\r\n\tcommitDelay = commitDelay || 0;\r\n\teventTurnBatching = eventTurnBatching === false ? false : true;\r\n\tvar enqueuedCommit;\r\n\tvar afterCommitCallbacks = [];\r\n\tvar beforeCommitCallbacks = [];\r\n\tvar enqueuedEventTurnBatch;\r\n\tvar batchDepth = 0;\r\n\tvar lastWritePromise;\r\n\tvar writeBatchStart, outstandingBatchCount;\r\n\ttxnStartThreshold = txnStartThreshold || 5;\r\n\tbatchStartThreshold = batchStartThreshold || 1000;\r\n\r\n\tallocateInstructionBuffer();\r\n\tdynamicBytes.uint32[0] = TXN_DELIMITER | TXN_COMMITTED | TXN_FLUSHED;\r\n\tvar txnResolution, lastQueuedResolution, nextResolution = { uint32: dynamicBytes.uint32, flagPosition: 0, };\r\n\tvar uncommittedResolution = { next: nextResolution };\r\n\tvar unwrittenResolution = nextResolution;\r\n\tfunction writeInstructions(flags, store, key, value, version, ifVersion) {\r\n\t\tlet writeStatus;\r\n\t\tlet targetBytes, position, encoder;\r\n\t\tlet valueBuffer, valueSize, valueBufferStart;\r\n\t\tif (flags & 2) {\r\n\t\t\t// encode first in case we have to write a shared structure\r\n\t\t\tencoder = store.encoder;\r\n\t\t\tif (value && value['\\x10binary-data\\x02'])\r\n\t\t\t\tvalueBuffer = value['\\x10binary-data\\x02'];\r\n\t\t\telse if (encoder) {\r\n\t\t\t\tif (encoder.copyBuffers) // use this as indicator for support buffer reuse for now\r\n\t\t\t\t\tvalueBuffer = encoder.encode(value, REUSE_BUFFER_MODE | (writeTxn ? RESET_BUFFER_MODE : 0)); // in addition, if we are writing sync, after using, we can immediately reset the encoder's position to reuse that space, which can improve performance\r\n\t\t\t\telse { // various other encoders, including JSON.stringify, that might serialize to a string\r\n\t\t\t\t\tvalueBuffer = encoder.encode(value);\r\n\t\t\t\t\tif (typeof valueBuffer == 'string')\r\n\t\t\t\t\t\tvalueBuffer = Buffer.from(valueBuffer); // TODO: Would be nice to write strings inline in the instructions\r\n\t\t\t\t}\r\n\t\t\t} else if (typeof value == 'string') {\r\n\t\t\t\tvalueBuffer = Buffer.from(value); // TODO: Would be nice to write strings inline in the instructions\r\n\t\t\t} else if (value instanceof Uint8Array)\r\n\t\t\t\tvalueBuffer = value;\r\n\t\t\telse\r\n\t\t\t\tthrow new Error('Invalid value to put in database ' + value + ' (' + (typeof value) +'), consider using encoder');\r\n\t\t\tvalueBufferStart = valueBuffer.start;\r\n\t\t\tif (valueBufferStart > -1) // if we have buffers with start/end position\r\n\t\t\t\tvalueSize = valueBuffer.end - valueBufferStart; // size\r\n\t\t\telse\r\n\t\t\t\tvalueSize = valueBuffer.length;\r\n\t\t\tif (store.dupSort && valueSize > maxKeySize)\r\n\t\t\t\tthrow new Error('The value is larger than the maximum size (' + maxKeySize + ') for a value in a dupSort database');\r\n\t\t} else\r\n\t\t\tvalueSize = 0;\r\n\t\tif (writeTxn) {\r\n\t\t\ttargetBytes = fixedBuffer;\r\n\t\t\tposition = 0;\r\n\t\t} else {\r\n\t\t\tif (eventTurnBatching && !enqueuedEventTurnBatch && batchDepth == 0) {\r\n\t\t\t\tenqueuedEventTurnBatch = queueTask(() => {\r\n\t\t\t\t\ttry {\r\n\t\t\t\t\t\tfor (let i = 0, l = beforeCommitCallbacks.length; i < l; i++) {\r\n\t\t\t\t\t\t\tbeforeCommitCallbacks[i]();\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t} catch(error) {\r\n\t\t\t\t\t\tconsole.error(error);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tenqueuedEventTurnBatch = null;\r\n\t\t\t\t\tbatchDepth--;\r\n\t\t\t\t\tfinishBatch();\r\n\t\t\t\t\tif (writeBatchStart)\r\n\t\t\t\t\t\twriteBatchStart(); // TODO: When we support delay start of batch, optionally don't delay this\r\n\t\t\t\t});\r\n\t\t\t\tcommitPromise = null; // reset the commit promise, can't know if it is really a new transaction prior to finishWrite being called\r\n\t\t\t\tflushPromise = null;\r\n\t\t\t\twriteBatchStart = writeInstructions(1, store);\r\n\t\t\t\toutstandingBatchCount = 0;\r\n\t\t\t\tbatchDepth++;\r\n\t\t\t}\r\n\t\t\ttargetBytes = dynamicBytes;\r\n\t\t\tposition = targetBytes.position;\r\n\t\t}\r\n\t\tlet uint32 = targetBytes.uint32, float64 = targetBytes.float64;\r\n\t\tlet flagPosition = position << 1; // flagPosition is the 32-bit word starting position\r\n\r\n\t\t// don't increment position until we are sure we don't have any key writing errors\r\n\t\tif (!uint32) {\r\n\t\t\tthrow new Error('Internal buffers have been corrupted');\r\n\t\t}\r\n\t\tuint32[flagPosition + 1] = store.db.dbi;\r\n\t\tif (flags & 4) {\r\n\t\t\tlet keyStartPosition = (position << 3) + 12;\r\n\t\t\tlet endPosition;\r\n\t\t\ttry {\r\n\t\t\t\tendPosition = store.writeKey(key, targetBytes, keyStartPosition);\r\n\t\t\t\tif (!(keyStartPosition <= endPosition) && flags != 12)\r\n\t\t\t\t\tthrow new Error('Invalid key is not allowed in libmdbx')\r\n\t\t\t} catch(error) {\r\n\t\t\t\ttargetBytes.fill(0, keyStartPosition);\r\n\t\t\t\tif (error.name == 'RangeError')\r\n\t\t\t\t\terror = new Error('Key size is larger than the maximum key size (' + maxKeySize + ')');\r\n\t\t\t\tthrow error;\r\n\t\t\t}\r\n\t\t\tlet keySize = endPosition - keyStartPosition;\r\n\t\t\tif (keySize > maxKeySize) {\r\n\t\t\t\ttargetBytes.fill(0, keyStartPosition); // restore zeros\r\n\t\t\t\tthrow new Error('Key size is larger than the maximum key size (' + maxKeySize + ')');\r\n\t\t\t}\r\n\t\t\tuint32[flagPosition + 2] = keySize;\r\n\t\t\tposition = (endPosition + 16) >> 3;\r\n\t\t\tif (flags & 2) {\r\n\t\t\t\tlet mustCompress;\r\n\t\t\t\tif (valueBufferStart > -1) { // if we have buffers with start/end position\r\n\t\t\t\t\t// record pointer to value buffer\r\n\t\t\t\t\tfloat64[position] = (valueBuffer.address ||\r\n\t\t\t\t\t\t(valueBuffer.address = getAddress(valueBuffer))) + valueBufferStart;\r\n\t\t\t\t\tmustCompress = valueBuffer[valueBufferStart] >= 250; // this is the compression indicator, so we must compress\r\n\t\t\t\t} else {\r\n\t\t\t\t\tlet valueArrayBuffer = valueBuffer.buffer;\r\n\t\t\t\t\t// record pointer to value buffer\r\n\t\t\t\t\tfloat64[position] = (valueArrayBuffer.address ||\r\n\t\t\t\t\t\t(valueArrayBuffer.address = (getAddress(valueBuffer) - valueBuffer.byteOffset)))\r\n\t\t\t\t\t\t\t+ valueBuffer.byteOffset;\r\n\t\t\t\t\tmustCompress = valueBuffer[0] >= 250; // this is the compression indicator, so we must compress\r\n\t\t\t\t}\r\n\t\t\t\tuint32[(position++ << 1) - 1] = valueSize;\r\n\t\t\t\tif (store.compression && (valueSize >= store.compression.threshold || mustCompress)) {\r\n\t\t\t\t\tflags |= 0x100000;\r\n\t\t\t\t\tfloat64[position] = store.compression.address;\r\n\t\t\t\t\tif (!writeTxn)\r\n\t\t\t\t\t\tenv.compress(uint32.address + (position << 3), () => {\r\n\t\t\t\t\t\t\t// this is never actually called in NodeJS, just use to pin the buffer in memory until it is finished\r\n\t\t\t\t\t\t\t// and is a no-op in Deno\r\n\t\t\t\t\t\t\tif (!float64)\r\n\t\t\t\t\t\t\t\tthrow new Error('No float64 available');\r\n\t\t\t\t\t\t});\r\n\t\t\t\t\tposition++;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tif (ifVersion !== undefined) {\r\n\t\t\t\tif (ifVersion === null)\r\n\t\t\t\t\tflags |= 0x10; // if it does not exist, MDB_NOOVERWRITE\r\n\t\t\t\telse {\r\n\t\t\t\t\tflags |= 0x100;\r\n\t\t\t\t\tfloat64[position++] = ifVersion;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tif (version !== undefined) {\r\n\t\t\t\tflags |= 0x200;\r\n\t\t\t\tfloat64[position++] = version || 0;\r\n\t\t\t}\r\n\t\t} else\r\n\t\t\tposition++;\r\n\t\ttargetBytes.position = position;\r\n\t\tif (writeTxn) {\r\n\t\t\tuint32[0] = flags;\r\n\t\t\tenv.write(uint32.address);\r\n\t\t\treturn () => (uint32[0] & FAILED_CONDITION) ? SYNC_PROMISE_FAIL : SYNC_PROMISE_SUCCESS;\r\n\t\t}\r\n\t\t// if we ever use buffers that haven't been zero'ed, need to clear out the next slot like this:\r\n\t\t// uint32[position << 1] = 0 // clear out the next slot\r\n\t\tlet nextUint32;\r\n\t\tif (position > newBufferThreshold) {\r\n\t\t\t// make new buffer and make pointer to it\r\n\t\t\tlet lastPosition = position;\r\n\t\t\ttargetBytes = allocateInstructionBuffer();\r\n\t\t\tposition = targetBytes.position;\r\n\t\t\tfloat64[lastPosition + 1] = targetBytes.uint32.address + position;\r\n\t\t\tuint32[lastPosition << 1] = 3; // pointer instruction\r\n\t\t\tnextUint32 = targetBytes.uint32;\r\n\t\t} else\r\n\t\t\tnextUint32 = uint32;\r\n\t\tlet resolution = nextResolution;\r\n\t\t// create the placeholder next resolution\r\n\t\tnextResolution = resolution.next = store.cache ?\r\n\t\t{\r\n\t\t\tuint32: nextUint32,\r\n\t\t\tflagPosition: position << 1,\r\n\t\t\tflag: 0, // TODO: eventually eliminate this, as we can probably signify success by zeroing the flagPosition\r\n\t\t\tvalueBuffer: fixedBuffer, // these are all just placeholders so that we have the right hidden class initially allocated\r\n\t\t\tnext: null,\r\n\t\t\tkey,\r\n\t\t\tstore,\r\n\t\t\tvalueSize,\r\n\t\t} :\r\n\t\t{\r\n\t\t\tuint32: nextUint32,\r\n\t\t\tflagPosition: position << 1,\r\n\t\t\tflag: 0, // TODO: eventually eliminate this, as we can probably signify success by zeroing the flagPosition\r\n\t\t\tvalueBuffer: fixedBuffer, // these are all just placeholders so that we have the right hidden class initially allocated\r\n\t\t\tnext: null,\r\n\t\t};\r\n\t\tlet writtenBatchDepth = batchDepth;\r\n\r\n\t\treturn (callback) => {\r\n\t\t\tif (writtenBatchDepth) {\r\n\t\t\t\t// if we are in a batch, the transaction can't close, so we do the faster,\r\n\t\t\t\t// but non-deterministic updates, knowing that the write thread can\r\n\t\t\t\t// just poll for the status change if we miss a status update\r\n\t\t\t\twriteStatus = uint32[flagPosition];\r\n\t\t\t\tuint32[flagPosition] = flags;\r\n\t\t\t\t//writeStatus = Atomics.or(uint32, flagPosition, flags)\r\n\t\t\t\tif (writeBatchStart && !writeStatus) {\r\n\t\t\t\t\toutstandingBatchCount += 1 + (valueSize >> 12);\r\n\t\t\t\t\tif (outstandingBatchCount > batchStartThreshold) {\r\n\t\t\t\t\t\toutstandingBatchCount = 0;\r\n\t\t\t\t\t\twriteBatchStart();\r\n\t\t\t\t\t\twriteBatchStart = null;\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t} else // otherwise the transaction could end at any time and we need to know the\r\n\t\t\t\t// deterministically if it is ending, so we can reset the commit promise\r\n\t\t\t\t// so we use the slower atomic operation\r\n\t\t\t\twriteStatus = Atomics.or(uint32, flagPosition, flags);\r\n\t\r\n\t\t\toutstandingWriteCount++;\r\n\t\t\tif (writeStatus & TXN_DELIMITER) {\r\n\t\t\t\tcommitPromise = null; // TODO: Don't reset these if this comes from the batch start operation on an event turn batch\r\n\t\t\t\tflushPromise = null;\r\n\t\t\t\tqueueCommitResolution(resolution);\r\n\t\t\t\tif (!startAddress) {\r\n\t\t\t\t\tstartAddress = uint32.address + (flagPosition << 2);\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tif (!flushPromise && overlappingSync && separateFlushed)\r\n\t\t\t\tflushPromise = new Promise(resolve => flushResolvers.push(resolve));\r\n\t\t\tif (writeStatus & WAITING_OPERATION) { // write thread is waiting\r\n\t\t\t\tenv.write(0);\r\n\t\t\t}\r\n\t\t\tif (outstandingWriteCount > BACKPRESSURE_THRESHOLD && !writeBatchStart) {\r\n\t\t\t\tif (!backpressureArray)\r\n\t\t\t\t\tbackpressureArray = new Int32Array(new SharedArrayBuffer(4), 0, 1);\r\n\t\t\t\tAtomics.wait(backpressureArray, 0, 0, Math.round(outstandingWriteCount / BACKPRESSURE_THRESHOLD));\r\n\t\t\t}\r\n\t\t\tif (startAddress) {\r\n\t\t\t\tif (eventTurnBatching)\r\n\t\t\t\t\tstartWriting(); // start writing immediately because this has already been batched/queued\r\n\t\t\t\telse if (!enqueuedCommit && txnStartThreshold) {\r\n\t\t\t\t\tenqueuedCommit = (commitDelay == 0 && typeof setImmediate != 'undefined') ? setImmediate(() => startWriting()) : setTimeout(() => startWriting(), commitDelay);\r\n\t\t\t\t} else if (outstandingWriteCount > txnStartThreshold)\r\n\t\t\t\t\tstartWriting();\r\n\t\t\t}\r\n\r\n\t\t\tif ((outstandingWriteCount & 7) === 0)\r\n\t\t\t\tresolveWrites();\r\n\t\t\t\r\n\t\t\tif (store.cache) {\r\n\t\t\t\tresolution.key = key;\r\n\t\t\t\tresolution.store = store;\r\n\t\t\t\tresolution.valueSize = valueBuffer ? valueBuffer.length : 0;\r\n\t\t\t}\r\n\t\t\tresolution.valueBuffer = valueBuffer;\r\n\t\t\tlastQueuedResolution = resolution;\r\n\r\n\t\t\tif (callback) {\r\n\t\t\t\tif (callback === IF_EXISTS)\r\n\t\t\t\t\tifVersion = IF_EXISTS;\r\n\t\t\t\telse {\r\n\t\t\t\t\tresolution.reject = callback;\r\n\t\t\t\t\tresolution.resolve = (value) => callback(null, value);\r\n\t\t\t\t\treturn;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tif (ifVersion === undefined) {\r\n\t\t\t\tif (writtenBatchDepth > 1)\r\n\t\t\t\t\treturn PROMISE_SUCCESS; // or return undefined?\r\n\t\t\t\tif (!commitPromise) {\r\n\t\t\t\t\tcommitPromise = new Promise((resolve, reject) => {\r\n\t\t\t\t\t\tresolution.resolve = resolve;\r\n\t\t\t\t\t\tresolve.unconditional = true;\r\n\t\t\t\t\t\tresolution.reject = reject;\r\n\t\t\t\t\t});\r\n\t\t\t\t\tif (separateFlushed)\r\n\t\t\t\t\t\tcommitPromise.flushed = overlappingSync ? flushPromise : commitPromise;\r\n\t\t\t\t}\r\n\t\t\t\treturn commitPromise;\r\n\t\t\t}\r\n\t\t\tlastWritePromise = new Promise((resolve, reject) => {\r\n\t\t\t\tresolution.resolve = resolve;\r\n\t\t\t\tresolution.reject = reject;\r\n\t\t\t});\r\n\t\t\tif (separateFlushed)\r\n\t\t\t\tlastWritePromise.flushed = overlappingSync ? flushPromise : lastWritePromise;\r\n\t\t\treturn lastWritePromise;\r\n\t\t};\r\n\t}\r\n\tfunction startWriting() {\r\n\t\tif (enqueuedCommit) {\r\n\t\t\tclearImmediate(enqueuedCommit);\r\n\t\t\tenqueuedCommit = null;\r\n\t\t}\r\n\t\tlet resolvers = flushResolvers;\r\n\t\tflushResolvers = [];\r\n\t\tenv.startWriting(startAddress, (status) => {\r\n\t\t\tif (dynamicBytes.uint32[dynamicBytes.position << 1] & TXN_DELIMITER)\r\n\t\t\t\tqueueCommitResolution(nextResolution);\r\n\r\n\t\t\tresolveWrites(true);\r\n\t\t\tswitch (status) {\r\n\t\t\t\tcase 0:\r\n\t\t\t\t\tfor (let i = 0; i < resolvers.length; i++)\r\n\t\t\t\t\t\tresolvers[i]();\r\n\t\t\t\tcase 1:\r\n\t\t\t\tbreak;\r\n\t\t\t\tcase 2:\r\n\t\t\t\t\texecuteTxnCallbacks();\r\n\t\t\t\tbreak;\r\n\t\t\t\tdefault:\r\n\t\t\t\tconsole.error(status);\r\n\t\t\t\tif (commitRejectPromise) {\r\n\t\t\t\t\tcommitRejectPromise.reject(status);\r\n\t\t\t\t\tcommitRejectPromise = null;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t});\r\n\t\tstartAddress = 0;\r\n\t}\r\n\r\n\tfunction queueCommitResolution(resolution) {\r\n\t\tif (!resolution.isTxn) {\r\n\t\t\tresolution.isTxn = true;\r\n\t\t\tif (txnResolution) {\r\n\t\t\t\ttxnResolution.nextTxn = resolution;\r\n\t\t\t\t//outstandingWriteCount = 0\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t\ttxnResolution = resolution;\r\n\t\t}\r\n\t}\r\n\tvar TXN_DONE = TXN_COMMITTED | TXN_FAILED;\r\n\tfunction resolveWrites(async) {\r\n\t\t// clean up finished instructions\r\n\t\tlet instructionStatus;\r\n\t\twhile ((instructionStatus = unwrittenResolution.uint32[unwrittenResolution.flagPosition])\r\n\t\t\t\t& 0x1000000) {\r\n\t\t\tif (unwrittenResolution.callbacks) {\r\n\t\t\t\tnextTxnCallbacks.push(unwrittenResolution.callbacks);\r\n\t\t\t\tunwrittenResolution.callbacks = null;\r\n\t\t\t}\r\n\t\t\tif (!unwrittenResolution.isTxn)\r\n\t\t\t\tunwrittenResolution.uint32 = null;\r\n\t\t\tunwrittenResolution.valueBuffer = null;\r\n\t\t\tunwrittenResolution.flag = instructionStatus;\r\n\t\t\toutstandingWriteCount--;\r\n\t\t\tunwrittenResolution = unwrittenResolution.next;\r\n\t\t}\r\n\t\twhile (txnResolution &&\r\n\t\t\t(instructionStatus = txnResolution.uint32[txnResolution.flagPosition] & TXN_DONE)) {\r\n\t\t\tif (instructionStatus & TXN_FAILED)\r\n\t\t\t\trejectCommit();\r\n\t\t\telse\r\n\t\t\t\tresolveCommit(async);\r\n\t\t}\r\n\t}\r\n\r\n\tfunction resolveCommit(async) {\r\n\t\tafterCommit();\r\n\t\tif (async)\r\n\t\t\tresetReadTxn();\r\n\t\telse\r\n\t\t\tqueueMicrotask(resetReadTxn); // TODO: only do this if there are actually committed writes?\r\n\t\tdo {\r\n\t\t\tif (uncommittedResolution.resolve) {\r\n\t\t\t\tlet resolve = uncommittedResolution.resolve;\r\n\t\t\t\tif (uncommittedResolution.flag & FAILED_CONDITION && !resolve.unconditional)\r\n\t\t\t\t\tresolve(false);\r\n\t\t\t\telse\r\n\t\t\t\t\tresolve(true);\r\n\t\t\t}\r\n\t\t} while((uncommittedResolution = uncommittedResolution.next) && uncommittedResolution != txnResolution)\r\n\t\ttxnResolution = txnResolution.nextTxn;\r\n\t}\r\n\tvar commitRejectPromise;\r\n\tfunction rejectCommit() {\r\n\t\tafterCommit();\r\n\t\tif (!commitRejectPromise) {\r\n\t\t\tlet rejectFunction;\r\n\t\t\tcommitRejectPromise = new Promise((resolve, reject) => rejectFunction = reject);\r\n\t\t\tcommitRejectPromise.reject = rejectFunction;\r\n\t\t}\r\n\t\tdo {\r\n\t\t\tif (uncommittedResolution.reject) {\r\n\t\t\t\tlet flag = uncommittedResolution.flag & 0xf;\r\n\t\t\t\tlet error = new Error(\"Commit failed (see commitError for details)\");\r\n\t\t\t\terror.commitError = commitRejectPromise;\r\n\t\t\t\tuncommittedResolution.reject(error);\r\n\t\t\t}\r\n\t\t} while((uncommittedResolution = uncommittedResolution.next) && uncommittedResolution != txnResolution)\r\n\t\ttxnResolution = txnResolution.nextTxn;\r\n\t}\r\n\tfunction atomicStatus(uint32, flagPosition, newStatus) {\r\n\t\tif (batchDepth) {\r\n\t\t\t// if we are in a batch, the transaction can't close, so we do the faster,\r\n\t\t\t// but non-deterministic updates, knowing that the write thread can\r\n\t\t\t// just poll for the status change if we miss a status update\r\n\t\t\tlet writeStatus = uint32[flagPosition];\r\n\t\t\tuint32[flagPosition] = newStatus;\r\n\t\t\treturn writeStatus;\r\n\t\t\t//return Atomics.or(uint32, flagPosition, newStatus)\r\n\t\t} else // otherwise the transaction could end at any time and we need to know the\r\n\t\t\t// deterministically if it is ending, so we can reset the commit promise\r\n\t\t\t// so we use the slower atomic operation\r\n\t\t\treturn Atomics.or(uint32, flagPosition, newStatus);\r\n\t}\r\n\tfunction afterCommit() {\r\n\t\tfor (let i = 0, l = afterCommitCallbacks.length; i < l; i++) {\r\n\t\t\tafterCommitCallbacks[i]({ next: uncommittedResolution, last: unwrittenResolution});\r\n\t\t}\r\n\t}\r\n\tasync function executeTxnCallbacks() {\r\n\t\tenv.writeTxn = writeTxn = { write: true };\r\n\t\tlet promises;\r\n\t\tlet txnCallbacks;\r\n\t\tfor (let i = 0, l = nextTxnCallbacks.length; i < l; i++) {\r\n\t\t\ttxnCallbacks = nextTxnCallbacks[i];\r\n\t\t\tfor (let i = 0, l = txnCallbacks.length; i < l; i++) {\r\n\t\t\t\tlet userTxnCallback = txnCallbacks[i];\r\n\t\t\t\tlet asChild = userTxnCallback.asChild;\r\n\t\t\t\tif (asChild) {\r\n\t\t\t\t\tif (promises) {\r\n\t\t\t\t\t\t// must complete any outstanding transactions before proceeding\r\n\t\t\t\t\t\tawait Promise.all(promises);\r\n\t\t\t\t\t\tpromises = null;\r\n\t\t\t\t\t}\r\n\t\t\t\t\tenv.beginTxn(1); // abortable\r\n\t\t\t\t\tlet parentTxn = writeTxn;\r\n\t\t\t\t\tenv.writeTxn = writeTxn = { write: true };\r\n\t\t\t\t\ttry {\r\n\t\t\t\t\t\tlet result = userTxnCallback.callback();\r\n\t\t\t\t\t\tif (result && result.then) {\r\n\t\t\t\t\t\t\tawait result;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tif (result === ABORT)\r\n\t\t\t\t\t\t\tenv.abortTxn();\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\tenv.commitTxn();\r\n\t\t\t\t\t\tclearWriteTxn(parentTxn);\r\n\t\t\t\t\t\ttxnCallbacks[i] = result;\r\n\t\t\t\t\t} catch(error) {\r\n\t\t\t\t\t\tclearWriteTxn(parentTxn);\r\n\t\t\t\t\t\tenv.abortTxn();\r\n\t\t\t\t\t\ttxnError(error, i);\r\n\t\t\t\t\t}\r\n\t\t\t\t} else {\r\n\t\t\t\t\ttry {\r\n\t\t\t\t\t\tlet result = userTxnCallback();\r\n\t\t\t\t\t\ttxnCallbacks[i] = result;\r\n\t\t\t\t\t\tif (result && result.then) {\r\n\t\t\t\t\t\t\tif (!promises)\r\n\t\t\t\t\t\t\t\tpromises = [];\r\n\t\t\t\t\t\t\tpromises.push(result.catch(() => {}));\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t} catch(error) {\r\n\t\t\t\t\t\ttxnError(error, i);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\t\tnextTxnCallbacks = [];\r\n\t\tif (promises) { // finish any outstanding commit functions\r\n\t\t\tawait Promise.all(promises);\r\n\t\t}\r\n\t\tclearWriteTxn(null);\r\n\t\tfunction txnError(error, i) {\r\n\t\t\t(txnCallbacks.errors || (txnCallbacks.errors = []))[i] = error;\r\n\t\t\ttxnCallbacks[i] = CALLBACK_THREW;\r\n\t\t}\r\n\t}\r\n\tfunction finishBatch() {\r\n\t\tdynamicBytes.uint32[(dynamicBytes.position + 1) << 1] = 0; // clear out the next slot\r\n\t\tlet writeStatus = atomicStatus(dynamicBytes.uint32, (dynamicBytes.position++) << 1, 2); // atomically write the end block\r\n\t\tnextResolution.flagPosition += 2;\r\n\t\tif (writeStatus & WAITING_OPERATION) {\r\n\t\t\tenv.write(0);\r\n\t\t}\r\n\t}\r\n\tfunction clearWriteTxn(parentTxn) {\r\n\t\t// TODO: We might actually want to track cursors in a write txn and manually\r\n\t\t// close them.\r\n\t\tif (writeTxn.cursorCount > 0)\r\n\t\t\twriteTxn.isDone = true;\r\n\t\tenv.writeTxn = writeTxn = parentTxn || null;\r\n\t}\r\n\tObject.assign(LMDBStore.prototype, {\r\n\t\tput(key, value, versionOrOptions, ifVersion) {\r\n\t\t\tlet callback, flags = 15, type = typeof versionOrOptions;\r\n\t\t\tif (type == 'object') {\r\n\t\t\t\tif (versionOrOptions.noOverwrite)\r\n\t\t\t\t\tflags |= 0x10;\r\n\t\t\t\tif (versionOrOptions.noDupData)\r\n\t\t\t\t\tflags |= 0x20;\r\n\t\t\t\tif (versionOrOptions.append)\r\n\t\t\t\t\tflags |= 0x20000;\r\n\t\t\t\tif (versionOrOptions.ifVersion != undefined)\r\n\t\t\t\t\tifVersion = versionsOrOptions.ifVersion;\r\n\t\t\t\tversionOrOptions = versionOrOptions.version;\r\n\t\t\t\tif (typeof ifVersion == 'function')\r\n\t\t\t\t\tcallback = ifVersion;\r\n\t\t\t} else if (type == 'function') {\r\n\t\t\t\tcallback = versionOrOptions;\r\n\t\t\t}\r\n\t\t\treturn writeInstructions(flags, this, key, value, this.useVersions ? versionOrOptions || 0 : undefined, ifVersion)(callback);\r\n\t\t},\r\n\t\tremove(key, ifVersionOrValue, callback) {\r\n\t\t\tlet flags = 13;\r\n\t\t\tlet ifVersion, value;\r\n\t\t\tif (ifVersionOrValue !== undefined) {\r\n\t\t\t\tif (typeof ifVersionOrValue == 'function')\r\n\t\t\t\t\tcallback = ifVersionOrValue;\r\n\t\t\t\telse if (ifVersionOrValue === IF_EXISTS && !callback)\r\n\t\t\t\t\t// we have a handler for IF_EXISTS in the callback handler for remove\r\n\t\t\t\t\tcallback = ifVersionOrValue;\r\n\t\t\t\telse if (this.useVersions)\r\n\t\t\t\t\tifVersion = ifVersionOrValue;\r\n\t\t\t\telse {\r\n\t\t\t\t\tflags = 14;\r\n\t\t\t\t\tvalue = ifVersionOrValue;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\treturn writeInstructions(flags, this, key, value, undefined, ifVersion)(callback);\r\n\t\t},\r\n\t\tdel(key, options, callback) {\r\n\t\t\treturn this.remove(key, options, callback);\r\n\t\t},\r\n\t\tifNoExists(key, callback) {\r\n\t\t\treturn this.ifVersion(key, null, callback);\r\n\t\t},\r\n\r\n\t\tifVersion(key, version, callback) {\r\n\t\t\tif (!callback) {\r\n\t\t\t\treturn new Batch((operations, callback) => {\r\n\t\t\t\t\tlet promise = this.ifVersion(key, version, operations);\r\n\t\t\t\t\tif (callback)\r\n\t\t\t\t\t\tpromise.then(callback);\r\n\t\t\t\t\treturn promise;\r\n\t\t\t\t});\r\n\t\t\t}\r\n\t\t\tif (writeTxn) {\r\n\t\t\t\tif (version === undefined || this.doesExist(key, version)) {\r\n\t\t\t\t\tcallback();\r\n\t\t\t\t\treturn SYNC_PROMISE_SUCCESS;\r\n\t\t\t\t}\r\n\t\t\t\treturn SYNC_PROMISE_FAIL;\r\n\t\t\t}\r\n\t\t\tlet finishStartWrite = writeInstructions(key === undefined || version === undefined ? 1 : 4, this, key, undefined, undefined, version);\r\n\t\t\tlet promise;\r\n\t\t\tbatchDepth += 2;\r\n\t\t\tif (batchDepth > 2)\r\n\t\t\t\tpromise = finishStartWrite();\r\n\t\t\telse {\r\n\t\t\t\twriteBatchStart = () => {\r\n\t\t\t\t\tpromise = finishStartWrite();\r\n\t\t\t\t};\r\n\t\t\t\toutstandingBatchCount = 0;\r\n\t\t\t}\r\n\t\t\ttry {\r\n\t\t\t\tif (typeof callback === 'function') {\r\n\t\t\t\t\tcallback();\r\n\t\t\t\t} else {\r\n\t\t\t\t\tfor (let i = 0, l = callback.length; i < l; i++) {\r\n\t\t\t\t\t\tlet operation = callback[i];\r\n\t\t\t\t\t\tthis[operation.type](operation.key, operation.value);\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t} finally {\r\n\t\t\t\tif (!promise) {\r\n\t\t\t\t\tfinishBatch();\r\n\t\t\t\t\tbatchDepth -= 2;\r\n\t\t\t\t\tpromise = finishStartWrite(); // finish write once all the operations have been written (and it hasn't been written prematurely)\r\n\t\t\t\t\twriteBatchStart = null;\r\n\t\t\t\t} else {\r\n\t\t\t\t\tbatchDepth -= 2;\r\n\t\t\t\t\tfinishBatch();\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\treturn promise;\r\n\t\t},\r\n\t\tbatch(callbackOrOperations) {\r\n\t\t\treturn this.ifVersion(undefined, undefined, callbackOrOperations);\r\n\t\t},\r\n\t\tdrop(callback) {\r\n\t\t\treturn writeInstructions(1024 + 12, this, undefined, undefined, undefined, undefined)(callback);\r\n\t\t},\r\n\t\tclearAsync(callback) {\r\n\t\t\tif (this.encoder) {\r\n\t\t\t\tif (this.encoder.clearSharedData)\r\n\t\t\t\t\tthis.encoder.clearSharedData()\r\n\t\t\t\telse if (this.encoder.structures)\r\n\t\t\t\t\tthis.encoder.structures = []\r\n\t\t\t}\r\n\t\t\treturn writeInstructions(12, this, undefined, undefined, undefined, undefined)(callback);\r\n\t\t},\r\n\t\t_triggerError() {\r\n\t\t\tfinishBatch();\r\n\t\t},\r\n\r\n\t\tputSync(key, value, versionOrOptions, ifVersion) {\r\n\t\t\tif (writeTxn)\r\n\t\t\t\treturn this.put(key, value, versionOrOptions, ifVersion);\r\n\t\t\telse\r\n\t\t\t\treturn this.transactionSync(() =>\r\n\t\t\t\t\tthis.put(key, value, versionOrOptions, ifVersion) == SYNC_PROMISE_SUCCESS, 2);\r\n\t\t},\r\n\t\tremoveSync(key, ifVersionOrValue) {\r\n\t\t\tif (writeTxn)\r\n\t\t\t\treturn this.remove(key, ifVersionOrValue);\r\n\t\t\telse\r\n\t\t\t\treturn this.transactionSync(() =>\r\n\t\t\t\t\tthis.remove(key, ifVersionOrValue) == SYNC_PROMISE_SUCCESS, 2);\r\n\t\t},\r\n\t\ttransaction(callback) {\r\n\t\t\tif (writeTxn) {\r\n\t\t\t\t// already nested in a transaction, just execute and return\r\n\t\t\t\treturn callback();\r\n\t\t\t}\r\n\t\t\treturn this.transactionAsync(callback);\r\n\t\t},\r\n\t\tchildTransaction(callback) {\r\n\t\t\tif (useWritemap)\r\n\t\t\t\tthrow new Error('Child transactions are not supported in writemap mode');\r\n\t\t\tif (writeTxn) {\r\n\t\t\t\tlet parentTxn = writeTxn;\r\n\t\t\t\tenv.writeTxn = writeTxn = { write: true };\r\n\t\t\t\tenv.beginTxn(1); // abortable\r\n\t\t\t\ttry {\r\n\t\t\t\t\treturn when(callback(), (result) => {\r\n\t\t\t\t\t\tif (result === ABORT)\r\n\t\t\t\t\t\t\tenv.abortTxn();\r\n\t\t\t\t\t\telse\r\n\t\t\t\t\t\t\tenv.commitTxn();\r\n\t\t\t\t\t\tclearWriteTxn(parentTxn);\r\n\t\t\t\t\t\treturn result;\r\n\t\t\t\t\t}, (error) => {\r\n\t\t\t\t\t\tenv.abortTxn();\r\n\t\t\t\t\t\tclearWriteTxn(parentTxn);\r\n\t\t\t\t\t\tthrow error;\r\n\t\t\t\t\t});\r\n\t\t\t\t} catch(error) {\r\n\t\t\t\t\tenv.abortTxn();\r\n\t\t\t\t\tclearWriteTxn(parentTxn);\r\n\t\t\t\t\tthrow error;\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\treturn this.transactionAsync(callback, true);\r\n\t\t},\r\n\t\ttransactionAsync(callback, asChild) {\r\n\t\t\tlet txnIndex;\r\n\t\t\tlet txnCallbacks;\r\n\t\t\tif (!nextResolution.callbacks) {\r\n\t\t\t\ttxnCallbacks = [asChild ? { callback, asChild } : callback];\r\n\t\t\t\tnextResolution.callbacks = txnCallbacks;\r\n\t\t\t\ttxnCallbacks.results = writeInstructions(8 | (this.strictAsyncOrder ? 0x100000 : 0), this)();\r\n\t\t\t\ttxnIndex = 0;\r\n\t\t\t} else {\r\n\t\t\t\ttxnCallbacks = lastQueuedResolution.callbacks;\r\n\t\t\t\ttxnIndex = txnCallbacks.push(asChild ? { callback, asChild } : callback) - 1;\r\n\t\t\t}\r\n\t\t\treturn txnCallbacks.results.then((results) => {\r\n\t\t\t\tlet result = txnCallbacks[txnIndex];\r\n\t\t\t\tif (result === CALLBACK_THREW)\r\n\t\t\t\t\tthrow txnCallbacks.errors[txnIndex];\r\n\t\t\t\treturn result;\r\n\t\t\t});\r\n\t\t},\r\n\t\ttransactionSync(callback, flags) {\r\n\t\t\tif (writeTxn) {\r\n\t\t\t\tif (!useWritemap && !this.isCaching) // can't use child transactions in write maps or caching stores\r\n\t\t\t\t\t// already nested in a transaction, execute as child transaction (if possible) and return\r\n\t\t\t\t\treturn this.childTransaction(callback);\r\n\t\t\t\tlet result = callback(); // else just run in current transaction\r\n\t\t\t\tif (result == ABORT && !abortedNonChildTransactionWarn) {\r\n\t\t\t\t\tconsole.warn('Can not abort a transaction inside another transaction with ' + (this.cache ? 'caching enabled' : 'useWritemap enabled'));\r\n\t\t\t\t\tabortedNonChildTransactionWarn = true;\r\n\t\t\t\t}\r\n\t\t\t\treturn result;\r\n\t\t\t}\r\n\t\t\ttry {\r\n\t\t\t\tthis.transactions++;\r\n\t\t\t\tenv.beginTxn(flags == undefined ? 3 : flags);\r\n\t\t\t\twriteTxn = env.writeTxn = { write: true };\r\n\t\t\t\treturn when(callback(), (result) => {\r\n\t\t\t\t\ttry {\r\n\t\t\t\t\t\tif (result === ABORT)\r\n\t\t\t\t\t\t\tenv.abortTxn();\r\n\t\t\t\t\t\telse {\r\n\t\t\t\t\t\t\tenv.commitTxn();\r\n\t\t\t\t\t\t\tresetReadTxn();\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\treturn result;\r\n\t\t\t\t\t} finally {\r\n\t\t\t\t\t\tclearWriteTxn(null);\r\n\t\t\t\t\t}\r\n\t\t\t\t}, (error) => {\r\n\t\t\t\t\ttry { env.abortTxn(); } catch(e) {}\r\n\t\t\t\t\tclearWriteTxn(null);\r\n\t\t\t\t\tthrow error;\r\n\t\t\t\t});\r\n\t\t\t} catch(error) {\r\n\t\t\t\ttry { env.abortTxn(); } catch(e) {}\r\n\t\t\t\tclearWriteTxn(null);\r\n\t\t\t\tthrow error;\r\n\t\t\t}\r\n\t\t},\r\n\t\ttransactionSyncStart(callback) {\r\n\t\t\treturn this.transactionSync(callback, 0);\r\n\t\t},\r\n\t\t// make the db a thenable/promise-like for when the last commit is committed\r\n\t\tcommitted: committed = {\r\n\t\t\tthen(onfulfilled, onrejected) {\r\n\t\t\t\tif (commitPromise)\r\n\t\t\t\t\treturn commitPromise.then(onfulfilled, onrejected);\r\n\t\t\t\tif (lastWritePromise) // always resolve to true\r\n\t\t\t\t\treturn lastWritePromise.then(() => onfulfilled(true), onrejected);\r\n\t\t\t\treturn SYNC_PROMISE_SUCCESS.then(onfulfilled, onrejected);\r\n\t\t\t}\r\n\t\t},\r\n\t\tflushed: {\r\n\t\t\t// make this a thenable for when the commit is flushed to disk\r\n\t\t\tthen(onfulfilled, onrejected) {\r\n\t\t\t\tif (flushPromise)\r\n\t\t\t\t\treturn flushPromise.then(onfulfilled, onrejected);\r\n\t\t\t\treturn committed.then(onfulfilled, onrejected);\r\n\t\t\t}\r\n\t\t},\r\n\t\t_endWrites(resolvedPromise) {\r\n\t\t\tthis.put = this.remove = this.del = this.batch = this.removeSync = this.putSync = this.transactionAsync = this.drop = this.clearAsync = () => { throw new Error('Database is closed') };\r\n\t\t\t// wait for all txns to finish, checking again after the current txn is done\r\n\t\t\tlet finalPromise = flushPromise || commitPromise || lastWritePromise;\r\n\t\t\tif (finalPromise && resolvedPromise != finalPromise) {\r\n\t\t\t\treturn finalPromise.then(() => this._endWrites(finalPromise), () => this._endWrites(finalPromise));\r\n\t\t\t}\r\n\t\t},\r\n\t\ton(event, callback) {\r\n\t\t\tif (event == 'beforecommit') {\r\n\t\t\t\teventTurnBatching = true;\r\n\t\t\t\tbeforeCommitCallbacks.push(callback);\r\n\t\t\t} else if (event == 'aftercommit')\r\n\t\t\t\tafterCommitCallbacks.push(callback);\r\n\t\t}\r\n\t});\r\n}\r\n\r\nclass Batch extends Array {\r\n\tconstructor(callback) {\r\n\t\tsuper();\r\n\t\tthis.callback = callback;\r\n\t}\r\n\tput(key, value) {\r\n\t\tthis.push({ type: 'put', key, value });\r\n\t}\r\n\tdel(key) {\r\n\t\tthis.push({ type: 'del', key });\r\n\t}\r\n\tclear() {\r\n\t\tthis.splice(0, this.length);\r\n\t}\r\n\twrite(callback) {\r\n\t\treturn this.callback(this, callback);\r\n\t}\r\n}\r\nexport function asBinary(buffer) {\r\n\treturn {\r\n\t\t['\\x10binary-data\\x02']: buffer\r\n\t};\r\n}\r\n","export function levelup(store) {\r\n\treturn Object.assign(Object.create(store), {\r\n\t\tget(key, options, callback) {\r\n\t\t\tlet result = store.get(key);\r\n\t\t\tif (typeof options == 'function')\r\n\t\t\t\tcallback = options;\r\n\t\t\tif (callback) {\r\n\t\t\t\tif (result === undefined)\r\n\t\t\t\t\tcallback(new NotFoundError());\r\n\t\t\t\telse\r\n\t\t\t\t\tcallback(null, result);\r\n\t\t\t} else {\r\n\t\t\t\tif (result === undefined)\r\n\t\t\t\t\treturn Promise.reject(new NotFoundError());\r\n\t\t\t\telse\r\n\t\t\t\t\treturn Promise.resolve(result);\r\n\t\t\t}\r\n\t\t},\r\n\t});\r\n}\r\nclass NotFoundError extends Error {\r\n\tconstructor(message) {\r\n\t\tsuper(message);\r\n\t\tthis.name = 'NotFoundError';\r\n\t\tthis.notFound = true;\r\n\t}\r\n}","import { WeakLRUCache, clearKeptObjects } from './external.js';\r\nimport { FAILED_CONDITION } from './write.js';\r\nlet getLastVersion;\r\nconst mapGet = Map.prototype.get;\r\nexport const CachingStore = Store => class extends Store {\r\n\tconstructor(dbName, options) {\r\n\t\tsuper(dbName, options);\r\n\t\tif (!this.env.cacheCommitter) {\r\n\t\t\tthis.env.cacheCommitter = true;\r\n\t\t\tthis.on('aftercommit', ({ next, last }) => {\r\n\t\t\t\tdo {\r\n\t\t\t\t\tlet store = next.store;\r\n\t\t\t\t\tif (store) {\r\n\t\t\t\t\t\tif (next.flag & FAILED_CONDITION)\r\n\t\t\t\t\t\t\tnext.store.cache.delete(next.key); // just delete it from the map\r\n\t\t\t\t\t\telse {\r\n\t\t\t\t\t\t\tlet expirationPriority = next.valueSize >> 10;\r\n\t\t\t\t\t\t\tlet cache = next.store.cache;\r\n\t\t\t\t\t\t\tlet entry = mapGet.call(cache, next.key);\r\n\t\t\t\t\t\t\tif (entry)\r\n\t\t\t\t\t\t\t\tcache.used(entry, expirationPriority + 4); // this will enter it into the LRFU (with a little lower priority than a read)\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t}\r\n\t\t\t\t} while (next != last && (next = next.next))\r\n\t\t\t});\r\n\t\t}\r\n\t\tthis.db.cachingDb = this;\r\n\t\tif (options.cache.clearKeptInterval)\r\n\t\t\toptions.cache.clearKeptObjects = clearKeptObjects;\r\n\t\tthis.cache = new WeakLRUCache(options.cache);\r\n\t}\r\n\tget isCaching() {\r\n\t\treturn true\r\n\t}\r\n\tget(id, cacheMode) {\r\n\t\tlet value = this.cache.getValue(id);\r\n\t\tif (value !== undefined)\r\n\t\t\treturn value;\r\n\t\tvalue = super.get(id);\r\n\t\tif (value && typeof value === 'object' && !cacheMode && typeof id !== 'object') {\r\n\t\t\tlet entry = this.cache.setValue(id, value, this.lastSize >> 10);\r\n\t\t\tif (this.useVersions) {\r\n\t\t\t\tentry.version = getLastVersion();\r\n\t\t\t}\r\n\t\t}\r\n\t\treturn value;\r\n\t}\r\n\tgetEntry(id, cacheMode) {\r\n\t\tlet entry = this.cache.get(id);\r\n\t\tif (entry)\r\n\t\t\treturn entry;\r\n\t\tlet value = super.get(id);\r\n\t\tif (value === undefined)\r\n\t\t\treturn;\r\n\t\tif (value && typeof value === 'object' && !cacheMode && typeof id !== 'object') {\r\n\t\t\tentry = this.cache.setValue(id, value, this.lastSize >> 10);\r\n\t\t} else {\r\n\t\t\tentry = { value };\r\n\t\t}\r\n\t\tif (this.useVersions) {\r\n\t\t\tentry.version = getLastVersion();\r\n\t\t}\r\n\t\treturn entry;\r\n\t}\r\n\tputEntry(id, entry, ifVersion) {\r\n\t\tlet result = super.put(id, entry.value, entry.version, ifVersion);\r\n\t\tif (typeof id === 'object')\r\n\t\t\treturn result;\r\n\t\tif (result && result.then)\r\n\t\t\tthis.cache.setManually(id, entry); // set manually so we can keep it pinned in memory until it is committed\r\n\t\telse // sync operation, immediately add to cache\r\n\t\t\tthis.cache.set(id, entry);\r\n\t}\r\n\tput(id, value, version, ifVersion) {\r\n\t\tlet result = super.put(id, value, version, ifVersion);\r\n\t\tif (typeof id !== 'object') {\r\n\t\t\tif (value && value['\\x10binary-data\\x02']) {\r\n\t\t\t\t// don't cache binary data, since it will be decoded on get\r\n\t\t\t\tthis.cache.delete(id);\r\n\t\t\t\treturn result;\r\n\t\t\t}\t\r\n\t\t\t// sync operation, immediately add to cache, otherwise keep it pinned in memory until it is committed\r\n\t\t\tlet entry = this.cache.setValue(id, value, !result || result.isSync ? 0 : -1);\r\n\t\t\tif (version !== undefined)\r\n\t\t\t\tentry.version = typeof version === 'object' ? version.version : version;\r\n\t\t}\r\n\t\treturn result;\r\n\t}\r\n\tputSync(id, value, version, ifVersion) {\r\n\t\tif (id !== 'object') {\r\n\t\t\t// sync operation, immediately add to cache, otherwise keep it pinned in memory until it is committed\r\n\t\t\tif (value && typeof value === 'object') {\r\n\t\t\t\tlet entry = this.cache.setValue(id, value);\r\n\t\t\t\tif (version !== undefined) {\r\n\t\t\t\t\tentry.version = typeof version === 'object' ? version.version : version;\r\n\t\t\t\t}\r\n\t\t\t} else // it is possible that a value used to exist here\r\n\t\t\t\tthis.cache.delete(id);\r\n\t\t}\r\n\t\treturn super.putSync(id, value, version, ifVersion);\r\n\t}\r\n\tremove(id, ifVersion) {\r\n\t\tthis.cache.delete(id);\r\n\t\treturn super.remove(id, ifVersion);\r\n\t}\r\n\tremoveSync(id, ifVersion) {\r\n\t\tthis.cache.delete(id);\r\n\t\treturn super.removeSync(id, ifVersion);\r\n\t}\r\n\tclearAsync(callback) {\r\n\t\tthis.cache.clear();\r\n\t\treturn super.clearAsync(callback);\r\n\t}\r\n\tclearSync() {\r\n\t\tthis.cache.clear();\r\n\t\tsuper.clearSync();\r\n\t}\r\n\tchildTransaction(execute) {\r\n\t\tthrow new Error('Child transactions are not supported in caching stores');\r\n\t}\r\n};\r\nexport function setGetLastVersion(get) {\r\n\tgetLastVersion = get;\r\n}\r\n","const SKIP = {};\r\nif (!Symbol.asyncIterator) {\r\n\tSymbol.asyncIterator = Symbol.for('Symbol.asyncIterator');\r\n}\r\n\r\nexport class RangeIterable {\r\n\tconstructor(sourceArray) {\r\n\t\tif (sourceArray) {\r\n\t\t\tthis.iterate = sourceArray[Symbol.iterator].bind(sourceArray);\r\n\t\t}\r\n\t}\r\n\tmap(func) {\r\n\t\tlet source = this;\r\n\t\tlet result = new RangeIterable();\r\n\t\tresult.iterate = (async) => {\r\n\t\t\tlet iterator = source[Symbol.iterator](async);\r\n\t\t\treturn {\r\n\t\t\t\tnext(resolvedResult) {\r\n\t\t\t\t\tlet result;\r\n\t\t\t\t\tdo {\r\n\t\t\t\t\t\tlet iteratorResult;\r\n\t\t\t\t\t\tif (resolvedResult) {\r\n\t\t\t\t\t\t\titeratorResult = resolvedResult;\r\n\t\t\t\t\t\t\tresolvedResult = null; // don't go in this branch on next iteration\r\n\t\t\t\t\t\t} else {\r\n\t\t\t\t\t\t\titeratorResult = iterator.next();\r\n\t\t\t\t\t\t\tif (iteratorResult.then) {\r\n\t\t\t\t\t\t\t\treturn iteratorResult.then(iteratorResult => this.next(iteratorResult));\r\n\t\t\t\t\t\t\t}\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tif (iteratorResult.done === true) {\r\n\t\t\t\t\t\t\tthis.done = true;\r\n\t\t\t\t\t\t\treturn iteratorResult;\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t\tresult = func(iteratorResult.value);\r\n\t\t\t\t\t\tif (result && result.then) {\r\n\t\t\t\t\t\t\treturn result.then(result =>\r\n\t\t\t\t\t\t\t\tresult == SKIP ?\r\n\t\t\t\t\t\t\t\t\tthis.next() :\r\n\t\t\t\t\t\t\t\t\t{\r\n\t\t\t\t\t\t\t\t\t\tvalue: result\r\n\t\t\t\t\t\t\t\t\t});\r\n\t\t\t\t\t\t}\r\n\t\t\t\t\t} while(result == SKIP)\r\n\t\t\t\t\treturn {\r\n\t\t\t\t\t\tvalue: result\r\n\t\t\t\t\t};\r\n\t\t\t\t},\r\n\t\t\t\treturn() {\r\n\t\t\t\t\treturn iterator.return();\r\n\t\t\t\t},\r\n\t\t\t\tthrow() {\r\n\t\t\t\t\treturn iterator.throw();\r\n\t\t\t\t}\r\n\t\t\t};\r\n\t\t};\r\n\t\treturn result;\r\n\t}\r\n\t[Symbol.asyncIterator]() {\r\n\t\treturn this.iterator = this.iterate();\r\n\t}\r\n\t[Symbol.iterator]() {\r\n\t\treturn this.iterator = this.iterate();\r\n\t}\r\n\tfilter(func) {\r\n\t\treturn this.map(element => func(element) ? element : SKIP);\r\n\t}\r\n\r\n\tforEach(callback) {\r\n\t\tlet iterator = this.iterator = this.iterate();\r\n\t\tlet result;\r\n\t\twhile ((result = iterator.next()).done !== true) {\r\n\t\t\tcallback(result.value);\r\n\t\t}\r\n\t}\r\n\tconcat(secondIterable) {\r\n\t\tlet concatIterable = new RangeIterable();\r\n\t\tconcatIterable.iterate = (async) => {\r\n\t\t\tlet iterator = this.iterator = this.iterate();\r\n\t\t\tlet isFirst = true;\r\n\t\t\tlet concatIterator = {\r\n\t\t\t\tnext() {\r\n\t\t\t\t\tlet result = iterator.next();\r\n\t\t\t\t\tif (isFirst && result.done) {\r\n\t\t\t\t\t\tisFirst = false;\r\n\t\t\t\t\t\titerator = secondIterable[Symbol.iterator](async);\r\n\t\t\t\t\t\treturn iterator.next();\r\n\t\t\t\t\t}\r\n\t\t\t\t\treturn result;\r\n\t\t\t\t},\r\n\t\t\t\treturn() {\r\n\t\t\t\t\treturn iterator.return();\r\n\t\t\t\t},\r\n\t\t\t\tthrow() {\r\n\t\t\t\t\treturn iterator.throw();\r\n\t\t\t\t}\r\n\t\t\t};\r\n\t\t\treturn concatIterator;\r\n\t\t};\r\n\t\treturn concatIterable;\r\n\t}\r\n\tnext() {\r\n\t\tif (!this.iterator)\r\n\t\t\tthis.iterator = this.iterate();\r\n\t\treturn this.iterator.next();\r\n\t}\r\n\ttoJSON() {\r\n\t\tif (this.asArray && this.asArray.forEach) {\r\n\t\t\treturn this.asArray;\r\n\t\t}\r\n\t\tthrow new Error('Can not serialize async iteratables without first calling resolveJSON');\r\n\t\t//return Array.from(this)\r\n\t}\r\n\tget asArray() {\r\n\t\tif (this._asArray)\r\n\t\t\treturn this._asArray;\r\n\t\tlet promise = new Promise((resolve, reject) => {\r\n\t\t\tlet iterator = this.iterate();\r\n\t\t\tlet array = [];\r\n\t\t\tlet iterable = this;\r\n\t\t\tfunction next(result) {\r\n\t\t\t\twhile (result.done !== true) {\r\n\t\t\t\t\tif (result.then) {\r\n\t\t\t\t\t\treturn result.then(next);\r\n\t\t\t\t\t} else {\r\n\t\t\t\t\t\tarray.push(result.value);\r\n\t\t\t\t\t}\r\n\t\t\t\t\tresult = iterator.next();\r\n\t\t\t\t}\r\n\t\t\t\tarray.iterable = iterable;\r\n\t\t\t\tresolve(iterable._asArray = array);\r\n\t\t\t}\r\n\t\t\tnext(iterator.next());\r\n\t\t});\r\n\t\tpromise.iterable = this;\r\n\t\treturn this._asArray || (this._asArray = promise);\r\n\t}\r\n\tresolveData() {\r\n\t\treturn this.asArray;\r\n\t}\r\n}\r\n","import { getAddress, orderedBinary } from './external.js';\r\n\r\nconst writeUint32Key = (key, target, start) => {\r\n\t(target.dataView || (target.dataView = new DataView(target.buffer, 0, target.length))).setUint32(start, key, true);\r\n\treturn start + 4;\r\n};\r\nconst readUint32Key = (target, start) => {\r\n\treturn (target.dataView || (target.dataView = new DataView(target.buffer, 0, target.length))).getUint32(start, true);\r\n};\r\nconst writeBufferKey = (key, target, start) => {\r\n\ttarget.set(key, start);\r\n\treturn key.length + start;\r\n};\r\nconst Uint8ArraySlice = Uint8Array.prototype.slice;\r\nconst readBufferKey = (target, start, end) => {\r\n\treturn Uint8ArraySlice.call(target, start, end);\r\n};\r\n\r\nexport function applyKeyHandling(store) {\r\n \tif (store.encoding == 'ordered-binary') {\r\n\t\tstore.encoder = store.decoder = {\r\n\t\t\twriteKey: orderedBinary.writeKey,\r\n\t\t\treadKey: orderedBinary.readKey,\r\n\t\t};\r\n\t}\r\n\tif (store.encoder && store.encoder.writeKey && !store.encoder.encode) {\r\n\t\tstore.encoder.encode = function(value) {\r\n\t\t\treturn saveKey(value, this.writeKey, false, store.maxKeySize);\r\n\t\t};\r\n\t}\r\n\tif (store.decoder && store.decoder.readKey && !store.decoder.decode)\r\n\t\tstore.decoder.decode = function(buffer) { return this.readKey(buffer, 0, buffer.length); };\r\n\tif (store.keyIsUint32 || store.keyEncoding == 'uint32') {\r\n\t\tstore.writeKey = writeUint32Key;\r\n\t\tstore.readKey = readUint32Key;\r\n\t} else if (store.keyIsBuffer || store.keyEncoding == 'binary') {\r\n\t\tstore.writeKey = writeBufferKey;\r\n\t\tstore.readKey = readBufferKey;\r\n\t} else if (store.keyEncoder) {\r\n\t\tstore.writeKey = store.keyEncoder.writeKey;\r\n\t\tstore.readKey = store.keyEncoder.readKey;\r\n\t} else {\r\n\t\tstore.writeKey = orderedBinary.writeKey;\r\n\t\tstore.readKey = orderedBinary.readKey;\r\n\t}\r\n}\r\n\r\nlet saveBuffer, saveDataView = { setFloat64() {}, setUint32() {} }, saveDataAddress;\r\nlet savePosition = 8000;\r\nlet DYNAMIC_KEY_BUFFER_SIZE = 8192;\r\nfunction allocateSaveBuffer() {\r\n\tsaveBuffer = typeof Buffer != 'undefined' ? Buffer.alloc(DYNAMIC_KEY_BUFFER_SIZE) : new Uint8Array(DYNAMIC_KEY_BUFFER_SIZE);\r\n\tsaveBuffer.buffer.address = getAddress(saveBuffer);\r\n\tsaveDataAddress = saveBuffer.buffer.address;\r\n\t// TODO: Conditionally only do this for key sequences?\r\n\tsaveDataView.setUint32(savePosition, 0xffffffff);\r\n\tsaveDataView.setFloat64(savePosition + 4, saveDataAddress, true); // save a pointer from the old buffer to the new address for the sake of the prefetch sequences\r\n\tsaveBuffer.dataView = saveDataView = new DataView(saveBuffer.buffer, saveBuffer.byteOffset, saveBuffer.byteLength);\r\n\tsavePosition = 0;\r\n}\r\nexport function saveKey(key, writeKey, saveTo, maxKeySize) {\r\n\tif (savePosition > 7800) {\r\n\t\tallocateSaveBuffer();\r\n\t}\r\n\tlet start = savePosition;\r\n\ttry {\r\n\t\tsavePosition = key === undefined ? start + 4 :\r\n\t\t\twriteKey(key, saveBuffer, start + 4);\r\n\t} catch (error) {\r\n\t\tsaveBuffer.fill(0, start + 4); // restore zeros\r\n\t\tif (error.name == 'RangeError') {\r\n\t\t\tif (8180 - start < maxKeySize) {\r\n\t\t\t\tallocateSaveBuffer(); // try again:\r\n\t\t\t\treturn saveKey(key, writeKey, saveTo, maxKeySize);\r\n\t\t\t}\r\n\t\t\tthrow new Error('Key was too large, max key size is ' + maxKeySize);\r\n\t\t} else\r\n\t\t\tthrow error;\r\n\t}\r\n\tlet length = savePosition - start - 4;\r\n\tif (length > maxKeySize) {\r\n\t\tthrow new Error('Key of size ' + length + ' was too large, max key size is ' + maxKeySize);\r\n\t}\r\n\tif (savePosition >= 8160) { // need to reserve enough room at the end for pointers\r\n\t\tsavePosition = start // reset position\r\n\t\tallocateSaveBuffer(); // try again:\r\n\t\treturn saveKey(key, writeKey, saveTo, maxKeySize);\r\n\t}\r\n\tif (saveTo) {\r\n\t\tsaveDataView.setUint32(start, length, true); // save the length\r\n\t\tsaveTo.saveBuffer = saveBuffer;\r\n\t\tsavePosition = (savePosition + 12) & 0xfffffc;\r\n\t\treturn start + saveDataAddress;\r\n\t} else {\r\n\t\tsaveBuffer.start = start + 4;\r\n\t\tsaveBuffer.end = savePosition;\r\n\t\tsavePosition = (savePosition + 7) & 0xfffff8; // full 64-bit word alignment since these are usually copied\r\n\t\treturn saveBuffer;\r\n\t}\r\n}\r\n","import { RangeIterable } from './util/RangeIterable.js';\r\nimport { getAddress, Cursor, setGlobalBuffer, orderedBinary, lmdbxError } from './external.js';\r\nimport { saveKey } from './keys.js';\r\nconst ITERATOR_DONE = { done: true, value: undefined };\r\nconst Uint8ArraySlice = Uint8Array.prototype.slice;\r\nlet getValueBytes = makeReusableBuffer(0);\r\nconst START_ADDRESS_POSITION = 4064;\r\n\r\nexport function addReadMethods(LMDBStore, {\r\n\tmaxKeySize, env, keyBytes, keyBytesView, getLastVersion\r\n}) {\r\n\tlet readTxn, readTxnRenewed, returnNullWhenBig = false;\r\n\tlet renewId = 1;\r\n\tObject.assign(LMDBStore.prototype, {\r\n\t\tgetString(id) {\r\n\t\t\t(env.writeTxn || (readTxnRenewed ? readTxn : renewReadTxn()));\r\n\t\t\tlet string = this.db.getStringByBinary(this.writeKey(id, keyBytes, 0));\r\n\t\t\tif (