Skip to content

Commit 5ae4b2f

Browse files
authored
Merge pull request #27 from krigga/improve-perf
Improve performance
2 parents f4ed489 + 66d2c76 commit 5ae4b2f

File tree

4 files changed

+64
-77
lines changed

4 files changed

+64
-77
lines changed

src/boc/BitBuilder.ts

+29-41
Original file line numberDiff line numberDiff line change
@@ -88,65 +88,53 @@ export class BitBuilder {
8888
* @param bits number of bits to write
8989
*/
9090
writeUint(value: bigint | number, bits: number) {
91-
92-
// Special case for 8 bits
93-
if (bits === 8 && this._length % 8 === 0) {
94-
let v = Number(value);
95-
if (v < 0 || v > 255 || !Number.isSafeInteger(v)) {
96-
throw Error(`value is out of range for ${bits} bits. Got ${value}`);
97-
}
98-
this._buffer[this._length / 8] = Number(value);
99-
this._length += 8;
100-
return;
101-
}
102-
103-
// Special case for 16 bits
104-
if (bits === 16 && this._length % 8 === 0) {
105-
let v = Number(value);
106-
if (v < 0 || v > 65536 || !Number.isSafeInteger(v)) {
107-
throw Error(`value is out of range for ${bits} bits. Got ${value}`);
108-
}
109-
this._buffer[this._length / 8] = v >> 8;
110-
this._buffer[this._length / 8 + 1] = v & 0xff;
111-
this._length += 16;
112-
return;
113-
}
114-
115-
// Generic case
116-
let v = BigInt(value);
11791
if (bits < 0 || !Number.isSafeInteger(bits)) {
11892
throw Error(`invalid bit length. Got ${bits}`);
11993
}
12094

121-
// Corner case for zero bits
95+
const v = BigInt(value);
96+
12297
if (bits === 0) {
123-
if (value !== 0n) {
98+
if (v !== 0n) {
12499
throw Error(`value is not zero for ${bits} bits. Got ${value}`);
125100
} else {
126101
return;
127102
}
128103
}
129104

130-
// Check input
131-
let vBits = (1n << BigInt(bits));
105+
const vBits = (1n << BigInt(bits));
132106
if (v < 0 || v >= vBits) {
133107
throw Error(`bitLength is too small for a value ${value}. Got ${bits}`);
134108
}
135109

136-
// Convert number to bits
137-
let b: boolean[] = [];
138-
while (v > 0) {
139-
b.push(v % 2n === 1n);
140-
v /= 2n;
110+
if (this._length + bits > this._buffer.length * 8) {
111+
throw new Error("BitBuilder overflow");
112+
}
113+
114+
const tillByte = 8 - (this._length % 8);
115+
if (tillByte > 0) {
116+
const bidx = Math.floor(this._length / 8);
117+
if (bits < tillByte) {
118+
const wb = Number(v);
119+
this._buffer[bidx] |= wb << (tillByte - bits);
120+
this._length += bits;
121+
} else {
122+
const wb = Number(v >> BigInt(bits - tillByte));
123+
this._buffer[bidx] |= wb;
124+
this._length += tillByte;
125+
}
141126
}
127+
bits -= tillByte;
142128

143-
// Write bits
144-
for (let i = 0; i < bits; i++) {
145-
let off = bits - i - 1;
146-
if (off < b.length) {
147-
this.writeBit(b[off]);
129+
while (bits > 0) {
130+
if (bits >= 8) {
131+
this._buffer[this._length / 8] = Number((v >> BigInt(bits - 8)) & 0xffn);
132+
this._length += 8;
133+
bits -= 8;
148134
} else {
149-
this.writeBit(false);
135+
this._buffer[this._length / 8] = Number((v << BigInt(8 - bits)) & 0xffn);
136+
this._length += bits;
137+
bits = 0;
150138
}
151139
}
152140
}

src/boc/cell/utils/topologicalSort.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export function topologicalSort(src: Cell) {
4040
for (let c of allCells.get(hash)!.refs) {
4141
visit(c);
4242
}
43-
sorted.unshift(hash);
43+
sorted.push(hash);
4444
tempMark.delete(hash);
4545
notPermCells.delete(hash);
4646
}
@@ -51,11 +51,12 @@ export function topologicalSort(src: Cell) {
5151

5252
let indexes = new Map<string, number>();
5353
for (let i = 0; i < sorted.length; i++) {
54-
indexes.set(sorted[i], i);
54+
indexes.set(sorted[sorted.length-i-1], i);
5555
}
5656

5757
let result: { cell: Cell, refs: number[] }[] = [];
58-
for (let ent of sorted) {
58+
for (let i = sorted.length - 1; i >= 0; i--) {
59+
let ent = sorted[i];
5960
const rrr = allCells.get(ent)!;
6061
result.push({ cell: rrr.cell, refs: rrr.refs.map((v) => indexes.get(v)!) });
6162
}

src/dict/serializeDict.ts

+18-21
Original file line numberDiff line numberDiff line change
@@ -49,18 +49,17 @@ function removePrefixMap<T>(src: Map<string, T>, length: number) {
4949
}
5050
}
5151

52-
function forkMap<T>(src: Map<string, T>) {
52+
function forkMap<T>(src: Map<string, T>, prefixLen: number) {
5353
if (src.size === 0) {
5454
throw Error('Internal inconsistency');
5555
}
5656
let left = new Map<string, T>();
5757
let right = new Map<string, T>();
58-
for (let k of src.keys()) {
59-
let d = src.get(k)!;
60-
if (k.startsWith('0')) {
61-
left.set(k.substr(1), d);
58+
for (let [k, d] of src.entries()) {
59+
if (k[prefixLen] === '0') {
60+
left.set(k, d);
6261
} else {
63-
right.set(k.substr(1), d);
62+
right.set(k, d);
6463
}
6564
}
6665
if (left.size === 0) {
@@ -72,27 +71,27 @@ function forkMap<T>(src: Map<string, T>) {
7271
return { left, right };
7372
}
7473

75-
function buildNode<T>(src: Map<string, T>): Node<T> {
74+
function buildNode<T>(src: Map<string, T>, prefixLen: number): Node<T> {
7675
if (src.size === 0) {
7776
throw Error('Internal inconsistency');
7877
}
7978
if (src.size === 1) {
8079
return { type: 'leaf', value: Array.from(src.values())[0] };
8180
}
82-
let { left, right } = forkMap(src);
81+
let { left, right } = forkMap(src, prefixLen);
8382
return {
8483
type: 'fork',
85-
left: buildEdge(left),
86-
right: buildEdge(right)
84+
left: buildEdge(left, prefixLen + 1),
85+
right: buildEdge(right, prefixLen + 1)
8786
}
8887
}
8988

90-
function buildEdge<T>(src: Map<string, T>): Edge<T> {
89+
function buildEdge<T>(src: Map<string, T>, prefixLen = 0): Edge<T> {
9190
if (src.size === 0) {
9291
throw Error('Internal inconsistency');
9392
}
94-
const label = findCommonPrefix(Array.from(src.keys()));
95-
return { label, node: buildNode(removePrefixMap(src, label.length)) };
93+
const label = findCommonPrefix(Array.from(src.keys()), prefixLen);
94+
return { label, node: buildNode(src, label.length + prefixLen) };
9695
}
9796

9897
export function buildTree<T>(src: Map<bigint, T>, keyLength: number) {
@@ -123,8 +122,8 @@ export function writeLabelShort(src: string, to: Builder) {
123122
to.storeBit(0);
124123

125124
// Value
126-
for (let i = 0; i < src.length; i++) {
127-
to.storeBit(src[i] === '1');
125+
if (src.length > 0) {
126+
to.storeUint(BigInt('0b' + src), src.length);
128127
}
129128
return to;
130129
}
@@ -143,8 +142,8 @@ export function writeLabelLong(src: string, keyLength: number, to: Builder) {
143142
to.storeUint(src.length, length);
144143

145144
// Value
146-
for (let i = 0; i < src.length; i++) {
147-
to.storeBit(src[i] === '1');
145+
if (src.length > 0) {
146+
to.storeUint(BigInt('0b' + src), src.length);
148147
}
149148
return to;
150149
}
@@ -207,11 +206,9 @@ function writeLabel(src: string, keyLength: number, to: Builder) {
207206
let type = detectLabelType(src, keyLength);
208207
if (type === 'short') {
209208
writeLabelShort(src, to);
210-
}
211-
if (type === 'long') {
209+
} else if (type === 'long') {
212210
writeLabelLong(src, keyLength, to);
213-
}
214-
if (type === 'same') {
211+
} else if (type === 'same') {
215212
writeLabelSame(src[0] === '1', src.length, keyLength, to);
216213
}
217214
}

src/dict/utils/findCommonPrefix.ts

+13-12
Original file line numberDiff line numberDiff line change
@@ -6,24 +6,25 @@
66
* LICENSE file in the root directory of this source tree.
77
*/
88

9-
export function findCommonPrefix(src: string[]) {
9+
export function findCommonPrefix(src: string[], startPos = 0) {
1010

1111
// Corner cases
1212
if (src.length === 0) {
1313
return '';
1414
}
15-
if (src.length === 1) {
16-
return src[0];
17-
}
1815

19-
// Searching for prefix
20-
const sorted = [...src].sort();
21-
let size = 0;
22-
for (let i = 0; i < sorted[0].length; i++) {
23-
if (sorted[0][i] !== sorted[sorted.length - 1][i]) {
24-
break;
16+
let r = src[0].slice(startPos);
17+
18+
for (let i = 1; i < src.length; i++) {
19+
const s = src[i];
20+
while (s.indexOf(r, startPos) !== startPos) {
21+
r = r.substring(0, r.length - 1);
22+
23+
if (r === '') {
24+
return r;
25+
}
2526
}
26-
size++;
2727
}
28-
return src[0].slice(0, size);
28+
29+
return r;
2930
}

0 commit comments

Comments
 (0)