@cloudpss/ubjson 0.5.34 → 0.5.36

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.
Files changed (43) hide show
  1. package/{benchmark-string.js → benchmark-string-decode.js} +4 -4
  2. package/benchmark-string-encode.js +32 -0
  3. package/benchmark-string-size-caculation.js +9 -11
  4. package/benchmark.js +1 -0
  5. package/dist/common/decoder.js +2 -1
  6. package/dist/common/decoder.js.map +1 -1
  7. package/dist/common/encoder.d.ts +4 -2
  8. package/dist/common/encoder.js +106 -45
  9. package/dist/common/encoder.js.map +1 -1
  10. package/dist/common/errors.d.ts +4 -0
  11. package/dist/common/errors.js +14 -0
  12. package/dist/common/errors.js.map +1 -0
  13. package/dist/common/string-decoder.d.ts +5 -3
  14. package/dist/common/string-decoder.js +23 -14
  15. package/dist/common/string-decoder.js.map +1 -1
  16. package/dist/common/string-encoder.d.ts +32 -2
  17. package/dist/common/string-encoder.js +105 -12
  18. package/dist/common/string-encoder.js.map +1 -1
  19. package/dist/stream-helper/encoder.d.ts +4 -4
  20. package/dist/stream-helper/encoder.js +116 -41
  21. package/dist/stream-helper/encoder.js.map +1 -1
  22. package/package.json +3 -3
  23. package/src/common/decoder.ts +4 -3
  24. package/src/common/encoder.ts +106 -48
  25. package/src/common/errors.ts +14 -0
  26. package/src/common/string-decoder.ts +63 -54
  27. package/src/common/string-encoder.ts +103 -14
  28. package/src/stream-helper/encoder.ts +118 -39
  29. package/tests/.utils.js +10 -0
  30. package/tests/e2e/.data.js +470 -0
  31. package/tests/e2e/no-buffer-text.js +37 -0
  32. package/tests/e2e/no-buffer.js +30 -0
  33. package/tests/e2e/no-encode-into.js +32 -0
  34. package/tests/e2e/no-textencoder-decoder.js +34 -0
  35. package/tests/e2e/normal.js +27 -0
  36. package/tests/e2e/stream.js +20 -0
  37. package/tests/encode.js +11 -19
  38. package/tests/huge-string.js +7 -9
  39. package/tests/rxjs/encode.js +4 -18
  40. package/tests/stream/encode.js +0 -15
  41. package/tests/string-encoding.js +3 -2
  42. package/tests/tsconfig.json +2 -1
  43. package/tests/e2e.js +0 -415
@@ -1,10 +1,11 @@
1
1
  import { constants } from './constants.js';
2
- import { getStringByteLength, getEncodeInto } from './string-encoder.js';
2
+ import { unsupportedType, unsupportedView } from './errors.js';
3
+ import { stringByteLength, encodeInto } from './string-encoder.js';
4
+
5
+ const LARGE_DATA_LENGTH = 65536;
3
6
 
4
7
  /** 编码至 ubjson */
