@cloudpss/ubjson 0.5.40 → 0.5.41
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/base/encoder.d.ts +2 -0
- package/dist/base/encoder.d.ts.map +1 -1
- package/dist/base/encoder.js +6 -1
- package/dist/base/encoder.js.map +1 -1
- package/dist/encoder.d.ts +2 -1
- package/dist/encoder.d.ts.map +1 -1
- package/dist/encoder.js +2 -1
- package/dist/encoder.js.map +1 -1
- package/dist/helper/decode-ae.d.ts +50 -0
- package/dist/helper/decode-ae.d.ts.map +1 -0
- package/dist/helper/decode-ae.js +584 -0
- package/dist/helper/decode-ae.js.map +1 -0
- package/dist/helper/decode.d.ts.map +1 -1
- package/dist/helper/decode.js +7 -5
- package/dist/helper/decode.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -4
- package/dist/index.js.map +1 -1
- package/dist/options.d.ts +6 -0
- package/dist/options.d.ts.map +1 -0
- package/dist/options.js +2 -0
- package/dist/options.js.map +1 -0
- package/dist/rxjs/decoder.d.ts.map +1 -1
- package/dist/rxjs/decoder.js +66 -40
- package/dist/rxjs/decoder.js.map +1 -1
- package/dist/rxjs/encoder.d.ts +2 -1
- package/dist/rxjs/encoder.d.ts.map +1 -1
- package/dist/rxjs/encoder.js +2 -2
- package/dist/rxjs/encoder.js.map +1 -1
- package/dist/rxjs/index.d.ts +1 -0
- package/dist/rxjs/index.d.ts.map +1 -1
- package/dist/stream/encoder.d.ts +3 -2
- package/dist/stream/encoder.d.ts.map +1 -1
- package/dist/stream/encoder.js +2 -2
- package/dist/stream/encoder.js.map +1 -1
- package/dist/stream/index.d.ts +5 -3
- package/dist/stream/index.d.ts.map +1 -1
- package/dist/stream/index.js +6 -6
- package/dist/stream/index.js.map +1 -1
- package/dist/stream-helper/encoder.d.ts +2 -1
- package/dist/stream-helper/encoder.d.ts.map +1 -1
- package/dist/stream-helper/encoder.js +2 -1
- package/dist/stream-helper/encoder.js.map +1 -1
- package/package.json +2 -2
- package/src/base/encoder.ts +6 -1
- package/src/encoder.ts +3 -1
- package/src/helper/decode-ae.ts +621 -0
- package/src/helper/decode.ts +6 -5
- package/src/index.ts +7 -4
- package/src/options.ts +5 -0
- package/src/rxjs/decoder.ts +66 -40
- package/src/rxjs/encoder.ts +3 -2
- package/src/rxjs/index.ts +1 -0
- package/src/stream/encoder.ts +4 -3
- package/src/stream/index.ts +8 -6
- package/src/stream-helper/encoder.ts +6 -1
- package/tests/.utils.js +1 -0
- package/tests/e2e/stream.js +13 -1
- package/tests/encode.js +1 -1
package/src/rxjs/decoder.ts
CHANGED
|
@@ -1,69 +1,95 @@
|
|
|
1
1
|
import { Observable, type OperatorFunction } from 'rxjs';
|
|
2
|
-
import {
|
|
2
|
+
import { UnexpectedEof } from '../stream-helper/decoder.js';
|
|
3
3
|
import { toUint8Array } from '../helper/utils.js';
|
|
4
|
+
import { read } from '../helper/decode-ae.js';
|
|
4
5
|
|
|
5
|
-
const DEFAULT_BUFFER_SIZE = 16 * 1024 * 1024; // 16 MiB
|
|
6
6
|
const EMPTY_BUFFER = new Uint8Array(0);
|
|
7
|
+
const EMPTY_VIEW = new DataView(EMPTY_BUFFER.buffer);
|
|
7
8
|
|
|
8
9
|
/** 流式解码 UBJSON */
|
|
9
10
|
export function decode(): OperatorFunction<BinaryData, unknown> {
|
|
10
11
|
return (observable) => {
|
|
11
12
|
return new Observable<unknown>((subscriber) => {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
const data = EMPTY_BUFFER;
|
|
14
|
+
const cursor = {
|
|
15
|
+
view: EMPTY_VIEW,
|
|
16
|
+
data,
|
|
17
|
+
capacity: 0,
|
|
18
|
+
size: 0,
|
|
19
|
+
offset: 0,
|
|
20
|
+
};
|
|
21
|
+
/** reader 返回的还需接收的字节数 */
|
|
22
|
+
let required = 1;
|
|
23
|
+
let reader = read(cursor);
|
|
16
24
|
return observable.subscribe({
|
|
17
25
|
next(value) {
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
26
|
+
const chunk = toUint8Array(value);
|
|
27
|
+
const chunkSize = chunk.byteLength;
|
|
28
|
+
if (cursor.capacity - cursor.size < chunkSize) {
|
|
29
|
+
// 当前缓冲区不足,需要扩容
|
|
30
|
+
const newSize = Math.max(
|
|
31
|
+
// 不缩小缓冲区
|
|
32
|
+
cursor.capacity,
|
|
33
|
+
// 扩大缓冲区到足够容纳 2 倍当前数据
|
|
34
|
+
chunkSize * 2 + cursor.size - cursor.offset,
|
|
35
|
+
);
|
|
36
|
+
if (newSize > cursor.capacity) {
|
|
37
|
+
// 需要增大缓冲区
|
|
38
|
+
const newData = new Uint8Array(newSize);
|
|
39
|
+
newData.set(cursor.data.subarray(cursor.offset, cursor.size), 0);
|
|
40
|
+
newData.set(chunk, cursor.size - cursor.offset);
|
|
41
|
+
cursor.data = newData;
|
|
42
|
+
cursor.view = new DataView(newData.buffer, newData.byteOffset, newData.byteLength);
|
|
43
|
+
cursor.capacity = newSize;
|
|
44
|
+
} else {
|
|
45
|
+
// 无需增大缓冲区,直接移动数据
|
|
46
|
+
cursor.data.copyWithin(0, cursor.offset, cursor.size);
|
|
47
|
+
cursor.data.set(chunk, cursor.size - cursor.offset);
|
|
48
|
+
}
|
|
49
|
+
cursor.size = cursor.size - cursor.offset + chunkSize;
|
|
50
|
+
cursor.offset = 0;
|
|
22
51
|
} else {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
allocSize > buffer.byteLength
|
|
27
|
-
? new Uint8Array(Math.max(chunkSize, data.length + end - begin))
|
|
28
|
-
: buffer;
|
|
29
|
-
newBuffer.set(buffer.subarray(begin, end), 0);
|
|
30
|
-
newBuffer.set(data, end - begin);
|
|
31
|
-
buffer = newBuffer;
|
|
32
|
-
end = end - begin + data.length;
|
|
33
|
-
begin = 0;
|
|
52
|
+
// 当前缓冲区足够,直接写入
|
|
53
|
+
cursor.data.set(chunk, cursor.size);
|
|
54
|
+
cursor.size += chunkSize;
|
|
34
55
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
56
|
+
|
|
57
|
+
required -= chunkSize;
|
|
58
|
+
// 未读够数据,继续等待
|
|
59
|
+
if (required > 0) return;
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
for (;;) {
|
|
63
|
+
const result = reader.next();
|
|
64
|
+
if (result.done) {
|
|
65
|
+
// 读取完成,新建 reader 读取下一个值
|
|
66
|
+
subscriber.next(result.value);
|
|
67
|
+
reader = read(cursor);
|
|
68
|
+
if (cursor.offset === cursor.size) {
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
46
71
|
} else {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return;
|
|
72
|
+
required = result.value;
|
|
73
|
+
break;
|
|
50
74
|
}
|
|
51
75
|
}
|
|
76
|
+
} catch (ex) {
|
|
77
|
+
subscriber.error(ex);
|
|
52
78
|
}
|
|
53
|
-
// 完全消费了 Buffer 内容,重置 Buffer
|
|
54
|
-
begin = end = 0;
|
|
55
79
|
},
|
|
56
80
|
error(err) {
|
|
57
81
|
subscriber.error(err);
|
|
58
|
-
|
|
82
|
+
cursor.data = EMPTY_BUFFER;
|
|
83
|
+
cursor.view = EMPTY_VIEW;
|
|
59
84
|
},
|
|
60
85
|
complete() {
|
|
61
|
-
if (
|
|
86
|
+
if (cursor.size > cursor.offset) {
|
|
62
87
|
subscriber.error(new UnexpectedEof());
|
|
63
88
|
} else {
|
|
64
89
|
subscriber.complete();
|
|
65
90
|
}
|
|
66
|
-
|
|
91
|
+
cursor.data = EMPTY_BUFFER;
|
|
92
|
+
cursor.view = EMPTY_VIEW;
|
|
67
93
|
},
|
|
68
94
|
});
|
|
69
95
|
});
|
package/src/rxjs/encoder.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { Observable, type OperatorFunction } from 'rxjs';
|
|
2
2
|
import { StreamEncoderHelper } from '../stream-helper/encoder.js';
|
|
3
|
+
import type { EncodeOptions } from '../options.js';
|
|
3
4
|
|
|
4
5
|
/** 流式编码 UBJSON */
|
|
5
|
-
export function encode(): OperatorFunction<unknown, Uint8Array> {
|
|
6
|
+
export function encode(options?: EncodeOptions): OperatorFunction<unknown, Uint8Array> {
|
|
6
7
|
return (observable) => {
|
|
7
8
|
return new Observable<Uint8Array>((subscriber) => {
|
|
8
|
-
const helper = new StreamEncoderHelper((chunk: Uint8Array): void => subscriber.next(chunk));
|
|
9
|
+
const helper = new StreamEncoderHelper(options, (chunk: Uint8Array): void => subscriber.next(chunk));
|
|
9
10
|
const sub = observable.subscribe({
|
|
10
11
|
next(value) {
|
|
11
12
|
try {
|
package/src/rxjs/index.ts
CHANGED
package/src/stream/encoder.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { Transform, type TransformCallback } from 'node:stream';
|
|
2
2
|
import { StreamEncoderHelper } from '../stream-helper/encoder.js';
|
|
3
|
+
import type { EncodeOptions } from '../options.js';
|
|
3
4
|
|
|
4
5
|
/** 流式编码 UBJSON */
|
|
5
6
|
export class StreamEncoder extends Transform {
|
|
6
|
-
constructor() {
|
|
7
|
+
constructor(options?: EncodeOptions) {
|
|
7
8
|
super({
|
|
8
9
|
readableObjectMode: false,
|
|
9
10
|
writableObjectMode: true,
|
|
10
11
|
});
|
|
11
|
-
this.helper = new StreamEncoderHelper((binary) => this.push(binary));
|
|
12
|
+
this.helper = new StreamEncoderHelper(options, (binary) => this.push(binary));
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
private readonly helper;
|
|
@@ -24,7 +25,7 @@ export class StreamEncoder extends Transform {
|
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
/** @inheritdoc */
|
|
27
|
-
override _destroy(error: Error | null, callback: (error?: Error | null
|
|
28
|
+
override _destroy(error: Error | null, callback: (error?: Error | null) => void): void {
|
|
28
29
|
this.helper.destroy();
|
|
29
30
|
super._destroy(error, callback);
|
|
30
31
|
}
|
package/src/stream/index.ts
CHANGED
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
import { Readable, type Transform } from 'node:stream';
|
|
2
2
|
import { StreamEncoder } from './encoder.js';
|
|
3
3
|
import { StreamDecoder } from './decoder.js';
|
|
4
|
+
import type { EncodeOptions } from '../options.js';
|
|
4
5
|
|
|
5
6
|
export { UnexpectedEofError as UnexpectedEof } from '../helper/errors.js';
|
|
7
|
+
export type { EncodeOptions };
|
|
6
8
|
|
|
7
9
|
/** 编码为 UBJSON */
|
|
8
|
-
export function encode(value: unknown): Readable {
|
|
10
|
+
export function encode(value: unknown, options?: EncodeOptions): Readable {
|
|
9
11
|
if (value == null) {
|
|
10
12
|
return Readable.from([value === null ? 'Z' : 'N'], { objectMode: false });
|
|
11
13
|
}
|
|
12
|
-
const encoder = new StreamEncoder();
|
|
14
|
+
const encoder = new StreamEncoder(options);
|
|
13
15
|
encoder.write(value);
|
|
14
16
|
encoder.end();
|
|
15
17
|
return encoder;
|
|
16
18
|
}
|
|
17
19
|
/** 编码为 UBJSON */
|
|
18
|
-
export function encodeMany(value: AsyncIterable<unknown
|
|
19
|
-
const encoder = new StreamEncoder();
|
|
20
|
+
export function encodeMany(value: AsyncIterable<unknown>, options?: EncodeOptions): Readable {
|
|
21
|
+
const encoder = new StreamEncoder(options);
|
|
20
22
|
void (async () => {
|
|
21
23
|
try {
|
|
22
24
|
for await (const v of value) {
|
|
@@ -32,8 +34,8 @@ export function encodeMany(value: AsyncIterable<unknown>): Readable {
|
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
/** 编码为 UBJSON */
|
|
35
|
-
export function encoder(): Transform {
|
|
36
|
-
return new StreamEncoder();
|
|
37
|
+
export function encoder(options?: EncodeOptions): Transform {
|
|
38
|
+
return new StreamEncoder(options);
|
|
37
39
|
}
|
|
38
40
|
|
|
39
41
|
/** 解码 UBJSON */
|
|
@@ -2,6 +2,7 @@ import { constants } from '../helper/constants.js';
|
|
|
2
2
|
import { encode, stringByteLength } from '../helper/string-encoder.js';
|
|
3
3
|
import { EncoderBase } from '../base/encoder.js';
|
|
4
4
|
import type { TypedArrayType } from '../helper/encode.js';
|
|
5
|
+
import type { EncodeOptions } from '../options.js';
|
|
5
6
|
|
|
6
7
|
const BLOCK_SIZE = 1024 * 64; // 64 KiB
|
|
7
8
|
const MAX_SIZE = 1024 * 1024 * 32; // 32 MiB
|
|
@@ -30,8 +31,12 @@ function free(buf: Uint8Array): boolean {
|
|
|
30
31
|
|
|
31
32
|
/** 流式编码 UBJSON */
|
|
32
33
|
export class StreamEncoderHelper extends EncoderBase {
|
|
33
|
-
constructor(
|
|
34
|
+
constructor(
|
|
35
|
+
options: EncodeOptions | null | undefined,
|
|
36
|
+
protected readonly onChunk: (chunk: Uint8Array) => void,
|
|
37
|
+
) {
|
|
34
38
|
super();
|
|
39
|
+
this.sortObjectKeys = options?.sortObjectKeys ?? false;
|
|
35
40
|
this.data = alloc(BLOCK_SIZE);
|
|
36
41
|
this.view = new DataView(this.data.buffer);
|
|
37
42
|
}
|
package/tests/.utils.js
CHANGED
|
@@ -2,6 +2,7 @@ import { resetEnv as resetDecoderEnv } from '../dist/helper/string-decoder.js';
|
|
|
2
2
|
import { resetEnv as resetEncoderEnv } from '../dist/helper/string-encoder.js';
|
|
3
3
|
import { resetEncoder } from '../dist/encoder.js';
|
|
4
4
|
import '../dist/helper/constants.js';
|
|
5
|
+
import '../dist/options.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* 重设所有环境
|
package/tests/e2e/stream.js
CHANGED
|
@@ -13,7 +13,19 @@ describe('stream', () => {
|
|
|
13
13
|
expect(decoded).toEqual(expected);
|
|
14
14
|
}).rejects.toThrow(expected);
|
|
15
15
|
} else {
|
|
16
|
-
const
|
|
16
|
+
const encoded = await encodeStream(input).toArray();
|
|
17
|
+
const data = encoded.flatMap((/** @type {Buffer} */ chunk) => {
|
|
18
|
+
// split to random chunks
|
|
19
|
+
const chunks = [];
|
|
20
|
+
let offset = 0;
|
|
21
|
+
while (offset < chunk.length) {
|
|
22
|
+
const size = Math.floor(Math.random() * chunk.length);
|
|
23
|
+
chunks.push(chunk.subarray(offset, offset + size));
|
|
24
|
+
offset += size;
|
|
25
|
+
}
|
|
26
|
+
return chunks;
|
|
27
|
+
});
|
|
28
|
+
const decoded = await decodeStream(Readable.from(data));
|
|
17
29
|
expect(decoded).toEqual(expected);
|
|
18
30
|
}
|
|
19
31
|
});
|
package/tests/encode.js
CHANGED
|
@@ -408,7 +408,7 @@ test('encode object', () => {
|
|
|
408
408
|
});
|
|
409
409
|
|
|
410
410
|
test('encode object (keep order)', () => {
|
|
411
|
-
expect(toArray(encode({ b: 2, a: 1, c: 3 }))).toEqual(
|
|
411
|
+
expect(toArray(encode({ b: 2, a: 1, c: 3 }, { sortObjectKeys: true }))).toEqual(
|
|
412
412
|
toArray('{', 'i', 1, 'a', 'U', 1, 'i', 1, 'b', 'U', 2, 'i', 1, 'c', 'U', 3, '}'),
|
|
413
413
|
);
|
|
414
414
|
// @ts-expect-error Access private property
|