@cloudpss/ubjson 0.5.10 → 0.5.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/benchmark-small.js +81 -0
- package/dist/common/decoder.d.ts +8 -1
- package/dist/common/decoder.js +29 -15
- package/dist/common/decoder.js.map +1 -1
- package/dist/common/encoder.d.ts +1 -3
- package/dist/common/encoder.js +33 -11
- package/dist/common/encoder.js.map +1 -1
- package/dist/decoder.d.ts +1 -1
- package/dist/decoder.js.map +1 -1
- package/dist/encoder.d.ts +9 -1
- package/dist/encoder.js +41 -20
- package/dist/encoder.js.map +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +4 -5
- package/dist/index.js.map +1 -1
- package/dist/rxjs/decoder.js +5 -2
- package/dist/rxjs/decoder.js.map +1 -1
- package/dist/rxjs/encoder.js +5 -4
- package/dist/rxjs/encoder.js.map +1 -1
- package/dist/stream/encoder.d.ts +3 -0
- package/dist/stream/encoder.js +8 -2
- package/dist/stream/encoder.js.map +1 -1
- package/dist/stream-helper/encoder.d.ts +10 -2
- package/dist/stream-helper/encoder.js +50 -24
- package/dist/stream-helper/encoder.js.map +1 -1
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +1 -4
- package/dist/utils.js.map +1 -1
- package/package.json +6 -15
- package/src/common/decoder.ts +36 -19
- package/src/common/encoder.ts +32 -10
- package/src/decoder.ts +1 -1
- package/src/encoder.ts +44 -21
- package/src/index.ts +7 -6
- package/src/rxjs/decoder.ts +6 -2
- package/src/rxjs/encoder.ts +5 -4
- package/src/stream/encoder.ts +10 -2
- package/src/stream-helper/encoder.ts +48 -26
- package/src/utils.ts +1 -4
- package/tests/decode.js +29 -9
- package/tests/e2e.js +3 -0
- package/tests/encode.js +173 -2
- package/tests/huge-string.js +3 -0
- package/tests/rxjs/decode.js +10 -8
- package/tests/rxjs/encode.js +6 -2
- package/tests/stream/decode.js +25 -8
- package/tests/stream/encode.js +13 -2
- package/tests/string-encoding.js +2 -2
package/dist/rxjs/encoder.js
CHANGED
|
@@ -4,12 +4,11 @@ import { StreamEncoderHelper } from '../stream-helper/encoder.js';
|
|
|
4
4
|
export function encode() {
|
|
5
5
|
return (observable) => {
|
|
6
6
|
return new Observable((subscriber) => {
|
|
7
|
-
const
|
|
8
|
-
|
|
7
|
+
const helper = new StreamEncoderHelper((chunk) => subscriber.next(chunk));
|
|
8
|
+
const sub = observable.subscribe({
|
|
9
9
|
next(value) {
|
|
10
10
|
try {
|
|
11
|
-
|
|
12
|
-
helper.encode();
|
|
11
|
+
helper.encode(value);
|
|
13
12
|
}
|
|
14
13
|
catch (ex) {
|
|
15
14
|
subscriber.error(ex);
|
|
@@ -22,6 +21,8 @@ export function encode() {
|
|
|
22
21
|
subscriber.complete();
|
|
23
22
|
},
|
|
24
23
|
});
|
|
24
|
+
sub.add(() => helper.destroy());
|
|
25
|
+
return sub;
|
|
25
26
|
});
|
|
26
27
|
};
|
|
27
28
|
}
|
package/dist/rxjs/encoder.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"encoder.js","sourceRoot":"","sources":["../../src/rxjs/encoder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAyB,MAAM,MAAM,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAElE,kBAAkB;AAClB,MAAM,UAAU,MAAM;IAClB,OAAO,CAAC,UAAU,EAAE,EAAE;QAClB,OAAO,IAAI,UAAU,CAAa,CAAC,UAAU,EAAE,EAAE;YAC7C,MAAM,
|
|
1
|
+
{"version":3,"file":"encoder.js","sourceRoot":"","sources":["../../src/rxjs/encoder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAyB,MAAM,MAAM,CAAC;AACzD,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAElE,kBAAkB;AAClB,MAAM,UAAU,MAAM;IAClB,OAAO,CAAC,UAAU,EAAE,EAAE;QAClB,OAAO,IAAI,UAAU,CAAa,CAAC,UAAU,EAAE,EAAE;YAC7C,MAAM,MAAM,GAAG,IAAI,mBAAmB,CAAC,CAAC,KAAiB,EAAQ,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;YAC5F,MAAM,GAAG,GAAG,UAAU,CAAC,SAAS,CAAC;gBAC7B,IAAI,CAAC,KAAK;oBACN,IAAI,CAAC;wBACD,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;oBACzB,CAAC;oBAAC,OAAO,EAAE,EAAE,CAAC;wBACV,UAAU,CAAC,KAAK,CAAC,EAAW,CAAC,CAAC;oBAClC,CAAC;gBACL,CAAC;gBACD,KAAK,CAAC,GAAG;oBACL,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;gBAC1B,CAAC;gBACD,QAAQ;oBACJ,UAAU,CAAC,QAAQ,EAAE,CAAC;gBAC1B,CAAC;aACJ,CAAC,CAAC;YACH,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC;YAChC,OAAO,GAAG,CAAC;QACf,CAAC,CAAC,CAAC;IACP,CAAC,CAAC;AACN,CAAC"}
|
package/dist/stream/encoder.d.ts
CHANGED
|
@@ -4,6 +4,9 @@ import { Transform, type TransformCallback } from 'node:stream';
|
|
|
4
4
|
/** 流式编码 UBJSON */
|
|
5
5
|
export declare class StreamEncoder extends Transform {
|
|
6
6
|
constructor();
|
|
7
|
+
private readonly helper;
|
|
7
8
|
/** @inheritdoc */
|
|
8
9
|
_transform(obj: unknown, _encoding: BufferEncoding, callback: TransformCallback): void;
|
|
10
|
+
/** @inheritdoc */
|
|
11
|
+
_destroy(error: Error | null, callback: (error?: Error | null | undefined) => void): void;
|
|
9
12
|
}
|
package/dist/stream/encoder.js
CHANGED
|
@@ -7,17 +7,23 @@ export class StreamEncoder extends Transform {
|
|
|
7
7
|
readableObjectMode: false,
|
|
8
8
|
writableObjectMode: true,
|
|
9
9
|
});
|
|
10
|
+
this.helper = new StreamEncoderHelper((binary) => this.push(binary));
|
|
10
11
|
}
|
|
12
|
+
helper;
|
|
11
13
|
/** @inheritdoc */
|
|
12
14
|
_transform(obj, _encoding, callback) {
|
|
13
|
-
const helper = new StreamEncoderHelper(obj, (binary) => this.push(binary));
|
|
14
15
|
try {
|
|
15
|
-
helper.encode();
|
|
16
|
+
this.helper.encode(obj);
|
|
16
17
|
callback();
|
|
17
18
|
}
|
|
18
19
|
catch (ex) {
|
|
19
20
|
callback(ex);
|
|
20
21
|
}
|
|
21
22
|
}
|
|
23
|
+
/** @inheritdoc */
|
|
24
|
+
_destroy(error, callback) {
|
|
25
|
+
this.helper.destroy();
|
|
26
|
+
super._destroy(error, callback);
|
|
27
|
+
}
|
|
22
28
|
}
|
|
23
29
|
//# sourceMappingURL=encoder.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"encoder.js","sourceRoot":"","sources":["../../src/stream/encoder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAA0B,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAElE,kBAAkB;AAClB,MAAM,OAAO,aAAc,SAAQ,SAAS;IACxC;QACI,KAAK,CAAC;YACF,kBAAkB,EAAE,KAAK;YACzB,kBAAkB,EAAE,IAAI;SAC3B,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"encoder.js","sourceRoot":"","sources":["../../src/stream/encoder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAA0B,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAElE,kBAAkB;AAClB,MAAM,OAAO,aAAc,SAAQ,SAAS;IACxC;QACI,KAAK,CAAC;YACF,kBAAkB,EAAE,KAAK;YACzB,kBAAkB,EAAE,IAAI;SAC3B,CAAC,CAAC;QACH,IAAI,CAAC,MAAM,GAAG,IAAI,mBAAmB,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IACzE,CAAC;IAEgB,MAAM,CAAC;IAExB,kBAAkB;IACT,UAAU,CAAC,GAAY,EAAE,SAAyB,EAAE,QAA2B;QACpF,IAAI,CAAC;YACD,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACxB,QAAQ,EAAE,CAAC;QACf,CAAC;QAAC,OAAO,EAAE,EAAE,CAAC;YACV,QAAQ,CAAC,EAAW,CAAC,CAAC;QAC1B,CAAC;IACL,CAAC;IAED,kBAAkB;IACT,QAAQ,CAAC,KAAmB,EAAE,QAAoD;QACvF,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;QACtB,KAAK,CAAC,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACpC,CAAC;CACJ"}
|
|
@@ -2,11 +2,19 @@ import { EncoderBase } from '../common/encoder.js';
|
|
|
2
2
|
/** 流式编码 UBJSON */
|
|
3
3
|
export declare class StreamEncoderHelper extends EncoderBase {
|
|
4
4
|
protected readonly onChunk: (chunk: Uint8Array) => void;
|
|
5
|
-
constructor(
|
|
5
|
+
constructor(onChunk: (chunk: Uint8Array) => void);
|
|
6
|
+
/**
|
|
7
|
+
* 销毁实例,释放内存池
|
|
8
|
+
*/
|
|
9
|
+
destroy(): void;
|
|
10
|
+
/** 通过内存池减少分配 */
|
|
11
|
+
private readonly pool;
|
|
6
12
|
/**
|
|
7
13
|
* 确保 buffer 还有 capacity 的空闲空间
|
|
8
14
|
*/
|
|
9
15
|
protected ensureCapacity(capacity: number): void;
|
|
16
|
+
/** 分配 buffer */
|
|
17
|
+
private allocUnsafe;
|
|
10
18
|
/** 获取写入结果 */
|
|
11
|
-
encode(): void;
|
|
19
|
+
encode(value: unknown): void;
|
|
12
20
|
}
|
|
@@ -1,13 +1,33 @@
|
|
|
1
1
|
import { EncoderBase } from '../common/encoder.js';
|
|
2
2
|
const BLOCK_SIZE = 1024 * 8; // 8 KiB
|
|
3
3
|
const MAX_SIZE = 1024 * 1024 * 256; // 256 MiB
|
|
4
|
+
/** 保存一个内存池以减少重复分配 */
|
|
5
|
+
let POOL = null;
|
|
4
6
|
/** 流式编码 UBJSON */
|
|
5
7
|
export class StreamEncoderHelper extends EncoderBase {
|
|
6
8
|
onChunk;
|
|
7
|
-
constructor(
|
|
8
|
-
super(
|
|
9
|
+
constructor(onChunk) {
|
|
10
|
+
super();
|
|
9
11
|
this.onChunk = onChunk;
|
|
12
|
+
if (POOL != null) {
|
|
13
|
+
this.pool = POOL;
|
|
14
|
+
POOL = null;
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
this.pool = new Uint8Array(BLOCK_SIZE);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* 销毁实例,释放内存池
|
|
22
|
+
*/
|
|
23
|
+
destroy() {
|
|
24
|
+
POOL ??= this.pool;
|
|
25
|
+
const self = this;
|
|
26
|
+
self.pool = null;
|
|
27
|
+
self.buffer = null;
|
|
10
28
|
}
|
|
29
|
+
/** 通过内存池减少分配 */
|
|
30
|
+
pool;
|
|
11
31
|
/**
|
|
12
32
|
* 确保 buffer 还有 capacity 的空闲空间
|
|
13
33
|
*/
|
|
@@ -16,43 +36,49 @@ export class StreamEncoderHelper extends EncoderBase {
|
|
|
16
36
|
// 超过最大尺寸限制
|
|
17
37
|
throw new Error('Buffer has exceed max size');
|
|
18
38
|
}
|
|
19
|
-
if (this.buffer == null) {
|
|
20
|
-
this.buffer = new Uint8Array(capacity);
|
|
21
|
-
this.view = new DataView(this.buffer.buffer);
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
39
|
if (capacity < 0) {
|
|
25
40
|
// 结束流
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
41
|
+
if (this.buffer === this.pool) {
|
|
42
|
+
this.onChunk(this.buffer.slice(0, this.length));
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
this.onChunk(this.buffer.subarray(0, this.length));
|
|
46
|
+
}
|
|
30
47
|
return;
|
|
31
48
|
}
|
|
32
49
|
// 无需扩容
|
|
33
50
|
if (this.buffer.byteLength >= this.length + capacity)
|
|
34
51
|
return;
|
|
35
52
|
// 提交目前的数据
|
|
36
|
-
|
|
53
|
+
if (this.buffer === this.pool) {
|
|
54
|
+
this.onChunk(this.buffer.slice(0, this.length));
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
this.onChunk(this.buffer.subarray(0, this.length));
|
|
58
|
+
}
|
|
37
59
|
// 重新分配缓冲区
|
|
38
60
|
if (capacity < BLOCK_SIZE)
|
|
39
61
|
capacity = BLOCK_SIZE;
|
|
40
|
-
this.
|
|
62
|
+
this.allocUnsafe(capacity);
|
|
63
|
+
}
|
|
64
|
+
/** 分配 buffer */
|
|
65
|
+
allocUnsafe(size) {
|
|
66
|
+
if (size === this.pool.byteLength) {
|
|
67
|
+
// 从 pool 中获取
|
|
68
|
+
this.buffer = this.pool;
|
|
69
|
+
this.view = new DataView(this.buffer.buffer);
|
|
70
|
+
this.length = 0;
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
this.buffer = new Uint8Array(size);
|
|
41
74
|
this.view = new DataView(this.buffer.buffer);
|
|
42
75
|
this.length = 0;
|
|
43
76
|
}
|
|
44
77
|
/** 获取写入结果 */
|
|
45
|
-
encode() {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
this.ensureCapacity(-1);
|
|
50
|
-
}
|
|
51
|
-
else {
|
|
52
|
-
this.ensureCapacity(BLOCK_SIZE);
|
|
53
|
-
this.writeValue();
|
|
54
|
-
this.ensureCapacity(-1);
|
|
55
|
-
}
|
|
78
|
+
encode(value) {
|
|
79
|
+
this.allocUnsafe(BLOCK_SIZE);
|
|
80
|
+
this.writeValue(value);
|
|
81
|
+
this.ensureCapacity(-1);
|
|
56
82
|
}
|
|
57
83
|
}
|
|
58
84
|
//# sourceMappingURL=encoder.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"encoder.js","sourceRoot":"","sources":["../../src/stream-helper/encoder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAEnD,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,QAAQ;AACrC,MAAM,QAAQ,GAAG,IAAI,GAAG,IAAI,GAAG,GAAG,CAAC,CAAC,UAAU;AAE9C,kBAAkB;AAClB,MAAM,OAAO,mBAAoB,SAAQ,WAAW;
|
|
1
|
+
{"version":3,"file":"encoder.js","sourceRoot":"","sources":["../../src/stream-helper/encoder.ts"],"names":[],"mappings":"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"}
|
package/dist/utils.d.ts
CHANGED
package/dist/utils.js
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
1
|
/** 支持的数据转为 Uint8Array */
|
|
2
|
-
export function toUint8Array(data
|
|
2
|
+
export function toUint8Array(data) {
|
|
3
3
|
if (data == null || typeof data != 'object' || typeof data.byteLength != 'number') {
|
|
4
4
|
throw new TypeError('Invalid data');
|
|
5
5
|
}
|
|
6
6
|
if (!ArrayBuffer.isView(data)) {
|
|
7
7
|
return new Uint8Array(data);
|
|
8
8
|
}
|
|
9
|
-
if (exact && (data.byteOffset !== 0 || data.buffer.byteLength !== data.byteLength)) {
|
|
10
|
-
return new Uint8Array(data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength));
|
|
11
|
-
}
|
|
12
9
|
return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
13
10
|
}
|
|
14
11
|
/** 未结束的流 */
|
package/dist/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,yBAAyB;AACzB,MAAM,UAAU,YAAY,CAAC,IAAgB
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,yBAAyB;AACzB,MAAM,UAAU,YAAY,CAAC,IAAgB;IACzC,IAAI,IAAI,IAAI,IAAI,IAAI,OAAO,IAAI,IAAI,QAAQ,IAAI,OAAO,IAAI,CAAC,UAAU,IAAI,QAAQ,EAAE,CAAC;QAChF,MAAM,IAAI,SAAS,CAAC,cAAc,CAAC,CAAC;IACxC,CAAC;IACD,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;QAC5B,OAAO,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IACD,OAAO,IAAI,UAAU,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,UAAU,CAAC,CAAC;AACzE,CAAC;AAED,YAAY;AACZ,MAAM,OAAO,aAAc,SAAQ,KAAK;IACpC;QACI,KAAK,CAAC,gBAAgB,CAAC,CAAC;IAC5B,CAAC;CACJ"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudpss/ubjson",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.12",
|
|
4
4
|
"author": "CloudPSS",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -27,23 +27,14 @@
|
|
|
27
27
|
}
|
|
28
28
|
},
|
|
29
29
|
"exports": {
|
|
30
|
-
".":
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
},
|
|
34
|
-
"./stream": {
|
|
35
|
-
"types": "./dist/stream/index.d.ts",
|
|
36
|
-
"default": "./dist/stream/index.js"
|
|
37
|
-
},
|
|
38
|
-
"./rxjs": {
|
|
39
|
-
"types": "./dist/rxjs/index.d.ts",
|
|
40
|
-
"default": "./dist/rxjs/index.js"
|
|
41
|
-
}
|
|
30
|
+
".": "./dist/index.js",
|
|
31
|
+
"./stream": "./dist/stream/index.js",
|
|
32
|
+
"./rxjs": "./dist/rxjs/index.js"
|
|
42
33
|
},
|
|
43
34
|
"devDependencies": {
|
|
44
|
-
"@msgpack/msgpack": "3.0.0-beta2",
|
|
45
35
|
"@types/lodash": "^4.14.202",
|
|
46
|
-
"
|
|
36
|
+
"lodash": "^4.17.21",
|
|
37
|
+
"ref-impl": "npm:@cloudpss/ubjson@0.5.10"
|
|
47
38
|
},
|
|
48
39
|
"dependencies": {
|
|
49
40
|
"rxjs": "^7.8.1"
|
package/src/common/decoder.ts
CHANGED
|
@@ -4,15 +4,29 @@ import { decode, decodeKey } from './string-decoder.js';
|
|
|
4
4
|
|
|
5
5
|
const fromEntries = Object.fromEntries;
|
|
6
6
|
|
|
7
|
+
/** decoder options */
|
|
8
|
+
export interface DecoderOptions {
|
|
9
|
+
/** 对 {@link Uint8Array} 使用 subarray */
|
|
10
|
+
noAllocBuffer?: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
7
13
|
/** decoder */
|
|
8
14
|
export class Decoder {
|
|
9
15
|
protected readonly view: DataView;
|
|
10
16
|
protected readonly data: Uint8Array;
|
|
11
17
|
/** 当前读指针位置 */
|
|
12
18
|
protected offset = 0;
|
|
13
|
-
|
|
14
|
-
|
|
19
|
+
|
|
20
|
+
/** 选项 */
|
|
21
|
+
protected readonly noAllocBuffer;
|
|
22
|
+
constructor(data: BinaryData, options?: DecoderOptions) {
|
|
23
|
+
this.data = toUint8Array(data);
|
|
15
24
|
this.view = new DataView(this.data.buffer, this.data.byteOffset, this.data.byteLength);
|
|
25
|
+
if (options) {
|
|
26
|
+
this.noAllocBuffer = !!options.noAllocBuffer;
|
|
27
|
+
} else {
|
|
28
|
+
this.noAllocBuffer = false;
|
|
29
|
+
}
|
|
16
30
|
}
|
|
17
31
|
|
|
18
32
|
/** EOF */
|
|
@@ -99,13 +113,9 @@ export class Decoder {
|
|
|
99
113
|
switch (type) {
|
|
100
114
|
case constants.UINT8:
|
|
101
115
|
try {
|
|
102
|
-
const buf = new Uint8Array(
|
|
103
|
-
this.data.buffer,
|
|
104
|
-
this.data.byteOffset + this.offset,
|
|
105
|
-
count,
|
|
106
|
-
).slice();
|
|
116
|
+
const buf = new Uint8Array(this.data.buffer, this.data.byteOffset + this.offset, count);
|
|
107
117
|
this.offset += count;
|
|
108
|
-
return buf;
|
|
118
|
+
return this.noAllocBuffer ? buf : buf.slice();
|
|
109
119
|
} catch {
|
|
110
120
|
return this.eof();
|
|
111
121
|
}
|
|
@@ -154,8 +164,7 @@ export class Decoder {
|
|
|
154
164
|
for (let i = 0; i < count; i++) {
|
|
155
165
|
result[i] = this.readInt64Data();
|
|
156
166
|
}
|
|
157
|
-
|
|
158
|
-
//return result;
|
|
167
|
+
return result;
|
|
159
168
|
}
|
|
160
169
|
case constants.NULL:
|
|
161
170
|
return Array.from({ length: count }).fill(null);
|
|
@@ -193,8 +202,13 @@ export class Decoder {
|
|
|
193
202
|
return true;
|
|
194
203
|
case constants.FALSE:
|
|
195
204
|
return false;
|
|
196
|
-
case constants.INT64:
|
|
197
|
-
|
|
205
|
+
case constants.INT64: {
|
|
206
|
+
const n = this.readInt64Data();
|
|
207
|
+
if (n < Number.MIN_SAFE_INTEGER || n > Number.MAX_SAFE_INTEGER) {
|
|
208
|
+
return n;
|
|
209
|
+
}
|
|
210
|
+
return Number(n);
|
|
211
|
+
}
|
|
198
212
|
case constants.HIGH_PRECISION_NUMBER: {
|
|
199
213
|
const length = this.readIntLength();
|
|
200
214
|
try {
|
|
@@ -213,7 +227,6 @@ export class Decoder {
|
|
|
213
227
|
/** 读取一个大于 0 的整数 */
|
|
214
228
|
private readIntLength(): number {
|
|
215
229
|
const marker = this.readMarker();
|
|
216
|
-
if (marker === undefined) this.eof();
|
|
217
230
|
let length;
|
|
218
231
|
switch (marker) {
|
|
219
232
|
case constants.INT8:
|
|
@@ -228,9 +241,14 @@ export class Decoder {
|
|
|
228
241
|
case constants.INT32:
|
|
229
242
|
length = this.readInt32Data();
|
|
230
243
|
break;
|
|
231
|
-
case constants.INT64:
|
|
232
|
-
|
|
244
|
+
case constants.INT64: {
|
|
245
|
+
const l = this.readInt64Data();
|
|
246
|
+
if (l < 0 || l > Number.MAX_SAFE_INTEGER) {
|
|
247
|
+
throw new Error('Invalid length');
|
|
248
|
+
}
|
|
249
|
+
length = Number(l);
|
|
233
250
|
break;
|
|
251
|
+
}
|
|
234
252
|
default:
|
|
235
253
|
throw new Error(`Unexpected marker '${String.fromCharCode(marker)}'(${marker}) for int length`);
|
|
236
254
|
}
|
|
@@ -285,15 +303,14 @@ export class Decoder {
|
|
|
285
303
|
}
|
|
286
304
|
}
|
|
287
305
|
/** readInt64Data */
|
|
288
|
-
private readInt64Data():
|
|
306
|
+
private readInt64Data(): bigint {
|
|
289
307
|
try {
|
|
290
|
-
const
|
|
308
|
+
const result = this.view.getBigInt64(this.offset);
|
|
291
309
|
this.offset += 8;
|
|
292
|
-
|
|
310
|
+
return result;
|
|
293
311
|
} catch {
|
|
294
312
|
this.eof();
|
|
295
313
|
}
|
|
296
|
-
throw new Error('Unsupported type int64');
|
|
297
314
|
}
|
|
298
315
|
/** readFloat32Data */
|
|
299
316
|
private readFloat32Data(): number {
|
package/src/common/encoder.ts
CHANGED
|
@@ -11,21 +11,19 @@ export abstract class EncoderBase {
|
|
|
11
11
|
protected buffer!: Uint8Array;
|
|
12
12
|
/** buffer 的 DataView */
|
|
13
13
|
protected view!: DataView;
|
|
14
|
-
|
|
15
|
-
constructor(readonly value: unknown) {}
|
|
16
14
|
/**
|
|
17
15
|
* 确保 buffer 还有 capacity 的空闲空间
|
|
18
16
|
*/
|
|
19
17
|
protected abstract ensureCapacity(capacity: number): void;
|
|
20
18
|
|
|
21
19
|
/** 编码至 ubjson */
|
|
22
|
-
protected writeValue(): void {
|
|
23
|
-
if (
|
|
20
|
+
protected writeValue(value: unknown): void {
|
|
21
|
+
if (value === undefined) {
|
|
24
22
|
this.ensureCapacity(1);
|
|
25
23
|
this.buffer[this.length++] = constants.NO_OP;
|
|
26
24
|
return;
|
|
27
25
|
}
|
|
28
|
-
this.write(
|
|
26
|
+
this.write(value);
|
|
29
27
|
}
|
|
30
28
|
/** 写入一个对象 */
|
|
31
29
|
private write(value: unknown): void {
|
|
@@ -98,9 +96,9 @@ export abstract class EncoderBase {
|
|
|
98
96
|
this.ensureCapacity(1);
|
|
99
97
|
this.buffer[this.length++] = constants.ARRAY_END;
|
|
100
98
|
} else if (!ArrayBuffer.isView(value)) {
|
|
101
|
-
const toJSON =
|
|
102
|
-
if (toJSON) {
|
|
103
|
-
this.write(
|
|
99
|
+
const { toJSON } = value as Record<string, unknown>;
|
|
100
|
+
if (typeof toJSON == 'function') {
|
|
101
|
+
this.write(toJSON.call(value));
|
|
104
102
|
return;
|
|
105
103
|
}
|
|
106
104
|
this.ensureCapacity(1);
|
|
@@ -132,8 +130,9 @@ export abstract class EncoderBase {
|
|
|
132
130
|
return;
|
|
133
131
|
}
|
|
134
132
|
|
|
135
|
-
const arrayLength = (value as Int16Array | Int32Array | Float32Array | Float64Array)
|
|
136
|
-
|
|
133
|
+
const arrayLength = (value as Int16Array | Int32Array | BigInt64Array | Float32Array | Float64Array)
|
|
134
|
+
.length;
|
|
135
|
+
const elementSize = (value as Int16Array | Int32Array | BigInt64Array | Float32Array | Float64Array)
|
|
137
136
|
.BYTES_PER_ELEMENT;
|
|
138
137
|
if (value instanceof Int16Array) {
|
|
139
138
|
this.buffer[this.length++] = constants.INT16;
|
|
@@ -167,6 +166,14 @@ export abstract class EncoderBase {
|
|
|
167
166
|
this.view.setFloat64(this.length, value[i]);
|
|
168
167
|
this.length += elementSize;
|
|
169
168
|
}
|
|
169
|
+
} else if (value instanceof BigInt64Array) {
|
|
170
|
+
this.buffer[this.length++] = constants.INT64;
|
|
171
|
+
this.buffer[this.length++] = constants.COUNT_MARKER;
|
|
172
|
+
this.setLength(arrayLength);
|
|
173
|
+
for (let i = 0; i < arrayLength; i++) {
|
|
174
|
+
this.view.setBigInt64(this.length, value[i]);
|
|
175
|
+
this.length += elementSize;
|
|
176
|
+
}
|
|
170
177
|
} else {
|
|
171
178
|
throw new TypeError(`Unsupported typed array type ${Object.prototype.toString.call(value)}`);
|
|
172
179
|
}
|
|
@@ -177,6 +184,21 @@ export abstract class EncoderBase {
|
|
|
177
184
|
this.ensureCapacity(1);
|
|
178
185
|
this.buffer[this.length++] = value ? constants.TRUE : constants.FALSE;
|
|
179
186
|
return;
|
|
187
|
+
case 'bigint':
|
|
188
|
+
// int32 range
|
|
189
|
+
if (value >= -2_147_483_648n && value <= 2_147_483_647n) {
|
|
190
|
+
this.write(Number(value));
|
|
191
|
+
}
|
|
192
|
+
// int64 range
|
|
193
|
+
else if (value >= -9_223_372_036_854_775_808n && value <= 9_223_372_036_854_775_807n) {
|
|
194
|
+
this.ensureCapacity(9);
|
|
195
|
+
this.buffer[this.length++] = constants.INT64;
|
|
196
|
+
this.view.setBigInt64(this.length, value);
|
|
197
|
+
this.length += 8;
|
|
198
|
+
} else {
|
|
199
|
+
throw new RangeError(`BigInt value out of range: ${value}`);
|
|
200
|
+
}
|
|
201
|
+
return;
|
|
180
202
|
default:
|
|
181
203
|
throw new Error(`Unsupported type ${Object.prototype.toString.call(value)}`);
|
|
182
204
|
}
|
package/src/decoder.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { Decoder } from './common/decoder.js';
|
|
1
|
+
export { Decoder, type DecoderOptions } from './common/decoder.js';
|
package/src/encoder.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { EncoderBase } from './common/encoder.js';
|
|
2
2
|
|
|
3
|
-
const BLOCK_SIZE = 1024 * 16; //
|
|
3
|
+
const BLOCK_SIZE = 1024 * 16; // 16 KiB
|
|
4
4
|
const MAX_SIZE = 1024 * 1024 * 128; //128 MiB
|
|
5
5
|
|
|
6
6
|
/** 编码至 ubjson */
|
|
7
7
|
export class Encoder extends EncoderBase {
|
|
8
8
|
private readonly flushedBuffers: Uint8Array[] = [];
|
|
9
|
+
/** 通过内存池减少分配 */
|
|
10
|
+
private readonly pool = new Uint8Array(BLOCK_SIZE);
|
|
9
11
|
/**
|
|
10
12
|
* 确保 buffer 还有 capacity 的空闲空间
|
|
11
13
|
*/
|
|
@@ -14,43 +16,49 @@ export class Encoder extends EncoderBase {
|
|
|
14
16
|
// 超过最大尺寸限制
|
|
15
17
|
throw new Error('Buffer has exceed max size');
|
|
16
18
|
}
|
|
17
|
-
if (this.buffer == null) {
|
|
18
|
-
this.buffer = new Uint8Array(capacity);
|
|
19
|
-
this.view = new DataView(this.buffer.buffer);
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
19
|
// 无需扩容
|
|
23
20
|
if (this.buffer.byteLength >= this.length + capacity) return;
|
|
24
21
|
|
|
25
22
|
// 提交目前的数据
|
|
26
|
-
|
|
23
|
+
if (this.buffer === this.pool) {
|
|
24
|
+
this.flushedBuffers.push(this.buffer.slice(0, this.length));
|
|
25
|
+
} else {
|
|
26
|
+
this.flushedBuffers.push(this.buffer.subarray(0, this.length));
|
|
27
|
+
}
|
|
27
28
|
|
|
28
29
|
// 重新分配缓冲区
|
|
29
30
|
if (capacity < BLOCK_SIZE) capacity = BLOCK_SIZE;
|
|
30
|
-
this.
|
|
31
|
+
this.allocUnsafe(capacity);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** 分配 buffer */
|
|
35
|
+
private allocUnsafe(size: number): void {
|
|
36
|
+
if (size === this.pool.byteLength) {
|
|
37
|
+
// 从 pool 中获取
|
|
38
|
+
this.buffer = this.pool;
|
|
39
|
+
this.view = new DataView(this.buffer.buffer);
|
|
40
|
+
this.length = 0;
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
this.buffer = new Uint8Array(size);
|
|
31
44
|
this.view = new DataView(this.buffer.buffer);
|
|
32
45
|
this.length = 0;
|
|
33
46
|
}
|
|
34
47
|
|
|
35
48
|
/** 获取写入结果 */
|
|
36
|
-
encode(): Uint8Array {
|
|
37
|
-
this.
|
|
38
|
-
this.writeValue();
|
|
49
|
+
encode(value: unknown): Uint8Array {
|
|
50
|
+
this.allocUnsafe(BLOCK_SIZE);
|
|
51
|
+
this.writeValue(value);
|
|
39
52
|
if (this.flushedBuffers.length === 0) {
|
|
40
|
-
|
|
41
|
-
// 无需再拷贝一次
|
|
42
|
-
return this.buffer;
|
|
43
|
-
} else {
|
|
44
|
-
return this.buffer.slice(0, this.length);
|
|
45
|
-
}
|
|
53
|
+
return this.buffer.slice(0, this.length);
|
|
46
54
|
}
|
|
47
55
|
|
|
48
56
|
// 合并缓冲区
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
);
|
|
57
|
+
const flushedBuffers = this.flushedBuffers.splice(0);
|
|
58
|
+
const size = flushedBuffers.reduce((sum, buffer) => sum + buffer.byteLength, this.length);
|
|
59
|
+
const result = new Uint8Array(size);
|
|
52
60
|
let offset = 0;
|
|
53
|
-
for (const buffer of
|
|
61
|
+
for (const buffer of flushedBuffers) {
|
|
54
62
|
result.set(buffer, offset);
|
|
55
63
|
offset += buffer.byteLength;
|
|
56
64
|
}
|
|
@@ -58,3 +66,18 @@ export class Encoder extends EncoderBase {
|
|
|
58
66
|
return result;
|
|
59
67
|
}
|
|
60
68
|
}
|
|
69
|
+
|
|
70
|
+
let _ENCODER: Encoder | undefined;
|
|
71
|
+
|
|
72
|
+
/** 获取默认的编码器 */
|
|
73
|
+
export function getEncoder(): Encoder {
|
|
74
|
+
if (_ENCODER == null) {
|
|
75
|
+
_ENCODER = new Encoder();
|
|
76
|
+
}
|
|
77
|
+
return _ENCODER;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** 重置编码器, For testing only */
|
|
81
|
+
export function resetEncoder(): void {
|
|
82
|
+
_ENCODER = undefined;
|
|
83
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { Decoder } from './decoder.js';
|
|
1
|
+
import { getEncoder } from './encoder.js';
|
|
2
|
+
import { Decoder, type DecoderOptions } from './decoder.js';
|
|
3
3
|
export { UnexpectedEof } from './utils.js';
|
|
4
4
|
|
|
5
|
+
export type { DecoderOptions };
|
|
6
|
+
|
|
5
7
|
/** 编码为 UBJSON */
|
|
6
8
|
export function encode(value: unknown): Uint8Array {
|
|
7
|
-
|
|
8
|
-
return encoder.encode();
|
|
9
|
+
return getEncoder().encode(value);
|
|
9
10
|
}
|
|
10
11
|
|
|
11
12
|
/** 解码 UBJSON */
|
|
12
|
-
export function decode(value: BinaryData): unknown {
|
|
13
|
-
const decoder = new Decoder(value);
|
|
13
|
+
export function decode(value: BinaryData, options?: DecoderOptions): unknown {
|
|
14
|
+
const decoder = new Decoder(value, options);
|
|
14
15
|
return decoder.decode();
|
|
15
16
|
}
|
package/src/rxjs/decoder.ts
CHANGED
|
@@ -15,13 +15,17 @@ export function decode(): OperatorFunction<BinaryData, unknown> {
|
|
|
15
15
|
let end = 0;
|
|
16
16
|
return observable.subscribe({
|
|
17
17
|
next(value) {
|
|
18
|
-
const data = toUint8Array(value
|
|
18
|
+
const data = toUint8Array(value);
|
|
19
19
|
if (buffer.length >= end + data.length) {
|
|
20
20
|
buffer.set(data, end);
|
|
21
21
|
end += data.length;
|
|
22
22
|
} else {
|
|
23
23
|
chunkSize = Math.max(chunkSize, data.length * 2 + end - begin);
|
|
24
|
-
const
|
|
24
|
+
const allocSize = Math.max(chunkSize, data.length + end - begin);
|
|
25
|
+
const newBuffer =
|
|
26
|
+
allocSize > buffer.byteLength
|
|
27
|
+
? new Uint8Array(Math.max(chunkSize, data.length + end - begin))
|
|
28
|
+
: buffer;
|
|
25
29
|
newBuffer.set(buffer.subarray(begin, end), 0);
|
|
26
30
|
newBuffer.set(data, end - begin);
|
|
27
31
|
buffer = newBuffer;
|