'use strict';
const assert = require('bsert');
const Path = require('path');
const fs = require('bfile');
const sha3 = require('bcrypto/lib/sha3');
const FILE = Path.resolve(__dirname, 'lockup.db');
const DATA = fs.readFileSync(FILE);
/**
* Locked up
*/
class LockedUp {
constructor(data) {
this.data = data;
this.size = readU32(data, 0);
}
get prefixSize() {
return 4;
}
_compare(b, off) {
const a = this.data;
for (let i = 0; i < 32; i++) {
const x = a[off + i];
const y = b[i];
if (x < y)
return -1;
if (x > y)
return 1;
}
return 0;
}
_find(key) {
let start = 0;
let end = this.size - 1;
while (start <= end) {
const index = (start + end) >>> 1;
const pos = this.prefixSize + index * 36;
const cmp = this._compare(key, pos);
if (cmp === 0)
return readU32(this.data, pos + 32);
if (cmp < 0)
start = index + 1;
else
end = index - 1;
}
return -1;
}
_target(pos) {
const len = this.data[pos];
return this.data.toString('ascii', pos + 1, pos + 1 + len);
}
_flags(pos) {
const len = this.data[pos];
return this.data[pos + 1 + len];
}
_index(pos) {
const len = this.data[pos];
return this.data[pos + 1 + len + 1];
}
_get(hash, pos) {
const target = this._target(pos);
const flags = this._flags(pos);
const index = this._index(pos);
const root = (flags & 1) !== 0;
const custom = (flags & 2) !== 0;
const name = target.substring(0, index);
return {
name,
hash,
target,
root,
custom
};
}
has(hash) {
assert(Buffer.isBuffer(hash) && hash.length === 32);
return this._find(hash) !== -1;
}
get(hash) {
assert(Buffer.isBuffer(hash) && hash.length === 32);
const pos = this._find(hash);
if (pos === -1)
return null;
return this._get(hash, pos);
}
hasByName(name) {
assert(typeof name === 'string');
if (name.length === 0 || name.length > 63)
return false;
return this.has(hashName(name));
}
getByName(name) {
assert(typeof name === 'string');
if (name.length === 0 || name.length > 63)
return null;
return this.get(hashName(name));
}
*entries() {
for (let i = 0; i < this.size; i++) {
const pos = this.prefixSize + i * 36;
const hash = this.data.slice(pos, pos + 32);
const ptr = readU32(this.data, pos + 32);
const item = this._get(hash, ptr);
yield [hash, item];
}
}
*keys() {
for (let i = 0; i < this.size; i++) {
const pos = this.prefixSize + i * 36;
yield this.data.slice(pos, pos + 32);
}
}
*values() {
for (const [, item] of this.entries())
yield item;
}
[Symbol.iterator]() {
return this.entries();
}
}
/*
* Helpers
*/
function readU32(data, off) {
return data.readUInt32LE(off);
}
function hashName(name) {
const raw = Buffer.from(name.toLowerCase(), 'ascii');
return sha3.digest(raw);
}
exports.LockedUp = LockedUp;
exports.locked = new LockedUp(DATA);