Skip to content

Commit af485cc

Browse files
committed
reduce allocation of Array
1 parent 0205ad8 commit af485cc

5 files changed

Lines changed: 49 additions & 37 deletions

File tree

src/ExtensionCodec.ts

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ import { encodeUint32, decodeUint32, encodeInt32, decodeInt32, decodeInt64, enco
44
export const EXT_TIMESTAMP = -1;
55

66
function isDate(object: unknown): object is Date {
7-
return Object.prototype.toString.call(object) === '[object Date]';
7+
return Object.prototype.toString.call(object) === "[object Date]";
88
}
99

1010
export type TimeSpec = {
1111
sec: number;
1212
nsec: number;
13-
}
13+
};
1414

1515
const TIMESTAMP32_MAX_SEC = 0x100000000; // 32-bit signed int
1616
const TIMESTAMP64_MAX_SEC = 0x400000000; // 34-bit unsigned int
@@ -20,21 +20,26 @@ export function encodeTimestampFromTimeSpec({ sec, nsec }: TimeSpec): ReadonlyAr
2020
// Here sec >= 0 && nsec >= 0
2121
if (nsec === 0 && sec < TIMESTAMP32_MAX_SEC) {
2222
// timestamp 32 = { sec32 (unsigned) }
23-
return encodeUint32(sec);
23+
const rv: Array<number> = [];
24+
encodeUint32(rv, sec);
25+
return rv;
2426
} else {
2527
// timestamp 64 = { nsec30 (unsigned), sec34 (unsigned) }
2628
const secHigh = sec / 0x100000000;
2729
const secLow = sec & 0xffffffff;
28-
return [
29-
// nsec30 + secHigh2
30-
...encodeUint32((nsec << 2) | (secHigh & 0x3)),
31-
// secLow32
32-
...encodeUint32(secLow),
33-
];
30+
const rv: Array<number> = [];
31+
// nsec30 + secHigh2
32+
encodeUint32(rv, (nsec << 2) | (secHigh & 0x3));
33+
// secLow32
34+
encodeUint32(rv, secLow);
35+
return rv;
3436
}
3537
} else {
3638
// timestamp 96 = { nsec32 (signed), sec64 (signed) }
37-
return [...encodeInt32(nsec), ...encodeInt64(sec)];
39+
const rv: Array<number> = [];
40+
encodeInt32(rv, nsec);
41+
encodeInt64(rv, sec);
42+
return rv;
3843
}
3944
}
4045

src/encode.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@ import { utf8Encode } from "./utils/uf8Encode";
22
import { ExtensionCodec, ExtensionCodecType } from "./ExtensionCodec";
33
import { encodeUint32, encodeInt64, encodeInt32, encodeUint64 } from "./utils/int";
44
import { isObject, isNodeJsBuffer } from "./utils/is";
5-
6-
export type Writable<T> = {
7-
push(...bytes: ReadonlyArray<T>): void;
8-
};
5+
import { Writable } from "./utils/Writable";
96

