@cloudpss/ubjson 0.5.39 → 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/decoder.d.ts +1 -0
- package/dist/base/decoder.d.ts.map +1 -0
- package/dist/base/encoder.d.ts +5 -1
- package/dist/base/encoder.d.ts.map +1 -0
- package/dist/base/encoder.js +50 -188
- package/dist/base/encoder.js.map +1 -1
- package/dist/decoder.d.ts +1 -0
- package/dist/decoder.d.ts.map +1 -0
- package/dist/encoder.d.ts +7 -1
- package/dist/encoder.d.ts.map +1 -0
- package/dist/encoder.js +24 -15
- package/dist/encoder.js.map +1 -1
- package/dist/helper/constants.d.ts +1 -0
- package/dist/helper/constants.d.ts.map +1 -0
- 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 +6 -3
- package/dist/helper/decode.d.ts.map +1 -0
- package/dist/helper/decode.js +33 -20
- package/dist/helper/decode.js.map +1 -1
- package/dist/helper/encode.d.ts +19 -1
- package/dist/helper/encode.d.ts.map +1 -0
- package/dist/helper/encode.js +138 -16
- package/dist/helper/encode.js.map +1 -1
- package/dist/helper/errors.d.ts +1 -0
- package/dist/helper/errors.d.ts.map +1 -0
- package/dist/helper/string-decoder.d.ts +5 -2
- package/dist/helper/string-decoder.d.ts.map +1 -0
- package/dist/helper/string-decoder.js +10 -38
- package/dist/helper/string-decoder.js.map +1 -1
- package/dist/helper/string-encoder.d.ts +1 -0
- package/dist/helper/string-encoder.d.ts.map +1 -0
- package/dist/helper/utils.d.ts +1 -0
- package/dist/helper/utils.d.ts.map +1 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -0
- 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 +1 -0
- package/dist/rxjs/decoder.d.ts.map +1 -0
- package/dist/rxjs/decoder.js +66 -40
- package/dist/rxjs/decoder.js.map +1 -1
- package/dist/rxjs/encoder.d.ts +3 -1
- package/dist/rxjs/encoder.d.ts.map +1 -0
- package/dist/rxjs/encoder.js +2 -2
- package/dist/rxjs/encoder.js.map +1 -1
- package/dist/rxjs/index.d.ts +2 -0
- package/dist/rxjs/index.d.ts.map +1 -0
- package/dist/stream/decoder.d.ts +1 -0
- package/dist/stream/decoder.d.ts.map +1 -0
- package/dist/stream/encoder.d.ts +4 -2
- package/dist/stream/encoder.d.ts.map +1 -0
- package/dist/stream/encoder.js +2 -2
- package/dist/stream/encoder.js.map +1 -1
- package/dist/stream/index.d.ts +6 -3
- package/dist/stream/index.d.ts.map +1 -0
- package/dist/stream/index.js +6 -6
- package/dist/stream/index.js.map +1 -1
- package/dist/stream-helper/decoder.d.ts +1 -0
- package/dist/stream-helper/decoder.d.ts.map +1 -0
- package/dist/stream-helper/encoder.d.ts +5 -2
- package/dist/stream-helper/encoder.d.ts.map +1 -0
- package/dist/stream-helper/encoder.js +33 -31
- package/dist/stream-helper/encoder.js.map +1 -1
- package/jest.config.js +1 -1
- package/package.json +3 -4
- package/src/base/encoder.ts +58 -174
- package/src/encoder.ts +27 -15
- package/src/helper/decode-ae.ts +621 -0
- package/src/helper/decode.ts +36 -23
- package/src/helper/encode.ts +141 -15
- package/src/helper/string-decoder.ts +10 -39
- 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 +39 -34
- package/tests/.utils.js +1 -0
- package/tests/e2e/stream.js +13 -1
- package/tests/encode.js +8 -0
- package/tests/string-encoding.js +1 -10
- package/tests/tsconfig.json +1 -7
- package/tsconfig.json +1 -1
package/src/helper/decode.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { constants } from './constants.js';
|
|
2
2
|
import { UnexpectedEofError, unsupportedType } from './errors.js';
|
|
3
|
-
import { decode
|
|
3
|
+
import { decode } from './string-decoder.js';
|
|
4
4
|
import { toUint8Array } from './utils.js';
|
|
5
|
-
|
|
6
|
-
const { fromEntries } = Object;
|
|
5
|
+
const { defineProperty } = Object;
|
|
7
6
|
const { fromCharCode } = String;
|
|
8
7
|
|
|
9
8
|
/** 数据包装 */
|
|
@@ -82,7 +81,7 @@ export function readLength(cursor: DecodeCursor): number {
|
|
|
82
81
|
break;
|
|
83
82
|
}
|
|
84
83
|
default:
|
|
85
|
-
throw new Error(`Unexpected marker '${
|
|
84
|
+
throw new Error(`Unexpected marker '${fromCharCode(marker)}'(${marker}) for int length`);
|
|
86
85
|
}
|
|
87
86
|
if (length < 0) {
|
|
88
87
|
throw new Error('Invalid length');
|
|
@@ -161,6 +160,21 @@ export function read(cursor: DecodeCursor): unknown {
|
|
|
161
160
|
return readData(cursor, marker);
|
|
162
161
|
}
|
|
163
162
|
|
|
163
|
+
/** 读取优化对象数据 */
|
|
164
|
+
function readObjectOptimizedData(cursor: DecodeCursor, marker: OptimizedFormatMarkers): unknown {
|
|
165
|
+
const { count, type } = marker;
|
|
166
|
+
const object: Record<string, unknown> = {};
|
|
167
|
+
for (let i = 0; i < count; i++) {
|
|
168
|
+
const key = readKey(cursor);
|
|
169
|
+
const value = readData(cursor, type ?? readMarker(cursor));
|
|
170
|
+
if (key === '__proto__') {
|
|
171
|
+
defineProperty(object, key, { value, enumerable: true, configurable: true, writable: true });
|
|
172
|
+
}
|
|
173
|
+
object[key] = value;
|
|
174
|
+
}
|
|
175
|
+
return object;
|
|
176
|
+
}
|
|
177
|
+
|
|
164
178
|
/** 根据标签读取后续数据 */
|
|
165
179
|
export function readData(cursor: DecodeCursor, marker: number): unknown {
|
|
166
180
|
// 按照出现频率排序
|
|
@@ -178,32 +192,29 @@ export function readData(cursor: DecodeCursor, marker: number): unknown {
|
|
|
178
192
|
const markers = readOptimizedFormatMarkers(cursor);
|
|
179
193
|
if (markers == null) {
|
|
180
194
|
// 直到 '}'
|
|
181
|
-
const object:
|
|
195
|
+
const object: Record<string, unknown> = {};
|
|
182
196
|
while (readMarker(cursor) !== constants.OBJECT_END) {
|
|
183
197
|
cursor.offset--;
|
|
184
|
-
|
|
198
|
+
const key = readKey(cursor);
|
|
199
|
+
const value = read(cursor);
|
|
200
|
+
if (key === '__proto__') {
|
|
201
|
+
defineProperty(object, key, { value, enumerable: true, configurable: true, writable: true });
|
|
202
|
+
}
|
|
203
|
+
object[key] = value;
|
|
185
204
|
}
|
|
186
|
-
return
|
|
205
|
+
return object;
|
|
187
206
|
}
|
|
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);
|
|
207
|
+
return readObjectOptimizedData(cursor, markers);
|
|
198
208
|
}
|
|
199
209
|
case constants.ARRAY: {
|
|
200
210
|
const markers = readOptimizedFormatMarkers(cursor);
|
|
201
211
|
if (markers == null) {
|
|
202
212
|
const array = [];
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
213
|
+
for (;;) {
|
|
214
|
+
const marker = readMarker(cursor);
|
|
215
|
+
// 直到 ']'
|
|
216
|
+
if (marker === constants.ARRAY_END) break;
|
|
217
|
+
array.push(readData(cursor, marker));
|
|
207
218
|
}
|
|
208
219
|
return array;
|
|
209
220
|
}
|
|
@@ -336,11 +347,13 @@ export function readKey(cursor: DecodeCursor): string {
|
|
|
336
347
|
cursor.offset = end;
|
|
337
348
|
const { data } = cursor;
|
|
338
349
|
if (end > data.length) cursor.eof();
|
|
339
|
-
return
|
|
350
|
+
return decode(data, begin, end);
|
|
340
351
|
}
|
|
341
352
|
|
|
353
|
+
/** Optimized Format 数据 */
|
|
354
|
+
export type OptimizedFormatMarkers = { type?: number; count: number };
|
|
342
355
|
/** 读取 Optimized Format 数据 */
|
|
343
|
-
export function readOptimizedFormatMarkers(cursor: DecodeCursor):
|
|
356
|
+
export function readOptimizedFormatMarkers(cursor: DecodeCursor): OptimizedFormatMarkers | undefined {
|
|
344
357
|
let type;
|
|
345
358
|
let count;
|
|
346
359
|
switch (readMarker(cursor)) {
|
package/src/helper/encode.ts
CHANGED
|
@@ -35,12 +35,14 @@ export function writeMarker(cursor: EncodeCursor, marker: constants): void {
|
|
|
35
35
|
cursor.data[cursor.length++] = marker;
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
export const I8_MASK = constants.INT8 << 8;
|
|
39
|
+
export const U8_MASK = constants.UINT8 << 8;
|
|
38
40
|
/** 写入长度 */
|
|
39
41
|
export function writeLength(cursor: EncodeCursor, length: number): void {
|
|
40
42
|
if (length < 0x80) {
|
|
41
43
|
cursor.ensureCapacity(2);
|
|
42
|
-
cursor.
|
|
43
|
-
cursor.
|
|
44
|
+
cursor.view.setUint16(cursor.length, I8_MASK | length);
|
|
45
|
+
cursor.length += 2;
|
|
44
46
|
} else if (length < 0x8000) {
|
|
45
47
|
cursor.ensureCapacity(3);
|
|
46
48
|
cursor.data[cursor.length++] = constants.INT16;
|
|
@@ -61,29 +63,153 @@ export function writeLength(cursor: EncodeCursor, length: number): void {
|
|
|
61
63
|
}
|
|
62
64
|
}
|
|
63
65
|
|
|
66
|
+
/** 写入数字 */
|
|
67
|
+
export function writeNumber(cursor: EncodeCursor, value: number): void {
|
|
68
|
+
// eslint-disable-next-line unicorn/prefer-math-trunc
|
|
69
|
+
if (value >> 0 === value) {
|
|
70
|
+
if (value >= 0 && value <= 0xff) {
|
|
71
|
+
cursor.ensureCapacity(2);
|
|
72
|
+
const { length } = cursor;
|
|
73
|
+
cursor.view.setUint16(length, U8_MASK | value);
|
|
74
|
+
cursor.length = length + 2;
|
|
75
|
+
} else if (value < 0x80 && value >= -0x80) {
|
|
76
|
+
cursor.ensureCapacity(2);
|
|
77
|
+
const { length } = cursor;
|
|
78
|
+
cursor.view.setUint16(length, I8_MASK | (value & 0xff));
|
|
79
|
+
cursor.length = length + 2;
|
|
80
|
+
} else if (value < 0x8000 && value >= -0x8000) {
|
|
81
|
+
cursor.ensureCapacity(3);
|
|
82
|
+
const { length } = cursor;
|
|
83
|
+
cursor.data[length] = constants.INT16;
|
|
84
|
+
cursor.view.setInt16(length + 1, value);
|
|
85
|
+
cursor.length = length + 3;
|
|
86
|
+
} else {
|
|
87
|
+
// must be 32 bit
|
|
88
|
+
cursor.ensureCapacity(5);
|
|
89
|
+
const { length } = cursor;
|
|
90
|
+
cursor.data[length] = constants.INT32;
|
|
91
|
+
cursor.view.setInt32(length + 1, value);
|
|
92
|
+
cursor.length = length + 5;
|
|
93
|
+
}
|
|
94
|
+
} else if (Number.isNaN(value) || Math.fround(value) === value) {
|
|
95
|
+
// 如果不会损失精度,使用 32 位浮点
|
|
96
|
+
cursor.ensureCapacity(5);
|
|
97
|
+
const { length } = cursor;
|
|
98
|
+
cursor.data[length] = constants.FLOAT32;
|
|
99
|
+
cursor.view.setFloat32(length + 1, value);
|
|
100
|
+
cursor.length = length + 5;
|
|
101
|
+
} else {
|
|
102
|
+
cursor.ensureCapacity(9);
|
|
103
|
+
const { length } = cursor;
|
|
104
|
+
cursor.data[length] = constants.FLOAT64;
|
|
105
|
+
cursor.view.setFloat64(length + 1, value);
|
|
106
|
+
cursor.length = length + 9;
|
|
107
|
+
}
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** TypedArray 类型 */
|
|
112
|
+
export type TypedArrayType =
|
|
113
|
+
| constants.UINT8
|
|
114
|
+
| constants.INT8
|
|
115
|
+
| constants.INT16
|
|
116
|
+
| constants.INT32
|
|
117
|
+
| constants.INT64
|
|
118
|
+
| constants.FLOAT32
|
|
119
|
+
| constants.FLOAT64;
|
|
120
|
+
const T_ARR_HEADER = (type: TypedArrayType): number =>
|
|
121
|
+
(constants.ARRAY << 24) | (constants.TYPE_MARKER << 16) | (type << 8) | constants.COUNT_MARKER;
|
|
122
|
+
export const U8_ARR_HEADER = T_ARR_HEADER(constants.UINT8);
|
|
123
|
+
export const I8_ARR_HEADER = T_ARR_HEADER(constants.INT8);
|
|
124
|
+
export const I16_ARR_HEADER = T_ARR_HEADER(constants.INT16);
|
|
125
|
+
export const I32_ARR_HEADER = T_ARR_HEADER(constants.INT32);
|
|
126
|
+
export const I64_ARR_HEADER = T_ARR_HEADER(constants.INT64);
|
|
127
|
+
export const F32_ARR_HEADER = T_ARR_HEADER(constants.FLOAT32);
|
|
128
|
+
export const F64_ARR_HEADER = T_ARR_HEADER(constants.FLOAT64);
|
|
129
|
+
|
|
64
130
|
/** 写入 TypedArray 前导,包括 marker 和长度 */
|
|
65
|
-
export function writeTypedArrayHeader(cursor: EncodeCursor, value: ArrayBufferView):
|
|
131
|
+
export function writeTypedArrayHeader(cursor: EncodeCursor, value: ArrayBufferView): TypedArrayType {
|
|
66
132
|
// ARRAY(1) + TYPE_MARKER(1) + TYPE(1) + COUNT_MARKER(1) + COUNT(MIN2 MAX5) + DATA
|
|
67
133
|
cursor.ensureCapacity(9);
|
|
68
|
-
|
|
69
|
-
|
|
134
|
+
let type: TypedArrayType;
|
|
135
|
+
const { length } = cursor;
|
|
70
136
|
if (value instanceof Uint8Array) {
|
|
71
|
-
cursor.
|
|
137
|
+
cursor.view.setUint32(length, U8_ARR_HEADER);
|
|
138
|
+
type = constants.UINT8;
|
|
72
139
|
} else if (value instanceof Float64Array) {
|
|
73
|
-
cursor.
|
|
140
|
+
cursor.view.setUint32(length, F64_ARR_HEADER);
|
|
141
|
+
type = constants.FLOAT64;
|
|
74
142
|
} else if (value instanceof Int32Array) {
|
|
75
|
-
cursor.
|
|
143
|
+
cursor.view.setUint32(length, I32_ARR_HEADER);
|
|
144
|
+
type = constants.INT32;
|
|
145
|
+
} else if (value instanceof BigInt64Array) {
|
|
146
|
+
cursor.view.setUint32(length, I64_ARR_HEADER);
|
|
147
|
+
type = constants.INT64;
|
|
148
|
+
} else if (value instanceof Float32Array) {
|
|
149
|
+
cursor.view.setUint32(length, F32_ARR_HEADER);
|
|
150
|
+
type = constants.FLOAT32;
|
|
76
151
|
} else if (value instanceof Int8Array) {
|
|
77
|
-
cursor.
|
|
152
|
+
cursor.view.setUint32(length, I8_ARR_HEADER);
|
|
153
|
+
type = constants.INT8;
|
|
78
154
|
} else if (value instanceof Int16Array) {
|
|
79
|
-
cursor.
|
|
80
|
-
|
|
81
|
-
cursor.data[cursor.length++] = constants.FLOAT32;
|
|
82
|
-
} else if (value instanceof BigInt64Array) {
|
|
83
|
-
cursor.data[cursor.length++] = constants.INT64;
|
|
155
|
+
cursor.view.setUint32(length, I16_ARR_HEADER);
|
|
156
|
+
type = constants.INT16;
|
|
84
157
|
} else {
|
|
85
158
|
unsupportedView(value);
|
|
86
159
|
}
|
|
87
|
-
cursor.
|
|
160
|
+
cursor.length = length + 4;
|
|
88
161
|
writeLength(cursor, value.length);
|
|
162
|
+
return type;
|
|
163
|
+
}
|
|
164
|
+
/** 写入 TypedArray */
|
|
165
|
+
export function writeTypedArray(cursor: EncodeCursor, value: ArrayBufferView): void {
|
|
166
|
+
cursor.ensureCapacity(9 + value.byteLength);
|
|
167
|
+
const type = writeTypedArrayHeader(cursor, value);
|
|
168
|
+
writeTypedArrayData(cursor, type, value);
|
|
169
|
+
}
|
|
170
|
+
/** 写入 TypedArray 数据 */
|
|
171
|
+
export function writeTypedArrayData(cursor: EncodeCursor, type: TypedArrayType, value: ArrayBufferView): void {
|
|
172
|
+
const { byteLength } = value;
|
|
173
|
+
cursor.ensureCapacity(byteLength);
|
|
174
|
+
let pointer = cursor.length;
|
|
175
|
+
cursor.length = pointer + byteLength;
|
|
176
|
+
if (type === constants.UINT8 || type === constants.INT8) {
|
|
177
|
+
// fast path for typed arrays with `BYTES_PER_ELEMENT` of 1
|
|
178
|
+
cursor.data.set(value as Uint8Array | Int8Array, pointer);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const { view } = cursor;
|
|
183
|
+
if (type === constants.FLOAT64) {
|
|
184
|
+
const arrayLength = byteLength / 8;
|
|
185
|
+
for (let i = 0; i < arrayLength; i++) {
|
|
186
|
+
view.setFloat64(pointer, (value as Float64Array)[i]!);
|
|
187
|
+
pointer += 8;
|
|
188
|
+
}
|
|
189
|
+
} else if (type === constants.INT32) {
|
|
190
|
+
const arrayLength = byteLength / 4;
|
|
191
|
+
for (let i = 0; i < arrayLength; i++) {
|
|
192
|
+
view.setInt32(pointer, (value as Int32Array)[i]!);
|
|
193
|
+
pointer += 4;
|
|
194
|
+
}
|
|
195
|
+
} else if (type === constants.INT64) {
|
|
196
|
+
const arrayLength = byteLength / 8;
|
|
197
|
+
for (let i = 0; i < arrayLength; i++) {
|
|
198
|
+
view.setBigInt64(pointer, (value as BigInt64Array)[i]!);
|
|
199
|
+
pointer += 8;
|
|
200
|
+
}
|
|
201
|
+
} else if (type === constants.FLOAT32) {
|
|
202
|
+
const arrayLength = byteLength / 4;
|
|
203
|
+
for (let i = 0; i < arrayLength; i++) {
|
|
204
|
+
view.setFloat32(pointer, (value as Float32Array)[i]!);
|
|
205
|
+
pointer += 4;
|
|
206
|
+
}
|
|
207
|
+
} else {
|
|
208
|
+
(type) satisfies constants.INT16;
|
|
209
|
+
const arrayLength = byteLength / 2;
|
|
210
|
+
for (let i = 0; i < arrayLength; i++) {
|
|
211
|
+
view.setInt16(pointer, (value as Int16Array)[i]!);
|
|
212
|
+
pointer += 2;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
89
215
|
}
|
|
@@ -53,19 +53,19 @@ export function jsDecode(bytes: Uint8Array, begin: number, end: number): string
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
/** 解码 Ascii */
|
|
56
|
-
function longStringInJS(buf: Uint8Array, begin: number, length: number): string | undefined {
|
|
57
|
-
const bytes =
|
|
56
|
+
export function longStringInJS(buf: Uint8Array, begin: number, length: number): string | undefined {
|
|
57
|
+
const bytes = [];
|
|
58
58
|
for (let i = 0; i < length; i++) {
|
|
59
59
|
const byte = buf[begin++]!;
|
|
60
|
-
if (
|
|
60
|
+
if (byte & 0x80) {
|
|
61
61
|
return;
|
|
62
62
|
}
|
|
63
|
-
bytes
|
|
63
|
+
bytes.push(byte);
|
|
64
64
|
}
|
|
65
65
|
return fromCharCode(...bytes);
|
|
66
66
|
}
|
|
67
67
|
/** 解码 Ascii */
|
|
68
|
-
function shortStringInJS(buf: Uint8Array, begin: number, length: number): string | undefined {
|
|
68
|
+
export function shortStringInJS(buf: Uint8Array, begin: number, length: number): string | undefined {
|
|
69
69
|
if (length < 4) {
|
|
70
70
|
if (length < 2) {
|
|
71
71
|
if (length === 0) return '';
|
|
@@ -198,8 +198,12 @@ export function decode(data: Uint8Array, begin: number, end: number): string {
|
|
|
198
198
|
const result = shortStringInJS(data, begin, length);
|
|
199
199
|
if (result != null) return result;
|
|
200
200
|
}
|
|
201
|
-
//
|
|
201
|
+
// 只有小字符串有优化价值
|
|
202
202
|
if (length < TEXT_DECODER_THRESHOLD) {
|
|
203
|
+
// if (length < 32) {
|
|
204
|
+
// const result = longStringInJS(data, begin, length);
|
|
205
|
+
// if (result != null) return result;
|
|
206
|
+
// }
|
|
203
207
|
// 为小字符串优化
|
|
204
208
|
return jsDecode(data, begin, end);
|
|
205
209
|
}
|
|
@@ -207,42 +211,9 @@ export function decode(data: Uint8Array, begin: number, end: number): string {
|
|
|
207
211
|
return nativeDecode(data, begin, end);
|
|
208
212
|
}
|
|
209
213
|
|
|
210
|
-
const KEY_CACHE = Array.from<{ value: string; buffer: Uint8Array } | undefined>({ length: 4096 });
|
|
211
|
-
|
|
212
|
-
/** 字符串解码,使用缓存 */
|
|
213
|
-
export function decodeKey(data: Uint8Array, begin: number, end: number): string {
|
|
214
|
-
const length = end - begin;
|
|
215
|
-
const cacheKey =
|
|
216
|
-
((length << 5) ^ (length > 1 ? data[begin]! & (data[begin + 1]! << 8) : length > 0 ? data[begin]! : 0)) & 0xfff;
|
|
217
|
-
let entry = KEY_CACHE[cacheKey];
|
|
218
|
-
if (entry != null && entry.buffer.byteLength === length) {
|
|
219
|
-
let i = 0;
|
|
220
|
-
for (; i < length; i++) {
|
|
221
|
-
if (entry.buffer[i] !== data[begin + i]) break;
|
|
222
|
-
}
|
|
223
|
-
if (i === length) return entry.value;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
let str = length < 16 ? shortStringInJS(data, begin, length) : longStringInJS(data, begin, length);
|
|
227
|
-
if (str == null) {
|
|
228
|
-
// 只有小字符串有优化价值,见 benchmark-string.js
|
|
229
|
-
if (length < TEXT_DECODER_THRESHOLD) {
|
|
230
|
-
// 为小字符串优化
|
|
231
|
-
str = jsDecode(data, begin, end);
|
|
232
|
-
} else {
|
|
233
|
-
// 使用系统解码
|
|
234
|
-
str = nativeDecode(data, begin, end);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
entry = { value: str, buffer: data.slice(begin, end) };
|
|
238
|
-
KEY_CACHE[cacheKey] = entry;
|
|
239
|
-
return str;
|
|
240
|
-
}
|
|
241
|
-
|
|
242
214
|
/** 重设环境 */
|
|
243
215
|
export function resetEnv(): void {
|
|
244
216
|
TEXT_DECODER = typeof TextDecoder == 'function' ? new TextDecoder('utf8', { ignoreBOM: true, fatal: false }) : null;
|
|
245
217
|
TEXT_DECODER_THRESHOLD = TEXT_DECODER == null ? 0xffff_ffff : 16;
|
|
246
|
-
KEY_CACHE.fill(undefined);
|
|
247
218
|
}
|
|
248
219
|
resetEnv();
|
package/src/index.ts
CHANGED
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
import { getEncoder } from './encoder.js';
|
|
2
2
|
import { Decoder } from './decoder.js';
|
|
3
|
+
import type { EncodeOptions } from './options.js';
|
|
4
|
+
|
|
3
5
|
export { UnexpectedEofError as UnexpectedEof } from './helper/errors.js';
|
|
6
|
+
export type { EncodeOptions };
|
|
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 */
|
package/src/options.ts
ADDED
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 */
|