@cloudpss/ubjson 0.5.40 → 0.5.42
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/decoder.d.ts +3 -1
- package/dist/base/decoder.d.ts.map +1 -1
- package/dist/base/decoder.js +3 -1
- package/dist/base/decoder.js.map +1 -1
- 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 +53 -0
- package/dist/helper/decode-ae.d.ts.map +1 -0
- package/dist/helper/decode-ae.js +590 -0
- package/dist/helper/decode-ae.js.map +1 -0
- package/dist/helper/decode.d.ts +8 -1
- package/dist/helper/decode.d.ts.map +1 -1
- package/dist/helper/decode.js +46 -8
- package/dist/helper/decode.js.map +1 -1
- package/dist/helper/string-decoder.d.ts.map +1 -1
- package/dist/helper/string-decoder.js +92 -114
- package/dist/helper/string-decoder.js.map +1 -1
- package/dist/index.d.ts +6 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -8
- package/dist/index.js.map +1 -1
- package/dist/options.d.ts +22 -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 +2 -1
- package/dist/rxjs/decoder.d.ts.map +1 -1
- package/dist/rxjs/decoder.js +84 -59
- 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/decoder.d.ts +2 -1
- package/dist/stream/decoder.d.ts.map +1 -1
- package/dist/stream/decoder.js +2 -2
- package/dist/stream/decoder.js.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 +8 -6
- package/dist/stream/index.d.ts.map +1 -1
- package/dist/stream/index.js +12 -12
- 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 +3 -3
- package/src/base/decoder.ts +5 -1
- package/src/base/encoder.ts +6 -1
- package/src/encoder.ts +3 -1
- package/src/helper/decode-ae.ts +630 -0
- package/src/helper/decode.ts +52 -8
- package/src/helper/string-decoder.ts +87 -102
- package/src/index.ts +11 -8
- package/src/options.ts +22 -0
- package/src/rxjs/decoder.ts +72 -45
- package/src/rxjs/encoder.ts +3 -2
- package/src/rxjs/index.ts +1 -0
- package/src/stream/decoder.ts +3 -2
- package/src/stream/encoder.ts +4 -3
- package/src/stream/index.ts +14 -12
- package/src/stream-helper/encoder.ts +6 -1
- package/tests/.utils.js +10 -1
- package/tests/decode.js +52 -0
- package/tests/e2e/.data.js +11 -0
- package/tests/e2e/stream.js +13 -1
- package/tests/encode.js +1 -1
- package/dist/stream-helper/decoder.d.ts +0 -12
- package/dist/stream-helper/decoder.d.ts.map +0 -1
- package/dist/stream-helper/decoder.js +0 -18
- package/dist/stream-helper/decoder.js.map +0 -1
- package/src/stream-helper/decoder.ts +0 -21
package/src/helper/decode.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { DecodeOptions } from '../options.js';
|
|
1
2
|
import { constants } from './constants.js';
|
|
2
3
|
import { UnexpectedEofError, unsupportedType } from './errors.js';
|
|
3
4
|
import { decode } from './string-decoder.js';
|
|
@@ -15,10 +16,12 @@ export interface DecodeCursor {
|
|
|
15
16
|
offset: number;
|
|
16
17
|
/** 读取到末尾时调用 */
|
|
17
18
|
eof(): never;
|
|
19
|
+
/** 选项 */
|
|
20
|
+
readonly options: DecodeOptions | undefined;
|
|
18
21
|
}
|
|
19
22
|
|
|
20
23
|
/** 创建数据包装 */
|
|
21
|
-
export function DecodeCursor(data: BinaryData): DecodeCursor {
|
|
24
|
+
export function DecodeCursor(data: BinaryData, options?: DecodeOptions): DecodeCursor {
|
|
22
25
|
const d = toUint8Array(data);
|
|
23
26
|
const v = new DataView(d.buffer, d.byteOffset, d.byteLength);
|
|
24
27
|
return {
|
|
@@ -28,6 +31,7 @@ export function DecodeCursor(data: BinaryData): DecodeCursor {
|
|
|
28
31
|
eof() {
|
|
29
32
|
throw new UnexpectedEofError();
|
|
30
33
|
},
|
|
34
|
+
options,
|
|
31
35
|
};
|
|
32
36
|
}
|
|
33
37
|
|
|
@@ -81,7 +85,7 @@ export function readLength(cursor: DecodeCursor): number {
|
|
|
81
85
|
break;
|
|
82
86
|
}
|
|
83
87
|
default:
|
|
84
|
-
throw new Error(`Unexpected marker '${
|
|
88
|
+
throw new Error(`Unexpected marker '${fromCharCode(marker)}'(${marker}) for int length`);
|
|
85
89
|
}
|
|
86
90
|
if (length < 0) {
|
|
87
91
|
throw new Error('Invalid length');
|
|
@@ -160,6 +164,35 @@ export function read(cursor: DecodeCursor): unknown {
|
|
|
160
164
|
return readData(cursor, marker);
|
|
161
165
|
}
|
|
162
166
|
|
|
167
|
+
/** 处理 `__proto__` */
|
|
168
|
+
export function protoAction(cursor: Pick<DecodeCursor, 'options'>, obj: Record<string, unknown>, value: unknown): void {
|
|
169
|
+
if (cursor.options?.protoAction === 'error') {
|
|
170
|
+
throw new Error('Unexpected "__proto__"');
|
|
171
|
+
} else if (cursor.options?.protoAction === 'allow') {
|
|
172
|
+
defineProperty(obj, '__proto__', {
|
|
173
|
+
value,
|
|
174
|
+
enumerable: true,
|
|
175
|
+
configurable: true,
|
|
176
|
+
writable: true,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** 处理 `constructor` */
|
|
182
|
+
export function constructorAction(
|
|
183
|
+
cursor: Pick<DecodeCursor, 'options'>,
|
|
184
|
+
obj: Record<string, unknown>,
|
|
185
|
+
value: unknown,
|
|
186
|
+
): void {
|
|
187
|
+
if (cursor.options?.constructorAction === 'error') {
|
|
188
|
+
throw new Error('Unexpected "constructor"');
|
|
189
|
+
} else if (cursor.options?.constructorAction === 'remove') {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
// eslint-disable-next-line @typescript-eslint/dot-notation
|
|
193
|
+
obj['constructor'] = value;
|
|
194
|
+
}
|
|
195
|
+
|
|
163
196
|
/** 读取优化对象数据 */
|
|
164
197
|
function readObjectOptimizedData(cursor: DecodeCursor, marker: OptimizedFormatMarkers): unknown {
|
|
165
198
|
const { count, type } = marker;
|
|
@@ -168,7 +201,12 @@ function readObjectOptimizedData(cursor: DecodeCursor, marker: OptimizedFormatMa
|
|
|
168
201
|
const key = readKey(cursor);
|
|
169
202
|
const value = readData(cursor, type ?? readMarker(cursor));
|
|
170
203
|
if (key === '__proto__') {
|
|
171
|
-
|
|
204
|
+
protoAction(cursor, object, value);
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
if (key === 'constructor') {
|
|
208
|
+
constructorAction(cursor, object, value);
|
|
209
|
+
continue;
|
|
172
210
|
}
|
|
173
211
|
object[key] = value;
|
|
174
212
|
}
|
|
@@ -198,7 +236,12 @@ export function readData(cursor: DecodeCursor, marker: number): unknown {
|
|
|
198
236
|
const key = readKey(cursor);
|
|
199
237
|
const value = read(cursor);
|
|
200
238
|
if (key === '__proto__') {
|
|
201
|
-
|
|
239
|
+
protoAction(cursor, object, value);
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
if (key === 'constructor') {
|
|
243
|
+
constructorAction(cursor, object, value);
|
|
244
|
+
continue;
|
|
202
245
|
}
|
|
203
246
|
object[key] = value;
|
|
204
247
|
}
|
|
@@ -210,10 +253,11 @@ export function readData(cursor: DecodeCursor, marker: number): unknown {
|
|
|
210
253
|
const markers = readOptimizedFormatMarkers(cursor);
|
|
211
254
|
if (markers == null) {
|
|
212
255
|
const array = [];
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
256
|
+
for (;;) {
|
|
257
|
+
const marker = readMarker(cursor);
|
|
258
|
+
// 直到 ']'
|
|
259
|
+
if (marker === constants.ARRAY_END) break;
|
|
260
|
+
array.push(readData(cursor, marker));
|
|
217
261
|
}
|
|
218
262
|
return array;
|
|
219
263
|
}
|
|
@@ -69,118 +69,103 @@ export function shortStringInJS(buf: Uint8Array, begin: number, length: number):
|
|
|
69
69
|
if (length < 4) {
|
|
70
70
|
if (length < 2) {
|
|
71
71
|
if (length === 0) return '';
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
if ((a & 0x80) > 1) {
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
return fromCharCode(a);
|
|
78
|
-
}
|
|
79
|
-
} else {
|
|
80
|
-
const a = buf[begin++]!;
|
|
81
|
-
const b = buf[begin++]!;
|
|
82
|
-
if ((a & 0x80) > 0 || (b & 0x80) > 0) {
|
|
72
|
+
const a = buf[begin]!;
|
|
73
|
+
if ((a & 0x80) > 0) {
|
|
83
74
|
return;
|
|
84
75
|
}
|
|
85
|
-
|
|
86
|
-
const c = buf[begin++]!;
|
|
87
|
-
if ((c & 0x80) > 0) {
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
return fromCharCode(a, b, c);
|
|
76
|
+
return fromCharCode(a);
|
|
91
77
|
}
|
|
92
|
-
} else {
|
|
93
78
|
const a = buf[begin++]!;
|
|
94
79
|
const b = buf[begin++]!;
|
|
80
|
+
if ((a & 0x80) > 0 || (b & 0x80) > 0) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (length < 3) return fromCharCode(a, b);
|
|
95
84
|
const c = buf[begin++]!;
|
|
96
|
-
|
|
97
|
-
if ((a & 0x80) > 0 || (b & 0x80) > 0 || (c & 0x80) > 0 || (d & 0x80) > 0) {
|
|
85
|
+
if ((c & 0x80) > 0) {
|
|
98
86
|
return;
|
|
99
87
|
}
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
if (length < 7) return fromCharCode(a, b, c, d, e, f);
|
|
116
|
-
const g = buf[begin++]!;
|
|
117
|
-
if ((g & 0x80) > 0) {
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
return fromCharCode(a, b, c, d, e, f, g);
|
|
121
|
-
} else {
|
|
122
|
-
const e = buf[begin++]!;
|
|
123
|
-
const f = buf[begin++]!;
|
|
124
|
-
const g = buf[begin++]!;
|
|
125
|
-
const h = buf[begin++]!;
|
|
126
|
-
if ((e & 0x80) > 0 || (f & 0x80) > 0 || (g & 0x80) > 0 || (h & 0x80) > 0) {
|
|
127
|
-
return;
|
|
128
|
-
}
|
|
129
|
-
if (length < 10) {
|
|
130
|
-
if (length === 8) return fromCharCode(a, b, c, d, e, f, g, h);
|
|
131
|
-
else {
|
|
132
|
-
const i = buf[begin++]!;
|
|
133
|
-
if ((i & 0x80) > 0) {
|
|
134
|
-
return;
|
|
135
|
-
}
|
|
136
|
-
return fromCharCode(a, b, c, d, e, f, g, h, i);
|
|
137
|
-
}
|
|
138
|
-
} else if (length < 12) {
|
|
139
|
-
const i = buf[begin++]!;
|
|
140
|
-
const j = buf[begin++]!;
|
|
141
|
-
if ((i & 0x80) > 0 || (j & 0x80) > 0) {
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
if (length < 11) return fromCharCode(a, b, c, d, e, f, g, h, i, j);
|
|
145
|
-
const k = buf[begin++]!;
|
|
146
|
-
if ((k & 0x80) > 0) {
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
return fromCharCode(a, b, c, d, e, f, g, h, i, j, k);
|
|
150
|
-
} else {
|
|
151
|
-
const i = buf[begin++]!;
|
|
152
|
-
const j = buf[begin++]!;
|
|
153
|
-
const k = buf[begin++]!;
|
|
154
|
-
const l = buf[begin++]!;
|
|
155
|
-
if ((i & 0x80) > 0 || (j & 0x80) > 0 || (k & 0x80) > 0 || (l & 0x80) > 0) {
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
if (length < 14) {
|
|
159
|
-
if (length === 12) return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l);
|
|
160
|
-
else {
|
|
161
|
-
const m = buf[begin++]!;
|
|
162
|
-
if ((m & 0x80) > 0) {
|
|
163
|
-
begin -= 13;
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m);
|
|
167
|
-
}
|
|
168
|
-
} else {
|
|
169
|
-
const m = buf[begin++]!;
|
|
170
|
-
const n = buf[begin++]!;
|
|
171
|
-
if ((m & 0x80) > 0 || (n & 0x80) > 0) {
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
174
|
-
if (length < 15) return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m, n);
|
|
175
|
-
const o = buf[begin++]!;
|
|
176
|
-
if ((o & 0x80) > 0) {
|
|
177
|
-
return;
|
|
178
|
-
}
|
|
179
|
-
return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
88
|
+
return fromCharCode(a, b, c);
|
|
89
|
+
}
|
|
90
|
+
const a = buf[begin++]!;
|
|
91
|
+
const b = buf[begin++]!;
|
|
92
|
+
const c = buf[begin++]!;
|
|
93
|
+
const d = buf[begin++]!;
|
|
94
|
+
if ((a & 0x80) > 0 || (b & 0x80) > 0 || (c & 0x80) > 0 || (d & 0x80) > 0) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
if (length < 6) {
|
|
98
|
+
if (length === 4) return fromCharCode(a, b, c, d);
|
|
99
|
+
const e = buf[begin++]!;
|
|
100
|
+
if ((e & 0x80) > 0) {
|
|
101
|
+
return;
|
|
182
102
|
}
|
|
103
|
+
return fromCharCode(a, b, c, d, e);
|
|
104
|
+
}
|
|
105
|
+
const e = buf[begin++]!;
|
|
106
|
+
const f = buf[begin++]!;
|
|
107
|
+
if ((e & 0x80) > 0 || (f & 0x80) > 0) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (length < 8) {
|
|
111
|
+
if (length < 7) return fromCharCode(a, b, c, d, e, f);
|
|
112
|
+
const g = buf[begin++]!;
|
|
113
|
+
if ((g & 0x80) > 0) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
return fromCharCode(a, b, c, d, e, f, g);
|
|
117
|
+
}
|
|
118
|
+
const g = buf[begin++]!;
|
|
119
|
+
const h = buf[begin++]!;
|
|
120
|
+
if ((g & 0x80) > 0 || (h & 0x80) > 0) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (length < 10) {
|
|
124
|
+
if (length === 8) return fromCharCode(a, b, c, d, e, f, g, h);
|
|
125
|
+
const i = buf[begin++]!;
|
|
126
|
+
if ((i & 0x80) > 0) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
return fromCharCode(a, b, c, d, e, f, g, h, i);
|
|
130
|
+
}
|
|
131
|
+
const i = buf[begin++]!;
|
|
132
|
+
const j = buf[begin++]!;
|
|
133
|
+
if ((i & 0x80) > 0 || (j & 0x80) > 0) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (length < 12) {
|
|
137
|
+
if (length < 11) return fromCharCode(a, b, c, d, e, f, g, h, i, j);
|
|
138
|
+
const k = buf[begin++]!;
|
|
139
|
+
if ((k & 0x80) > 0) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
return fromCharCode(a, b, c, d, e, f, g, h, i, j, k);
|
|
143
|
+
}
|
|
144
|
+
const k = buf[begin++]!;
|
|
145
|
+
const l = buf[begin++]!;
|
|
146
|
+
if ((k & 0x80) > 0 || (l & 0x80) > 0) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
if (length < 14) {
|
|
150
|
+
if (length === 12) return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l);
|
|
151
|
+
const m = buf[begin++]!;
|
|
152
|
+
if ((m & 0x80) > 0) {
|
|
153
|
+
begin -= 13;
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m);
|
|
157
|
+
}
|
|
158
|
+
const m = buf[begin++]!;
|
|
159
|
+
const n = buf[begin++]!;
|
|
160
|
+
if ((m & 0x80) > 0 || (n & 0x80) > 0) {
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
if (length < 15) return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m, n);
|
|
164
|
+
const o = buf[begin++]!;
|
|
165
|
+
if ((o & 0x80) > 0) {
|
|
166
|
+
return;
|
|
183
167
|
}
|
|
168
|
+
return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o);
|
|
184
169
|
}
|
|
185
170
|
|
|
186
171
|
let TEXT_DECODER: TextDecoder | null;
|
package/src/index.ts
CHANGED
|
@@ -1,26 +1,29 @@
|
|
|
1
1
|
import { getEncoder } from './encoder.js';
|
|
2
2
|
import { Decoder } from './decoder.js';
|
|
3
|
+
import type { EncodeOptions, DecodeOptions } from './options.js';
|
|
4
|
+
|
|
3
5
|
export { UnexpectedEofError as UnexpectedEof } from './helper/errors.js';
|
|
6
|
+
export type { EncodeOptions, DecodeOptions };
|
|
4
7
|
|
|
5
8
|
/** 编码为 UBJSON */
|
|
6
|
-
export function encode(value: unknown): Uint8Array {
|
|
7
|
-
return getEncoder().encode(value);
|
|
9
|
+
export function encode(value: unknown, options?: EncodeOptions): Uint8Array {
|
|
10
|
+
return getEncoder(options).encode(value);
|
|
8
11
|
}
|
|
9
12
|
|
|
10
13
|
/** 编码为 UBJSON */
|
|
11
|
-
export function encodeMany(value: Iterable<unknown
|
|
12
|
-
return getEncoder().encodeMany(value);
|
|
14
|
+
export function encodeMany(value: Iterable<unknown>, options?: EncodeOptions): Uint8Array {
|
|
15
|
+
return getEncoder(options).encodeMany(value);
|
|
13
16
|
}
|
|
14
17
|
|
|
15
18
|
/** 解码 UBJSON */
|
|
16
|
-
export function decode(value: BinaryData): unknown {
|
|
17
|
-
const decoder = new Decoder(value);
|
|
19
|
+
export function decode(value: BinaryData, options?: DecodeOptions): unknown {
|
|
20
|
+
const decoder = new Decoder(value, options);
|
|
18
21
|
return decoder.decode();
|
|
19
22
|
}
|
|
20
23
|
|
|
21
24
|
/** 解码 UBJSON */
|
|
22
|
-
export function* decodeMany(value: BinaryData): Iterable<unknown> {
|
|
23
|
-
const decoder = new Decoder(value);
|
|
25
|
+
export function* decodeMany(value: BinaryData, options?: DecodeOptions): Iterable<unknown> {
|
|
26
|
+
const decoder = new Decoder(value, options);
|
|
24
27
|
while (!decoder.ended) {
|
|
25
28
|
yield decoder.decode();
|
|
26
29
|
}
|
package/src/options.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** 序列化选项 */
|
|
2
|
+
export interface EncodeOptions {
|
|
3
|
+
/**
|
|
4
|
+
* 序列化对象时排序属性
|
|
5
|
+
* @default false
|
|
6
|
+
*/
|
|
7
|
+
sortObjectKeys?: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** 反序列化选项 */
|
|
11
|
+
export interface DecodeOptions {
|
|
12
|
+
/**
|
|
13
|
+
* 解析对象时对 `__proto__` 属性的处理方式
|
|
14
|
+
* @default 'remove'
|
|
15
|
+
*/
|
|
16
|
+
protoAction?: 'error' | 'allow' | 'remove';
|
|
17
|
+
/**
|
|
18
|
+
* 解析对象时对 `constructor` 属性的处理方式
|
|
19
|
+
* @default 'allow'
|
|
20
|
+
*/
|
|
21
|
+
constructorAction?: 'error' | 'allow' | 'remove';
|
|
22
|
+
}
|
package/src/rxjs/decoder.ts
CHANGED
|
@@ -1,71 +1,98 @@
|
|
|
1
1
|
import { Observable, type OperatorFunction } from 'rxjs';
|
|
2
|
-
import { StreamDecoderHelper, kEof, UnexpectedEof } from '../stream-helper/decoder.js';
|
|
3
2
|
import { toUint8Array } from '../helper/utils.js';
|
|
3
|
+
import { read } from '../helper/decode-ae.js';
|
|
4
|
+
import type { DecodeOptions } from '../options.js';
|
|
5
|
+
import { UnexpectedEofError } from '../helper/errors.js';
|
|
4
6
|
|
|
5
|
-
const DEFAULT_BUFFER_SIZE = 16 * 1024 * 1024; // 16 MiB
|
|
6
7
|
const EMPTY_BUFFER = new Uint8Array(0);
|
|
8
|
+
const EMPTY_VIEW = new DataView(EMPTY_BUFFER.buffer);
|
|
7
9
|
|
|
8
10
|
/** 流式解码 UBJSON */
|
|
9
|
-
export function decode(): OperatorFunction<BinaryData, unknown> {
|
|
10
|
-
return (observable) =>
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
11
|
+
export function decode(options?: DecodeOptions): OperatorFunction<BinaryData, unknown> {
|
|
12
|
+
return (observable) =>
|
|
13
|
+
new Observable<unknown>((subscriber) => {
|
|
14
|
+
const data = EMPTY_BUFFER;
|
|
15
|
+
const cursor = {
|
|
16
|
+
view: EMPTY_VIEW,
|
|
17
|
+
data,
|
|
18
|
+
capacity: 0,
|
|
19
|
+
size: 0,
|
|
20
|
+
offset: 0,
|
|
21
|
+
options,
|
|
22
|
+
};
|
|
23
|
+
/** reader 返回的还需接收的字节数 */
|
|
24
|
+
let required = 1;
|
|
25
|
+
let reader = read(cursor);
|
|
16
26
|
return observable.subscribe({
|
|
17
27
|
next(value) {
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
28
|
+
const chunk = toUint8Array(value);
|
|
29
|
+
const chunkSize = chunk.byteLength;
|
|
30
|
+
if (cursor.capacity - cursor.size < chunkSize) {
|
|
31
|
+
// 当前缓冲区不足,需要扩容
|
|
32
|
+
const newSize = Math.max(
|
|
33
|
+
// 不缩小缓冲区
|
|
34
|
+
cursor.capacity,
|
|
35
|
+
// 扩大缓冲区到足够容纳 2 倍当前数据
|
|
36
|
+
chunkSize * 2 + cursor.size - cursor.offset,
|
|
37
|
+
);
|
|
38
|
+
if (newSize > cursor.capacity) {
|
|
39
|
+
// 需要增大缓冲区
|
|
40
|
+
const newData = new Uint8Array(newSize);
|
|
41
|
+
newData.set(cursor.data.subarray(cursor.offset, cursor.size), 0);
|
|
42
|
+
newData.set(chunk, cursor.size - cursor.offset);
|
|
43
|
+
cursor.data = newData;
|
|
44
|
+
cursor.view = new DataView(newData.buffer, newData.byteOffset, newData.byteLength);
|
|
45
|
+
cursor.capacity = newSize;
|
|
46
|
+
} else {
|
|
47
|
+
// 无需增大缓冲区,直接移动数据
|
|
48
|
+
cursor.data.copyWithin(0, cursor.offset, cursor.size);
|
|
49
|
+
cursor.data.set(chunk, cursor.size - cursor.offset);
|
|
50
|
+
}
|
|
51
|
+
cursor.size = cursor.size - cursor.offset + chunkSize;
|
|
52
|
+
cursor.offset = 0;
|
|
22
53
|
} 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;
|
|
54
|
+
// 当前缓冲区足够,直接写入
|
|
55
|
+
cursor.data.set(chunk, cursor.size);
|
|
56
|
+
cursor.size += chunkSize;
|
|
34
57
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
58
|
+
|
|
59
|
+
required -= chunkSize;
|
|
60
|
+
// 未读够数据,继续等待
|
|
61
|
+
if (required > 0) return;
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
for (;;) {
|
|
65
|
+
const result = reader.next();
|
|
66
|
+
if (result.done) {
|
|
67
|
+
// 读取完成,新建 reader 读取下一个值
|
|
68
|
+
subscriber.next(result.value);
|
|
69
|
+
reader = read(cursor);
|
|
70
|
+
if (cursor.offset === cursor.size) {
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
46
73
|
} else {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return;
|
|
74
|
+
required = result.value;
|
|
75
|
+
break;
|
|
50
76
|
}
|
|
51
77
|
}
|
|
78
|
+
} catch (ex) {
|
|
79
|
+
subscriber.error(ex);
|
|
52
80
|
}
|
|
53
|
-
// 完全消费了 Buffer 内容,重置 Buffer
|
|
54
|
-
begin = end = 0;
|
|
55
81
|
},
|
|
56
82
|
error(err) {
|
|
57
83
|
subscriber.error(err);
|
|
58
|
-
|
|
84
|
+
cursor.data = EMPTY_BUFFER;
|
|
85
|
+
cursor.view = EMPTY_VIEW;
|
|
59
86
|
},
|
|
60
87
|
complete() {
|
|
61
|
-
if (
|
|
62
|
-
subscriber.error(new
|
|
88
|
+
if (cursor.size > cursor.offset) {
|
|
89
|
+
subscriber.error(new UnexpectedEofError());
|
|
63
90
|
} else {
|
|
64
91
|
subscriber.complete();
|
|
65
92
|
}
|
|
66
|
-
|
|
93
|
+
cursor.data = EMPTY_BUFFER;
|
|
94
|
+
cursor.view = EMPTY_VIEW;
|
|
67
95
|
},
|
|
68
96
|
});
|
|
69
97
|
});
|
|
70
|
-
};
|
|
71
98
|
}
|
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/decoder.ts
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import { Transform, type TransformCallback } from 'node:stream';
|
|
2
2
|
import { Subject } from 'rxjs';
|
|
3
3
|
import { decode } from '../rxjs/decoder.js';
|
|
4
|
+
import type { DecodeOptions } from '../options.js';
|
|
4
5
|
|
|
5
6
|
/** 流式解码 UBJSON */
|
|
6
7
|
export class StreamDecoder extends Transform {
|
|
7
|
-
constructor() {
|
|
8
|
+
constructor(options?: DecodeOptions) {
|
|
8
9
|
super({
|
|
9
10
|
readableObjectMode: true,
|
|
10
11
|
writableObjectMode: false,
|
|
11
12
|
});
|
|
12
13
|
this.buffer = new Subject<BinaryData>();
|
|
13
|
-
this.buffer.pipe(decode()).subscribe({
|
|
14
|
+
this.buffer.pipe(decode(options)).subscribe({
|
|
14
15
|
next: (value) => {
|
|
15
16
|
// null is not allowed in a stream
|
|
16
17
|
if (value == null) return;
|
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, DecodeOptions } from '../options.js';
|
|
4
5
|
|
|
5
6
|
export { UnexpectedEofError as UnexpectedEof } from '../helper/errors.js';
|
|
7
|
+
export type { EncodeOptions, DecodeOptions };
|
|
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,14 +34,14 @@ 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 */
|
|
40
|
-
export async function decode(stream: NodeJS.ReadableStream): Promise<unknown> {
|
|
42
|
+
export async function decode(stream: NodeJS.ReadableStream, options?: DecodeOptions): Promise<unknown> {
|
|
41
43
|
return new Promise((resolve, reject) => {
|
|
42
|
-
const decoder = new StreamDecoder();
|
|
44
|
+
const decoder = new StreamDecoder(options);
|
|
43
45
|
decoder.on('error', reject);
|
|
44
46
|
decoder.on('end', resolve);
|
|
45
47
|
decoder.on('data', resolve);
|
|
@@ -48,12 +50,12 @@ export async function decode(stream: NodeJS.ReadableStream): Promise<unknown> {
|
|
|
48
50
|
}
|
|
49
51
|
|
|
50
52
|
/** 解码 UBJSON */
|
|
51
|
-
export function decodeMany(stream: NodeJS.ReadableStream): AsyncIterable<unknown> {
|
|
52
|
-
const decoder = new StreamDecoder();
|
|
53
|
+
export function decodeMany(stream: NodeJS.ReadableStream, options?: DecodeOptions): AsyncIterable<unknown> {
|
|
54
|
+
const decoder = new StreamDecoder(options);
|
|
53
55
|
return stream.pipe(decoder);
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
/** 解码 UBJSON */
|
|
57
|
-
export function decoder(): Transform {
|
|
58
|
-
return new StreamDecoder();
|
|
59
|
+
export function decoder(options?: DecodeOptions): Transform {
|
|
60
|
+
return new StreamDecoder(options);
|
|
59
61
|
}
|