5
8
  export abstract class EncoderBase {
6
- protected readonly stringByteLength = getStringByteLength();
7
- protected readonly encodeInto = getEncodeInto();
8
9
  /** 当前写指针位置 */
9
10
  protected length = 0;
10
11
  /** 数据 */
@@ -79,7 +80,9 @@ export abstract class EncoderBase {
79
80
  if (value === null) {
80
81
  this.ensureCapacity(1);
81
82
  this.buffer[this.length++] = constants.NULL;
82
- } else if (Array.isArray(value)) {
83
+ return;
84
+ }
85
+ if (Array.isArray(value)) {
83
86
  this.ensureCapacity(1);
84
87
  this.buffer[this.length++] = constants.ARRAY;
85
88
  const size = value.length;
@@ -95,7 +98,9 @@ export abstract class EncoderBase {
95
98
  }
96
99
  this.ensureCapacity(1);
97
100
  this.buffer[this.length++] = constants.ARRAY_END;
98
- } else if (!ArrayBuffer.isView(value)) {
101
+ return;
102
+ }
103
+ if (!ArrayBuffer.isView(value)) {
99
104
  const { toJSON } = value as Record<string, unknown>;
100
105
  if (typeof toJSON == 'function') {
101
106
  this.write(toJSON.call(value));
@@ -107,7 +112,7 @@ export abstract class EncoderBase {
107
112
  const keys = Object.keys(value).sort();
108
113
  const size = keys.length;
109
114
  for (let index = 0; index < size; index++) {
110
- const key = keys[index];
115
+ const key = keys[index]!;
111
116
  const element = (value as Record<string, unknown>)[key];
112
117
  if (element === undefined || typeof element == 'function') continue;
113
118
  this.writeStringData(key);
@@ -115,6 +120,33 @@ export abstract class EncoderBase {
115
120
  }
116
121
  this.ensureCapacity(1);
117
122
  this.buffer[this.length++] = constants.OBJECT_END;
123
+ return;
124
+ }
125
+ if (value.byteLength > LARGE_DATA_LENGTH) {
126
+ // ARRAY(1) + TYPE_MARKER(1) + TYPE(1) + COUNT_MARKER(1) + COUNT(5)
127
+ this.ensureCapacity(9);
128
+ this.buffer[this.length++] = constants.ARRAY;
129
+ this.buffer[this.length++] = constants.TYPE_MARKER;
130
+ if (value instanceof Uint8Array) {
131
+ this.buffer[this.length++] = constants.UINT8;
132
+ } else if (value instanceof Int8Array) {
133
+ this.buffer[this.length++] = constants.INT8;
134
+ } else if (value instanceof Int16Array) {
135
+ this.buffer[this.length++] = constants.INT16;
136
+ } else if (value instanceof Int32Array) {
137
+ this.buffer[this.length++] = constants.INT32;
138
+ } else if (value instanceof Float32Array) {
139
+ this.buffer[this.length++] = constants.FLOAT32;
140
+ } else if (value instanceof Float64Array) {
141
+ this.buffer[this.length++] = constants.FLOAT64;
142
+ } else if (value instanceof BigInt64Array) {
143
+ this.buffer[this.length++] = constants.INT64;
144
+ } else {
145
+ unsupportedView(value);
146
+ }
147
+ this.buffer[this.length++] = constants.COUNT_MARKER;
148
+ this.setLength(value.length);
149
+ this.writeLargeTypedArrayData(value);
118
150
  } else {
119
151
  // ARRAY(1) + TYPE_MARKER(1) + TYPE(1) + COUNT_MARKER(1) + COUNT(MAX5) + DATA
120
152
  this.ensureCapacity(9 + value.byteLength);
@@ -139,7 +171,7 @@ export abstract class EncoderBase {
139
171
  this.buffer[this.length++] = constants.COUNT_MARKER;
140
172
  this.setLength(arrayLength);
141
173
  for (let i = 0; i < arrayLength; i++) {
142
- this.view.setInt16(this.length, value[i]);
174
+ this.view.setInt16(this.length, value[i]!);
143
175
  this.length += elementSize;
144
176
  }
145
177
  } else if (value instanceof Int32Array) {
@@ -147,7 +179,7 @@ export abstract class EncoderBase {
147
179
  this.buffer[this.length++] = constants.COUNT_MARKER;
148
180
  this.setLength(arrayLength);
149
181
  for (let i = 0; i < arrayLength; i++) {
150
- this.view.setInt32(this.length, value[i]);
182
+ this.view.setInt32(this.length, value[i]!);
151
183
  this.length += elementSize;
152
184
  }
153
185
  } else if (value instanceof Float32Array) {
@@ -155,7 +187,7 @@ export abstract class EncoderBase {
155
187
  this.buffer[this.length++] = constants.COUNT_MARKER;
156
188
  this.setLength(arrayLength);
157
189
  for (let i = 0; i < arrayLength; i++) {
158
- this.view.setFloat32(this.length, value[i]);
190
+ this.view.setFloat32(this.length, value[i]!);
159
191
  this.length += elementSize;
160
192
  }
161
193
  } else if (value instanceof Float64Array) {
@@ -163,7 +195,7 @@ export abstract class EncoderBase {
163
195
  this.buffer[this.length++] = constants.COUNT_MARKER;
164
196
  this.setLength(arrayLength);
165
197
  for (let i = 0; i < arrayLength; i++) {
166
- this.view.setFloat64(this.length, value[i]);
198
+ this.view.setFloat64(this.length, value[i]!);
167
199
  this.length += elementSize;
168
200
  }
169
201
  } else if (value instanceof BigInt64Array) {
@@ -171,11 +203,11 @@ export abstract class EncoderBase {
171
203
  this.buffer[this.length++] = constants.COUNT_MARKER;
172
204
  this.setLength(arrayLength);
173
205
  for (let i = 0; i < arrayLength; i++) {
174
- this.view.setBigInt64(this.length, value[i]);
206
+ this.view.setBigInt64(this.length, value[i]!);
175
207
  this.length += elementSize;
176
208
  }
177
209
  } else {
178
- throw new TypeError(`Unsupported typed array type ${Object.prototype.toString.call(value)}`);
210
+ unsupportedView(value);
179
211
  }
180
212
  }
181
213
  return;
@@ -200,18 +232,18 @@ export abstract class EncoderBase {
200
232
  }
201
233
  return;
202
234
  default:
203
- throw new Error(`Unsupported type ${Object.prototype.toString.call(value)}`);
235
+ unsupportedType(value);
204
236
  }
205
237
  }
206
238
 
207
239
  /** writeStringData */
208
240
  private writeStringData(value: string): void {
209
241
  const strLength = value.length;
210
- const maxUsage =
211
- strLength < 65536
212
- ? // 对于短字符串,直接计算最大使用空间
213
- strLength * 3
214
- : this.stringByteLength(value);
242
+ if (strLength > LARGE_DATA_LENGTH) {
243
+ return this.writeLargeStringData(value);
244
+ }
245
+ // 对于短字符串,直接计算最大使用空间
246
+ const maxUsage = strLength * 3;
215
247
  // 一次性分配 setLength 和 encodeInto 的空间,避免无法回溯
216
248
  // 额外分配 3 字节,避免 encodeInto 无法写入最后一个字符
217
249
  this.ensureCapacity(maxUsage + 5 + 3);
@@ -219,36 +251,7 @@ export abstract class EncoderBase {
219
251
  // 预估头部大小
220
252
  const headerSize = strLength < 128 ? 2 : strLength < 32768 ? 3 : 5;
221
253
  const headerPos = this.length;
222
- let bufLength;
223
- // 优化小字符串
224
- if (strLength < 0x40 || !this.encodeInto) {
225
- let c1, c2;
226
- let strPosition = headerPos + headerSize;
227
- const target = this.buffer;
228
- for (let i = 0; i < strLength; i++) {
229
- c1 = value.charCodeAt(i);
230
- if (c1 < 0x80) {
231
- target[strPosition++] = c1;
232
- } else if (c1 < 0x800) {
233
- target[strPosition++] = (c1 >> 6) | 0xc0;
234
- target[strPosition++] = (c1 & 0x3f) | 0x80;
235
- } else if ((c1 & 0xfc00) === 0xd800 && ((c2 = value.charCodeAt(i + 1)) & 0xfc00) === 0xdc00) {
236
- c1 = 0x1_0000 + ((c1 & 0x03ff) << 10) + (c2 & 0x03ff);
237
- i++;
238
- target[strPosition++] = (c1 >> 18) | 0xf0;
239
- target[strPosition++] = ((c1 >> 12) & 0x3f) | 0x80;
240
- target[strPosition++] = ((c1 >> 6) & 0x3f) | 0x80;
241
- target[strPosition++] = (c1 & 0x3f) | 0x80;
242
- } else {
243
- target[strPosition++] = (c1 >> 12) | 0xe0;
244
- target[strPosition++] = ((c1 >> 6) & 0x3f) | 0x80;
245
- target[strPosition++] = (c1 & 0x3f) | 0x80;
246
- }
247
- }
248
- bufLength = strPosition - headerPos - headerSize;
249
- } else {
250
- bufLength = this.encodeInto(value, this.buffer, headerPos + headerSize);
251
- }
254
+ const bufLength = encodeInto(value, this.buffer, this.length + headerSize);
252
255
  if (bufLength < 128) {
253
256
  this.buffer[this.length++] = constants.INT8;
254
257
  this.buffer[this.length++] = bufLength;
@@ -270,6 +273,61 @@ export abstract class EncoderBase {
270
273
  this.length += bufLength;
271
274
  }
272
275
 
276
+ /** 写入大字符串 */
277
+ protected writeLargeStringData(value: string): void {
278
+ const binLen = stringByteLength(value);
279
+ this.ensureCapacity(5);
280
+ this.buffer[this.length++] = constants.INT32;
281
+ this.view.setInt32(this.length, binLen);
282
+ this.length += 4;
283
+ this.ensureCapacity(binLen);
284
+ encodeInto(value, this.buffer, this.length);
285
+ this.length += binLen;
286
+ }
287
+
288
+ /** 写入数组 */
289
+ protected writeLargeTypedArrayData(value: ArrayBufferView): void {
290
+ this.ensureCapacity(value.byteLength);
291
+ if (value instanceof Uint8Array || value instanceof Int8Array) {
292
+ // fast path for typed arrays with `BYTES_PER_ELEMENT` of 1
293
+ this.buffer.set(value, this.length);
294
+ this.length += value.byteLength;
295
+ return;
296
+ }
297
+
298
+ const arrayLength = (value as Int16Array | Int32Array | BigInt64Array | Float32Array | Float64Array).length;
299
+ const elementSize = (value as Int16Array | Int32Array | BigInt64Array | Float32Array | Float64Array)
300
+ .BYTES_PER_ELEMENT;
301
+ if (value instanceof Int16Array) {
302
+ for (let i = 0; i < arrayLength; i++) {
303
+ this.view.setInt16(this.length, value[i]!);
304
+ this.length += elementSize;
305
+ }
306
+ } else if (value instanceof Int32Array) {
307
+ for (let i = 0; i < arrayLength; i++) {
308
+ this.view.setInt32(this.length, value[i]!);
309
+ this.length += elementSize;
310
+ }
311
+ } else if (value instanceof Float32Array) {
312
+ for (let i = 0; i < arrayLength; i++) {
313
+ this.view.setFloat32(this.length, value[i]!);
314
+ this.length += elementSize;
315
+ }
316
+ } else if (value instanceof Float64Array) {
317
+ for (let i = 0; i < arrayLength; i++) {
318
+ this.view.setFloat64(this.length, value[i]!);
319
+ this.length += elementSize;
320
+ }
321
+ } else if (value instanceof BigInt64Array) {
322
+ for (let i = 0; i < arrayLength; i++) {
323
+ this.view.setBigInt64(this.length, value[i]!);
324
+ this.length += elementSize;
325
+ }
326
+ } else {
327
+ unsupportedView(value);
328
+ }
329
+ }
330
+
273
331
  /**
274
332
  * 写入整形数字,选取合适的大小,需提前分配空间
275
333
  */
@@ -0,0 +1,14 @@
1
+ /** unsupported view */
2
+ export function unsupportedView(view: ArrayBufferView): never {
3
+ const type = Object.prototype.toString.call(view).slice(8, -1);
4
+ throw new Error(`Unsupported array buffer view of type ${type}`);
5
+ }
6
+
7
+ /** unsupported type */
8
+ export function unsupportedType(value: unknown): never {
9
+ if (value && typeof value == 'string') {
10
+ throw new Error(`Unsupported type ${value}`);
11
+ }
12
+ const type = Object.prototype.toString.call(value).slice(8, -1);
13
+ throw new Error(`Unsupported type ${type}`);
14
+ }
@@ -1,40 +1,33 @@
1
- /* c8 ignore next 2: TextDecoder always present, fallback tested */
2
- export const textDecoder =
3
- typeof TextDecoder == 'function' ? new TextDecoder('utf8', { ignoreBOM: true, fatal: false }) : null;
4
-
5
- /* c8 ignore next: TextDecoder always present, fallback tested */
6
- export const TEXT_ENCODER_THRESHOLD = textDecoder == null ? 0xffff_ffff : 200;
7
-
8
1
  const CHUNK_SIZE = 0x1000;
9
2
  const REPLACE_CHAR = 0xfffd;
10
3
 
11
4
  const fromCharCode = String.fromCharCode;
12
5
 
13
6
  /** 解码 */
14
- export function decodeJs(bytes: Uint8Array, begin: number, end: number): string {
7
+ export function jsDecode(bytes: Uint8Array, begin: number, end: number): string {
15
8
  let offset = begin;
16
9
 
17
10
  const units: number[] = [];
18
11
  let result = '';
19
12
  while (offset < end) {
20
- const byte1 = bytes[offset++];
13
+ const byte1 = bytes[offset++]!;
21
14
  if ((byte1 & 0x80) === 0) {
22
15
  // 1 byte
23
16
  units.push(byte1);
24
17
  } else if ((byte1 & 0xe0) === 0xc0) {
25
18
  // 2 bytes
26
- const byte2 = bytes[offset++] & 0x3f;
19
+ const byte2 = bytes[offset++]! & 0x3f;
27
20
  units.push(((byte1 & 0x1f) << 6) | byte2);
28
21
  } else if ((byte1 & 0xf0) === 0xe0) {
29
22
  // 3 bytes
30
- const byte2 = bytes[offset++] & 0x3f;
31
- const byte3 = bytes[offset++] & 0x3f;
23
+ const byte2 = bytes[offset++]! & 0x3f;
24
+ const byte3 = bytes[offset++]! & 0x3f;
32
25
  units.push(((byte1 & 0x1f) << 12) | (byte2 << 6) | byte3);
33
26
  } else if ((byte1 & 0xf8) === 0xf0) {
34
27
  // 4 bytes
35
- const byte2 = bytes[offset++] & 0x3f;
36
- const byte3 = bytes[offset++] & 0x3f;
37
- const byte4 = bytes[offset++] & 0x3f;
28
+ const byte2 = bytes[offset++]! & 0x3f;
29
+ const byte3 = bytes[offset++]! & 0x3f;
30
+ const byte4 = bytes[offset++]! & 0x3f;
38
31
  let unit = ((byte1 & 0x07) << 0x12) | (byte2 << 0x0c) | (byte3 << 0x06) | byte4;
39
32
  if (unit > 0xffff) {
40
33
  unit -= 0x1_0000;
@@ -63,7 +56,7 @@ export function decodeJs(bytes: Uint8Array, begin: number, end: number): string
63
56
  function longStringInJS(buf: Uint8Array, begin: number, length: number): string | undefined {
64
57
  const bytes = Array.from<number>({ length });
65
58
  for (let i = 0; i < length; i++) {
66
- const byte = buf[begin++];
59
+ const byte = buf[begin++]!;
67
60
  if ((byte & 0x80) > 0) {
68
61
  return;
69
62
  }
@@ -77,95 +70,95 @@ function shortStringInJS(buf: Uint8Array, begin: number, length: number): string
77
70
  if (length < 2) {
78
71
  if (length === 0) return '';
79
72
  else {
80
- const a = buf[begin++];
73
+ const a = buf[begin++]!;
81
74
  if ((a & 0x80) > 1) {
82
75
  return;
83
76
  }
84
77
  return fromCharCode(a);
85
78
  }
86
79
  } else {
87
- const a = buf[begin++];
88
- const b = buf[begin++];
80
+ const a = buf[begin++]!;
81
+ const b = buf[begin++]!;
89
82
  if ((a & 0x80) > 0 || (b & 0x80) > 0) {
90
83
  return;
91
84
  }
92
85
  if (length < 3) return fromCharCode(a, b);
93
- const c = buf[begin++];
86
+ const c = buf[begin++]!;
94
87
  if ((c & 0x80) > 0) {
95
88
  return;
96
89
  }
97
90
  return fromCharCode(a, b, c);
98
91
  }
99
92
  } else {
100
- const a = buf[begin++];
101
- const b = buf[begin++];
102
- const c = buf[begin++];
103
- const d = buf[begin++];
93
+ const a = buf[begin++]!;
94
+ const b = buf[begin++]!;
95
+ const c = buf[begin++]!;
96
+ const d = buf[begin++]!;
104
97
  if ((a & 0x80) > 0 || (b & 0x80) > 0 || (c & 0x80) > 0 || (d & 0x80) > 0) {
105
98
  return;
106
99
  }
107
100
  if (length < 6) {
108
101
  if (length === 4) return fromCharCode(a, b, c, d);
109
102
  else {
110
- const e = buf[begin++];
103
+ const e = buf[begin++]!;
111
104
  if ((e & 0x80) > 0) {
112
105
  return;
113
106
  }
114
107
  return fromCharCode(a, b, c, d, e);
115
108
  }
116
109
  } else if (length < 8) {
117
- const e = buf[begin++];
118
- const f = buf[begin++];
110
+ const e = buf[begin++]!;
111
+ const f = buf[begin++]!;
119
112
  if ((e & 0x80) > 0 || (f & 0x80) > 0) {
120
113
  return;
121
114
  }
122
115
  if (length < 7) return fromCharCode(a, b, c, d, e, f);
123
- const g = buf[begin++];
116
+ const g = buf[begin++]!;
124
117
  if ((g & 0x80) > 0) {
125
118
  return;
126
119
  }
127
120
  return fromCharCode(a, b, c, d, e, f, g);
128
121
  } else {
129
- const e = buf[begin++];
130
- const f = buf[begin++];
131
- const g = buf[begin++];
132
- const h = buf[begin++];
122
+ const e = buf[begin++]!;
123
+ const f = buf[begin++]!;
124
+ const g = buf[begin++]!;
125
+ const h = buf[begin++]!;
133
126
  if ((e & 0x80) > 0 || (f & 0x80) > 0 || (g & 0x80) > 0 || (h & 0x80) > 0) {
134
127
  return;
135
128
  }
136
129
  if (length < 10) {
137
130
  if (length === 8) return fromCharCode(a, b, c, d, e, f, g, h);
138
131
  else {
139
- const i = buf[begin++];
132
+ const i = buf[begin++]!;
140
133
  if ((i & 0x80) > 0) {
141
134
  return;
142
135
  }
143
136
  return fromCharCode(a, b, c, d, e, f, g, h, i);
144
137
  }
145
138
  } else if (length < 12) {
146
- const i = buf[begin++];
147
- const j = buf[begin++];
139
+ const i = buf[begin++]!;
140
+ const j = buf[begin++]!;
148
141
  if ((i & 0x80) > 0 || (j & 0x80) > 0) {
149
142
  return;
150
143
  }
151
144
  if (length < 11) return fromCharCode(a, b, c, d, e, f, g, h, i, j);
152
- const k = buf[begin++];
145
+ const k = buf[begin++]!;
153
146
  if ((k & 0x80) > 0) {
154
147
  return;
155
148
  }
156
149
  return fromCharCode(a, b, c, d, e, f, g, h, i, j, k);
157
150
  } else {
158
- const i = buf[begin++];
159
- const j = buf[begin++];
160
- const k = buf[begin++];
161
- const l = buf[begin++];
151
+ const i = buf[begin++]!;
152
+ const j = buf[begin++]!;
153
+ const k = buf[begin++]!;
154
+ const l = buf[begin++]!;
162
155
  if ((i & 0x80) > 0 || (j & 0x80) > 0 || (k & 0x80) > 0 || (l & 0x80) > 0) {
163
156
  return;
164
157
  }
165
158
  if (length < 14) {
166
159
  if (length === 12) return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l);
167
160
  else {
168
- const m = buf[begin++];
161
+ const m = buf[begin++]!;
169
162
  if ((m & 0x80) > 0) {
170
163
  begin -= 13;
171
164
  return;
@@ -173,13 +166,13 @@ function shortStringInJS(buf: Uint8Array, begin: number, length: number): string
173
166
  return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m);
174
167
  }
175
168
  } else {
176
- const m = buf[begin++];
177
- const n = buf[begin++];
169
+ const m = buf[begin++]!;
170
+ const n = buf[begin++]!;
178
171
  if ((m & 0x80) > 0 || (n & 0x80) > 0) {
179
172
  return;
180
173
  }
181
174
  if (length < 15) return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m, n);
182
- const o = buf[begin++];
175
+ const o = buf[begin++]!;
183
176
  if ((o & 0x80) > 0) {
184
177
  return;
185
178
  }
@@ -190,6 +183,14 @@ function shortStringInJS(buf: Uint8Array, begin: number, length: number): string
190
183
  }
191
184
  }
192
185
 
186
+ let TEXT_DECODER: TextDecoder | null;
187
+ let TEXT_DECODER_THRESHOLD: number;
188
+
189
+ /** 解码 */
190
+ export function nativeDecode(data: Uint8Array, begin: number, end: number): string {
191
+ return TEXT_DECODER!.decode(data.subarray(begin, end));
192
+ }
193
+
193
194
  /** 字符串解码,无缓存 */
194
195
  export function decode(data: Uint8Array, begin: number, end: number): string {
195
196
  const length = end - begin;
@@ -198,22 +199,22 @@ export function decode(data: Uint8Array, begin: number, end: number): string {
198
199
  if (result != null) return result;
199
200
  }
200
201
  // 只有小字符串有优化价值,见 benchmark-string.js
201
- if (length < TEXT_ENCODER_THRESHOLD) {
202
+ if (length < TEXT_DECODER_THRESHOLD) {
202
203
  // 为小字符串优化
203
- return decodeJs(data, begin, end);
204
+ return jsDecode(data, begin, end);
204
205
  }
205
206
  // 使用系统解码
206
- return textDecoder!.decode(data.subarray(begin, end));
207
+ return nativeDecode(data, begin, end);
207
208
  }
208
209
 
209
- const keyCache = Array.from<{ value: string; buffer: Uint8Array } | undefined>({ length: 4096 });
210
+ const KEY_CACHE = Array.from<{ value: string; buffer: Uint8Array } | undefined>({ length: 4096 });
210
211
 
211
212
  /** 字符串解码,使用缓存 */
212
213
  export function decodeKey(data: Uint8Array, begin: number, end: number): string {
213
214
  const length = end - begin;
214
215
  const cacheKey =
215
- ((length << 5) ^ (length > 1 ? data[begin] & (data[begin + 1] << 8) : length > 0 ? data[begin] : 0)) & 0xfff;
216
- let entry = keyCache[cacheKey];
216
+ ((length << 5) ^ (length > 1 ? data[begin]! & (data[begin + 1]! << 8) : length > 0 ? data[begin]! : 0)) & 0xfff;
217
+ let entry = KEY_CACHE[cacheKey];
217
218
  if (entry != null && entry.buffer.byteLength === length) {
218
219
  let i = 0;
219
220
  for (; i < length; i++) {
@@ -225,15 +226,23 @@ export function decodeKey(data: Uint8Array, begin: number, end: number): string
225
226
  let str = length < 16 ? shortStringInJS(data, begin, length) : longStringInJS(data, begin, length);
226
227
  if (str == null) {
227
228
  // 只有小字符串有优化价值,见 benchmark-string.js
228
- if (length < TEXT_ENCODER_THRESHOLD) {
229
+ if (length < TEXT_DECODER_THRESHOLD) {
229
230
  // 为小字符串优化
230
- str = decodeJs(data, begin, end);
231
+ str = jsDecode(data, begin, end);
231
232
  } else {
232
233
  // 使用系统解码
233
- str = textDecoder!.decode(data.subarray(begin, end));
234
+ str = nativeDecode(data, begin, end);
234
235
  }
235
236
  }
236
237
  entry = { value: str, buffer: data.slice(begin, end) };
237
- keyCache[cacheKey] = entry;
238
+ KEY_CACHE[cacheKey] = entry;
238
239
  return str;
239
240
  }
241
+
242
+ /** 重设环境 */
243
+ export function resetEnv(): void {
244
+ TEXT_DECODER = typeof TextDecoder == 'function' ? new TextDecoder('utf8', { ignoreBOM: true, fatal: false }) : null;
245
+ TEXT_DECODER_THRESHOLD = TEXT_DECODER == null ? 0xffff_ffff : 16;
246
+ KEY_CACHE.fill(undefined);
247
+ }
248
+ resetEnv();