@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.
Files changed (92) hide show
  1. package/dist/base/decoder.d.ts +1 -0
  2. package/dist/base/decoder.d.ts.map +1 -0
  3. package/dist/base/encoder.d.ts +5 -1
  4. package/dist/base/encoder.d.ts.map +1 -0
  5. package/dist/base/encoder.js +50 -188
  6. package/dist/base/encoder.js.map +1 -1
  7. package/dist/decoder.d.ts +1 -0
  8. package/dist/decoder.d.ts.map +1 -0
  9. package/dist/encoder.d.ts +7 -1
  10. package/dist/encoder.d.ts.map +1 -0
  11. package/dist/encoder.js +24 -15
  12. package/dist/encoder.js.map +1 -1
  13. package/dist/helper/constants.d.ts +1 -0
  14. package/dist/helper/constants.d.ts.map +1 -0
  15. package/dist/helper/decode-ae.d.ts +50 -0
  16. package/dist/helper/decode-ae.d.ts.map +1 -0
  17. package/dist/helper/decode-ae.js +584 -0
  18. package/dist/helper/decode-ae.js.map +1 -0
  19. package/dist/helper/decode.d.ts +6 -3
  20. package/dist/helper/decode.d.ts.map +1 -0
  21. package/dist/helper/decode.js +33 -20
  22. package/dist/helper/decode.js.map +1 -1
  23. package/dist/helper/encode.d.ts +19 -1
  24. package/dist/helper/encode.d.ts.map +1 -0
  25. package/dist/helper/encode.js +138 -16
  26. package/dist/helper/encode.js.map +1 -1
  27. package/dist/helper/errors.d.ts +1 -0
  28. package/dist/helper/errors.d.ts.map +1 -0
  29. package/dist/helper/string-decoder.d.ts +5 -2
  30. package/dist/helper/string-decoder.d.ts.map +1 -0
  31. package/dist/helper/string-decoder.js +10 -38
  32. package/dist/helper/string-decoder.js.map +1 -1
  33. package/dist/helper/string-encoder.d.ts +1 -0
  34. package/dist/helper/string-encoder.d.ts.map +1 -0
  35. package/dist/helper/utils.d.ts +1 -0
  36. package/dist/helper/utils.d.ts.map +1 -0
  37. package/dist/index.d.ts +5 -2
  38. package/dist/index.d.ts.map +1 -0
  39. package/dist/index.js +4 -4
  40. package/dist/index.js.map +1 -1
  41. package/dist/options.d.ts +6 -0
  42. package/dist/options.d.ts.map +1 -0
  43. package/dist/options.js +2 -0
  44. package/dist/options.js.map +1 -0
  45. package/dist/rxjs/decoder.d.ts +1 -0
  46. package/dist/rxjs/decoder.d.ts.map +1 -0
  47. package/dist/rxjs/decoder.js +66 -40
  48. package/dist/rxjs/decoder.js.map +1 -1
  49. package/dist/rxjs/encoder.d.ts +3 -1
  50. package/dist/rxjs/encoder.d.ts.map +1 -0
  51. package/dist/rxjs/encoder.js +2 -2
  52. package/dist/rxjs/encoder.js.map +1 -1
  53. package/dist/rxjs/index.d.ts +2 -0
  54. package/dist/rxjs/index.d.ts.map +1 -0
  55. package/dist/stream/decoder.d.ts +1 -0
  56. package/dist/stream/decoder.d.ts.map +1 -0
  57. package/dist/stream/encoder.d.ts +4 -2
  58. package/dist/stream/encoder.d.ts.map +1 -0
  59. package/dist/stream/encoder.js +2 -2
  60. package/dist/stream/encoder.js.map +1 -1
  61. package/dist/stream/index.d.ts +6 -3
  62. package/dist/stream/index.d.ts.map +1 -0
  63. package/dist/stream/index.js +6 -6
  64. package/dist/stream/index.js.map +1 -1
  65. package/dist/stream-helper/decoder.d.ts +1 -0
  66. package/dist/stream-helper/decoder.d.ts.map +1 -0
  67. package/dist/stream-helper/encoder.d.ts +5 -2
  68. package/dist/stream-helper/encoder.d.ts.map +1 -0
  69. package/dist/stream-helper/encoder.js +33 -31
  70. package/dist/stream-helper/encoder.js.map +1 -1
  71. package/jest.config.js +1 -1
  72. package/package.json +3 -4
  73. package/src/base/encoder.ts +58 -174
  74. package/src/encoder.ts +27 -15
  75. package/src/helper/decode-ae.ts +621 -0
  76. package/src/helper/decode.ts +36 -23
  77. package/src/helper/encode.ts +141 -15
  78. package/src/helper/string-decoder.ts +10 -39
  79. package/src/index.ts +7 -4
  80. package/src/options.ts +5 -0
  81. package/src/rxjs/decoder.ts +66 -40
  82. package/src/rxjs/encoder.ts +3 -2
  83. package/src/rxjs/index.ts +1 -0
  84. package/src/stream/encoder.ts +4 -3
  85. package/src/stream/index.ts +8 -6
  86. package/src/stream-helper/encoder.ts +39 -34
  87. package/tests/.utils.js +1 -0
  88. package/tests/e2e/stream.js +13 -1
  89. package/tests/encode.js +8 -0
  90. package/tests/string-encoding.js +1 -10
  91. package/tests/tsconfig.json +1 -7
  92. package/tsconfig.json +1 -1
