@cloudpss/ubjson 0.5.37 → 0.5.38
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 +12 -0
- package/dist/base/decoder.js +26 -0
- package/dist/base/decoder.js.map +1 -0
- package/dist/{common → base}/encoder.d.ts +2 -6
- package/dist/{common → base}/encoder.js +71 -121
- package/dist/base/encoder.js.map +1 -0
- package/dist/decoder.d.ts +1 -2
- package/dist/decoder.js +1 -1
- package/dist/decoder.js.map +1 -1
- package/dist/encoder.d.ts +1 -1
- package/dist/encoder.js +11 -11
- package/dist/encoder.js.map +1 -1
- package/dist/{common → helper}/constants.js.map +1 -1
- package/dist/helper/decode.d.ts +49 -0
- package/dist/helper/decode.js +344 -0
- package/dist/helper/decode.js.map +1 -0
- package/dist/helper/encode.d.ts +20 -0
- package/dist/helper/encode.js +84 -0
- package/dist/helper/encode.js.map +1 -0
- package/dist/{common → helper}/errors.d.ts +4 -0
- package/dist/{common → helper}/errors.js +10 -2
- package/dist/helper/errors.js.map +1 -0
- package/dist/{common → helper}/string-decoder.js +1 -1
- package/dist/{common → helper}/string-decoder.js.map +1 -1
- package/dist/{common → helper}/string-encoder.js.map +1 -1
- package/dist/{utils.d.ts → helper/utils.d.ts} +0 -4
- package/dist/{utils.js → helper/utils.js} +0 -6
- package/dist/helper/utils.js.map +1 -0
- package/dist/index.d.ts +3 -5
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/dist/rxjs/decoder.js +1 -1
- package/dist/rxjs/decoder.js.map +1 -1
- package/dist/rxjs/index.d.ts +1 -1
- package/dist/rxjs/index.js +1 -1
- package/dist/rxjs/index.js.map +1 -1
- package/dist/stream/index.d.ts +2 -2
- package/dist/stream/index.js +2 -2
- package/dist/stream/index.js.map +1 -1
- package/dist/stream-helper/decoder.d.ts +2 -3
- package/dist/stream-helper/decoder.js +3 -6
- package/dist/stream-helper/decoder.js.map +1 -1
- package/dist/stream-helper/encoder.d.ts +1 -1
- package/dist/stream-helper/encoder.js +15 -15
- package/dist/stream-helper/encoder.js.map +1 -1
- package/package.json +4 -4
- package/src/base/decoder.ts +27 -0
- package/src/{common → base}/encoder.ts +72 -116
- package/src/decoder.ts +1 -2
- package/src/encoder.ts +11 -11
- package/src/helper/decode.ts +362 -0
- package/src/helper/encode.ts +89 -0
- package/src/{common → helper}/errors.ts +12 -2
- package/src/{common → helper}/string-decoder.ts +1 -1
- package/src/{utils.ts → helper/utils.ts} +0 -7
- package/src/index.ts +6 -8
- package/src/rxjs/decoder.ts +1 -1
- package/src/rxjs/index.ts +1 -1
- package/src/stream/index.ts +3 -3
- package/src/stream-helper/decoder.ts +3 -6
- package/src/stream-helper/encoder.ts +16 -16
- package/tests/.utils.js +6 -4
- package/tests/decode.js +0 -18
- package/tests/e2e/no-encode-into.js +1 -1
- package/tests/encode.js +1 -1
- package/tests/huge-string.js +1 -0
- package/tests/rxjs/decode.js +14 -13
- package/tests/rxjs/encode.js +13 -12
- package/tests/stream/decode.js +15 -13
- package/tests/stream/encode.js +9 -8
- package/tests/stream/many.js +2 -1
- package/tests/string-encoding.js +4 -6
- package/benchmark-small.js +0 -81
- package/benchmark-string-decode.js +0 -41
- package/benchmark-string-encode.js +0 -32
- package/benchmark-string-size-caculation.js +0 -50
- package/benchmark.js +0 -50
- package/dist/common/decoder.d.ts +0 -45
- package/dist/common/decoder.js +0 -348
- package/dist/common/decoder.js.map +0 -1
- package/dist/common/encoder.js.map +0 -1
- package/dist/common/errors.js.map +0 -1
- package/dist/utils.js.map +0 -1
- package/src/common/decoder.ts +0 -356
- /package/dist/{common → helper}/constants.d.ts +0 -0
- /package/dist/{common → helper}/constants.js +0 -0
- /package/dist/{common → helper}/string-decoder.d.ts +0 -0
- /package/dist/{common → helper}/string-encoder.d.ts +0 -0
- /package/dist/{common → helper}/string-encoder.js +0 -0
- /package/src/{common → helper}/constants.ts +0 -0
- /package/src/{common → helper}/string-encoder.ts +0 -0
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
import { constants } from './constants.js';
|
|
2
|
+
import { UnexpectedEofError, unsupportedType } from './errors.js';
|
|
3
|
+
import { decode, decodeKey } from './string-decoder.js';
|
|
4
|
+
import { toUint8Array } from './utils.js';
|
|
5
|
+
|
|
6
|
+
const { fromEntries } = Object;
|
|
7
|
+
const { fromCharCode } = String;
|
|
8
|
+
|
|
9
|
+
/** 数据包装 */
|
|
10
|
+
export interface DecodeCursor {
|
|
11
|
+
/** 数据 */
|
|
12
|
+
readonly view: DataView;
|
|
13
|
+
/** 数据 */
|
|
14
|
+
readonly data: Uint8Array;
|
|
15
|
+
/** 当前读指针位置 */
|
|
16
|
+
offset: number;
|
|
17
|
+
/** 读取到末尾时调用 */
|
|
18
|
+
eof(): never;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** 创建数据包装 */
|
|
22
|
+
export function DecodeCursor(data: BinaryData): DecodeCursor {
|
|
23
|
+
const d = toUint8Array(data);
|
|
24
|
+
const v = new DataView(d.buffer, d.byteOffset, d.byteLength);
|
|
25
|
+
return {
|
|
26
|
+
data: d,
|
|
27
|
+
view: v,
|
|
28
|
+
offset: 0,
|
|
29
|
+
eof() {
|
|
30
|
+
throw new UnexpectedEofError();
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 读取第一个非 NOOP 的字节
|
|
37
|
+
* @returns 返回读取到的字节,如果读取到末尾则返回 undefined
|
|
38
|
+
*/
|
|
39
|
+
export function readMarkerOrUndefined(cursor: DecodeCursor): number | undefined {
|
|
40
|
+
let marker: number | undefined;
|
|
41
|
+
do {
|
|
42
|
+
marker = cursor.data[cursor.offset++];
|
|
43
|
+
} while (marker === constants.NO_OP);
|
|
44
|
+
return marker;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 读取第一个非 NOOP 的字节
|
|
49
|
+
*/
|
|
50
|
+
export function readMarker(cursor: DecodeCursor): number {
|
|
51
|
+
let marker: number | undefined;
|
|
52
|
+
do {
|
|
53
|
+
marker = cursor.data[cursor.offset++];
|
|
54
|
+
} while (marker === constants.NO_OP);
|
|
55
|
+
if (marker === undefined) return cursor.eof();
|
|
56
|
+
return marker;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** 读取一个大于 0 的整数 */
|
|
60
|
+
export function readLength(cursor: DecodeCursor): number {
|
|
61
|
+
const marker = readMarker(cursor);
|
|
62
|
+
let length: number;
|
|
63
|
+
switch (marker) {
|
|
64
|
+
case constants.INT8:
|
|
65
|
+
length = readInt8Data(cursor);
|
|
66
|
+
break;
|
|
67
|
+
case constants.UINT8:
|
|
68
|
+
length = readUint8Data(cursor);
|
|
69
|
+
break;
|
|
70
|
+
case constants.INT16:
|
|
71
|
+
length = readInt16Data(cursor);
|
|
72
|
+
break;
|
|
73
|
+
case constants.INT32:
|
|
74
|
+
length = readInt32Data(cursor);
|
|
75
|
+
break;
|
|
76
|
+
case constants.INT64: {
|
|
77
|
+
const l = readInt64Data(cursor);
|
|
78
|
+
if (l < 0 || l > Number.MAX_SAFE_INTEGER) {
|
|
79
|
+
throw new Error('Invalid length');
|
|
80
|
+
}
|
|
81
|
+
length = Number(l);
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
default:
|
|
85
|
+
throw new Error(`Unexpected marker '${String.fromCharCode(marker)}'(${marker}) for int length`);
|
|
86
|
+
}
|
|
87
|
+
if (length < 0) {
|
|
88
|
+
throw new Error('Invalid length');
|
|
89
|
+
}
|
|
90
|
+
return length;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** readInt8Data */
|
|
94
|
+
export function readInt8Data(cursor: DecodeCursor): number {
|
|
95
|
+
try {
|
|
96
|
+
return cursor.view.getInt8(cursor.offset++);
|
|
97
|
+
} catch {
|
|
98
|
+
return cursor.eof();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
/** readUint8Data */
|
|
102
|
+
export function readUint8Data(cursor: DecodeCursor): number {
|
|
103
|
+
const result = cursor.data[cursor.offset++];
|
|
104
|
+
if (result === undefined) return cursor.eof();
|
|
105
|
+
return result;
|
|
106
|
+
}
|
|
107
|
+
/** readInt16Data */
|
|
108
|
+
export function readInt16Data(cursor: DecodeCursor): number {
|
|
109
|
+
try {
|
|
110
|
+
const result = cursor.view.getInt16(cursor.offset);
|
|
111
|
+
cursor.offset += 2;
|
|
112
|
+
return result;
|
|
113
|
+
} catch {
|
|
114
|
+
return cursor.eof();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/** readInt32Data */
|
|
118
|
+
export function readInt32Data(cursor: DecodeCursor): number {
|
|
119
|
+
try {
|
|
120
|
+
const result = cursor.view.getInt32(cursor.offset);
|
|
121
|
+
cursor.offset += 4;
|
|
122
|
+
return result;
|
|
123
|
+
} catch {
|
|
124
|
+
return cursor.eof();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/** readInt64Data */
|
|
128
|
+
export function readInt64Data(cursor: DecodeCursor): bigint {
|
|
129
|
+
try {
|
|
130
|
+
const result = cursor.view.getBigInt64(cursor.offset);
|
|
131
|
+
cursor.offset += 8;
|
|
132
|
+
return result;
|
|
133
|
+
} catch {
|
|
134
|
+
return cursor.eof();
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/** readFloat32Data */
|
|
138
|
+
export function readFloat32Data(cursor: DecodeCursor): number {
|
|
139
|
+
try {
|
|
140
|
+
const result = cursor.view.getFloat32(cursor.offset);
|
|
141
|
+
cursor.offset += 4;
|
|
142
|
+
return result;
|
|
143
|
+
} catch {
|
|
144
|
+
return cursor.eof();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
/** readFloat64Data */
|
|
148
|
+
export function readFloat64Data(cursor: DecodeCursor): number {
|
|
149
|
+
try {
|
|
150
|
+
const result = cursor.view.getFloat64(cursor.offset);
|
|
151
|
+
cursor.offset += 8;
|
|
152
|
+
return result;
|
|
153
|
+
} catch {
|
|
154
|
+
return cursor.eof();
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** 读取数据 */
|
|
159
|
+
export function read(cursor: DecodeCursor): unknown {
|
|
160
|
+
const marker = readMarker(cursor);
|
|
161
|
+
return readData(cursor, marker);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** 根据标签读取后续数据 */
|
|
165
|
+
export function readData(cursor: DecodeCursor, marker: number): unknown {
|
|
166
|
+
// 按照出现频率排序
|
|
167
|
+
switch (marker) {
|
|
168
|
+
case constants.STRING: {
|
|
169
|
+
const length = readLength(cursor);
|
|
170
|
+
const begin = cursor.offset;
|
|
171
|
+
const end = begin + length;
|
|
172
|
+
cursor.offset = end;
|
|
173
|
+
const { data } = cursor;
|
|
174
|
+
if (end > data.length) cursor.eof();
|
|
175
|
+
return decode(data, begin, end);
|
|
176
|
+
}
|
|
177
|
+
case constants.OBJECT: {
|
|
178
|
+
const markers = readOptimizedFormatMarkers(cursor);
|
|
179
|
+
if (markers == null) {
|
|
180
|
+
// 直到 '}'
|
|
181
|
+
const object: Array<[string, unknown]> = [];
|
|
182
|
+
while (readMarker(cursor) !== constants.OBJECT_END) {
|
|
183
|
+
cursor.offset--;
|
|
184
|
+
object.push([readKey(cursor), read(cursor)]);
|
|
185
|
+
}
|
|
186
|
+
return fromEntries(object);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const { count, type } = markers;
|
|
190
|
+
const object: Array<[string, unknown]> = [];
|
|
191
|
+
object.length = count;
|
|
192
|
+
for (let i = 0; i < count; i++) {
|
|
193
|
+
const key = readKey(cursor);
|
|
194
|
+
const value = readData(cursor, type ?? readMarker(cursor));
|
|
195
|
+
object[i] = [key, value];
|
|
196
|
+
}
|
|
197
|
+
return fromEntries(object);
|
|
198
|
+
}
|
|
199
|
+
case constants.ARRAY: {
|
|
200
|
+
const markers = readOptimizedFormatMarkers(cursor);
|
|
201
|
+
if (markers == null) {
|
|
202
|
+
const array = [];
|
|
203
|
+
// 直到 ']'
|
|
204
|
+
while (readMarker(cursor) !== constants.ARRAY_END) {
|
|
205
|
+
cursor.offset--;
|
|
206
|
+
array.push(read(cursor));
|
|
207
|
+
}
|
|
208
|
+
return array;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const { count, type } = markers;
|
|
212
|
+
switch (type) {
|
|
213
|
+
case constants.UINT8:
|
|
214
|
+
try {
|
|
215
|
+
const buf = new Uint8Array(
|
|
216
|
+
cursor.data.buffer,
|
|
217
|
+
cursor.data.byteOffset + cursor.offset,
|
|
218
|
+
count,
|
|
219
|
+
).slice();
|
|
220
|
+
cursor.offset += count;
|
|
221
|
+
return buf;
|
|
222
|
+
} catch {
|
|
223
|
+
return cursor.eof();
|
|
224
|
+
}
|
|
225
|
+
case constants.INT8:
|
|
226
|
+
try {
|
|
227
|
+
const buf = new Int8Array(
|
|
228
|
+
cursor.data.buffer,
|
|
229
|
+
cursor.data.byteOffset + cursor.offset,
|
|
230
|
+
count,
|
|
231
|
+
).slice();
|
|
232
|
+
cursor.offset += count;
|
|
233
|
+
return buf;
|
|
234
|
+
} catch {
|
|
235
|
+
return cursor.eof();
|
|
236
|
+
}
|
|
237
|
+
case constants.INT16: {
|
|
238
|
+
const result = new Int16Array(count);
|
|
239
|
+
for (let i = 0; i < count; i++) {
|
|
240
|
+
result[i] = readInt16Data(cursor);
|
|
241
|
+
}
|
|
242
|
+
return result;
|
|
243
|
+
}
|
|
244
|
+
case constants.INT32: {
|
|
245
|
+
const result = new Int32Array(count);
|
|
246
|
+
for (let i = 0; i < count; i++) {
|
|
247
|
+
result[i] = readInt32Data(cursor);
|
|
248
|
+
}
|
|
249
|
+
return result;
|
|
250
|
+
}
|
|
251
|
+
case constants.FLOAT32: {
|
|
252
|
+
const result = new Float32Array(count);
|
|
253
|
+
for (let i = 0; i < count; i++) {
|
|
254
|
+
result[i] = readFloat32Data(cursor);
|
|
255
|
+
}
|
|
256
|
+
return result;
|
|
257
|
+
}
|
|
258
|
+
case constants.FLOAT64: {
|
|
259
|
+
const result = new Float64Array(count);
|
|
260
|
+
for (let i = 0; i < count; i++) {
|
|
261
|
+
result[i] = readFloat64Data(cursor);
|
|
262
|
+
}
|
|
263
|
+
return result;
|
|
264
|
+
}
|
|
265
|
+
case constants.INT64: {
|
|
266
|
+
const result = new BigInt64Array(count);
|
|
267
|
+
for (let i = 0; i < count; i++) {
|
|
268
|
+
result[i] = readInt64Data(cursor);
|
|
269
|
+
}
|
|
270
|
+
return result;
|
|
271
|
+
}
|
|
272
|
+
case constants.NULL:
|
|
273
|
+
return Array.from({ length: count }).fill(null);
|
|
274
|
+
case constants.TRUE:
|
|
275
|
+
return Array.from({ length: count }).fill(true);
|
|
276
|
+
case constants.FALSE:
|
|
277
|
+
return Array.from({ length: count }).fill(false);
|
|
278
|
+
default:
|
|
279
|
+
break;
|
|
280
|
+
}
|
|
281
|
+
const array: unknown[] = [];
|
|
282
|
+
array.length = count;
|
|
283
|
+
for (let i = 0; i < count; i++) {
|
|
284
|
+
array[i] = type === undefined ? read(cursor) : readData(cursor, type);
|
|
285
|
+
}
|
|
286
|
+
return array;
|
|
287
|
+
}
|
|
288
|
+
case constants.FLOAT64:
|
|
289
|
+
return readFloat64Data(cursor);
|
|
290
|
+
case constants.UINT8:
|
|
291
|
+
return readUint8Data(cursor);
|
|
292
|
+
case constants.INT16:
|
|
293
|
+
return readInt16Data(cursor);
|
|
294
|
+
case constants.FLOAT32:
|
|
295
|
+
return readFloat32Data(cursor);
|
|
296
|
+
case constants.CHAR:
|
|
297
|
+
return fromCharCode(readUint8Data(cursor));
|
|
298
|
+
case constants.INT32:
|
|
299
|
+
return readInt32Data(cursor);
|
|
300
|
+
case constants.INT8:
|
|
301
|
+
return readInt8Data(cursor);
|
|
302
|
+
case constants.NULL:
|
|
303
|
+
return null;
|
|
304
|
+
case constants.TRUE:
|
|
305
|
+
return true;
|
|
306
|
+
case constants.FALSE:
|
|
307
|
+
return false;
|
|
308
|
+
case constants.INT64: {
|
|
309
|
+
const n = readInt64Data(cursor);
|
|
310
|
+
if (n < Number.MIN_SAFE_INTEGER || n > Number.MAX_SAFE_INTEGER) {
|
|
311
|
+
return n;
|
|
312
|
+
}
|
|
313
|
+
return Number(n);
|
|
314
|
+
}
|
|
315
|
+
case constants.HIGH_PRECISION_NUMBER: {
|
|
316
|
+
const length = readLength(cursor);
|
|
317
|
+
try {
|
|
318
|
+
const _buffer = new Uint8Array(cursor.data.buffer, cursor.offset, length).slice();
|
|
319
|
+
cursor.offset += length;
|
|
320
|
+
// return buffer;
|
|
321
|
+
} catch {
|
|
322
|
+
return cursor.eof();
|
|
323
|
+
}
|
|
324
|
+
unsupportedType('high precision number');
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
if (typeof marker != 'number') return marker;
|
|
328
|
+
throw new Error(`Unexpected marker '${fromCharCode(marker)}'(${marker})`);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/** readKey */
|
|
332
|
+
export function readKey(cursor: DecodeCursor): string {
|
|
333
|
+
const length = readLength(cursor);
|
|
334
|
+
const begin = cursor.offset;
|
|
335
|
+
const end = begin + length;
|
|
336
|
+
cursor.offset = end;
|
|
337
|
+
const { data } = cursor;
|
|
338
|
+
if (end > data.length) cursor.eof();
|
|
339
|
+
return decodeKey(data, begin, end);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/** 读取 Optimized Format 数据 */
|
|
343
|
+
export function readOptimizedFormatMarkers(cursor: DecodeCursor): { type?: number; count: number } | undefined {
|
|
344
|
+
let type;
|
|
345
|
+
let count;
|
|
346
|
+
switch (readMarker(cursor)) {
|
|
347
|
+
case constants.TYPE_MARKER:
|
|
348
|
+
type = readMarker(cursor);
|
|
349
|
+
if (readMarker(cursor) !== constants.COUNT_MARKER) {
|
|
350
|
+
throw new Error('Expected count marker');
|
|
351
|
+
}
|
|
352
|
+
/* fall through */
|
|
353
|
+
case constants.COUNT_MARKER:
|
|
354
|
+
count = readLength(cursor);
|
|
355
|
+
break;
|
|
356
|
+
default:
|
|
357
|
+
// 不是 '$' 或 '#',回溯
|
|
358
|
+
cursor.offset--;
|
|
359
|
+
return undefined;
|
|
360
|
+
}
|
|
361
|
+
return { type, count };
|
|
362
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { constants } from './constants.js';
|
|
2
|
+
import { unsupportedView } from './errors.js';
|
|
3
|
+
|
|
4
|
+
/** 数据包装 */
|
|
5
|
+
export interface EncodeCursor {
|
|
6
|
+
/** 数据 */
|
|
7
|
+
readonly view: DataView;
|
|
8
|
+
/** 数据 */
|
|
9
|
+
readonly data: Uint8Array;
|
|
10
|
+
/** 当前写指针位置 */
|
|
11
|
+
length: number;
|
|
12
|
+
/** 确保 buffer 还有 capacity 的空闲空间 */
|
|
13
|
+
ensureCapacity(capacity: number): void;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** 创建数据包装 */
|
|
17
|
+
export function EncodeCursor(length: number): EncodeCursor {
|
|
18
|
+
const data = new Uint8Array(length);
|
|
19
|
+
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
20
|
+
return {
|
|
21
|
+
data,
|
|
22
|
+
view,
|
|
23
|
+
length: 0,
|
|
24
|
+
ensureCapacity(capacity: number) {
|
|
25
|
+
if (this.length + capacity > this.data.length) {
|
|
26
|
+
throw new RangeError('Out of capacity');
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** 写入标签 */
|
|
33
|
+
export function writeMarker(cursor: EncodeCursor, marker: constants): void {
|
|
34
|
+
cursor.ensureCapacity(1);
|
|
35
|
+
cursor.data[cursor.length++] = marker;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** 写入长度 */
|
|
39
|
+
export function writeLength(cursor: EncodeCursor, length: number): void {
|
|
40
|
+
if (length < 0x80) {
|
|
41
|
+
cursor.ensureCapacity(2);
|
|
42
|
+
cursor.data[cursor.length++] = constants.INT8;
|
|
43
|
+
cursor.data[cursor.length++] = length;
|
|
44
|
+
} else if (length < 0x8000) {
|
|
45
|
+
cursor.ensureCapacity(3);
|
|
46
|
+
cursor.data[cursor.length++] = constants.INT16;
|
|
47
|
+
cursor.view.setInt16(cursor.length, length);
|
|
48
|
+
cursor.length += 2;
|
|
49
|
+
} else if (length < 0x8000_0000) {
|
|
50
|
+
cursor.ensureCapacity(5);
|
|
51
|
+
cursor.data[cursor.length++] = constants.INT32;
|
|
52
|
+
cursor.view.setInt32(cursor.length, length);
|
|
53
|
+
cursor.length += 4;
|
|
54
|
+
} else if (length < Number.MAX_SAFE_INTEGER) {
|
|
55
|
+
cursor.ensureCapacity(9);
|
|
56
|
+
cursor.data[cursor.length++] = constants.INT64;
|
|
57
|
+
cursor.view.setBigInt64(cursor.length, BigInt(length));
|
|
58
|
+
cursor.length += 8;
|
|
59
|
+
} else {
|
|
60
|
+
throw new RangeError('Invalid length');
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** 写入 TypedArray 前导,包括 marker 和长度 */
|
|
65
|
+
export function writeTypedArrayHeader(cursor: EncodeCursor, value: ArrayBufferView): void {
|
|
66
|
+
// ARRAY(1) + TYPE_MARKER(1) + TYPE(1) + COUNT_MARKER(1) + COUNT(MIN2 MAX5) + DATA
|
|
67
|
+
cursor.ensureCapacity(9);
|
|
68
|
+
cursor.data[cursor.length++] = constants.ARRAY;
|
|
69
|
+
cursor.data[cursor.length++] = constants.TYPE_MARKER;
|
|
70
|
+
if (value instanceof Uint8Array) {
|
|
71
|
+
cursor.data[cursor.length++] = constants.UINT8;
|
|
72
|
+
} else if (value instanceof Float64Array) {
|
|
73
|
+
cursor.data[cursor.length++] = constants.FLOAT64;
|
|
74
|
+
} else if (value instanceof Int32Array) {
|
|
75
|
+
cursor.data[cursor.length++] = constants.INT32;
|
|
76
|
+
} else if (value instanceof Int8Array) {
|
|
77
|
+
cursor.data[cursor.length++] = constants.INT8;
|
|
78
|
+
} else if (value instanceof Int16Array) {
|
|
79
|
+
cursor.data[cursor.length++] = constants.INT16;
|
|
80
|
+
} else if (value instanceof Float32Array) {
|
|
81
|
+
cursor.data[cursor.length++] = constants.FLOAT32;
|
|
82
|
+
} else if (value instanceof BigInt64Array) {
|
|
83
|
+
cursor.data[cursor.length++] = constants.INT64;
|
|
84
|
+
} else {
|
|
85
|
+
unsupportedView(value);
|
|
86
|
+
}
|
|
87
|
+
cursor.data[cursor.length++] = constants.COUNT_MARKER;
|
|
88
|
+
writeLength(cursor, value.length);
|
|
89
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
const getType = (obj: unknown): string => Object.prototype.toString.call(obj).slice(8, -1);
|
|
2
|
+
|
|
1
3
|
/** unsupported view */
|
|
2
4
|
export function unsupportedView(view: ArrayBufferView): never {
|
|
3
|
-
const type =
|
|
5
|
+
const type = getType(view);
|
|
4
6
|
throw new Error(`Unsupported array buffer view of type ${type}`);
|
|
5
7
|
}
|
|
6
8
|
|
|
@@ -9,6 +11,14 @@ export function unsupportedType(value: unknown): never {
|
|
|
9
11
|
if (value && typeof value == 'string') {
|
|
10
12
|
throw new Error(`Unsupported type ${value}`);
|
|
11
13
|
}
|
|
12
|
-
const type =
|
|
14
|
+
const type = getType(value);
|
|
13
15
|
throw new Error(`Unsupported type ${type}`);
|
|
14
16
|
}
|
|
17
|
+
|
|
18
|
+
/** 未结束的流 */
|
|
19
|
+
export class UnexpectedEofError extends Error {
|
|
20
|
+
constructor() {
|
|
21
|
+
super('Unexpected EOF');
|
|
22
|
+
this.name = 'UnexpectedEofError';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -8,10 +8,3 @@ export function toUint8Array(data: BinaryData): Uint8Array {
|
|
|
8
8
|
}
|
|
9
9
|
return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
10
10
|
}
|
|
11
|
-
|
|
12
|
-
/** 未结束的流 */
|
|
13
|
-
export class UnexpectedEof extends Error {
|
|
14
|
-
constructor() {
|
|
15
|
-
super('Unexpected EOF');
|
|
16
|
-
}
|
|
17
|
-
}
|
package/src/index.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { getEncoder } from './encoder.js';
|
|
2
|
-
import { Decoder
|
|
3
|
-
export { UnexpectedEof } from './
|
|
4
|
-
|
|
5
|
-
export type { DecoderOptions };
|
|
2
|
+
import { Decoder } from './decoder.js';
|
|
3
|
+
export { UnexpectedEofError as UnexpectedEof } from './helper/errors.js';
|
|
6
4
|
|
|
7
5
|
/** 编码为 UBJSON */
|
|
8
6
|
export function encode(value: unknown): Uint8Array {
|
|
@@ -15,14 +13,14 @@ export function encodeMany(value: Iterable<unknown>): Uint8Array {
|
|
|
15
13
|
}
|
|
16
14
|
|
|
17
15
|
/** 解码 UBJSON */
|
|
18
|
-
export function decode(value: BinaryData
|
|
19
|
-
const decoder = new Decoder(value
|
|
16
|
+
export function decode(value: BinaryData): unknown {
|
|
17
|
+
const decoder = new Decoder(value);
|
|
20
18
|
return decoder.decode();
|
|
21
19
|
}
|
|
22
20
|
|
|
23
21
|
/** 解码 UBJSON */
|
|
24
|
-
export function* decodeMany(value: BinaryData
|
|
25
|
-
const decoder = new Decoder(value
|
|
22
|
+
export function* decodeMany(value: BinaryData): Iterable<unknown> {
|
|
23
|
+
const decoder = new Decoder(value);
|
|
26
24
|
while (!decoder.ended) {
|
|
27
25
|
yield decoder.decode();
|
|
28
26
|
}
|
package/src/rxjs/decoder.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Observable, type OperatorFunction } from 'rxjs';
|
|
2
2
|
import { StreamDecoderHelper, kEof, UnexpectedEof } from '../stream-helper/decoder.js';
|
|
3
|
-
import { toUint8Array } from '../utils.js';
|
|
3
|
+
import { toUint8Array } from '../helper/utils.js';
|
|
4
4
|
|
|
5
5
|
const DEFAULT_BUFFER_SIZE = 16 * 1024 * 1024; // 16 MiB
|
|
6
6
|
const EMPTY_BUFFER = new Uint8Array(0);
|
package/src/rxjs/index.ts
CHANGED
package/src/stream/index.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Readable, Transform } from 'node:stream';
|
|
1
|
+
import { Readable, type Transform } from 'node:stream';
|
|
2
2
|
import { StreamEncoder } from './encoder.js';
|
|
3
3
|
import { StreamDecoder } from './decoder.js';
|
|
4
4
|
|
|
5
|
-
export { UnexpectedEof } from '../
|
|
5
|
+
export { UnexpectedEofError as UnexpectedEof } from '../helper/errors.js';
|
|
6
6
|
|
|
7
7
|
/** 编码为 UBJSON */
|
|
8
8
|
export function encode(value: unknown): Readable {
|
|
@@ -37,7 +37,7 @@ export function encoder(): Transform {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
/** 解码 UBJSON */
|
|
40
|
-
export function decode(stream: NodeJS.ReadableStream): Promise<unknown> {
|
|
40
|
+
export async function decode(stream: NodeJS.ReadableStream): Promise<unknown> {
|
|
41
41
|
return new Promise((resolve, reject) => {
|
|
42
42
|
const decoder = new StreamDecoder();
|
|
43
43
|
decoder.on('error', reject);
|
|
@@ -1,17 +1,14 @@
|
|
|
1
|
-
import { DecoderBase } from '../
|
|
1
|
+
import { DecoderBase } from '../base/decoder.js';
|
|
2
2
|
|
|
3
3
|
/** 未结束的流 */
|
|
4
4
|
export const kEof = Symbol('EOF');
|
|
5
5
|
|
|
6
6
|
/** 流式解码 UBJSON */
|
|
7
7
|
export class StreamDecoderHelper extends DecoderBase {
|
|
8
|
-
constructor(data: Uint8Array) {
|
|
9
|
-
super(data);
|
|
10
|
-
}
|
|
11
8
|
/** @inheritdoc */
|
|
12
9
|
protected override eof(): never {
|
|
13
10
|
// 性能优化,避免 new Error 的开销
|
|
14
|
-
// eslint-disable-next-line @typescript-eslint/
|
|
11
|
+
// eslint-disable-next-line @typescript-eslint/only-throw-error
|
|
15
12
|
throw kEof;
|
|
16
13
|
}
|
|
17
14
|
|
|
@@ -21,4 +18,4 @@ export class StreamDecoderHelper extends DecoderBase {
|
|
|
21
18
|
}
|
|
22
19
|
}
|
|
23
20
|
|
|
24
|
-
export { UnexpectedEof } from '../
|
|
21
|
+
export { UnexpectedEofError as UnexpectedEof } from '../helper/errors.js';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { constants } from '../
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { constants } from '../helper/constants.js';
|
|
2
|
+
import { unsupportedView } from '../helper/errors.js';
|
|
3
|
+
import { encode, stringByteLength } from '../helper/string-encoder.js';
|
|
4
|
+
import { EncoderBase } from '../base/encoder.js';
|
|
5
5
|
|
|
6
6
|
const BLOCK_SIZE = 1024 * 64; // 64 KiB
|
|
7
7
|
const MAX_SIZE = 1024 * 1024 * 32; // 32 MiB
|
|
@@ -32,14 +32,14 @@ function free(buf: Uint8Array): boolean {
|
|
|
32
32
|
export class StreamEncoderHelper extends EncoderBase {
|
|
33
33
|
constructor(protected readonly onChunk: (chunk: Uint8Array) => void) {
|
|
34
34
|
super();
|
|
35
|
-
this.
|
|
36
|
-
this.view = new DataView(this.
|
|
35
|
+
this.data = alloc(BLOCK_SIZE);
|
|
36
|
+
this.view = new DataView(this.data.buffer);
|
|
37
37
|
}
|
|
38
38
|
/**
|
|
39
39
|
* 销毁实例,释放内存池
|
|
40
40
|
*/
|
|
41
41
|
destroy(): void {
|
|
42
|
-
free(this.
|
|
42
|
+
free(this.data);
|
|
43
43
|
const self = this as unknown as { view: DataView | null; buffer: Uint8Array | null };
|
|
44
44
|
self.view = null;
|
|
45
45
|
self.buffer = null;
|
|
@@ -53,26 +53,26 @@ export class StreamEncoderHelper extends EncoderBase {
|
|
|
53
53
|
throw new Error('Buffer has exceed max size');
|
|
54
54
|
}
|
|
55
55
|
// 无需扩容
|
|
56
|
-
if (capacity >= 0 && this.
|
|
56
|
+
if (capacity >= 0 && this.data.byteLength >= this.length + capacity) return;
|
|
57
57
|
|
|
58
|
-
const CURRENT_SIZE = this.
|
|
58
|
+
const CURRENT_SIZE = this.data.byteLength;
|
|
59
59
|
const NEXT_SIZE = capacity < BLOCK_SIZE ? BLOCK_SIZE : capacity;
|
|
60
60
|
const REUSE_BUF =
|
|
61
61
|
CURRENT_SIZE >= NEXT_SIZE && // 满足容量需求
|
|
62
62
|
CURRENT_SIZE - BLOCK_SIZE < NEXT_SIZE; // 不过于浪费
|
|
63
63
|
// 提交目前的数据
|
|
64
64
|
if (REUSE_BUF) {
|
|
65
|
-
this.onChunk(this.
|
|
65
|
+
this.onChunk(this.data.slice(0, this.length));
|
|
66
66
|
} else {
|
|
67
|
-
if (free(this.
|
|
67
|
+
if (free(this.data)) {
|
|
68
68
|
// 归还内存池成功,buffer 可能重用,需要拷贝数据
|
|
69
|
-
this.onChunk(this.
|
|
69
|
+
this.onChunk(this.data.slice(0, this.length));
|
|
70
70
|
} else {
|
|
71
|
-
this.onChunk(this.
|
|
71
|
+
this.onChunk(this.data.subarray(0, this.length));
|
|
72
72
|
}
|
|
73
73
|
// 重新分配缓冲区
|
|
74
|
-
this.
|
|
75
|
-
this.view = new DataView(this.
|
|
74
|
+
this.data = alloc(NEXT_SIZE);
|
|
75
|
+
this.view = new DataView(this.data.buffer);
|
|
76
76
|
}
|
|
77
77
|
this.length = 0;
|
|
78
78
|
}
|
|
@@ -81,7 +81,7 @@ export class StreamEncoderHelper extends EncoderBase {
|
|
|
81
81
|
const strLen = value.length;
|
|
82
82
|
const binLen = stringByteLength(value);
|
|
83
83
|
this.ensureCapacity(5);
|
|
84
|
-
this.
|
|
84
|
+
this.data[this.length++] = constants.INT32;
|
|
85
85
|
this.view.setInt32(this.length, binLen);
|
|
86
86
|
this.length += 4;
|
|
87
87
|
this.ensureCapacity(-1);
|