@cloudpss/ubjson 0.5.40 → 0.5.42

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 (83) hide show
  1. package/dist/base/decoder.d.ts +3 -1
  2. package/dist/base/decoder.d.ts.map +1 -1
  3. package/dist/base/decoder.js +3 -1
  4. package/dist/base/decoder.js.map +1 -1
  5. package/dist/base/encoder.d.ts +2 -0
  6. package/dist/base/encoder.d.ts.map +1 -1
  7. package/dist/base/encoder.js +6 -1
  8. package/dist/base/encoder.js.map +1 -1
  9. package/dist/encoder.d.ts +2 -1
  10. package/dist/encoder.d.ts.map +1 -1
  11. package/dist/encoder.js +2 -1
  12. package/dist/encoder.js.map +1 -1
  13. package/dist/helper/decode-ae.d.ts +53 -0
  14. package/dist/helper/decode-ae.d.ts.map +1 -0
  15. package/dist/helper/decode-ae.js +590 -0
  16. package/dist/helper/decode-ae.js.map +1 -0
  17. package/dist/helper/decode.d.ts +8 -1
  18. package/dist/helper/decode.d.ts.map +1 -1
  19. package/dist/helper/decode.js +46 -8
  20. package/dist/helper/decode.js.map +1 -1
  21. package/dist/helper/string-decoder.d.ts.map +1 -1
  22. package/dist/helper/string-decoder.js +92 -114
  23. package/dist/helper/string-decoder.js.map +1 -1
  24. package/dist/index.d.ts +6 -4
  25. package/dist/index.d.ts.map +1 -1
  26. package/dist/index.js +8 -8
  27. package/dist/index.js.map +1 -1
  28. package/dist/options.d.ts +22 -0
  29. package/dist/options.d.ts.map +1 -0
  30. package/dist/options.js +2 -0
  31. package/dist/options.js.map +1 -0
  32. package/dist/rxjs/decoder.d.ts +2 -1
  33. package/dist/rxjs/decoder.d.ts.map +1 -1
  34. package/dist/rxjs/decoder.js +84 -59
  35. package/dist/rxjs/decoder.js.map +1 -1
  36. package/dist/rxjs/encoder.d.ts +2 -1
  37. package/dist/rxjs/encoder.d.ts.map +1 -1
  38. package/dist/rxjs/encoder.js +2 -2
  39. package/dist/rxjs/encoder.js.map +1 -1
  40. package/dist/rxjs/index.d.ts +1 -0
  41. package/dist/rxjs/index.d.ts.map +1 -1
  42. package/dist/stream/decoder.d.ts +2 -1
  43. package/dist/stream/decoder.d.ts.map +1 -1
  44. package/dist/stream/decoder.js +2 -2
  45. package/dist/stream/decoder.js.map +1 -1
  46. package/dist/stream/encoder.d.ts +3 -2
  47. package/dist/stream/encoder.d.ts.map +1 -1
  48. package/dist/stream/encoder.js +2 -2
  49. package/dist/stream/encoder.js.map +1 -1
  50. package/dist/stream/index.d.ts +8 -6
  51. package/dist/stream/index.d.ts.map +1 -1
  52. package/dist/stream/index.js +12 -12
  53. package/dist/stream/index.js.map +1 -1
  54. package/dist/stream-helper/encoder.d.ts +2 -1
  55. package/dist/stream-helper/encoder.d.ts.map +1 -1
  56. package/dist/stream-helper/encoder.js +2 -1
  57. package/dist/stream-helper/encoder.js.map +1 -1
  58. package/package.json +3 -3
  59. package/src/base/decoder.ts +5 -1
  60. package/src/base/encoder.ts +6 -1
  61. package/src/encoder.ts +3 -1
  62. package/src/helper/decode-ae.ts +630 -0
  63. package/src/helper/decode.ts +52 -8
  64. package/src/helper/string-decoder.ts +87 -102
  65. package/src/index.ts +11 -8
  66. package/src/options.ts +22 -0
  67. package/src/rxjs/decoder.ts +72 -45
  68. package/src/rxjs/encoder.ts +3 -2
  69. package/src/rxjs/index.ts +1 -0
  70. package/src/stream/decoder.ts +3 -2
  71. package/src/stream/encoder.ts +4 -3
  72. package/src/stream/index.ts +14 -12
  73. package/src/stream-helper/encoder.ts +6 -1
  74. package/tests/.utils.js +10 -1
  75. package/tests/decode.js +52 -0
  76. package/tests/e2e/.data.js +11 -0
  77. package/tests/e2e/stream.js +13 -1
  78. package/tests/encode.js +1 -1
  79. package/dist/stream-helper/decoder.d.ts +0 -12
  80. package/dist/stream-helper/decoder.d.ts.map +0 -1
  81. package/dist/stream-helper/decoder.js +0 -18
  82. package/dist/stream-helper/decoder.js.map +0 -1
  83. package/src/stream-helper/decoder.ts +0 -21
