Manage FlatDb index explosion (branch on nibbles) (#210)
This commit is contained in:
@@ -29,6 +29,7 @@
|
||||
"homepage": "https://github.com/polkadot-js/common/tree/master/packages/db#readme",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.0.0-rc.1",
|
||||
"@polkadot/trie-hash": "^0.29.12",
|
||||
"@polkadot/util": "^0.29.12",
|
||||
"@types/mkdirp": "^0.5.2",
|
||||
"@types/snappy": "^6.0.0",
|
||||
|
||||
@@ -8,6 +8,7 @@ import fs from 'fs';
|
||||
import mkdirp from 'mkdirp';
|
||||
import path from 'path';
|
||||
import snappy from 'snappy';
|
||||
import asNibbles from '@polkadot/trie-hash/util/asNibbles';
|
||||
import assert from '@polkadot/util/assert';
|
||||
import logger from '@polkadot/util/logger';
|
||||
import isUndefined from '@polkadot/util/is/undefined';
|
||||
@@ -26,8 +27,13 @@ type Entry = {
|
||||
entryAt: number
|
||||
};
|
||||
|
||||
type NibbleBuffer = {
|
||||
buffer: Buffer,
|
||||
nibbles: Uint8Array
|
||||
};
|
||||
|
||||
type Key = {
|
||||
key: Buffer,
|
||||
key: NibbleBuffer,
|
||||
keyAt: number,
|
||||
keyValue: Buffer
|
||||
};
|
||||
@@ -48,7 +54,7 @@ type Value = Key & {
|
||||
const UINT_SIZE = 5;
|
||||
const KEY_SIZE = 32;
|
||||
const KEY_TOTAL_SIZE = KEY_SIZE + UINT_SIZE + UINT_SIZE;
|
||||
const ENTRY_NUM = 256;
|
||||
const ENTRY_NUM = 16; // nibbles, 256 for bytes (where serialize would be noop)
|
||||
const ENTRY_SIZE = 1 + UINT_SIZE;
|
||||
const HEADER_SIZE = ENTRY_NUM * ENTRY_SIZE;
|
||||
const DEFAULT_FILE = 'store.db';
|
||||
@@ -161,18 +167,282 @@ export default class FileFlatDb implements BaseDb {
|
||||
: u8aToBuffer(value);
|
||||
}
|
||||
|
||||
private _serializeKey (key: Uint8Array): Buffer {
|
||||
if (key.length === KEY_SIZE) {
|
||||
return u8aToBuffer(key);
|
||||
} else if (key.length < KEY_SIZE) {
|
||||
const paddedKey = Buffer.alloc(KEY_SIZE);
|
||||
|
||||
paddedKey.set(key, 0);
|
||||
|
||||
return paddedKey;
|
||||
private _serializeKey (u8a: Uint8Array): NibbleBuffer {
|
||||
if (u8a.length > KEY_SIZE) {
|
||||
throw new Error(`${u8aToHex(u8a)} too large, expected <= 32 bytes`);
|
||||
}
|
||||
|
||||
throw new Error(`${u8aToHex(key)} too large, expected <= 32 bytes`);
|
||||
let buffer;
|
||||
|
||||
if (u8a.length === KEY_SIZE) {
|
||||
buffer = u8aToBuffer(u8a);
|
||||
} else {
|
||||
buffer = Buffer.alloc(KEY_SIZE);
|
||||
|
||||
buffer.set(u8a, 0);
|
||||
}
|
||||
|
||||
return {
|
||||
buffer,
|
||||
nibbles: asNibbles(u8a)
|
||||
};
|
||||
}
|
||||
|
||||
private _findKey (key: NibbleBuffer, doCreate: boolean, keyIndex: number, diskAt: number): Leaf | null {
|
||||
const entry = Buffer.alloc(ENTRY_SIZE);
|
||||
const entryAt = diskAt + (key.nibbles[keyIndex] * ENTRY_SIZE);
|
||||
|
||||
fs.readSync(this._fd, entry, 0, ENTRY_SIZE, entryAt);
|
||||
|
||||
l.debug(() => ['findKey', { key, doCreate, keyIndex, diskAt, entry, entryAt }]);
|
||||
|
||||
const entryType = entry[0];
|
||||
|
||||
if (entryType === Slot.BRANCH) {
|
||||
const branchAt = entry.readUIntBE(1, UINT_SIZE);
|
||||
|
||||
l.debug(() => ['findKey/isBranch', { branchAt }]);
|
||||
|
||||
return this._findKey(key, doCreate, keyIndex + 1, branchAt);
|
||||
}
|
||||
|
||||
if (entryType === Slot.EMPTY) {
|
||||
l.debug(() => ['findKey/isEmpty']);
|
||||
|
||||
return doCreate
|
||||
? this.writeNewLeaf(entry, entryAt, key)
|
||||
: null;
|
||||
}
|
||||
|
||||
if (entryType === Slot.LEAF) {
|
||||
const keyAt = entry.readUIntBE(1, UINT_SIZE);
|
||||
const keyValue = Buffer.alloc(KEY_TOTAL_SIZE);
|
||||
|
||||
fs.readSync(this._fd, keyValue, 0, KEY_TOTAL_SIZE, keyAt);
|
||||
|
||||
const prevValue = this._serializeKey(keyValue.subarray(0, KEY_SIZE));
|
||||
|
||||
l.debug(() => ['findKey/isLeaf', { keyAt, entry, keyValue }]);
|
||||
|
||||
let matchIndex = keyIndex;
|
||||
|
||||
while (matchIndex < KEY_SIZE) {
|
||||
if (prevValue.nibbles[matchIndex] !== key.nibbles[matchIndex]) {
|
||||
break;
|
||||
}
|
||||
|
||||
matchIndex++;
|
||||
}
|
||||
|
||||
if (matchIndex !== KEY_SIZE) {
|
||||
return doCreate
|
||||
? this.writeNewBranch(entry, entryAt, key, keyAt, prevValue, matchIndex, matchIndex - keyIndex - 1)
|
||||
: null;
|
||||
}
|
||||
|
||||
return {
|
||||
entry,
|
||||
entryAt,
|
||||
key,
|
||||
keyAt,
|
||||
keyValue
|
||||
};
|
||||
}
|
||||
|
||||
throw new Error(`Unhandled entry type ${entryType}`);
|
||||
}
|
||||
|
||||
private findKey (key: NibbleBuffer, doCreate: boolean): Leaf | null {
|
||||
const result = this._findKey(key, doCreate, 0, 0);
|
||||
|
||||
l.debug(() => ['findKey', { result }]);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private _extractValueInfo (keyValue: Buffer): { valueAt: number, valueLength: number } {
|
||||
return {
|
||||
valueLength: keyValue.readUIntBE(KEY_SIZE, UINT_SIZE),
|
||||
valueAt: keyValue.readUIntBE(KEY_SIZE + UINT_SIZE, UINT_SIZE)
|
||||
};
|
||||
}
|
||||
|
||||
private _readValue (key: NibbleBuffer, keyAt: number, keyValue: Buffer): Value {
|
||||
l.debug(() => ['readValue', { key, keyAt, keyValue }]);
|
||||
|
||||
const { valueAt, valueLength } = this._extractValueInfo(keyValue);
|
||||
const value = Buffer.alloc(valueLength);
|
||||
|
||||
fs.readSync(this._fd, value, 0, valueLength, valueAt);
|
||||
|
||||
return {
|
||||
key,
|
||||
keyAt,
|
||||
keyValue,
|
||||
value,
|
||||
valueAt
|
||||
};
|
||||
}
|
||||
|
||||
private readValue ({ key, keyAt, keyValue }: Key): Value {
|
||||
const result = this._readValue(key, keyAt, keyValue);
|
||||
|
||||
l.debug(() => ['readValue', { result }]);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private _writeValue (key: NibbleBuffer, keyAt: number, keyValue: Buffer, value: Buffer): Value {
|
||||
l.debug(() => ['writeValue', { keyAt, keyValue, value }]);
|
||||
|
||||
let { valueAt, valueLength } = this._extractValueInfo(keyValue);
|
||||
|
||||
if (valueLength < value.length) {
|
||||
const stats = fs.fstatSync(this._fd);
|
||||
|
||||
valueAt = stats.size;
|
||||
}
|
||||
|
||||
fs.writeSync(this._fd, value, 0, value.length, valueAt);
|
||||
|
||||
keyValue.writeUIntBE(value.length, KEY_SIZE, UINT_SIZE);
|
||||
keyValue.writeUIntBE(valueAt, KEY_SIZE + UINT_SIZE, UINT_SIZE);
|
||||
|
||||
fs.writeSync(this._fd, keyValue, KEY_SIZE, 2 * UINT_SIZE, keyAt + KEY_SIZE);
|
||||
|
||||
return {
|
||||
key,
|
||||
keyAt,
|
||||
keyValue,
|
||||
value,
|
||||
valueAt
|
||||
};
|
||||
}
|
||||
|
||||
private writeValue ({ key, keyAt, keyValue }: Key, value: Buffer): Value {
|
||||
const result = this._writeValue(key, keyAt, keyValue, value);
|
||||
|
||||
l.debug(() => ['writeValue', '=>', { result }]);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private _writeNewKey (key: NibbleBuffer): Key {
|
||||
l.debug(() => ['writeNewKey', { key }]);
|
||||
|
||||
const stats = fs.fstatSync(this._fd);
|
||||
const keyAt = stats.size;
|
||||
const keyValue = Buffer.alloc(KEY_TOTAL_SIZE);
|
||||
|
||||
keyValue.set(key.buffer, 0);
|
||||
|
||||
fs.writeSync(this._fd, keyValue, 0, KEY_TOTAL_SIZE, keyAt);
|
||||
|
||||
return {
|
||||
key,
|
||||
keyAt,
|
||||
keyValue
|
||||
};
|
||||
}
|
||||
|
||||
private writeNewKey (key: NibbleBuffer): Key {
|
||||
const result = this._writeNewKey(key);
|
||||
|
||||
l.debug(() => ['writeNewKey', '=>', { result }]);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private _writeNewBranch (entry: Buffer, entryAt: number, key: NibbleBuffer, prevAt: number, prevValue: NibbleBuffer, matchIndex: number, depth: number): Branch {
|
||||
l.debug(() => ['writeNewBranch', { entry, entryAt, key, prevValue, matchIndex, depth }]);
|
||||
|
||||
const { keyAt, keyValue } = this.writeNewKey(key);
|
||||
const branch = Buffer.alloc(HEADER_SIZE);
|
||||
const keyIndex = ENTRY_SIZE * key.nibbles[matchIndex];
|
||||
const prevIndex = ENTRY_SIZE * prevValue.nibbles[matchIndex];
|
||||
const stats = fs.fstatSync(this._fd);
|
||||
let branchAt = stats.size;
|
||||
|
||||
branch.set([Slot.LEAF], keyIndex);
|
||||
branch.writeUIntBE(keyAt, keyIndex + 1, UINT_SIZE);
|
||||
branch.set([Slot.LEAF], prevIndex);
|
||||
branch.writeUIntBE(prevAt, prevIndex + 1, UINT_SIZE);
|
||||
fs.writeSync(this._fd, branch, 0, HEADER_SIZE, branchAt);
|
||||
|
||||
let intermediateAt = branchAt;
|
||||
|
||||
for (let offset = 1; depth > 0; depth--, offset++) {
|
||||
const intermediate = Buffer.alloc(HEADER_SIZE);
|
||||
const intermediateIndex = ENTRY_SIZE * key.nibbles[matchIndex - offset];
|
||||
const stats = fs.fstatSync(this._fd);
|
||||
|
||||
intermediate.set([Slot.BRANCH], intermediateIndex);
|
||||
intermediate.writeUIntBE(intermediateAt, intermediateIndex + 1, UINT_SIZE);
|
||||
intermediateAt = stats.size;
|
||||
|
||||
fs.writeSync(this._fd, intermediate, 0, HEADER_SIZE, intermediateAt);
|
||||
}
|
||||
|
||||
entry.set([Slot.BRANCH], 0);
|
||||
entry.writeUIntBE(intermediateAt, 1, UINT_SIZE);
|
||||
fs.writeSync(this._fd, entry, 0, ENTRY_SIZE, entryAt);
|
||||
|
||||
return {
|
||||
branch,
|
||||
branchAt,
|
||||
entry: branch.slice(keyIndex, keyIndex + ENTRY_SIZE),
|
||||
entryAt: branchAt + keyIndex,
|
||||
key,
|
||||
keyAt,
|
||||
keyValue
|
||||
};
|
||||
}
|
||||
|
||||
private writeNewBranch (entry: Buffer, entryAt: number, key: NibbleBuffer, prevAt: number, prevValue: NibbleBuffer, matchIndex: number, depth: number): Leaf {
|
||||
const result = this._writeNewBranch(entry, entryAt, key, prevAt, prevValue, matchIndex, depth);
|
||||
|
||||
l.debug(() => ['writeNewBranch', '=>', { result }]);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private _writeNewLeaf (entry: Buffer, entryAt: number, key: NibbleBuffer): Leaf {
|
||||
l.debug(() => ['writeNewLeaf', { entry, entryAt, key }]);
|
||||
|
||||
const { keyAt, keyValue } = this.writeNewKey(key);
|
||||
|
||||
entry.set([Slot.LEAF], 0);
|
||||
entry.writeUIntBE(keyAt, 1, UINT_SIZE);
|
||||
|
||||
fs.writeSync(this._fd, entry, 0, ENTRY_SIZE, entryAt);
|
||||
|
||||
return {
|
||||
entry,
|
||||
entryAt,
|
||||
key,
|
||||
keyAt,
|
||||
keyValue
|
||||
};
|
||||
}
|
||||
|
||||
private writeNewLeaf (entry: Buffer, entryAt: number, key: NibbleBuffer): Leaf {
|
||||
const result = this._writeNewLeaf(entry, entryAt, key);
|
||||
|
||||
l.debug(() => ['writeNewLeaf', '=>', { result }]);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private assertOpen (): void {
|
||||
assert(this._fd !== -1, 'Expected an open database');
|
||||
}
|
||||
|
||||
private _open (file: string, startEmpty: boolean = false): number {
|
||||
if (!fs.existsSync(file) || startEmpty) {
|
||||
fs.writeFileSync(file, Buffer.alloc(HEADER_SIZE));
|
||||
}
|
||||
|
||||
return fs.openSync(file, 'a+');
|
||||
}
|
||||
|
||||
private _compactReadEntry (fd: number, at: number, index: number): Buffer {
|
||||
@@ -281,259 +551,4 @@ export default class FileFlatDb implements BaseDb {
|
||||
|
||||
return keys;
|
||||
}
|
||||
|
||||
private _findKey (key: Buffer, doCreate: boolean, keyIndex: number, diskAt: number): Leaf | null {
|
||||
const entry = Buffer.alloc(ENTRY_SIZE);
|
||||
const entryAt = diskAt + (key[keyIndex] * ENTRY_SIZE);
|
||||
|
||||
fs.readSync(this._fd, entry, 0, ENTRY_SIZE, entryAt);
|
||||
|
||||
l.debug(() => ['findKey', { key, doCreate, keyIndex, diskAt, entry, entryAt }]);
|
||||
|
||||
const entryType = entry[0];
|
||||
|
||||
if (entryType === Slot.BRANCH) {
|
||||
const branchAt = entry.readUIntBE(1, UINT_SIZE);
|
||||
|
||||
l.debug(() => ['findKey/isBranch', { branchAt }]);
|
||||
|
||||
return this._findKey(key, doCreate, keyIndex + 1, branchAt);
|
||||
}
|
||||
|
||||
if (entryType === Slot.EMPTY) {
|
||||
l.debug(() => ['findKey/isEmpty']);
|
||||
|
||||
return doCreate
|
||||
? this.writeNewLeaf(entry, entryAt, key)
|
||||
: null;
|
||||
}
|
||||
|
||||
if (entryType === Slot.LEAF) {
|
||||
const keyAt = entry.readUIntBE(1, UINT_SIZE);
|
||||
const keyValue = Buffer.alloc(KEY_TOTAL_SIZE);
|
||||
|
||||
fs.readSync(this._fd, keyValue, 0, KEY_TOTAL_SIZE, keyAt);
|
||||
|
||||
l.debug(() => ['findKey/isLeaf', { keyAt, entry, keyValue }]);
|
||||
|
||||
let matchIndex = keyIndex;
|
||||
|
||||
while (matchIndex < KEY_SIZE) {
|
||||
if (keyValue[matchIndex] !== key[matchIndex]) {
|
||||
break;
|
||||
}
|
||||
|
||||
matchIndex++;
|
||||
}
|
||||
|
||||
if (matchIndex !== KEY_SIZE) {
|
||||
return doCreate
|
||||
? this.writeNewBranch(entry, entryAt, key, keyAt, keyValue, matchIndex, matchIndex - keyIndex - 1)
|
||||
: null;
|
||||
}
|
||||
|
||||
return {
|
||||
entry,
|
||||
entryAt,
|
||||
key,
|
||||
keyAt,
|
||||
keyValue
|
||||
};
|
||||
}
|
||||
|
||||
throw new Error(`Unhandled entry type ${entryType}`);
|
||||
}
|
||||
|
||||
private findKey (key: Buffer, doCreate: boolean): Leaf | null {
|
||||
const result = this._findKey(key, doCreate, 0, 0);
|
||||
|
||||
l.debug(() => ['findKey', { result }]);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private _extractValueInfo (keyValue: Buffer): { valueAt: number, valueLength: number } {
|
||||
return {
|
||||
valueLength: keyValue.readUIntBE(KEY_SIZE, UINT_SIZE),
|
||||
valueAt: keyValue.readUIntBE(KEY_SIZE + UINT_SIZE, UINT_SIZE)
|
||||
};
|
||||
}
|
||||
|
||||
private _readValue (key: Buffer, keyAt: number, keyValue: Buffer): Value {
|
||||
l.debug(() => ['readValue', { key, keyAt, keyValue }]);
|
||||
|
||||
const { valueAt, valueLength } = this._extractValueInfo(keyValue);
|
||||
const value = Buffer.alloc(valueLength);
|
||||
|
||||
fs.readSync(this._fd, value, 0, valueLength, valueAt);
|
||||
|
||||
return {
|
||||
key,
|
||||
keyAt,
|
||||
keyValue,
|
||||
value,
|
||||
valueAt
|
||||
};
|
||||
}
|
||||
|
||||
private readValue ({ key, keyAt, keyValue }: Key): Value {
|
||||
const result = this._readValue(key, keyAt, keyValue);
|
||||
|
||||
l.debug(() => ['readValue', { result }]);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private _writeValue (key: Buffer, keyAt: number, keyValue: Buffer, value: Buffer): Value {
|
||||
l.debug(() => ['writeValue', { keyAt, keyValue, value }]);
|
||||
|
||||
let { valueAt, valueLength } = this._extractValueInfo(keyValue);
|
||||
|
||||
if (valueLength < value.length) {
|
||||
const stats = fs.fstatSync(this._fd);
|
||||
|
||||
valueAt = stats.size;
|
||||
}
|
||||
|
||||
fs.writeSync(this._fd, value, 0, value.length, valueAt);
|
||||
|
||||
keyValue.writeUIntBE(value.length, KEY_SIZE, UINT_SIZE);
|
||||
keyValue.writeUIntBE(valueAt, KEY_SIZE + UINT_SIZE, UINT_SIZE);
|
||||
|
||||
fs.writeSync(this._fd, keyValue, KEY_SIZE, 2 * UINT_SIZE, keyAt + KEY_SIZE);
|
||||
|
||||
return {
|
||||
key,
|
||||
keyAt,
|
||||
keyValue,
|
||||
value,
|
||||
valueAt
|
||||
};
|
||||
}
|
||||
|
||||
private writeValue ({ key, keyAt, keyValue }: Key, value: Buffer): Value {
|
||||
const result = this._writeValue(key, keyAt, keyValue, value);
|
||||
|
||||
l.debug(() => ['writeValue', '=>', { result }]);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private _writeNewKey (key: Buffer): Key {
|
||||
l.debug(() => ['writeNewKey', { key }]);
|
||||
|
||||
const stats = fs.fstatSync(this._fd);
|
||||
const keyAt = stats.size;
|
||||
const keyValue = Buffer.alloc(KEY_TOTAL_SIZE);
|
||||
|
||||
keyValue.set(key, 0);
|
||||
|
||||
fs.writeSync(this._fd, keyValue, 0, KEY_TOTAL_SIZE, keyAt);
|
||||
|
||||
return {
|
||||
key,
|
||||
keyAt,
|
||||
keyValue
|
||||
};
|
||||
}
|
||||
|
||||
private writeNewKey (key: Buffer): Key {
|
||||
const result = this._writeNewKey(key);
|
||||
|
||||
l.debug(() => ['writeNewKey', '=>', { result }]);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private _writeNewBranch (entry: Buffer, entryAt: number, key: Buffer, prevAt: number, prevValue: Buffer, matchIndex: number, depth: number): Branch {
|
||||
l.debug(() => ['writeNewBranch', { entry, entryAt, key, prevValue, matchIndex, depth }]);
|
||||
|
||||
const { keyAt, keyValue } = this.writeNewKey(key);
|
||||
const branch = Buffer.alloc(HEADER_SIZE);
|
||||
const keyIndex = ENTRY_SIZE * key[matchIndex];
|
||||
const prevIndex = ENTRY_SIZE * prevValue[matchIndex];
|
||||
const stats = fs.fstatSync(this._fd);
|
||||
let branchAt = stats.size;
|
||||
|
||||
branch.set([Slot.LEAF], keyIndex);
|
||||
branch.writeUIntBE(keyAt, keyIndex + 1, UINT_SIZE);
|
||||
branch.set([Slot.LEAF], prevIndex);
|
||||
branch.writeUIntBE(prevAt, prevIndex + 1, UINT_SIZE);
|
||||
fs.writeSync(this._fd, branch, 0, HEADER_SIZE, branchAt);
|
||||
|
||||
let intermediateAt = branchAt;
|
||||
|
||||
for (let offset = 1; depth > 0; depth--, offset++) {
|
||||
const intermediate = Buffer.alloc(HEADER_SIZE);
|
||||
const intermediateIndex = ENTRY_SIZE * key[matchIndex - offset];
|
||||
const stats = fs.fstatSync(this._fd);
|
||||
|
||||
intermediate.set([Slot.BRANCH], intermediateIndex);
|
||||
intermediate.writeUIntBE(intermediateAt, intermediateIndex + 1, UINT_SIZE);
|
||||
intermediateAt = stats.size;
|
||||
|
||||
fs.writeSync(this._fd, intermediate, 0, HEADER_SIZE, intermediateAt);
|
||||
}
|
||||
|
||||
entry.set([Slot.BRANCH], 0);
|
||||
entry.writeUIntBE(intermediateAt, 1, UINT_SIZE);
|
||||
fs.writeSync(this._fd, entry, 0, ENTRY_SIZE, entryAt);
|
||||
|
||||
return {
|
||||
branch,
|
||||
branchAt,
|
||||
entry: branch.slice(keyIndex, keyIndex + ENTRY_SIZE),
|
||||
entryAt: branchAt + keyIndex,
|
||||
key,
|
||||
keyAt,
|
||||
keyValue
|
||||
};
|
||||
}
|
||||
|
||||
private writeNewBranch (entry: Buffer, entryAt: number, key: Buffer, prevAt: number, prevValue: Buffer, matchIndex: number, depth: number): Leaf {
|
||||
const result = this._writeNewBranch(entry, entryAt, key, prevAt, prevValue, matchIndex, depth);
|
||||
|
||||
l.debug(() => ['writeNewBranch', '=>', { result }]);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private _writeNewLeaf (entry: Buffer, entryAt: number, key: Buffer): Leaf {
|
||||
l.debug(() => ['writeNewLeaf', { entry, entryAt, key }]);
|
||||
|
||||
const { keyAt, keyValue } = this.writeNewKey(key);
|
||||
|
||||
entry.set([Slot.LEAF], 0);
|
||||
entry.writeUIntBE(keyAt, 1, UINT_SIZE);
|
||||
|
||||
fs.writeSync(this._fd, entry, 0, ENTRY_SIZE, entryAt);
|
||||
|
||||
return {
|
||||
entry,
|
||||
entryAt,
|
||||
key,
|
||||
keyAt,
|
||||
keyValue
|
||||
};
|
||||
}
|
||||
|
||||
private writeNewLeaf (entry: Buffer, entryAt: number, key: Buffer): Leaf {
|
||||
const result = this._writeNewLeaf(entry, entryAt, key);
|
||||
|
||||
l.debug(() => ['writeNewLeaf', '=>', { result }]);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private assertOpen (): void {
|
||||
assert(this._fd !== -1, 'Expected an open database');
|
||||
}
|
||||
|
||||
private _open (file: string, startEmpty: boolean = false): number {
|
||||
if (!fs.existsSync(file) || startEmpty) {
|
||||
fs.writeFileSync(file, Buffer.alloc(HEADER_SIZE));
|
||||
}
|
||||
|
||||
return fs.openSync(file, 'a+');
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user