@@ -1,12 +1,26 @@
1
1
  import { constants } from '../helper/constants.js';
2
- import { type EncodeCursor, writeLength, writeTypedArrayHeader } from '../helper/encode.js';
3
- import { unsupportedType, unsupportedView } from '../helper/errors.js';
2
+ import {
3
+ type EncodeCursor,
4
+ I8_MASK,
5
+ type TypedArrayType,
6
+ writeNumber,
7
+ writeTypedArray,
8
+ writeTypedArrayData,
9
+ writeTypedArrayHeader,
10
+ } from '../helper/encode.js';
11
+ import { unsupportedType } from '../helper/errors.js';
4
12
  import { stringByteLength, encodeInto } from '../helper/string-encoder.js';
5
13
 
6
14
  const LARGE_DATA_LENGTH = 65536;
15
+ const { isArray } = Array;
16
+ // eslint-disable-next-line @typescript-eslint/unbound-method
17
+ const { isView } = ArrayBuffer;
18
+ const { keys: objectKeys } = Object;
7
19
 
8
20
  /** 编码至 ubjson */
9
21
  export abstract class EncoderBase {
22
+ /** 序列化对象时排序属性 */
23
+ sortObjectKeys = false;
10
24
  /** 当前写指针位置 */
11
25
  protected length = 0;
12
26
  /** 数据 */
@@ -43,47 +57,14 @@ export abstract class EncoderBase {
43
57
  }
44
58
  return;
45
59
  case 'number':
46
- // eslint-disable-next-line unicorn/prefer-math-trunc
47
- if ((value | 0) === value) {
48
- if (value >= 0 && value <= 255) {
49
- this.ensureCapacity(2);
50
- this.data[this.length++] = constants.UINT8;
51
- this.data[this.length++] = value;
52
- } else if (value >= -128 && value <= 127) {
53
- this.ensureCapacity(2);
54
- this.data[this.length++] = constants.INT8;
55
- this.data[this.length++] = value;
56
- } else if (value >= -32768 && value <= 32767) {
57
- this.ensureCapacity(3);
58
- this.data[this.length++] = constants.INT16;
59
- this.data[this.length++] = value >> 8;
60
- this.data[this.length++] = value & 0xff;
61
- } else {
62
- this.ensureCapacity(5);
63
- this.data[this.length++] = constants.INT32;
64
- this.view.setInt32(this.length, value);
65
- this.length += 4;
66
- }
67
- } else if (Number.isNaN(value) || Math.fround(value) === value) {
68
- // 如果不会损失精度,使用 32 位浮点
69
- this.ensureCapacity(5);
70
- this.data[this.length++] = constants.FLOAT32;
71
- this.view.setFloat32(this.length, value);
72
- this.length += 4;
73
- } else {
74
- this.ensureCapacity(9);
75
- this.data[this.length++] = constants.FLOAT64;
76
- this.view.setFloat64(this.length, value);
77
- this.length += 8;
78
- }
79
- return;
60
+ return writeNumber(this as this & EncodeCursor, value);
80
61
  case 'object': {
81
62
  if (value === null) {
82
63
  this.ensureCapacity(1);
83
64
  this.data[this.length++] = constants.NULL;
84
65
  return;
85
66
  }
86
- if (Array.isArray(value)) {
67
+ if (isArray(value)) {
87
68
  this.ensureCapacity(1);
88
69
  this.data[this.length++] = constants.ARRAY;
89
70
  const size = value.length;
@@ -101,95 +82,37 @@ export abstract class EncoderBase {
101
82
  this.data[this.length++] = constants.ARRAY_END;
102
83
  return;
103
84
  }
104
- if (!ArrayBuffer.isView(value)) {
105
- const { toJSON } = value as Record<string, unknown>;
106
- if (typeof toJSON == 'function') {
107
- this.write(toJSON.call(value));
85
+ if (isView(value)) {
86
+ if (value.byteLength <= LARGE_DATA_LENGTH) {
87
+ writeTypedArray(this as this & EncodeCursor, value);
108
88
  return;
109
89
  }
110
- this.ensureCapacity(1);
111
- this.data[this.length++] = constants.OBJECT;
112
- // 生成稳定的结果以便 hash 计算
113
- const keys = Object.keys(value).sort();
114
- const size = keys.length;
115
- for (let index = 0; index < size; index++) {
116
- const key = keys[index]!;
117
- const element = (value as Record<string, unknown>)[key];
118
- if (element === undefined || typeof element == 'function') continue;
119
- this.writeStringData(key);
120
- this.write(element);
121
- }
122
- this.ensureCapacity(1);
123
- this.data[this.length++] = constants.OBJECT_END;
90
+ const type = writeTypedArrayHeader(this as this & EncodeCursor, value);
91
+ this.writeLargeTypedArrayData(type, value);
124
92
  return;
125
93
  }
126
- if (value.byteLength > LARGE_DATA_LENGTH) {
127
- // ARRAY(1) + TYPE_MARKER(1) + TYPE(1) + COUNT_MARKER(1) + COUNT(5)
128
- writeTypedArrayHeader(this as this & EncodeCursor, value);
129
- this.writeLargeTypedArrayData(value);
130
- } else {
131
- // ARRAY(1) + TYPE_MARKER(1) + TYPE(1) + COUNT_MARKER(1) + COUNT(MAX5) + DATA
132
- this.ensureCapacity(9 + value.byteLength);
133
- this.data[this.length++] = constants.ARRAY;
134
- this.data[this.length++] = constants.TYPE_MARKER;
135
- if (value instanceof Uint8Array || value instanceof Int8Array) {
136
- // fast path for typed arrays with `BYTES_PER_ELEMENT` of 1
137
- this.data[this.length++] = value instanceof Uint8Array ? constants.UINT8 : constants.INT8;
138
- this.data[this.length++] = constants.COUNT_MARKER;
139
- writeLength(this as this & EncodeCursor, value.length);
140
- this.data.set(value, this.length);
141
- this.length += value.byteLength;
142
- return;
143
- }
144
-
145
- const arrayLength = (value as Int16Array | Int32Array | BigInt64Array | Float32Array | Float64Array)
146
- .length;
147
- const elementSize = (value as Int16Array | Int32Array | BigInt64Array | Float32Array | Float64Array)
148
- .BYTES_PER_ELEMENT;
149
- if (value instanceof Float64Array) {
150
- this.data[this.length++] = constants.FLOAT64;
151
- this.data[this.length++] = constants.COUNT_MARKER;
152
- writeLength(this as this & EncodeCursor, arrayLength);
153
- for (let i = 0; i < arrayLength; i++) {
154
- this.view.setFloat64(this.length, value[i]!);
155
- this.length += elementSize;
156
- }
157
- } else if (value instanceof Int32Array) {
158
- this.data[this.length++] = constants.INT32;
159
- this.data[this.length++] = constants.COUNT_MARKER;
160
- writeLength(this as this & EncodeCursor, arrayLength);
161
- for (let i = 0; i < arrayLength; i++) {
162
- this.view.setInt32(this.length, value[i]!);
163
- this.length += elementSize;
164
- }
165
- } else if (value instanceof Int16Array) {
166
- this.data[this.length++] = constants.INT16;
167
- this.data[this.length++] = constants.COUNT_MARKER;
168
- writeLength(this as this & EncodeCursor, arrayLength);
169
- for (let i = 0; i < arrayLength; i++) {
170
- this.view.setInt16(this.length, value[i]!);
171
- this.length += elementSize;
172
- }
173
- } else if (value instanceof Float32Array) {
174
- this.data[this.length++] = constants.FLOAT32;
175
- this.data[this.length++] = constants.COUNT_MARKER;
176
- writeLength(this as this & EncodeCursor, arrayLength);
177
- for (let i = 0; i < arrayLength; i++) {
178
- this.view.setFloat32(this.length, value[i]!);
179
- this.length += elementSize;
180
- }
181
- } else if (value instanceof BigInt64Array) {
182
- this.data[this.length++] = constants.INT64;
183
- this.data[this.length++] = constants.COUNT_MARKER;
184
- writeLength(this as this & EncodeCursor, arrayLength);
185
- for (let i = 0; i < arrayLength; i++) {
186
- this.view.setBigInt64(this.length, value[i]!);
187
- this.length += elementSize;
188
- }
189
- } else {
190
- unsupportedView(value);
191
- }
94
+ const { toJSON } = value as Record<string, unknown>;
95
+ if (typeof toJSON == 'function') {
96
+ this.write(toJSON.call(value));
97
+ return;
98
+ }
99
+ // 生成稳定的结果以便 hash 计算
100
+ const keys = objectKeys(value);
101
+ const size = keys.length;
102
+ if (this.sortObjectKeys && size > 1) {
103
+ keys.sort();
192
104
  }
105
+ this.ensureCapacity(2 + size);
106
+ this.data[this.length++] = constants.OBJECT;
107
+ for (let index = 0; index < size; index++) {
108
+ const key = keys[index]!;
109
+ const element = (value as Record<string, unknown>)[key];
110
+ if (element === undefined || typeof element == 'function') continue;
111
+ this.writeStringData(key);
112
+ this.write(element);
113
+ }
114
+ this.ensureCapacity(1);
115
+ this.data[this.length++] = constants.OBJECT_END;
193
116
  return;
194
117
  }
195
118
  case 'boolean':
@@ -230,27 +153,26 @@ export abstract class EncoderBase {
230
153
 
231
154
  // 预估头部大小
232
155
  const headerSize = strLength < 0x80 ? 2 : strLength < 0x8000 ? 3 : 5;
233
- const headerPos = this.length;
234
- const bufLength = encodeInto(value, this.data, this.length + headerSize);
156
+ const { length: headerPos, data, view } = this;
157
+ const bufLength = encodeInto(value, data, headerPos + headerSize);
235
158
  if (bufLength < 0x80) {
236
- this.data[this.length++] = constants.INT8;
237
- this.data[this.length++] = bufLength;
159
+ view.setInt16(headerPos, I8_MASK | bufLength);
160
+ this.length = headerPos + 2 + bufLength;
238
161
  } else if (bufLength < 0x8000) {
239
162
  if (headerSize < 3) {
240
- this.data.copyWithin(headerPos + 3, headerPos + headerSize, headerPos + headerSize + bufLength);
163
+ data.copyWithin(headerPos + 3, headerPos + headerSize, headerPos + headerSize + bufLength);
241
164
  }
242
- this.data[this.length++] = constants.INT16;
243
- this.data[this.length++] = bufLength >> 8;
244
- this.data[this.length++] = bufLength & 0xff;
165
+ data[headerPos] = constants.INT16;
166
+ view.setInt16(headerPos + 1, bufLength);
167
+ this.length = headerPos + 3 + bufLength;
245
168
  } else {
246
169
  if (headerSize < 5) {
247
- this.data.copyWithin(headerPos + 5, headerPos + headerSize, headerPos + headerSize + bufLength);
170
+ data.copyWithin(headerPos + 5, headerPos + headerSize, headerPos + headerSize + bufLength);
248
171
  }
249
- this.data[this.length++] = constants.INT32;
250
- this.view.setInt32(this.length, bufLength);
251
- this.length += 4;
172
+ data[headerPos] = constants.INT32;
173
+ view.setInt32(headerPos + 1, bufLength);
174
+ this.length = headerPos + 5 + bufLength;
252
175
  }
253
- this.length += bufLength;
254
176
  }
255
177
 
256
178
  /** 写入大字符串 */
@@ -266,45 +188,7 @@ export abstract class EncoderBase {
266
188
  }
267
189
 
268
190
  /** 写入数组 */
269
- protected writeLargeTypedArrayData(value: ArrayBufferView): void {
270
- this.ensureCapacity(value.byteLength);
271
- if (value instanceof Uint8Array || value instanceof Int8Array) {
272
- // fast path for typed arrays with `BYTES_PER_ELEMENT` of 1
273
- this.data.set(value, this.length);
274
- this.length += value.byteLength;
275
- return;
276
- }
277
-
278
- const arrayLength = (value as Int16Array | Int32Array | BigInt64Array | Float32Array | Float64Array).length;
279
- const elementSize = (value as Int16Array | Int32Array | BigInt64Array | Float32Array | Float64Array)
280
- .BYTES_PER_ELEMENT;
281
- if (value instanceof Int16Array) {
282
- for (let i = 0; i < arrayLength; i++) {
283
- this.view.setInt16(this.length, value[i]!);
284
- this.length += elementSize;
285
- }
286
- } else if (value instanceof Int32Array) {
287
- for (let i = 0; i < arrayLength; i++) {
288
- this.view.setInt32(this.length, value[i]!);
289
- this.length += elementSize;
290
- }
291
- } else if (value instanceof Float32Array) {
292
- for (let i = 0; i < arrayLength; i++) {
293
- this.view.setFloat32(this.length, value[i]!);
294
- this.length += elementSize;
295
- }
296
- } else if (value instanceof Float64Array) {
297
- for (let i = 0; i < arrayLength; i++) {
298
- this.view.setFloat64(this.length, value[i]!);
299
- this.length += elementSize;
300
- }
301
- } else if (value instanceof BigInt64Array) {
302
- for (let i = 0; i < arrayLength; i++) {
303
- this.view.setBigInt64(this.length, value[i]!);
304
- this.length += elementSize;
305
- }
306
- } else {
307
- unsupportedView(value);
308
- }
191
+ protected writeLargeTypedArrayData(type: TypedArrayType, value: ArrayBufferView): void {
192
+ writeTypedArrayData(this as this & EncodeCursor, type, value);
309
193
  }
310
194
  }
package/src/encoder.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { EncoderBase } from './base/encoder.js';
2
+ import type { EncodeOptions } from './options.js';
2
3
 
3
- const BLOCK_SIZE = 1024 * 16; // 16 KiB
4
+ const BLOCK_SIZE = 1024 * 64; // 64 KiB
4
5
  const MAX_SIZE = 1024 * 1024 * 128; //128 MiB
5
6
 
6
7
  /** 编码至 ubjson */
@@ -8,6 +9,8 @@ export class Encoder extends EncoderBase {
8
9
  private readonly flushedBuffers: Uint8Array[] = [];
9
10
  /** 通过内存池减少分配 */
10
11
  private readonly pool = new Uint8Array(BLOCK_SIZE);
12
+ /** 缓存当前容量,避免对象访问耗时 */
13
+ private capacity = 0;
11
14
  /**
12
15
  * 确保 buffer 还有 capacity 的空闲空间
13
16
  */
@@ -16,9 +19,15 @@ export class Encoder extends EncoderBase {
16
19
  // 超过最大尺寸限制
17
20
  throw new Error('Buffer has exceed max size');
18
21
  }
19
- // 无需扩容
20
- if (this.data.byteLength >= this.length + capacity) return;
22
+ if (this.capacity >= this.length + capacity) {
23
+ // 无需扩容
24
+ return;
25
+ }
26
+ this.flushBuffer(capacity);
27
+ }
21
28
 
29
+ /** 提交并扩容 */
30
+ protected flushBuffer(capacity: number): void {
22
31
  // 提交目前的数据
23
32
  if (this.data === this.pool) {
24
33
  this.flushedBuffers.push(this.data.slice(0, this.length));
@@ -33,16 +42,16 @@ export class Encoder extends EncoderBase {
33
42
 
34
43
  /** 分配 buffer */
35
44
  private allocUnsafe(size: number): void {
36
- if (size === this.pool.byteLength) {
37
- // pool 中获取
38
- this.data = this.pool;
39
- this.view = new DataView(this.data.buffer);
40
- this.length = 0;
41
- return;
42
- }
43
- this.data = new Uint8Array(size);
44
- this.view = new DataView(this.data.buffer);
45
+ const data =
46
+ size === BLOCK_SIZE
47
+ ? // pool 中获取
48
+ this.pool
49
+ : // 新建 buffer
50
+ new Uint8Array(size);
51
+ this.data = data;
52
+ this.view = new DataView(data.buffer);
45
53
  this.length = 0;
54
+ this.capacity = size;
46
55
  }
47
56
 
48
57
  /** 获取结果 */
@@ -53,15 +62,17 @@ export class Encoder extends EncoderBase {
53
62
  }
54
63
 
55
64
  // 合并缓冲区
65
+ const { length } = this;
66
+ const activeBuffer = this.data.subarray(0, length);
56
67
  const flushedBuffers = this.flushedBuffers.splice(0);
57
- const size = flushedBuffers.reduce((sum, buffer) => sum + buffer.byteLength, this.length);
68
+ const size = flushedBuffers.reduce((sum, buffer) => sum + buffer.byteLength, length);
58
69
  const result = new Uint8Array(size);
59
70
  let offset = 0;
60
71
  for (const buffer of flushedBuffers) {
61
72
  result.set(buffer, offset);
62
73
  offset += buffer.byteLength;
63
74
  }
64
- result.set(this.data.subarray(0, this.length), offset);
75
+ result.set(activeBuffer, offset);
65
76
  return result;
66
77
  }
67
78
 
@@ -100,10 +111,11 @@ export class Encoder extends EncoderBase {
100
111
  let _ENCODER: Encoder | undefined;
101
112
 
102
113
  /** 获取默认的编码器 */
103
- export function getEncoder(): Encoder {
114
+ export function getEncoder(options?: EncodeOptions): Encoder {
104
115
  if (_ENCODER == null) {
105
116
  _ENCODER = new Encoder();
106
117
  }
118
+ _ENCODER.sortObjectKeys = options?.sortObjectKeys ?? false;
107
119
  return _ENCODER;
108
120
  }
109
121