107
export type EncodeOptions = Readonly<{
118
maxDepth: number;
@@ -57,11 +54,11 @@ export class Encoder {
5754
} else if (object < 0x100000000) {
5855
// uint 32
5956
rv.push(0xce);
60-
rv.push(...encodeUint32(object));
57+
encodeUint32(rv, object);
6158
} else {
6259
// uint 64
6360
rv.push(0xcf);
64-
rv.push(...encodeUint64(object));
61+
encodeUint64(rv, object);
6562
}
6663
} else {
6764
if (object >= -0x20) {
@@ -77,11 +74,11 @@ export class Encoder {
7774
} else if (object >= -0x80000000) {
7875
// int 32
7976
rv.push(0xd2);
80-
rv.push(...encodeInt32(object));
77+
encodeInt32(rv, object);
8178
} else {
8279
// int 64
8380
rv.push(0xd3);
84-
rv.push(...encodeInt64(object));
81+
encodeInt64(rv, object);
8582
}
8683
}
8784
} else if (Number.isFinite(object)) {
@@ -171,7 +168,10 @@ export class Encoder {
171168
rv.push(0xc8, size >> 8, size & 0xff, typeByte, ...ext.data);
172169
} else if (size < 0x100000000) {
173170
// ext 32
174-
rv.push(0xc9, ...encodeUint32(size), typeByte, ...ext.data);
171+
rv.push(0xc9)
172+
encodeUint32(rv, size),
173+
rv.push(typeByte);
174+
rv.push(...ext.data);
175175
} else {
176176
throw new Error(`Too large extension object: ${size}`);
177177
}

src/utils/Writable.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export type Writable<T> = {
2+
push(...bytes: ReadonlyArray<T>): void;
3+
};

src/utils/int.ts

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1-
export function encodeUint32(value: number): [number, number, number, number] {
2-
return [(value >>> 24) & 0xff, (value >> 16) & 0xff, (value >> 8) & 0xff, value & 0xff];
1+
import { Writable } from "./Writable";
2+
3+
export function encodeUint32<T extends Writable<number>>(rv: T, value: number): void {
4+
rv.push((value >>> 24) & 0xff, (value >> 16) & 0xff, (value >> 8) & 0xff, value & 0xff);
35
}
46

57
export function decodeUint32(b1: number, b2: number, b3: number, b4: number) {
@@ -15,12 +17,12 @@ export function decodeInt32(b1: number, b2: number, b3: number, b4: number): num
1517
}
1618

1719
// the actual range is int52 (a.k.a. safe integer)
18-
export function encodeInt64(value: number): [number, number, number, number, number, number, number, number] {
20+
export function encodeInt64<T extends Writable<number>>(rv: T, value: number): void {
1921
if (value < 0) {
2022
const absMinusOne = -value - 1;
2123
const high = absMinusOne / 0x100000000;
2224
const low = absMinusOne & 0xffffffff;
23-
return [
25+
rv.push(
2426
(((high >> 24) & 0xff) ^ 0xff) | 0x80,
2527
((high >> 16) & 0xff) ^ 0xff,
2628
((high >> 8) & 0xff) ^ 0xff,
@@ -29,11 +31,11 @@ export function encodeInt64(value: number): [number, number, number, number, num
2931
((low >> 16) & 0xff) ^ 0xff,
3032
((low >> 8) & 0xff) ^ 0xff,
3133
(low & 0xff) ^ 0xff,
32-
];
34+
);
3335
} else {
3436
const high = value / 0x100000000;
3537
const low = value & 0xffffffff;
36-
return [
38+
rv.push(
3739
(high >> 24) & 0xff,
3840
(high >> 16) & 0xff,
3941
(high >> 8) & 0xff,
@@ -42,7 +44,7 @@ export function encodeInt64(value: number): [number, number, number, number, num
4244
(low >> 16) & 0xff,
4345
(low >> 8) & 0xff,
4446
low & 0xff,
45-
];
47+
);
4648
}
4749
}
4850

@@ -82,11 +84,11 @@ export function decodeInt64(
8284
);
8385
}
8486

85-
export function encodeUint64(value: number): [number, number, number, number, number, number, number, number] {
87+
export function encodeUint64<T extends Writable<number>>(rv: T, value: number): void {
8688
const high = value / 0x100000000;
8789
const low = value & 0xffffffff;
8890

89-
return [
91+
rv.push(
9092
(high >> 24) & 0xff,
9193
(high >> 16) & 0xff,
9294
(high >> 8) & 0xff,
@@ -95,5 +97,5 @@ export function encodeUint64(value: number): [number, number, number, number, nu
9597
(low >> 16) & 0xff,
9698
(low >> 8) & 0xff,
9799
low & 0xff,
98-
];
100+
);
99101
}

test/codec-int.test.ts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import assert from "assert";
2-
import { encodeInt64, decodeInt64, encodeInt32, decodeInt32 } from '../src/utils/int';
2+
import { encodeInt64, decodeInt64, encodeInt32, decodeInt32 } from "../src/utils/int";
33

44
const INT32SPECS = {
55
ZERO: 0,
@@ -8,7 +8,7 @@ const INT32SPECS = {
88
X_FF: 0xff,
99
MINUS_X_FF: -0xff,
1010
INT32_MAX: 0x7fffffff,
11-
INT32_MIN: (-0x7fffffff - 1),
11+
INT32_MIN: -0x7fffffff - 1,
1212
} as Record<string, number>;
1313

1414
const INT64SPECS = {
@@ -22,9 +22,10 @@ describe("codec: encode and decode int 32/64", () => {
2222
for (const name of Object.keys(INT32SPECS)) {
2323
const value = INT32SPECS[name];
2424

25-
it(`${value} (${value < 0 ? '-' : ''}0x${Math.abs(value).toString(16)})`, () => {
26-
const encoded = encodeInt32(value);
27-
assert.deepStrictEqual(decodeInt32(...encoded), value, );
25+
it(`${value} (${value < 0 ? "-" : ""}0x${Math.abs(value).toString(16)})`, () => {
26+
const b: Array<number> = [];
27+
encodeInt32(b, value);
28+
assert.deepStrictEqual(decodeInt32(b[0], b[1], b[2], b[3]), value);
2829
});
2930
}
3031
});
@@ -33,9 +34,10 @@ describe("codec: encode and decode int 32/64", () => {
3334
for (const name of Object.keys(INT64SPECS)) {
3435
const value = INT64SPECS[name];
3536

36-
it(`${value} (${value < 0 ? '-' : ''}0x${Math.abs(value).toString(16)})`, () => {
37-
const encoded = encodeInt64(value);
38-
assert.deepStrictEqual(decodeInt64(...encoded), value, );
37+
it(`${value} (${value < 0 ? "-" : ""}0x${Math.abs(value).toString(16)})`, () => {
38+
const b: Array<number> = [];
39+
encodeInt64(b, value);
40+
assert.deepStrictEqual(decodeInt64(b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]), value);
3941
});
4042
}
4143
});

0 commit comments

Comments
 (0)