@@ -1,3 +1,4 @@
1
+ import type { DecodeOptions } from '../options.js';
1
2
  import { constants } from './constants.js';
2
3
  import { UnexpectedEofError, unsupportedType } from './errors.js';
3
4
  import { decode } from './string-decoder.js';
@@ -15,10 +16,12 @@ export interface DecodeCursor {
15
16
  offset: number;
16
17
  /** 读取到末尾时调用 */
17
18
  eof(): never;
19
+ /** 选项 */
20
+ readonly options: DecodeOptions | undefined;
18
21
  }
19
22
 
20
23
  /** 创建数据包装 */
21
- export function DecodeCursor(data: BinaryData): DecodeCursor {
24
+ export function DecodeCursor(data: BinaryData, options?: DecodeOptions): DecodeCursor {
22
25
  const d = toUint8Array(data);
23
26
  const v = new DataView(d.buffer, d.byteOffset, d.byteLength);
24
27
  return {
@@ -28,6 +31,7 @@ export function DecodeCursor(data: BinaryData): DecodeCursor {
28
31
  eof() {
29
32
  throw new UnexpectedEofError();
30
33
  },
34
+ options,
31
35
  };
32
36
  }
33
37
 
@@ -81,7 +85,7 @@ export function readLength(cursor: DecodeCursor): number {
81
85
  break;
82
86
  }
83
87
  default:
84
- throw new Error(`Unexpected marker '${String.fromCharCode(marker)}'(${marker}) for int length`);
88
+ throw new Error(`Unexpected marker '${fromCharCode(marker)}'(${marker}) for int length`);
85
89
  }
86
90
  if (length < 0) {
87
91
  throw new Error('Invalid length');
@@ -160,6 +164,35 @@ export function read(cursor: DecodeCursor): unknown {
160
164
  return readData(cursor, marker);
161
165
  }
162
166
 
167
+ /** 处理 `__proto__` */
168
+ export function protoAction(cursor: Pick<DecodeCursor, 'options'>, obj: Record<string, unknown>, value: unknown): void {
169
+ if (cursor.options?.protoAction === 'error') {
170
+ throw new Error('Unexpected "__proto__"');
171
+ } else if (cursor.options?.protoAction === 'allow') {
172
+ defineProperty(obj, '__proto__', {
173
+ value,
174
+ enumerable: true,
175
+ configurable: true,
176
+ writable: true,
177
+ });
178
+ }
179
+ }
180
+
181
+ /** 处理 `constructor` */
182
+ export function constructorAction(
183
+ cursor: Pick<DecodeCursor, 'options'>,
184
+ obj: Record<string, unknown>,
185
+ value: unknown,
186
+ ): void {
187
+ if (cursor.options?.constructorAction === 'error') {
188
+ throw new Error('Unexpected "constructor"');
189
+ } else if (cursor.options?.constructorAction === 'remove') {
190
+ return;
191
+ }
192
+ // eslint-disable-next-line @typescript-eslint/dot-notation
193
+ obj['constructor'] = value;
194
+ }
195
+
163
196
  /** 读取优化对象数据 */
164
197
  function readObjectOptimizedData(cursor: DecodeCursor, marker: OptimizedFormatMarkers): unknown {
165
198
  const { count, type } = marker;
@@ -168,7 +201,12 @@ function readObjectOptimizedData(cursor: DecodeCursor, marker: OptimizedFormatMa
168
201
  const key = readKey(cursor);
169
202
  const value = readData(cursor, type ?? readMarker(cursor));
170
203
  if (key === '__proto__') {
171
- defineProperty(object, key, { value, enumerable: true, configurable: true, writable: true });
204
+ protoAction(cursor, object, value);
205
+ continue;
206
+ }
207
+ if (key === 'constructor') {
208
+ constructorAction(cursor, object, value);
209
+ continue;
172
210
  }
173
211
  object[key] = value;
174
212
  }
@@ -198,7 +236,12 @@ export function readData(cursor: DecodeCursor, marker: number): unknown {
198
236
  const key = readKey(cursor);
199
237
  const value = read(cursor);
200
238
  if (key === '__proto__') {
201
- defineProperty(object, key, { value, enumerable: true, configurable: true, writable: true });
239
+ protoAction(cursor, object, value);
240
+ continue;
241
+ }
242
+ if (key === 'constructor') {
243
+ constructorAction(cursor, object, value);
244
+ continue;
202
245
  }
203
246
  object[key] = value;
204
247
  }
@@ -210,10 +253,11 @@ export function readData(cursor: DecodeCursor, marker: number): unknown {
210
253
  const markers = readOptimizedFormatMarkers(cursor);
211
254
  if (markers == null) {
212
255
  const array = [];
213
- // 直到 ']'
214
- while (readMarker(cursor) !== constants.ARRAY_END) {
215
- cursor.offset--;
216
- array.push(read(cursor));
256
+ for (;;) {
257
+ const marker = readMarker(cursor);
258
+ // 直到 ']'
259
+ if (marker === constants.ARRAY_END) break;
260
+ array.push(readData(cursor, marker));
217
261
  }
218
262
  return array;
219
263
  }
@@ -69,118 +69,103 @@ export function shortStringInJS(buf: Uint8Array, begin: number, length: number):
69
69
  if (length < 4) {
70
70
  if (length < 2) {
71
71
  if (length === 0) return '';
72
- else {
73
- const a = buf[begin++]!;
74
- if ((a & 0x80) > 1) {
75
- return;
76
- }
77
- return fromCharCode(a);
78
- }
79
- } else {
80
- const a = buf[begin++]!;
81
- const b = buf[begin++]!;
82
- if ((a & 0x80) > 0 || (b & 0x80) > 0) {
72
+ const a = buf[begin]!;
73
+ if ((a & 0x80) > 0) {
83
74
  return;
84
75
  }
85
- if (length < 3) return fromCharCode(a, b);
86
- const c = buf[begin++]!;
87
- if ((c & 0x80) > 0) {
88
- return;
89
- }
90
- return fromCharCode(a, b, c);
76
+ return fromCharCode(a);
91
77
  }
92
- } else {
93
78
  const a = buf[begin++]!;
94
79
  const b = buf[begin++]!;
80
+ if ((a & 0x80) > 0 || (b & 0x80) > 0) {
81
+ return;
82
+ }
83
+ if (length < 3) return fromCharCode(a, b);
95
84
  const c = buf[begin++]!;
96
- const d = buf[begin++]!;
97
- if ((a & 0x80) > 0 || (b & 0x80) > 0 || (c & 0x80) > 0 || (d & 0x80) > 0) {
85
+ if ((c & 0x80) > 0) {
98
86
  return;
99
87
  }
100
- if (length < 6) {
101
- if (length === 4) return fromCharCode(a, b, c, d);
102
- else {
103
- const e = buf[begin++]!;
104
- if ((e & 0x80) > 0) {
105
- return;
106
- }
107
- return fromCharCode(a, b, c, d, e);
108
- }
109
- } else if (length < 8) {
110
- const e = buf[begin++]!;
111
- const f = buf[begin++]!;
112
- if ((e & 0x80) > 0 || (f & 0x80) > 0) {
113
- return;
114
- }
115
- if (length < 7) return fromCharCode(a, b, c, d, e, f);
116
- const g = buf[begin++]!;
117
- if ((g & 0x80) > 0) {
118
- return;
119
- }
120
- return fromCharCode(a, b, c, d, e, f, g);
121
- } else {
122
- const e = buf[begin++]!;
123
- const f = buf[begin++]!;
124
- const g = buf[begin++]!;
125
- const h = buf[begin++]!;
126
- if ((e & 0x80) > 0 || (f & 0x80) > 0 || (g & 0x80) > 0 || (h & 0x80) > 0) {
127
- return;
128
- }
129
- if (length < 10) {
130
- if (length === 8) return fromCharCode(a, b, c, d, e, f, g, h);
131
- else {
132
- const i = buf[begin++]!;
133
- if ((i & 0x80) > 0) {
134
- return;
135
- }
136
- return fromCharCode(a, b, c, d, e, f, g, h, i);
137
- }
138
- } else if (length < 12) {
139
- const i = buf[begin++]!;
140
- const j = buf[begin++]!;
141
- if ((i & 0x80) > 0 || (j & 0x80) > 0) {
142
- return;
143
- }
144
- if (length < 11) return fromCharCode(a, b, c, d, e, f, g, h, i, j);
145
- const k = buf[begin++]!;
146
- if ((k & 0x80) > 0) {
147
- return;
148
- }
149
- return fromCharCode(a, b, c, d, e, f, g, h, i, j, k);
150
- } else {
151
- const i = buf[begin++]!;
152
- const j = buf[begin++]!;
153
- const k = buf[begin++]!;
154
- const l = buf[begin++]!;
155
- if ((i & 0x80) > 0 || (j & 0x80) > 0 || (k & 0x80) > 0 || (l & 0x80) > 0) {
156
- return;
157
- }
158
- if (length < 14) {
159
- if (length === 12) return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l);
160
- else {
161
- const m = buf[begin++]!;
162
- if ((m & 0x80) > 0) {
163
- begin -= 13;
164
- return;
165
- }
166
- return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m);
167
- }
168
- } else {
169
- const m = buf[begin++]!;
170
- const n = buf[begin++]!;
171
- if ((m & 0x80) > 0 || (n & 0x80) > 0) {
172
- return;
173
- }
174
- if (length < 15) return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m, n);
175
- const o = buf[begin++]!;
176
- if ((o & 0x80) > 0) {
177
- return;
178
- }
179
- return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o);
180
- }
181
- }
88
+ return fromCharCode(a, b, c);
89
+ }
90
+ const a = buf[begin++]!;
91
+ const b = buf[begin++]!;
92
+ const c = buf[begin++]!;
93
+ const d = buf[begin++]!;
94
+ if ((a & 0x80) > 0 || (b & 0x80) > 0 || (c & 0x80) > 0 || (d & 0x80) > 0) {
95
+ return;
96
+ }
97
+ if (length < 6) {
98
+ if (length === 4) return fromCharCode(a, b, c, d);
99
+ const e = buf[begin++]!;
100
+ if ((e & 0x80) > 0) {
101
+ return;
182
102
  }
103
+ return fromCharCode(a, b, c, d, e);
104
+ }
105
+ const e = buf[begin++]!;
106
+ const f = buf[begin++]!;
107
+ if ((e & 0x80) > 0 || (f & 0x80) > 0) {
108
+ return;
109
+ }
110
+ if (length < 8) {
111
+ if (length < 7) return fromCharCode(a, b, c, d, e, f);
112
+ const g = buf[begin++]!;
113
+ if ((g & 0x80) > 0) {
114
+ return;
115
+ }
116
+ return fromCharCode(a, b, c, d, e, f, g);
117
+ }
118
+ const g = buf[begin++]!;
119
+ const h = buf[begin++]!;
120
+ if ((g & 0x80) > 0 || (h & 0x80) > 0) {
121
+ return;
122
+ }
123
+ if (length < 10) {
124
+ if (length === 8) return fromCharCode(a, b, c, d, e, f, g, h);
125
+ const i = buf[begin++]!;
126
+ if ((i & 0x80) > 0) {
127
+ return;
128
+ }
129
+ return fromCharCode(a, b, c, d, e, f, g, h, i);
130
+ }
131
+ const i = buf[begin++]!;
132
+ const j = buf[begin++]!;
133
+ if ((i & 0x80) > 0 || (j & 0x80) > 0) {
134
+ return;
135
+ }
136
+ if (length < 12) {
137
+ if (length < 11) return fromCharCode(a, b, c, d, e, f, g, h, i, j);
138
+ const k = buf[begin++]!;
139
+ if ((k & 0x80) > 0) {
140
+ return;
141
+ }
142
+ return fromCharCode(a, b, c, d, e, f, g, h, i, j, k);
143
+ }
144
+ const k = buf[begin++]!;
145
+ const l = buf[begin++]!;
146
+ if ((k & 0x80) > 0 || (l & 0x80) > 0) {
147
+ return;
148
+ }
149
+ if (length < 14) {
150
+ if (length === 12) return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l);
151
+ const m = buf[begin++]!;
152
+ if ((m & 0x80) > 0) {
153
+ begin -= 13;
154
+ return;
155
+ }
156
+ return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m);
157
+ }
158
+ const m = buf[begin++]!;
159
+ const n = buf[begin++]!;
160
+ if ((m & 0x80) > 0 || (n & 0x80) > 0) {
161
+ return;
162
+ }
163
+ if (length < 15) return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m, n);
164
+ const o = buf[begin++]!;
165
+ if ((o & 0x80) > 0) {
166
+ return;
183
167
  }
168
+ return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o);
184
169
  }
