@cloudpss/ubjson 0.5.38 → 0.5.40

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 (64) 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 +3 -1
  4. package/dist/base/encoder.d.ts.map +1 -0
  5. package/dist/base/encoder.js +45 -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 +5 -0
  10. package/dist/encoder.d.ts.map +1 -0
  11. package/dist/encoder.js +22 -14
  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.d.ts +6 -3
  16. package/dist/helper/decode.d.ts.map +1 -0
  17. package/dist/helper/decode.js +26 -15
  18. package/dist/helper/decode.js.map +1 -1
  19. package/dist/helper/encode.d.ts +19 -1
  20. package/dist/helper/encode.d.ts.map +1 -0
  21. package/dist/helper/encode.js +138 -16
  22. package/dist/helper/encode.js.map +1 -1
  23. package/dist/helper/errors.d.ts +1 -0
  24. package/dist/helper/errors.d.ts.map +1 -0
  25. package/dist/helper/string-decoder.d.ts +5 -2
  26. package/dist/helper/string-decoder.d.ts.map +1 -0
  27. package/dist/helper/string-decoder.js +10 -38
  28. package/dist/helper/string-decoder.js.map +1 -1
  29. package/dist/helper/string-encoder.d.ts +1 -0
  30. package/dist/helper/string-encoder.d.ts.map +1 -0
  31. package/dist/helper/utils.d.ts +1 -0
  32. package/dist/helper/utils.d.ts.map +1 -0
  33. package/dist/index.d.ts +1 -0
  34. package/dist/index.d.ts.map +1 -0
  35. package/dist/rxjs/decoder.d.ts +1 -0
  36. package/dist/rxjs/decoder.d.ts.map +1 -0
  37. package/dist/rxjs/encoder.d.ts +1 -0
  38. package/dist/rxjs/encoder.d.ts.map +1 -0
  39. package/dist/rxjs/index.d.ts +1 -0
  40. package/dist/rxjs/index.d.ts.map +1 -0
  41. package/dist/stream/decoder.d.ts +1 -0
  42. package/dist/stream/decoder.d.ts.map +1 -0
  43. package/dist/stream/encoder.d.ts +1 -0
  44. package/dist/stream/encoder.d.ts.map +1 -0
  45. package/dist/stream/index.d.ts +1 -0
  46. package/dist/stream/index.d.ts.map +1 -0
  47. package/dist/stream-helper/decoder.d.ts +1 -0
  48. package/dist/stream-helper/decoder.d.ts.map +1 -0
  49. package/dist/stream-helper/encoder.d.ts +3 -1
  50. package/dist/stream-helper/encoder.d.ts.map +1 -0
  51. package/dist/stream-helper/encoder.js +31 -30
  52. package/dist/stream-helper/encoder.js.map +1 -1
  53. package/jest.config.js +1 -1
  54. package/package.json +3 -4
  55. package/src/base/encoder.ts +53 -174
  56. package/src/encoder.ts +24 -14
  57. package/src/helper/decode.ts +30 -18
  58. package/src/helper/encode.ts +141 -15
  59. package/src/helper/string-decoder.ts +10 -39
  60. package/src/stream-helper/encoder.ts +33 -33
  61. package/tests/encode.js +8 -0
  62. package/tests/string-encoding.js +1 -10
  63. package/tests/tsconfig.json +1 -7
  64. package/tsconfig.json +1 -1
@@ -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.data[cursor.length++] = constants.INT8;
43
- cursor.data[cursor.length++] = length;
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): void {
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
- cursor.data[cursor.length++] = constants.ARRAY;
69
- cursor.data[cursor.length++] = constants.TYPE_MARKER;
134
+ let type: TypedArrayType;
135
+ const { length } = cursor;
70
136
  if (value instanceof Uint8Array) {
71
- cursor.data[cursor.length++] = constants.UINT8;
137
+ cursor.view.setUint32(length, U8_ARR_HEADER);
138
+ type = constants.UINT8;
72
139
  } else if (value instanceof Float64Array) {
73
- cursor.data[cursor.length++] = constants.FLOAT64;
140
+ cursor.view.setUint32(length, F64_ARR_HEADER);
141
+ type = constants.FLOAT64;
74
142
  } else if (value instanceof Int32Array) {
75
- cursor.data[cursor.length++] = constants.INT32;
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.data[cursor.length++] = constants.INT8;
152
+ cursor.view.setUint32(length, I8_ARR_HEADER);
153
+ type = constants.INT8;
78
154
  } 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;
155
+ cursor.view.setUint32(length, I16_ARR_HEADER);
156
+ type = constants.INT16;
84
157
  } else {
85
158
  unsupportedView(value);
86
159
  }
