@cloudpss/ubjson 0.3.11 → 0.4.1
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/dist/common/decoder.d.ts +7 -3
- package/dist/common/decoder.js +9 -1
- package/dist/common/decoder.js.map +1 -1
- package/dist/common/encoder.js +2 -1
- package/dist/common/encoder.js.map +1 -1
- package/dist/common/string-decoder.js +2 -1
- package/dist/common/string-decoder.js.map +1 -1
- package/dist/decoder.d.ts +2 -0
- package/dist/decoder.js +3 -0
- package/dist/decoder.js.map +1 -0
- package/dist/encoder.d.ts +1 -0
- package/dist/encoder.js +35 -39
- package/dist/encoder.js.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/rxjs/decoder.d.ts +3 -0
- package/dist/rxjs/decoder.js +68 -0
- package/dist/rxjs/decoder.js.map +1 -0
- package/dist/rxjs/encoder.d.ts +3 -0
- package/dist/rxjs/encoder.js +28 -0
- package/dist/rxjs/encoder.js.map +1 -0
- package/dist/rxjs/index.d.ts +2 -0
- package/dist/rxjs/index.js +3 -0
- package/dist/rxjs/index.js.map +1 -0
- package/dist/stream/decoder.d.ts +13 -0
- package/dist/stream/decoder.js +52 -0
- package/dist/stream/decoder.js.map +1 -0
- package/dist/stream/encoder.d.ts +6 -11
- package/dist/stream/encoder.js +15 -78
- package/dist/stream/encoder.js.map +1 -1
- package/dist/stream/index.d.ts +8 -1
- package/dist/stream/index.js +27 -2
- package/dist/stream/index.js.map +1 -1
- package/dist/stream-helper/decoder.d.ts +8 -0
- package/dist/stream-helper/decoder.js +13 -0
- package/dist/stream-helper/decoder.js.map +1 -0
- package/dist/stream-helper/encoder.d.ts +12 -0
- package/dist/stream-helper/encoder.js +57 -0
- package/dist/stream-helper/encoder.js.map +1 -0
- package/package.json +5 -2
- package/src/common/decoder.ts +13 -4
- package/src/common/encoder.ts +2 -1
- package/src/common/string-decoder.ts +3 -1
- package/src/decoder.ts +3 -0
- package/src/encoder.ts +34 -38
- package/src/index.ts +1 -1
- package/src/rxjs/decoder.ts +65 -0
- package/src/rxjs/encoder.ts +27 -0
- package/src/rxjs/index.ts +2 -0
- package/src/stream/decoder.ts +54 -0
- package/src/stream/encoder.ts +16 -76
- package/src/stream/index.ts +30 -3
- package/src/stream-helper/decoder.ts +15 -0
- package/src/stream-helper/encoder.ts +56 -0
- package/tests/encode.js +13 -2
- package/tests/rxjs/decode.js +535 -0
- package/tests/rxjs/encode.js +412 -0
- package/tests/stream/decode.js +502 -0
- package/tests/stream/encode.js +13 -11
- package/tests/string-encoding.js +21 -1
- package/tests/tsconfig.json +0 -8
- package/tsconfig.json +0 -7
package/dist/stream/encoder.js
CHANGED
|
@@ -1,86 +1,23 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
const BLOCK_SIZE = 1024 * 8; // 8 KiB
|
|
4
|
-
const MAX_SIZE = 1024 * 1024 * 256; //256 MiB
|
|
1
|
+
import { Transform } from 'node:stream';
|
|
2
|
+
import { StreamEncoderHelper } from '../stream-helper/encoder.js';
|
|
5
3
|
/** 流式编码 UBJSON */
|
|
6
|
-
export class StreamEncoder extends
|
|
4
|
+
export class StreamEncoder extends Transform {
|
|
7
5
|
constructor() {
|
|
8
|
-
super(
|
|
9
|
-
|
|
6
|
+
super({
|
|
7
|
+
readableObjectMode: false,
|
|
8
|
+
writableObjectMode: true,
|
|
9
|
+
});
|
|
10
10
|
}
|
|
11
|
-
/**
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
throw new Error('Buffer has exceed max size');
|
|
11
|
+
/** @inheritdoc */
|
|
12
|
+
_transform(obj, _encoding, callback) {
|
|
13
|
+
const helper = new StreamEncoderHelper(obj, (binary) => this.push(binary));
|
|
14
|
+
try {
|
|
15
|
+
helper.encode();
|
|
16
|
+
callback();
|
|
18
17
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
this.view = new DataView(this.buffer.buffer);
|
|
22
|
-
return;
|
|
18
|
+
catch (ex) {
|
|
19
|
+
callback(ex);
|
|
23
20
|
}
|
|
24
|
-
if (capacity < 0) {
|
|
25
|
-
// 结束流
|
|
26
|
-
this.stream.push(this.buffer.subarray(0, this.length));
|
|
27
|
-
this.buffer = new Uint8Array(0);
|
|
28
|
-
this.view = new DataView(this.buffer.buffer);
|
|
29
|
-
this.length = 0;
|
|
30
|
-
this.stream.push(null);
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
// 无需扩容
|
|
34
|
-
if (this.buffer.byteLength >= this.length + capacity)
|
|
35
|
-
return;
|
|
36
|
-
// 提交目前的数据
|
|
37
|
-
this.stream.push(this.buffer.subarray(0, this.length));
|
|
38
|
-
// 重新分配缓冲区
|
|
39
|
-
if (capacity < BLOCK_SIZE)
|
|
40
|
-
capacity = BLOCK_SIZE;
|
|
41
|
-
this.buffer = new Uint8Array(capacity);
|
|
42
|
-
this.view = new DataView(this.buffer.buffer);
|
|
43
|
-
this.length = 0;
|
|
44
|
-
}
|
|
45
|
-
/** 获取写入结果 */
|
|
46
|
-
encode() {
|
|
47
|
-
if (typeof this.value != 'object' || this.value == null || ArrayBuffer.isView(this.value)) {
|
|
48
|
-
this.stream = new Readable({
|
|
49
|
-
objectMode: false,
|
|
50
|
-
construct: (callback) => {
|
|
51
|
-
this.ensureCapacity(20);
|
|
52
|
-
callback();
|
|
53
|
-
},
|
|
54
|
-
read: () => {
|
|
55
|
-
try {
|
|
56
|
-
this.write(this.value);
|
|
57
|
-
this.ensureCapacity(-1);
|
|
58
|
-
}
|
|
59
|
-
catch (ex) {
|
|
60
|
-
this.stream.destroy(ex);
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
else {
|
|
66
|
-
this.stream = new Readable({
|
|
67
|
-
objectMode: false,
|
|
68
|
-
construct: (callback) => {
|
|
69
|
-
this.ensureCapacity(BLOCK_SIZE);
|
|
70
|
-
callback();
|
|
71
|
-
},
|
|
72
|
-
read: () => {
|
|
73
|
-
try {
|
|
74
|
-
this.write(this.value);
|
|
75
|
-
this.ensureCapacity(-1);
|
|
76
|
-
}
|
|
77
|
-
catch (ex) {
|
|
78
|
-
this.stream.destroy(ex);
|
|
79
|
-
}
|
|
80
|
-
},
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
return this.stream;
|
|
84
21
|
}
|
|
85
22
|
}
|
|
86
23
|
//# sourceMappingURL=encoder.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"encoder.js","sourceRoot":"","sources":["../../src/stream/encoder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"encoder.js","sourceRoot":"","sources":["../../src/stream/encoder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAqB,MAAM,aAAa,CAAC;AAC3D,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;IACP,CAAC;IAED,kBAAkB;IACT,UAAU,CAAC,GAAY,EAAE,SAAyB,EAAE,QAA2B;QACpF,MAAM,MAAM,GAAG,IAAI,mBAAmB,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;QAC3E,IAAI;YACA,MAAM,CAAC,MAAM,EAAE,CAAC;YAChB,QAAQ,EAAE,CAAC;SACd;QAAC,OAAO,EAAE,EAAE;YACT,QAAQ,CAAC,EAAW,CAAC,CAAC;SACzB;IACL,CAAC;CACJ"}
|
package/dist/stream/index.d.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
-
|
|
2
|
+
/// <reference types="node" />
|
|
3
|
+
import { Readable, Transform } from 'node:stream';
|
|
3
4
|
/** 编码为 UBJSON */
|
|
4
5
|
export declare function encode(value: unknown): Readable;
|
|
6
|
+
/** 编码为 UBJSON */
|
|
7
|
+
export declare function encoder(): Transform;
|
|
8
|
+
/** 解码 UBJSON */
|
|
9
|
+
export declare function decode(stream: NodeJS.ReadableStream): Promise<unknown>;
|
|
10
|
+
/** 解码 UBJSON */
|
|
11
|
+
export declare function decoder(): Transform;
|
package/dist/stream/index.js
CHANGED
|
@@ -1,7 +1,32 @@
|
|
|
1
|
+
import { Readable } from 'node:stream';
|
|
1
2
|
import { StreamEncoder } from './encoder.js';
|
|
3
|
+
import { StreamDecoder } from './decoder.js';
|
|
2
4
|
/** 编码为 UBJSON */
|
|
3
5
|
export function encode(value) {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
+
if (value == null) {
|
|
7
|
+
return Readable.from([value === null ? 'Z' : 'N'], { objectMode: false });
|
|
8
|
+
}
|
|
9
|
+
const encoder = new StreamEncoder();
|
|
10
|
+
encoder.write(value);
|
|
11
|
+
encoder.end();
|
|
12
|
+
return encoder;
|
|
13
|
+
}
|
|
14
|
+
/** 编码为 UBJSON */
|
|
15
|
+
export function encoder() {
|
|
16
|
+
return new StreamEncoder();
|
|
17
|
+
}
|
|
18
|
+
/** 解码 UBJSON */
|
|
19
|
+
export function decode(stream) {
|
|
20
|
+
return new Promise((resolve, reject) => {
|
|
21
|
+
const decoder = new StreamDecoder();
|
|
22
|
+
decoder.on('error', reject);
|
|
23
|
+
decoder.on('end', resolve);
|
|
24
|
+
decoder.on('data', resolve);
|
|
25
|
+
stream.pipe(decoder);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
/** 解码 UBJSON */
|
|
29
|
+
export function decoder() {
|
|
30
|
+
return new StreamDecoder();
|
|
6
31
|
}
|
|
7
32
|
//# sourceMappingURL=index.js.map
|
package/dist/stream/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/stream/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/stream/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAa,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,iBAAiB;AACjB,MAAM,UAAU,MAAM,CAAC,KAAc;IACjC,IAAI,KAAK,IAAI,IAAI,EAAE;QACf,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC;KAC7E;IACD,MAAM,OAAO,GAAG,IAAI,aAAa,EAAE,CAAC;IACpC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACrB,OAAO,CAAC,GAAG,EAAE,CAAC;IACd,OAAO,OAAO,CAAC;AACnB,CAAC;AAED,iBAAiB;AACjB,MAAM,UAAU,OAAO;IACnB,OAAO,IAAI,aAAa,EAAE,CAAC;AAC/B,CAAC;AAED,gBAAgB;AAChB,MAAM,UAAU,MAAM,CAAC,MAA6B;IAChD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACnC,MAAM,OAAO,GAAG,IAAI,aAAa,EAAE,CAAC;QACpC,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC5B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QAC3B,OAAO,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC5B,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACzB,CAAC,CAAC,CAAC;AACP,CAAC;AAED,gBAAgB;AAChB,MAAM,UAAU,OAAO;IACnB,OAAO,IAAI,aAAa,EAAE,CAAC;AAC/B,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Decoder, UnexpectedEof } from '../common/decoder.js';
|
|
2
|
+
export { UnexpectedEof };
|
|
3
|
+
/** 流式解码 UBJSON */
|
|
4
|
+
export class StreamDecoderHelper extends Decoder {
|
|
5
|
+
constructor(data) {
|
|
6
|
+
super(data);
|
|
7
|
+
}
|
|
8
|
+
/** 读取的字节数 */
|
|
9
|
+
get readLength() {
|
|
10
|
+
return this.offset;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=decoder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decoder.js","sourceRoot":"","sources":["../../src/stream-helper/decoder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE9D,OAAO,EAAE,aAAa,EAAE,CAAC;AAEzB,kBAAkB;AAClB,MAAM,OAAO,mBAAoB,SAAQ,OAAO;IAC5C,YAAY,IAAgB;QACxB,KAAK,CAAC,IAAI,CAAC,CAAC;IAChB,CAAC;IAED,aAAa;IACb,IAAI,UAAU;QACV,OAAO,IAAI,CAAC,MAAM,CAAC;IACvB,CAAC;CACJ"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { EncoderBase } from '../common/encoder.js';
|
|
2
|
+
/** 流式编码 UBJSON */
|
|
3
|
+
export declare class StreamEncoderHelper extends EncoderBase {
|
|
4
|
+
protected readonly onChunk: (chunk: Uint8Array) => void;
|
|
5
|
+
constructor(value: unknown, onChunk: (chunk: Uint8Array) => void);
|
|
6
|
+
/**
|
|
7
|
+
* 确保 buffer 还有 capacity 的空闲空间
|
|
8
|
+
*/
|
|
9
|
+
protected ensureCapacity(capacity: number): void;
|
|
10
|
+
/** 获取写入结果 */
|
|
11
|
+
encode(): void;
|
|
12
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { EncoderBase } from '../common/encoder.js';
|
|
2
|
+
const BLOCK_SIZE = 1024 * 8; // 8 KiB
|
|
3
|
+
const MAX_SIZE = 1024 * 1024 * 256; // 256 MiB
|
|
4
|
+
/** 流式编码 UBJSON */
|
|
5
|
+
export class StreamEncoderHelper extends EncoderBase {
|
|
6
|
+
constructor(value, onChunk) {
|
|
7
|
+
super(value);
|
|
8
|
+
this.onChunk = onChunk;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* 确保 buffer 还有 capacity 的空闲空间
|
|
12
|
+
*/
|
|
13
|
+
ensureCapacity(capacity) {
|
|
14
|
+
if (capacity > MAX_SIZE) {
|
|
15
|
+
// 超过最大尺寸限制
|
|
16
|
+
throw new Error('Buffer has exceed max size');
|
|
17
|
+
}
|
|
18
|
+
if (this.buffer == null) {
|
|
19
|
+
this.buffer = new Uint8Array(capacity);
|
|
20
|
+
this.view = new DataView(this.buffer.buffer);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (capacity < 0) {
|
|
24
|
+
// 结束流
|
|
25
|
+
this.onChunk(this.buffer.subarray(0, this.length));
|
|
26
|
+
this.buffer = new Uint8Array(0);
|
|
27
|
+
this.view = new DataView(this.buffer.buffer);
|
|
28
|
+
this.length = 0;
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
// 无需扩容
|
|
32
|
+
if (this.buffer.byteLength >= this.length + capacity)
|
|
33
|
+
return;
|
|
34
|
+
// 提交目前的数据
|
|
35
|
+
this.onChunk(this.buffer.subarray(0, this.length));
|
|
36
|
+
// 重新分配缓冲区
|
|
37
|
+
if (capacity < BLOCK_SIZE)
|
|
38
|
+
capacity = BLOCK_SIZE;
|
|
39
|
+
this.buffer = new Uint8Array(capacity);
|
|
40
|
+
this.view = new DataView(this.buffer.buffer);
|
|
41
|
+
this.length = 0;
|
|
42
|
+
}
|
|
43
|
+
/** 获取写入结果 */
|
|
44
|
+
encode() {
|
|
45
|
+
if (typeof this.value != 'object' || this.value == null || ArrayBuffer.isView(this.value)) {
|
|
46
|
+
this.ensureCapacity(20);
|
|
47
|
+
this.write(this.value);
|
|
48
|
+
this.ensureCapacity(-1);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
this.ensureCapacity(BLOCK_SIZE);
|
|
52
|
+
this.write(this.value);
|
|
53
|
+
this.ensureCapacity(-1);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=encoder.js.map
|
|
@@ -0,0 +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;IAChD,YAAY,KAAc,EAAqB,OAAoC;QAC/E,KAAK,CAAC,KAAK,CAAC,CAAC;QAD8B,YAAO,GAAP,OAAO,CAA6B;IAEnF,CAAC;IACD;;OAEG;IACO,cAAc,CAAC,QAAgB;QACrC,IAAI,QAAQ,GAAG,QAAQ,EAAE;YACrB,WAAW;YACX,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;SACjD;QACD,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI,EAAE;YACrB,IAAI,CAAC,MAAM,GAAG,IAAI,UAAU,CAAC,QAAQ,CAAC,CAAC;YACvC,IAAI,CAAC,IAAI,GAAG,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC7C,OAAO;SACV;QACD,IAAI,QAAQ,GAAG,CAAC,EAAE;YACd,MAAM;YACN,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;YACnD,IAAI,CAAC,MAAM,GAAG,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC;YAChC,IAAI,CAAC,IAAI,GAAG,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC7C,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;YAChB,OAAO;SACV;QACD,OAAO;QACP,IAAI,IAAI,CAAC,MAAM,CAAC,UAAU,IAAI,IAAI,CAAC,MAAM,GAAG,QAAQ;YAAE,OAAO;QAE7D,UAAU;QACV,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;QAEnD,UAAU;QACV,IAAI,QAAQ,GAAG,UAAU;YAAE,QAAQ,GAAG,UAAU,CAAC;QACjD,IAAI,CAAC,MAAM,GAAG,IAAI,UAAU,CAAC,QAAQ,CAAC,CAAC;QACvC,IAAI,CAAC,IAAI,GAAG,IAAI,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAC7C,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;IACpB,CAAC;IACD,aAAa;IACb,MAAM;QACF,IAAI,OAAO,IAAI,CAAC,KAAK,IAAI,QAAQ,IAAI,IAAI,CAAC,KAAK,IAAI,IAAI,IAAI,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;YACvF,IAAI,CAAC,cAAc,CAAC,EAAE,CAAC,CAAC;YACxB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACvB,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;SAC3B;aAAM;YACH,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;YAChC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACvB,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;SAC3B;IACL,CAAC;CACJ"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudpss/ubjson",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"author": "CloudPSS",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -42,6 +42,9 @@
|
|
|
42
42
|
"test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
|
-
"@msgpack/msgpack": "^2.
|
|
45
|
+
"@msgpack/msgpack": "^2.8.0"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"rxjs": "^7.5.7"
|
|
46
49
|
}
|
|
47
50
|
}
|
package/src/common/decoder.ts
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import * as constants from './constants.js';
|
|
2
2
|
import { StringDecoder } from './string-decoder.js';
|
|
3
3
|
|
|
4
|
+
/** 未结束的流 */
|
|
5
|
+
export class UnexpectedEof extends Error {
|
|
6
|
+
constructor() {
|
|
7
|
+
super('Unexpected EOF');
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
4
11
|
/** decoder */
|
|
5
12
|
export class Decoder {
|
|
6
|
-
|
|
13
|
+
protected readonly view: DataView;
|
|
7
14
|
/** 当前读指针位置 */
|
|
8
|
-
|
|
15
|
+
protected offset = 0;
|
|
9
16
|
constructor(readonly data: Uint8Array) {
|
|
10
17
|
this.view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
11
18
|
}
|
|
@@ -15,9 +22,9 @@ export class Decoder {
|
|
|
15
22
|
}
|
|
16
23
|
|
|
17
24
|
/** 检查是否有给定字节数的未读数据 */
|
|
18
|
-
|
|
25
|
+
protected ensureData(byteLength: number): void {
|
|
19
26
|
if (this.offset + byteLength > this.data.byteLength) {
|
|
20
|
-
throw new
|
|
27
|
+
throw new UnexpectedEof();
|
|
21
28
|
}
|
|
22
29
|
}
|
|
23
30
|
|
|
@@ -247,6 +254,8 @@ export class Decoder {
|
|
|
247
254
|
case constants.INT64:
|
|
248
255
|
this.ensureData(count * 8);
|
|
249
256
|
return BigInt64Array.from({ length: count }, () => this.readInt64Data());
|
|
257
|
+
default:
|
|
258
|
+
break;
|
|
250
259
|
}
|
|
251
260
|
const array: unknown[] = [];
|
|
252
261
|
array.length = count;
|
package/src/common/encoder.ts
CHANGED
|
@@ -45,8 +45,9 @@ export abstract class EncoderBase {
|
|
|
45
45
|
}
|
|
46
46
|
return this.writeObject(value as Record<string, unknown>);
|
|
47
47
|
}
|
|
48
|
+
default:
|
|
49
|
+
throw new Error(`Unsupported type ${Object.prototype.toString.call(value)}`);
|
|
48
50
|
}
|
|
49
|
-
throw new Error(`Unsupported type ${Object.prototype.toString.call(value)}`);
|
|
50
51
|
}
|
|
51
52
|
/** 写入 marker */
|
|
52
53
|
protected writeMarker(marker: number): void {
|
|
@@ -4,6 +4,8 @@ export const textDecoder =
|
|
|
4
4
|
export const TEXT_ENCODER_THRESHOLD = textDecoder == null ? 0xffff_ffff : 200;
|
|
5
5
|
|
|
6
6
|
const CHUNK_SIZE = 0x1_000;
|
|
7
|
+
const REPLACE_CHAR = 0xfffd;
|
|
8
|
+
|
|
7
9
|
/** 解码 */
|
|
8
10
|
export function decodeJs(bytes: Uint8Array, begin: number, end: number): string {
|
|
9
11
|
let offset = begin;
|
|
@@ -37,7 +39,7 @@ export function decodeJs(bytes: Uint8Array, begin: number, end: number): string
|
|
|
37
39
|
}
|
|
38
40
|
units.push(unit);
|
|
39
41
|
} else {
|
|
40
|
-
units.push(
|
|
42
|
+
units.push(REPLACE_CHAR);
|
|
41
43
|
}
|
|
42
44
|
|
|
43
45
|
if (units.length >= CHUNK_SIZE) {
|
package/src/decoder.ts
ADDED
package/src/encoder.ts
CHANGED
|
@@ -1,53 +1,35 @@
|
|
|
1
1
|
import { EncoderBase } from './common/encoder.js';
|
|
2
2
|
|
|
3
|
-
const
|
|
3
|
+
const BLOCK_SIZE = 1024 * 16; // 8 KiB
|
|
4
4
|
const MAX_SIZE = 1024 * 1024 * 128; //128 MiB
|
|
5
|
-
const RESIZE_FACTOR = 4;
|
|
6
|
-
const RESIZE_FACTOR_2 = 2;
|
|
7
5
|
|
|
8
6
|
/** 编码至 ubjson */
|
|
9
7
|
export class Encoder extends EncoderBase {
|
|
8
|
+
private flushedBuffers: Uint8Array[] = [];
|
|
10
9
|
/**
|
|
11
10
|
* 确保 buffer 还有 capacity 的空闲空间
|
|
12
11
|
*/
|
|
13
12
|
protected ensureCapacity(capacity: number): void {
|
|
13
|
+
if (capacity > MAX_SIZE) {
|
|
14
|
+
// 超过最大尺寸限制
|
|
15
|
+
throw new Error('Buffer has exceed max size');
|
|
16
|
+
}
|
|
14
17
|
if (this.buffer == null) {
|
|
15
|
-
if (capacity > MAX_SIZE) {
|
|
16
|
-
// 超过最大尺寸限制
|
|
17
|
-
throw new Error('Buffer has exceed max size');
|
|
18
|
-
}
|
|
19
18
|
this.buffer = new Uint8Array(capacity);
|
|
20
19
|
this.view = new DataView(this.buffer.buffer);
|
|
21
20
|
return;
|
|
22
21
|
}
|
|
23
|
-
/** 需求总尺寸 */
|
|
24
|
-
const nextCapacityMin = this.length + capacity;
|
|
25
22
|
// 无需扩容
|
|
26
|
-
if (this.buffer.byteLength >=
|
|
27
|
-
if (nextCapacityMin > MAX_SIZE) {
|
|
28
|
-
// 超过最大尺寸限制
|
|
29
|
-
throw new Error('Buffer has exceed max size');
|
|
30
|
-
}
|
|
23
|
+
if (this.buffer.byteLength >= this.length + capacity) return;
|
|
31
24
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if (nextCapacityMin < this.buffer.byteLength * RESIZE_FACTOR) {
|
|
35
|
-
// 一般情况下,扩容 RESIZE_FACTOR 倍
|
|
36
|
-
nextCapacity = (this.buffer.byteLength * RESIZE_FACTOR) | 0;
|
|
37
|
-
} else {
|
|
38
|
-
// 扩容 RESIZE_FACTOR 倍也无法满足需求时,扩容至 (需求总尺寸 + 当前尺寸 * 1 / RESIZE_FACTOR_2)
|
|
39
|
-
// 前者满足需求总尺寸,后者预留一部分空间防止频繁扩容
|
|
40
|
-
nextCapacity = (this.buffer.byteLength / RESIZE_FACTOR_2 + nextCapacityMin) | 0;
|
|
41
|
-
}
|
|
42
|
-
if (nextCapacity > MAX_SIZE) {
|
|
43
|
-
// 约束至最大尺寸限制,由于前面已经检查过一次,此处约束后的尺寸一定能满足要求
|
|
44
|
-
nextCapacity = MAX_SIZE;
|
|
45
|
-
}
|
|
25
|
+
// 提交目前的数据
|
|
26
|
+
this.flushedBuffers.push(this.buffer.subarray(0, this.length));
|
|
46
27
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
this.buffer =
|
|
50
|
-
this.view = new DataView(
|
|
28
|
+
// 重新分配缓冲区
|
|
29
|
+
if (capacity < BLOCK_SIZE) capacity = BLOCK_SIZE;
|
|
30
|
+
this.buffer = new Uint8Array(capacity);
|
|
31
|
+
this.view = new DataView(this.buffer.buffer);
|
|
32
|
+
this.length = 0;
|
|
51
33
|
}
|
|
52
34
|
|
|
53
35
|
/** 获取写入结果 */
|
|
@@ -65,14 +47,28 @@ export class Encoder extends EncoderBase {
|
|
|
65
47
|
// throw new Error('Bad buffer size');
|
|
66
48
|
// }
|
|
67
49
|
} else {
|
|
68
|
-
this.ensureCapacity(
|
|
50
|
+
this.ensureCapacity(BLOCK_SIZE);
|
|
69
51
|
this.write(this.value);
|
|
70
52
|
}
|
|
71
|
-
if (this.
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
53
|
+
if (this.flushedBuffers.length === 0) {
|
|
54
|
+
if (this.buffer.byteLength === this.length) {
|
|
55
|
+
// 无需再拷贝一次
|
|
56
|
+
return this.buffer;
|
|
57
|
+
} else {
|
|
58
|
+
return this.buffer.slice(0, this.length);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 合并缓冲区
|
|
63
|
+
const result = new Uint8Array(
|
|
64
|
+
this.length + this.flushedBuffers.reduce((sum, buffer) => sum + buffer.byteLength, 0),
|
|
65
|
+
);
|
|
66
|
+
let offset = 0;
|
|
67
|
+
for (const buffer of this.flushedBuffers) {
|
|
68
|
+
result.set(buffer, offset);
|
|
69
|
+
offset += buffer.byteLength;
|
|
76
70
|
}
|
|
71
|
+
result.set(this.buffer.subarray(0, this.length), offset);
|
|
72
|
+
return result;
|
|
77
73
|
}
|
|
78
74
|
}
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { Observable, OperatorFunction } from 'rxjs';
|
|
2
|
+
import { StreamDecoderHelper, UnexpectedEof } from '../stream-helper/decoder.js';
|
|
3
|
+
|
|
4
|
+
const DEFAULT_BUFFER_SIZE = 16 * 1024 * 1024; // 16 MiB
|
|
5
|
+
const EMPTY_BUFFER = new Uint8Array(0);
|
|
6
|
+
|
|
7
|
+
/** 流式解码 UBJSON */
|
|
8
|
+
export function decode(): OperatorFunction<Uint8Array, unknown> {
|
|
9
|
+
return (observable) => {
|
|
10
|
+
return new Observable<unknown>((subscriber) => {
|
|
11
|
+
let chunkSize = DEFAULT_BUFFER_SIZE;
|
|
12
|
+
let buffer = new Uint8Array(chunkSize);
|
|
13
|
+
let begin = 0;
|
|
14
|
+
let end = 0;
|
|
15
|
+
return observable.subscribe({
|
|
16
|
+
next(value) {
|
|
17
|
+
if (buffer.length >= end + value.length) {
|
|
18
|
+
buffer.set(value, end);
|
|
19
|
+
end += value.length;
|
|
20
|
+
} else {
|
|
21
|
+
chunkSize = Math.max(chunkSize, value.length * 2 + end - begin);
|
|
22
|
+
const newBuffer = new Uint8Array(Math.max(chunkSize, value.length + end - begin));
|
|
23
|
+
newBuffer.set(buffer.subarray(begin, end), 0);
|
|
24
|
+
newBuffer.set(value, end - begin);
|
|
25
|
+
buffer = newBuffer;
|
|
26
|
+
end = end - begin + value.length;
|
|
27
|
+
begin = 0;
|
|
28
|
+
}
|
|
29
|
+
while (end - begin > 0) {
|
|
30
|
+
try {
|
|
31
|
+
const helper = new StreamDecoderHelper(buffer.subarray(begin, end));
|
|
32
|
+
const result = helper.decode();
|
|
33
|
+
if (result !== undefined) {
|
|
34
|
+
subscriber.next(result);
|
|
35
|
+
}
|
|
36
|
+
begin += helper.readLength;
|
|
37
|
+
} catch (ex) {
|
|
38
|
+
if (ex instanceof UnexpectedEof) {
|
|
39
|
+
return;
|
|
40
|
+
} else {
|
|
41
|
+
subscriber.error(ex as Error);
|
|
42
|
+
buffer = EMPTY_BUFFER;
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// 完全消费了 Buffer 内容,重置 Buffer
|
|
48
|
+
begin = end = 0;
|
|
49
|
+
},
|
|
50
|
+
error(err) {
|
|
51
|
+
subscriber.error(err);
|
|
52
|
+
buffer = EMPTY_BUFFER;
|
|
53
|
+
},
|
|
54
|
+
complete() {
|
|
55
|
+
if (end - begin > 0) {
|
|
56
|
+
subscriber.error(new UnexpectedEof());
|
|
57
|
+
} else {
|
|
58
|
+
subscriber.complete();
|
|
59
|
+
}
|
|
60
|
+
buffer = EMPTY_BUFFER;
|
|
61
|
+
},
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { Observable, OperatorFunction } from 'rxjs';
|
|
2
|
+
import { StreamEncoderHelper } from '../stream-helper/encoder.js';
|
|
3
|
+
|
|
4
|
+
/** 流式编码 UBJSON */
|
|
5
|
+
export function encode(): OperatorFunction<unknown, Uint8Array> {
|
|
6
|
+
return (observable) => {
|
|
7
|
+
return new Observable<Uint8Array>((subscriber) => {
|
|
8
|
+
const onChunk = (chunk: Uint8Array): void => subscriber.next(chunk);
|
|
9
|
+
return observable.subscribe({
|
|
10
|
+
next(value) {
|
|
11
|
+
try {
|
|
12
|
+
const helper = new StreamEncoderHelper(value, onChunk);
|
|
13
|
+
helper.encode();
|
|
14
|
+
} catch (ex) {
|
|
15
|
+
subscriber.error(ex as Error);
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
error(err) {
|
|
19
|
+
subscriber.error(err);
|
|
20
|
+
},
|
|
21
|
+
complete() {
|
|
22
|
+
subscriber.complete();
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Transform, TransformCallback } from 'node:stream';
|
|
2
|
+
import { Subject } from 'rxjs';
|
|
3
|
+
import { decode } from '../rxjs/decoder.js';
|
|
4
|
+
|
|
5
|
+
/** 流式解码 UBJSON */
|
|
6
|
+
export class StreamDecoder extends Transform {
|
|
7
|
+
constructor() {
|
|
8
|
+
super({
|
|
9
|
+
readableObjectMode: true,
|
|
10
|
+
writableObjectMode: false,
|
|
11
|
+
});
|
|
12
|
+
this.buffer = new Subject<Uint8Array>();
|
|
13
|
+
this.buffer.pipe(decode()).subscribe({
|
|
14
|
+
next: (value) => {
|
|
15
|
+
// null is not allowed in a stream
|
|
16
|
+
if (value == null) return;
|
|
17
|
+
this.push(value);
|
|
18
|
+
},
|
|
19
|
+
error: (err: Error) => {
|
|
20
|
+
if (this.callback) {
|
|
21
|
+
this.callback(err);
|
|
22
|
+
this.callback = undefined;
|
|
23
|
+
} else {
|
|
24
|
+
this.emit('error', err);
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
complete: () => {
|
|
28
|
+
this.push(null);
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
private buffer;
|
|
33
|
+
private callback?: TransformCallback;
|
|
34
|
+
|
|
35
|
+
/** @inheritdoc */
|
|
36
|
+
override _transform(chunk: Buffer, _: BufferEncoding, callback: TransformCallback): void {
|
|
37
|
+
this.callback = callback;
|
|
38
|
+
this.buffer.next(chunk);
|
|
39
|
+
if (this.callback) {
|
|
40
|
+
callback();
|
|
41
|
+
this.callback = undefined;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** @inheritdoc */
|
|
46
|
+
override _flush(callback: TransformCallback): void {
|
|
47
|
+
this.callback = callback;
|
|
48
|
+
this.buffer.complete();
|
|
49
|
+
if (this.callback) {
|
|
50
|
+
callback();
|
|
51
|
+
this.callback = undefined;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|