185
170
 
186
171
  let TEXT_DECODER: TextDecoder | null;
package/src/index.ts CHANGED
@@ -1,26 +1,29 @@
1
1
  import { getEncoder } from './encoder.js';
2
2
  import { Decoder } from './decoder.js';
3
+ import type { EncodeOptions, DecodeOptions } from './options.js';
4
+
3
5
  export { UnexpectedEofError as UnexpectedEof } from './helper/errors.js';
6
+ export type { EncodeOptions, DecodeOptions };
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>): Uint8Array {
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 */
16
- export function decode(value: BinaryData): unknown {
17
- const decoder = new Decoder(value);
19
+ export function decode(value: BinaryData, options?: DecodeOptions): unknown {
20
+ const decoder = new Decoder(value, options);
18
21
  return decoder.decode();
19
22
  }
20
23
 
21
24
  /** 解码 UBJSON */
22
- export function* decodeMany(value: BinaryData): Iterable<unknown> {
23
- const decoder = new Decoder(value);
25
+ export function* decodeMany(value: BinaryData, options?: DecodeOptions): Iterable<unknown> {
26
+ const decoder = new Decoder(value, options);
24
27
  while (!decoder.ended) {
25
28
  yield decoder.decode();
26
29
  }
package/src/options.ts ADDED
@@ -0,0 +1,22 @@
1
+ /** 序列化选项 */
2
+ export interface EncodeOptions {
3
+ /**
4
+ * 序列化对象时排序属性
5
+ * @default false
6
+ */
7
+ sortObjectKeys?: boolean;
8
+ }
9
+
10
+ /** 反序列化选项 */
11
+ export interface DecodeOptions {
12
+ /**
13
+ * 解析对象时对 `__proto__` 属性的处理方式
14
+ * @default 'remove'
15
+ */
16
+ protoAction?: 'error' | 'allow' | 'remove';
17
+ /**
18
+ * 解析对象时对 `constructor` 属性的处理方式
19
+ * @default 'allow'
20
+ */
21
+ constructorAction?: 'error' | 'allow' | 'remove';
22
+ }
@@ -1,71 +1,98 @@
1
1
  import { Observable, type OperatorFunction } from 'rxjs';
2
- import { StreamDecoderHelper, kEof, UnexpectedEof } from '../stream-helper/decoder.js';
3
2
  import { toUint8Array } from '../helper/utils.js';
3
+ import { read } from '../helper/decode-ae.js';
4
+ import type { DecodeOptions } from '../options.js';
5
+ import { UnexpectedEofError } from '../helper/errors.js';
4
6
 
5
- const DEFAULT_BUFFER_SIZE = 16 * 1024 * 1024; // 16 MiB
6
7
  const EMPTY_BUFFER = new Uint8Array(0);
8
+ const EMPTY_VIEW = new DataView(EMPTY_BUFFER.buffer);
7
9
 
8
10
  /** 流式解码 UBJSON */
9
- export function decode(): OperatorFunction<BinaryData, unknown> {
10
- return (observable) => {
11
- return new Observable<unknown>((subscriber) => {
12
- let chunkSize = DEFAULT_BUFFER_SIZE;
13
- let buffer = new Uint8Array(chunkSize);
14
- let begin = 0;
15
- let end = 0;
11
+ export function decode(options?: DecodeOptions): OperatorFunction<BinaryData, unknown> {
12
+ return (observable) =>
13
+ new Observable<unknown>((subscriber) => {
14
+ const data = EMPTY_BUFFER;
15
+ const cursor = {
16
+ view: EMPTY_VIEW,
17
+ data,
18
+ capacity: 0,
19
+ size: 0,
20
+ offset: 0,
21
+ options,
22
+ };
23
+ /** reader 返回的还需接收的字节数 */
24
+ let required = 1;
25
+ let reader = read(cursor);
16
26
  return observable.subscribe({
17
27
  next(value) {
18
- const data = toUint8Array(value);
19
- if (buffer.length >= end + data.length) {
20
- buffer.set(data, end);
21
- end += data.length;
28
+ const chunk = toUint8Array(value);
29
+ const chunkSize = chunk.byteLength;
30
+ if (cursor.capacity - cursor.size < chunkSize) {
31
+ // 当前缓冲区不足,需要扩容
32
+ const newSize = Math.max(
33
+ // 不缩小缓冲区
34
+ cursor.capacity,
35
+ // 扩大缓冲区到足够容纳 2 倍当前数据
36
+ chunkSize * 2 + cursor.size - cursor.offset,
37
+ );
38
+ if (newSize > cursor.capacity) {
39
+ // 需要增大缓冲区
40
+ const newData = new Uint8Array(newSize);
41
+ newData.set(cursor.data.subarray(cursor.offset, cursor.size), 0);
42
+ newData.set(chunk, cursor.size - cursor.offset);
43
+ cursor.data = newData;
44
+ cursor.view = new DataView(newData.buffer, newData.byteOffset, newData.byteLength);
45
+ cursor.capacity = newSize;
46
+ } else {
47
+ // 无需增大缓冲区,直接移动数据
48
+ cursor.data.copyWithin(0, cursor.offset, cursor.size);
49
+ cursor.data.set(chunk, cursor.size - cursor.offset);
50
+ }
51
+ cursor.size = cursor.size - cursor.offset + chunkSize;
52
+ cursor.offset = 0;
22
53
  } else {
23
- chunkSize = Math.max(chunkSize, data.length * 2 + end - begin);
24
- const allocSize = Math.max(chunkSize, data.length + end - begin);
25
- const newBuffer =
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;
54
+ // 当前缓冲区足够,直接写入
55
+ cursor.data.set(chunk, cursor.size);
56
+ cursor.size += chunkSize;
34
57
  }
35
- while (end - begin > 0) {
36
- try {
37
- const helper = new StreamDecoderHelper(buffer.slice(begin, end));
38
- const result = helper.decode();
39
- if (result !== undefined) {
40
- subscriber.next(result);
41
- }
42
- begin += helper.readLength;
43
- } catch (ex) {
44
- if (ex === kEof) {
45
- return;
58
+
59
+ required -= chunkSize;
60
+ // 未读够数据,继续等待
61
+ if (required > 0) return;
62
+
63
+ try {
64
+ for (;;) {
65
+ const result = reader.next();
66
+ if (result.done) {
67
+ // 读取完成,新建 reader 读取下一个值
68
+ subscriber.next(result.value);
69
+ reader = read(cursor);
70
+ if (cursor.offset === cursor.size) {
71
+ break;
72
+ }
46
73
  } else {
47
- subscriber.error(ex as Error);
48
- buffer = EMPTY_BUFFER;
49
- return;
74
+ required = result.value;
75
+ break;
50
76
  }
51
77
  }
78
+ } catch (ex) {
79
+ subscriber.error(ex);
52
80
  }
53
- // 完全消费了 Buffer 内容,重置 Buffer
54
- begin = end = 0;
55
81
  },
56
82
  error(err) {
57
83
  subscriber.error(err);
58
- buffer = EMPTY_BUFFER;
84
+ cursor.data = EMPTY_BUFFER;
85
+ cursor.view = EMPTY_VIEW;
59
86
  },
60
87
  complete() {
61
- if (end - begin > 0) {
62
- subscriber.error(new UnexpectedEof());
88
+ if (cursor.size > cursor.offset) {
89
+ subscriber.error(new UnexpectedEofError());
63
90
  } else {
64
91
  subscriber.complete();
65
92
  }
66
- buffer = EMPTY_BUFFER;
93
+ cursor.data = EMPTY_BUFFER;
94
+ cursor.view = EMPTY_VIEW;
67
95
  },
68
96
  });
69
97
  });
70
- };
71
98
  }
@@ -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
@@ -1,3 +1,4 @@
1
1
  export { encode } from './encoder.js';
2
2
  export { decode } from './decoder.js';
3
3
  export { UnexpectedEofError as UnexpectedEof } from '../helper/errors.js';
4
+ export type { EncodeOptions, DecodeOptions } from '../options.js';
@@ -1,16 +1,17 @@
1
1
  import { Transform, type TransformCallback } from 'node:stream';
2
2
  import { Subject } from 'rxjs';
3
3
  import { decode } from '../rxjs/decoder.js';
4
+ import type { DecodeOptions } from '../options.js';
4
5
 
5
6
  /** 流式解码 UBJSON */
6
7
  export class StreamDecoder extends Transform {
7
- constructor() {
8
+ constructor(options?: DecodeOptions) {
8
9
  super({
9
10
  readableObjectMode: true,
10
11
  writableObjectMode: false,
11
12
  });
12
13
  this.buffer = new Subject<BinaryData>();
13
- this.buffer.pipe(decode()).subscribe({
14
+ this.buffer.pipe(decode(options)).subscribe({
14
15
  next: (value) => {
15
16
  // null is not allowed in a stream
16
17
  if (value == null) return;
@@ -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 | undefined) => void): void {
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
  }
@@ -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, DecodeOptions } from '../options.js';
4
5
 
5
6
  export { UnexpectedEofError as UnexpectedEof } from '../helper/errors.js';
7
+ export type { EncodeOptions, DecodeOptions };
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>): Readable {
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,14 +34,14 @@ 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 */
40
- export async function decode(stream: NodeJS.ReadableStream): Promise<unknown> {
42
+ export async function decode(stream: NodeJS.ReadableStream, options?: DecodeOptions): Promise<unknown> {
41
43
  return new Promise((resolve, reject) => {
42
- const decoder = new StreamDecoder();
44
+ const decoder = new StreamDecoder(options);
43
45
  decoder.on('error', reject);
44
46
  decoder.on('end', resolve);
45
47
  decoder.on('data', resolve);
@@ -48,12 +50,12 @@ export async function decode(stream: NodeJS.ReadableStream): Promise<unknown> {
48
50
  }
49
51
 
50
52
  /** 解码 UBJSON */
51
- export function decodeMany(stream: NodeJS.ReadableStream): AsyncIterable<unknown> {
52
- const decoder = new StreamDecoder();
53
+ export function decodeMany(stream: NodeJS.ReadableStream, options?: DecodeOptions): AsyncIterable<unknown> {
54
+ const decoder = new StreamDecoder(options);
53
55
  return stream.pipe(decoder);
54
56
  }
55
57
 
56
58
  /** 解码 UBJSON */
57
- export function decoder(): Transform {
58
- return new StreamDecoder();
59
+ export function decoder(options?: DecodeOptions): Transform {
60
+ return new StreamDecoder(options);
59
61
  }