ton3-core
Version:
TON low-level API tools
274 lines • 9.61 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Hashmap = exports.HashmapE = void 0;
const builder_1 = require("./builder");
class Hashmap {
constructor(keySize, options) {
const { serializers = { key: (key) => key, value: (value) => value }, deserializers = { key: (key) => key, value: (value) => value } } = options || {};
this.hashmap = new Map();
this.keySize = keySize;
this.serializeKey = serializers.key;
this.serializeValue = serializers.value;
this.deserializeKey = deserializers.key;
this.deserializeValue = deserializers.value;
}
*[Symbol.iterator]() {
for (const [k, v] of this.hashmap) {
const key = this.deserializeKey(k.split('').map(b => Number(b)));
const value = this.deserializeValue(v);
yield [key, value];
}
}
get(key) {
const k = this.serializeKey(key).join('');
const v = this.hashmap.get(k);
return v !== undefined
? this.deserializeValue(v)
: undefined;
}
has(key) {
return this.get(key) !== undefined;
}
set(key, value) {
const k = this.serializeKey(key).join('');
const v = this.serializeValue(value);
this.hashmap.set(k, v);
return this;
}
add(key, value) {
return !this.has(key)
? this.set(key, value)
: this;
}
replace(key, value) {
return this.has(key)
? this.set(key, value)
: this;
}
getSet(key, value) {
const prev = this.get(key);
this.set(key, value);
return prev;
}
getAdd(key, value) {
const prev = this.get(key);
this.add(key, value);
return prev;
}
getReplace(key, value) {
const prev = this.get(key);
this.replace(key, value);
return prev;
}
delete(key) {
const k = this.serializeKey(key).join('');
this.hashmap.delete(k);
return this;
}
isEmpty() {
return this.hashmap.size === 0;
}
forEach(callbackfn) {
return [...this].forEach(([key, value]) => callbackfn(key, value));
}
getRaw(key) {
return this.hashmap.get(key.join(''));
}
setRaw(key, value) {
this.hashmap.set(key.join(''), value);
return this;
}
sortHashmap() {
const sorted = [...this.hashmap].reduce((acc, [bitstring, value]) => {
const key = bitstring.split('').map(b => Number(b));
const order = parseInt(bitstring, 2);
const lt = acc.findIndex(el => order > el.order);
const index = lt > -1 ? lt : acc.length;
acc.splice(index, 0, { order, key, value });
return acc;
}, []);
return sorted.map(el => [el.key, el.value]);
}
serialize() {
const nodes = this.sortHashmap();
if (nodes.length === 0) {
throw new Error('Hashmap: can\'t be empty. It must contain at least 1 key-value pair.');
}
return Hashmap.serializeEdge(nodes);
}
static serializeEdge(nodes) {
if (!nodes.length) {
const label = this.serializeLabelShort([]);
return new builder_1.Builder()
.storeBits(label)
.cell();
}
const edge = new builder_1.Builder();
const label = this.serializeLabel(nodes);
edge.storeBits(label);
if (nodes.length === 1) {
const leaf = this.serializeLeaf(nodes[0]);
edge.storeSlice(leaf.slice());
}
if (nodes.length > 1) {
const [leftNodes, rightNodes] = this.serializeFork(nodes);
const leftEdge = this.serializeEdge(leftNodes);
edge.storeRef(leftEdge);
if (rightNodes.length) {
const rightEdge = this.serializeEdge(rightNodes);
edge.storeRef(rightEdge);
}
}
return edge.cell();
}
static serializeFork(nodes) {
return nodes.reduce((acc, [key, value]) => {
acc[key.shift()].push([key, value]);
return acc;
}, [[], []]);
}
static serializeLeaf(node) {
return node[1];
}
static serializeLabel(nodes) {
const [first] = nodes[0];
const [last] = nodes[nodes.length - 1];
const m = first.length;
const sameBitsIndex = first.findIndex((bit, i) => bit !== last[i]);
const sameBitsLength = sameBitsIndex === -1 ? first.length : sameBitsIndex;
if (first[0] !== last[0] || m === 0) {
return this.serializeLabelShort([]);
}
const label = first.slice(0, sameBitsLength);
const repeated = label.join('').match(/(^0+)|(^1+)/)[0].split('').map(b => Number(b));
const labelShort = this.serializeLabelShort(label);
const labelLong = this.serializeLabelLong(label, m);
const labelSame = nodes.length > 1 && repeated.length > 1
? this.serializeLabelSame(repeated, m)
: null;
const labels = [
{ bits: label.length, label: labelShort },
{ bits: label.length, label: labelLong },
{ bits: repeated.length, label: labelSame }
].filter(el => el.label !== null);
labels.sort((a, b) => a.label.length - b.label.length);
const choosen = labels[0];
nodes.forEach(([key]) => key.splice(0, choosen.bits));
return choosen.label;
}
static serializeLabelShort(bits) {
const label = new builder_1.Builder();
label.storeBit(0)
.storeBits(bits.map(() => 1))
.storeBit(0)
.storeBits(bits);
return label.bits;
}
static serializeLabelLong(bits, m) {
const label = new builder_1.Builder();
label.storeBits([1, 0])
.storeUint(bits.length, Math.ceil(Math.log2(m + 1)))
.storeBits(bits);
return label.bits;
}
static serializeLabelSame(bits, m) {
const label = new builder_1.Builder();
label.storeBits([1, 1])
.storeBit(bits[0])
.storeUint(bits.length, Math.ceil(Math.log2(m + 1)));
return label.bits;
}
static deserialize(keySize, slice, options) {
if (slice.bits.length < 2) {
throw new Error('Hashmap: can\'t be empty. It must contain at least 1 key-value pair.');
}
const hashmap = new Hashmap(keySize, options);
const nodes = Hashmap.deserializeEdge(slice, keySize);
for (let i = 0; i < nodes.length; i += 1) {
const [key, value] = nodes[i];
hashmap.setRaw(key, value);
}
return hashmap;
}
static deserializeEdge(edge, keySize, key = []) {
const nodes = [];
key.push(...this.deserializeLabel(edge, keySize - key.length));
if (key.length === keySize) {
const value = new builder_1.Builder().storeSlice(edge).cell();
return nodes.concat([[key, value]]);
}
return edge.refs.reduce((acc, _r, i) => {
const forkEdge = edge.loadRef().slice();
const forkKey = key.concat([i]);
return acc.concat(this.deserializeEdge(forkEdge, keySize, forkKey));
}, []);
}
static deserializeLabel(edge, m) {
if (edge.loadBit() === 0) {
return this.deserializeLabelShort(edge);
}
if (edge.loadBit() === 0) {
return this.deserializeLabelLong(edge, m);
}
return this.deserializeLabelSame(edge, m);
}
static deserializeLabelShort(edge) {
const length = edge.bits.findIndex(b => b === 0);
return edge.skip(length + 1) && edge.loadBits(length);
}
static deserializeLabelLong(edge, m) {
const length = edge.loadUint(Math.ceil(Math.log2(m + 1)));
return edge.loadBits(length);
}
static deserializeLabelSame(edge, m) {
const repeated = edge.loadBit();
const length = edge.loadUint(Math.ceil(Math.log2(m + 1)));
return [...Array(length)].map(() => repeated);
}
cell() {
return this.serialize();
}
static parse(keySize, slice, options) {
return this.deserialize(keySize, slice, options);
}
}
exports.Hashmap = Hashmap;
class HashmapE extends Hashmap {
constructor(keySize, options) {
super(keySize, options);
}
serialize() {
const nodes = this.sortHashmap();
const result = new builder_1.Builder();
if (!nodes.length) {
return result
.storeBit(0)
.cell();
}
return result
.storeBit(1)
.storeRef(HashmapE.serializeEdge(nodes))
.cell();
}
static deserialize(keySize, slice, options) {
if (slice.bits.length !== 1) {
throw new Error('HashmapE: bad hashmap size flag.');
}
if (slice.loadBit() === 0) {
return new HashmapE(keySize, options);
}
const hashmap = new HashmapE(keySize, options);
const edge = slice.loadRef().slice();
const nodes = Hashmap.deserializeEdge(edge, keySize);
for (let i = 0; i < nodes.length; i += 1) {
const [key, value] = nodes[i];
hashmap.setRaw(key, value);
}
return hashmap;
}
static parse(keySize, slice, options) {
return this.deserialize(keySize, slice, options);
}
}
exports.HashmapE = HashmapE;
//# sourceMappingURL=hashmap.js.map