87
- cursor.data[cursor.length++] = constants.COUNT_MARKER;
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 = Array.from<number>({ length });
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 ((byte & 0x80) > 0) {
60
+ if (byte & 0x80) {
61
61
  return;
62
62
  }
63
- bytes[i] = byte;
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
- // 只有小字符串有优化价值,见 benchmark-string.js
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();
@@ -1,7 +1,7 @@
1
1
  import { constants } from '../helper/constants.js';
2
- import { unsupportedView } from '../helper/errors.js';
3
2
  import { encode, stringByteLength } from '../helper/string-encoder.js';
4
3
  import { EncoderBase } from '../base/encoder.js';
4
+ import type { TypedArrayType } from '../helper/encode.js';
5
5
 
6
6
  const BLOCK_SIZE = 1024 * 64; // 64 KiB
7
7
  const MAX_SIZE = 1024 * 1024 * 32; // 32 MiB
@@ -102,54 +102,54 @@ export class StreamEncoderHelper extends EncoderBase {
102
102
  }
103
103
  }
104
104
  /** @inheritdoc */
105
- protected override writeLargeTypedArrayData(value: ArrayBufferView): void {
105
+ protected override writeLargeTypedArrayData(type: TypedArrayType, value: ArrayBufferView): void {
106
106
  this.ensureCapacity(-1);
107
- if (value instanceof Uint8Array || value instanceof Int8Array) {
107
+ const { byteLength } = value;
108
+ if (type === constants.UINT8 || type === constants.INT8) {
108
109
  // fast path for typed arrays with `BYTES_PER_ELEMENT` of 1
109
110
  // divide buffer to 64k chunks
110
- for (let i = 0; i < value.byteLength; i += BLOCK_SIZE) {
111
- this.onChunk(
112
- new Uint8Array(value.buffer.slice(value.byteOffset + i, value.byteOffset + i + BLOCK_SIZE)),
113
- );
111
+ const { buffer, byteOffset } = value;
112
+ for (let i = 0; i < byteLength; i += BLOCK_SIZE) {
113
+ this.onChunk(new Uint8Array(buffer.slice(byteOffset + i, byteOffset + i + BLOCK_SIZE)));
114
114
  }
115
115
  return;
116
116
  }
117
-
118
- const arrayLength = (value as Int16Array | Int32Array | BigInt64Array | Float32Array | Float64Array).length;
119
- const elementSize = (value as Int16Array | Int32Array | BigInt64Array | Float32Array | Float64Array)
120
- .BYTES_PER_ELEMENT;
121
- if (value instanceof Int16Array) {
117
+ if (type === constants.FLOAT64) {
118
+ const arrayLength = byteLength / 8;
122
119
  for (let i = 0; i < arrayLength; i++) {
123
- this.ensureCapacity(elementSize);
124
- this.view.setInt16(this.length, value[i]!);
125
- this.length += elementSize;
120
+ this.ensureCapacity(8);
121
+ this.view.setFloat64(this.length, (value as Float64Array)[i]!);
122
+ this.length += 8;
126
123
  }
127
- } else if (value instanceof Int32Array) {
124
+ } else if (type === constants.INT32) {
125
+ const arrayLength = byteLength / 4;
128
126
  for (let i = 0; i < arrayLength; i++) {
129
- this.ensureCapacity(elementSize);
130
- this.view.setInt32(this.length, value[i]!);
131
- this.length += elementSize;
127
+ this.ensureCapacity(4);
128
+ this.view.setInt32(this.length, (value as Int32Array)[i]!);
129
+ this.length += 4;
132
130
  }
133
- } else if (value instanceof Float32Array) {
131
+ } else if (type === constants.INT64) {
132
+ const arrayLength = byteLength / 8;
134
133
  for (let i = 0; i < arrayLength; i++) {
135
- this.ensureCapacity(elementSize);
136
- this.view.setFloat32(this.length, value[i]!);
137
- this.length += elementSize;
134
+ this.ensureCapacity(8);
135
+ this.view.setBigInt64(this.length, (value as BigInt64Array)[i]!);
136
+ this.length += 8;
138
137
  }
139
- } else if (value instanceof Float64Array) {
138
+ } else if (type === constants.FLOAT32) {
139
+ const arrayLength = byteLength / 4;
140
140
  for (let i = 0; i < arrayLength; i++) {
141
- this.ensureCapacity(elementSize);
142
- this.view.setFloat64(this.length, value[i]!);
143
- this.length += elementSize;
141
+ this.ensureCapacity(4);
142
+ this.view.setFloat32(this.length, (value as Float32Array)[i]!);
143
+ this.length += 4;
144
144
  }
145
- } else if (value instanceof BigInt64Array) {
145
+ } else {
146
+ (type) satisfies constants.INT16;
147
+ const arrayLength = byteLength / 2;
146
148
  for (let i = 0; i < arrayLength; i++) {
147
- this.ensureCapacity(elementSize);
148
- this.view.setBigInt64(this.length, value[i]!);
149
- this.length += elementSize;
149
+ this.ensureCapacity(2);
150
+ this.view.setInt16(this.length, (value as Int16Array)[i]!);
151
+ this.length += 2;
150
152
  }
151
- } else {
152
- unsupportedView(value);
153
153
  }
154
154
  }
155
155
  /** 获取写入结果 */
package/tests/encode.js CHANGED
@@ -407,6 +407,14 @@ test('encode object', () => {
407
407
  expect(getEncoder().pool).toBe(poolInit);
408
408
  });
409
409
 
410
+ test('encode object (keep order)', () => {
411
+ expect(toArray(encode({ b: 2, a: 1, c: 3 }))).toEqual(
412
+ toArray('{', 'i', 1, 'a', 'U', 1, 'i', 1, 'b', 'U', 2, 'i', 1, 'c', 'U', 3, '}'),
413
+ );
414
+ // @ts-expect-error Access private property
415
+ expect(getEncoder().pool).toBe(poolInit);
416
+ });
417
+
410
418
  test('encode object (empty)', () => {
411
419
  expect(toArray(encode({}))).toEqual(toArray('{', '}'));
412
420
  // @ts-expect-error Access private property
@@ -1,5 +1,5 @@
1
1
  import { Encoder } from '../dist/encoder.js';
2
- import { decode, decodeKey, jsDecode } from '../dist/helper/string-decoder.js';
2
+ import { decode, jsDecode } from '../dist/helper/string-decoder.js';
3
3
 
4
4
  /**
5
5
  * 测试编码
@@ -111,15 +111,6 @@ test('decode string', () => {
111
111
  });
112
112
  });
113
113
 
114
- test('decode string key', () => {
115
- testEncoding(new TextEncoder(), {
116
- decode(buffer) {
117
- if (!(buffer instanceof Uint8Array)) return '';
118
- return decodeKey(buffer, 0, buffer.byteLength);
119
- },
120
- });
121
- });
122
-
123
114
  test('decode string js', () => {
124
115
  testEncoding(new TextEncoder(), {
125
116
  decode(buffer) {
@@ -1,9 +1,3 @@
1
1
  {
2
- "extends": "../tsconfig",
3
- "include": ["./"],
4
- "compilerOptions": {
5
- "checkJs": true,
6
- "noEmit": true,
7
- "noUncheckedIndexedAccess": false
8
- }
2
+ "extends": "../../tsconfig.test"
9
3
  }
package/tsconfig.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "extends": "../../tsconfig",
2
+ "extends": "../tsconfig.build",
3
3
  "compilerOptions": {
4
4
  "isolatedModules": false,
5
5
  "verbatimModuleSyntax": false