@cloudpss/ubjson 0.4.32 → 0.4.34
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/common/constants.d.ts +22 -20
- package/dist/common/constants.js +1 -21
- package/dist/common/constants.js.map +1 -1
- package/dist/common/decoder.d.ts +4 -20
- package/dist/common/decoder.js +217 -178
- package/dist/common/decoder.js.map +1 -1
- package/dist/common/encoder.d.ts +8 -54
- package/dist/common/encoder.js +241 -259
- package/dist/common/encoder.js.map +1 -1
- package/dist/common/string-decoder.d.ts +3 -8
- package/dist/common/string-decoder.js +190 -46
- package/dist/common/string-decoder.js.map +1 -1
- package/dist/common/string-encoder.d.ts +2 -8
- package/dist/common/string-encoder.js +8 -44
- package/dist/common/string-encoder.js.map +1 -1
- package/dist/encoder.js +2 -21
- package/dist/encoder.js.map +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.js +2 -4
- package/dist/index.js.map +1 -1
- package/dist/rxjs/decoder.js +2 -2
- package/dist/rxjs/decoder.js.map +1 -1
- package/dist/rxjs/index.d.ts +1 -0
- package/dist/rxjs/index.js +1 -0
- package/dist/rxjs/index.js.map +1 -1
- package/dist/stream/index.d.ts +1 -0
- package/dist/stream/index.js +2 -1
- package/dist/stream/index.js.map +1 -1
- package/dist/stream-helper/decoder.d.ts +1 -1
- package/dist/stream-helper/decoder.js +1 -1
- package/dist/stream-helper/decoder.js.map +1 -1
- package/dist/stream-helper/encoder.js +2 -2
- package/dist/stream-helper/encoder.js.map +1 -1
- package/dist/utils.d.ts +5 -1
- package/dist/utils.js +13 -4
- package/dist/utils.js.map +1 -1
- package/package.json +3 -2
- package/src/common/constants.ts +22 -21
- package/src/common/decoder.ts +197 -162
- package/src/common/encoder.ts +230 -277
- package/src/common/string-decoder.ts +173 -41
- package/src/common/string-encoder.ts +10 -41
- package/src/encoder.ts +2 -16
- package/src/index.ts +2 -5
- package/src/rxjs/decoder.ts +2 -2
- package/src/rxjs/index.ts +1 -0
- package/src/stream/index.ts +2 -0
- package/src/stream-helper/decoder.ts +1 -1
- package/src/stream-helper/encoder.ts +2 -2
- package/src/utils.ts +14 -4
- package/tests/decode.js +69 -5
- package/tests/e2e.js +12 -1
- package/tests/encode.js +52 -23
- package/tests/rxjs/decode.js +3 -2
- package/tests/rxjs/encode.js +5 -8
- package/tests/stream/decode.js +2 -1
- package/tests/stream/encode.js +4 -5
- package/tests/string-encoding.js +77 -25
- package/tsconfig.json +3 -1
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { UnexpectedEof } from '../utils.js';
|
|
2
|
+
|
|
1
3
|
/* c8 ignore next 2: TextDecoder always present, fallback tested */
|
|
2
4
|
export const textDecoder =
|
|
3
5
|
typeof TextDecoder == 'function' ? new TextDecoder('utf8', { ignoreBOM: true, fatal: false }) : null;
|
|
@@ -8,6 +10,8 @@ export const TEXT_ENCODER_THRESHOLD = textDecoder == null ? 0xffff_ffff : 200;
|
|
|
8
10
|
const CHUNK_SIZE = 0x1000;
|
|
9
11
|
const REPLACE_CHAR = 0xfffd;
|
|
10
12
|
|
|
13
|
+
const fromCharCode = String.fromCharCode;
|
|
14
|
+
|
|
11
15
|
/** 解码 */
|
|
12
16
|
export function decodeJs(bytes: Uint8Array, begin: number, end: number): string {
|
|
13
17
|
let offset = begin;
|
|
@@ -45,67 +49,195 @@ export function decodeJs(bytes: Uint8Array, begin: number, end: number): string
|
|
|
45
49
|
}
|
|
46
50
|
|
|
47
51
|
if (units.length >= CHUNK_SIZE) {
|
|
48
|
-
result +=
|
|
52
|
+
result += fromCharCode(...units);
|
|
49
53
|
units.length = 0;
|
|
50
54
|
}
|
|
51
55
|
}
|
|
52
56
|
|
|
53
57
|
if (units.length > 0) {
|
|
54
|
-
result +=
|
|
58
|
+
result += fromCharCode(...units);
|
|
55
59
|
}
|
|
56
60
|
|
|
57
61
|
return result;
|
|
58
62
|
}
|
|
59
63
|
|
|
60
|
-
/** 解码 */
|
|
64
|
+
/** 解码 Ascii */
|
|
65
|
+
function longStringInJS(buf: Uint8Array, begin: number, length: number): string | undefined {
|
|
66
|
+
const bytes = Array.from<number>({ length });
|
|
67
|
+
for (let i = 0; i < length; i++) {
|
|
68
|
+
const byte = buf[begin++];
|
|
69
|
+
if ((byte & 0x80) > 0) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
bytes[i] = byte;
|
|
73
|
+
}
|
|
74
|
+
return fromCharCode(...bytes);
|
|
75
|
+
}
|
|
76
|
+
/** 解码 Ascii */
|
|
77
|
+
function shortStringInJS(buf: Uint8Array, begin: number, length: number): string | undefined {
|
|
78
|
+
if (length < 4) {
|
|
79
|
+
if (length < 2) {
|
|
80
|
+
if (length === 0) return '';
|
|
81
|
+
else {
|
|
82
|
+
const a = buf[begin++];
|
|
83
|
+
if ((a & 0x80) > 1) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
return fromCharCode(a);
|
|
87
|
+
}
|
|
88
|
+
} else {
|
|
89
|
+
const a = buf[begin++];
|
|
90
|
+
const b = buf[begin++];
|
|
91
|
+
if ((a & 0x80) > 0 || (b & 0x80) > 0) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
if (length < 3) return fromCharCode(a, b);
|
|
95
|
+
const c = buf[begin++];
|
|
96
|
+
if ((c & 0x80) > 0) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
return fromCharCode(a, b, c);
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
const a = buf[begin++];
|
|
103
|
+
const b = buf[begin++];
|
|
104
|
+
const c = buf[begin++];
|
|
105
|
+
const d = buf[begin++];
|
|
106
|
+
if ((a & 0x80) > 0 || (b & 0x80) > 0 || (c & 0x80) > 0 || (d & 0x80) > 0) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (length < 6) {
|
|
110
|
+
if (length === 4) return fromCharCode(a, b, c, d);
|
|
111
|
+
else {
|
|
112
|
+
const e = buf[begin++];
|
|
113
|
+
if ((e & 0x80) > 0) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
return fromCharCode(a, b, c, d, e);
|
|
117
|
+
}
|
|
118
|
+
} else if (length < 8) {
|
|
119
|
+
const e = buf[begin++];
|
|
120
|
+
const f = buf[begin++];
|
|
121
|
+
if ((e & 0x80) > 0 || (f & 0x80) > 0) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (length < 7) return fromCharCode(a, b, c, d, e, f);
|
|
125
|
+
const g = buf[begin++];
|
|
126
|
+
if ((g & 0x80) > 0) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
return fromCharCode(a, b, c, d, e, f, g);
|
|
130
|
+
} else {
|
|
131
|
+
const e = buf[begin++];
|
|
132
|
+
const f = buf[begin++];
|
|
133
|
+
const g = buf[begin++];
|
|
134
|
+
const h = buf[begin++];
|
|
135
|
+
if ((e & 0x80) > 0 || (f & 0x80) > 0 || (g & 0x80) > 0 || (h & 0x80) > 0) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
if (length < 10) {
|
|
139
|
+
if (length === 8) return fromCharCode(a, b, c, d, e, f, g, h);
|
|
140
|
+
else {
|
|
141
|
+
const i = buf[begin++];
|
|
142
|
+
if ((i & 0x80) > 0) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
return fromCharCode(a, b, c, d, e, f, g, h, i);
|
|
146
|
+
}
|
|
147
|
+
} else if (length < 12) {
|
|
148
|
+
const i = buf[begin++];
|
|
149
|
+
const j = buf[begin++];
|
|
150
|
+
if ((i & 0x80) > 0 || (j & 0x80) > 0) {
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
if (length < 11) return fromCharCode(a, b, c, d, e, f, g, h, i, j);
|
|
154
|
+
const k = buf[begin++];
|
|
155
|
+
if ((k & 0x80) > 0) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
return fromCharCode(a, b, c, d, e, f, g, h, i, j, k);
|
|
159
|
+
} else {
|
|
160
|
+
const i = buf[begin++];
|
|
161
|
+
const j = buf[begin++];
|
|
162
|
+
const k = buf[begin++];
|
|
163
|
+
const l = buf[begin++];
|
|
164
|
+
if ((i & 0x80) > 0 || (j & 0x80) > 0 || (k & 0x80) > 0 || (l & 0x80) > 0) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
if (length < 14) {
|
|
168
|
+
if (length === 12) return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l);
|
|
169
|
+
else {
|
|
170
|
+
const m = buf[begin++];
|
|
171
|
+
if ((m & 0x80) > 0) {
|
|
172
|
+
begin -= 13;
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m);
|
|
176
|
+
}
|
|
177
|
+
} else {
|
|
178
|
+
const m = buf[begin++];
|
|
179
|
+
const n = buf[begin++];
|
|
180
|
+
if ((m & 0x80) > 0 || (n & 0x80) > 0) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
if (length < 15) return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m, n);
|
|
184
|
+
const o = buf[begin++];
|
|
185
|
+
if ((o & 0x80) > 0) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
return fromCharCode(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/** 字符串解码,无缓存 */
|
|
61
196
|
export function decode(data: Uint8Array, begin: number, end: number): string {
|
|
62
|
-
if (
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
197
|
+
if (end > data.length) throw new UnexpectedEof();
|
|
198
|
+
const length = end - begin;
|
|
199
|
+
if (length < 16) {
|
|
200
|
+
const result = shortStringInJS(data, begin, length);
|
|
201
|
+
if (result != null) return result;
|
|
202
|
+
}
|
|
203
|
+
// 只有小字符串有优化价值,见 benchmark-string.js
|
|
204
|
+
if (length < TEXT_ENCODER_THRESHOLD) {
|
|
66
205
|
// 为小字符串优化
|
|
67
206
|
return decodeJs(data, begin, end);
|
|
68
207
|
}
|
|
69
208
|
// 使用系统解码
|
|
70
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
71
209
|
return textDecoder!.decode(data.subarray(begin, end));
|
|
72
210
|
}
|
|
73
211
|
|
|
74
|
-
|
|
75
|
-
export class StringDecoder {
|
|
76
|
-
/** 小字符串缓存 */
|
|
77
|
-
private readonly cache = [
|
|
78
|
-
undefined, // 0 字节字符串,直接返回
|
|
79
|
-
undefined, // 1 字节字符串,使用 String.fromCharCode,兼容 UTF8
|
|
80
|
-
new Map<number, string>(), // 2 字节
|
|
81
|
-
new Map<number, string>(), // 3 字节
|
|
82
|
-
new Map<number, string>(), // 4 字节
|
|
83
|
-
new Map<number, string>(), // 5 字节
|
|
84
|
-
new Map<number, string>(), // 6 字节
|
|
85
|
-
] as const;
|
|
212
|
+
const keyCache = Array.from<{ value: string; buffer: Uint8Array } | undefined>({ length: 4096 });
|
|
86
213
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
214
|
+
/** 字符串解码,使用缓存 */
|
|
215
|
+
export function decodeKey(data: Uint8Array, begin: number, end: number): string {
|
|
216
|
+
const length = end - begin;
|
|
217
|
+
const cacheKey =
|
|
218
|
+
((length << 5) ^ (length > 1 ? data[begin] & (data[begin + 1] << 8) : length > 0 ? data[begin] : 0)) & 0xfff;
|
|
219
|
+
let entry = keyCache[cacheKey];
|
|
220
|
+
if (entry != null && entry.buffer.byteLength === length) {
|
|
221
|
+
let i = 0;
|
|
222
|
+
for (; i < length; i++) {
|
|
223
|
+
if (entry.buffer[i] !== data[begin + i]) break;
|
|
224
|
+
}
|
|
225
|
+
if (i === length) return entry.value;
|
|
226
|
+
}
|
|
92
227
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
key = key * 0xff + data[i];
|
|
228
|
+
if (end > data.length) throw new UnexpectedEof();
|
|
229
|
+
let str = length < 16 ? shortStringInJS(data, begin, length) : longStringInJS(data, begin, length);
|
|
230
|
+
if (str == null) {
|
|
231
|
+
// 只有小字符串有优化价值,见 benchmark-string.js
|
|
232
|
+
if (length < TEXT_ENCODER_THRESHOLD) {
|
|
233
|
+
// 为小字符串优化
|
|
234
|
+
str = decodeJs(data, begin, end);
|
|
235
|
+
} else {
|
|
236
|
+
// 使用系统解码
|
|
237
|
+
str = textDecoder!.decode(data.subarray(begin, end));
|
|
104
238
|
}
|
|
105
|
-
const match = cache.get(key);
|
|
106
|
-
if (match != null) return match;
|
|
107
|
-
const string = decodeJs(data, begin, end);
|
|
108
|
-
cache.set(key, string);
|
|
109
|
-
return string;
|
|
110
239
|
}
|
|
240
|
+
entry = { value: str, buffer: data.slice(begin, end) };
|
|
241
|
+
keyCache[cacheKey] = entry;
|
|
242
|
+
return str;
|
|
111
243
|
}
|
|
@@ -53,45 +53,14 @@ function getCharCodeByteLength(string: string): number {
|
|
|
53
53
|
return byteLength;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
export const getStringByteLength = (): ((v: string) => number) =>
|
|
57
|
+
typeof Buffer == 'function' && typeof Buffer.byteLength == 'function'
|
|
58
|
+
? (v) => Buffer.byteLength(v, 'utf8')
|
|
59
|
+
: getCharCodeByteLength;
|
|
59
60
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
let buf = this.cache.get(value);
|
|
67
|
-
if (buf == null) {
|
|
68
|
-
let isAscii = true;
|
|
69
|
-
for (let index = 0; index < value.length; index++) {
|
|
70
|
-
const ch = value.charCodeAt(index);
|
|
71
|
-
if (ch >= 128) {
|
|
72
|
-
isAscii = false;
|
|
73
|
-
break;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
if (isAscii) {
|
|
77
|
-
buf = new Uint8Array(value.length);
|
|
78
|
-
for (let index = 0; index < value.length; index++) {
|
|
79
|
-
const ch = value.charCodeAt(index);
|
|
80
|
-
buf[index] = ch;
|
|
81
|
-
}
|
|
82
|
-
} else {
|
|
83
|
-
buf = super.encode(value);
|
|
84
|
-
}
|
|
85
|
-
this.cache.set(value, buf);
|
|
86
|
-
}
|
|
87
|
-
return buf;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/** 计算使用的空间 */
|
|
91
|
-
byteLength(value: string): number {
|
|
92
|
-
if (typeof Buffer == 'function' && typeof Buffer.byteLength == 'function') {
|
|
93
|
-
return Buffer.byteLength(value, 'utf8');
|
|
94
|
-
}
|
|
95
|
-
return getCharCodeByteLength(value);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
61
|
+
/* c8 ignore next 1 */
|
|
62
|
+
const encoder = typeof TextEncoder == 'function' ? new TextEncoder() : undefined;
|
|
63
|
+
export const getEncodeInto = (): ((v: string, buf: Uint8Array, offset: number) => number) | undefined =>
|
|
64
|
+
typeof encoder?.encodeInto == 'function'
|
|
65
|
+
? (v, buf, offset) => encoder.encodeInto(v, buf.subarray(offset)).written
|
|
66
|
+
: undefined;
|
package/src/encoder.ts
CHANGED
|
@@ -34,22 +34,8 @@ export class Encoder extends EncoderBase {
|
|
|
34
34
|
|
|
35
35
|
/** 获取写入结果 */
|
|
36
36
|
encode(): Uint8Array {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (this.value.byteLength < 128) this.ensureCapacity(6 + this.value.byteLength);
|
|
40
|
-
else if (this.value.byteLength < 32768) this.ensureCapacity(7 + this.value.byteLength);
|
|
41
|
-
else if (this.value.byteLength < 2_147_483_648) this.ensureCapacity(9 + this.value.byteLength);
|
|
42
|
-
else this.ensureCapacity(13 + this.value.byteLength);
|
|
43
|
-
this.writeTypedArray(this.value);
|
|
44
|
-
// 取消注释后运行测试
|
|
45
|
-
// if (this.buffer.byteLength !== this.length) {
|
|
46
|
-
// // 这里永远无法到达
|
|
47
|
-
// throw new Error('Bad buffer size');
|
|
48
|
-
// }
|
|
49
|
-
} else {
|
|
50
|
-
this.ensureCapacity(BLOCK_SIZE);
|
|
51
|
-
this.write(this.value);
|
|
52
|
-
}
|
|
37
|
+
this.ensureCapacity(BLOCK_SIZE);
|
|
38
|
+
this.writeValue();
|
|
53
39
|
if (this.flushedBuffers.length === 0) {
|
|
54
40
|
if (this.buffer.byteLength === this.length) {
|
|
55
41
|
// 无需再拷贝一次
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Encoder } from './encoder.js';
|
|
2
2
|
import { Decoder } from './decoder.js';
|
|
3
|
-
|
|
3
|
+
export { UnexpectedEof } from './utils.js';
|
|
4
4
|
|
|
5
5
|
/** 编码为 UBJSON */
|
|
6
6
|
export function encode(value: unknown): Uint8Array {
|
|
@@ -10,9 +10,6 @@ export function encode(value: unknown): Uint8Array {
|
|
|
10
10
|
|
|
11
11
|
/** 解码 UBJSON */
|
|
12
12
|
export function decode(value: BinaryData): unknown {
|
|
13
|
-
const decoder = new Decoder(
|
|
13
|
+
const decoder = new Decoder(value);
|
|
14
14
|
return decoder.decode();
|
|
15
15
|
}
|
|
16
|
-
|
|
17
|
-
export { StringEncoder } from './common/string-encoder.js';
|
|
18
|
-
export { StringDecoder } from './common/string-decoder.js';
|
package/src/rxjs/decoder.ts
CHANGED
|
@@ -15,7 +15,7 @@ export function decode(): OperatorFunction<BinaryData, unknown> {
|
|
|
15
15
|
let end = 0;
|
|
16
16
|
return observable.subscribe({
|
|
17
17
|
next(value) {
|
|
18
|
-
const data = toUint8Array(value);
|
|
18
|
+
const data = toUint8Array(value, false);
|
|
19
19
|
if (buffer.length >= end + data.length) {
|
|
20
20
|
buffer.set(data, end);
|
|
21
21
|
end += data.length;
|
|
@@ -30,7 +30,7 @@ export function decode(): OperatorFunction<BinaryData, unknown> {
|
|
|
30
30
|
}
|
|
31
31
|
while (end - begin > 0) {
|
|
32
32
|
try {
|
|
33
|
-
const helper = new StreamDecoderHelper(buffer.
|
|
33
|
+
const helper = new StreamDecoderHelper(buffer.slice(begin, end));
|
|
34
34
|
const result = helper.decode();
|
|
35
35
|
if (result !== undefined) {
|
|
36
36
|
subscriber.next(result);
|
package/src/rxjs/index.ts
CHANGED
package/src/stream/index.ts
CHANGED
|
@@ -2,6 +2,8 @@ import { Readable, Transform } from 'node:stream';
|
|
|
2
2
|
import { StreamEncoder } from './encoder.js';
|
|
3
3
|
import { StreamDecoder } from './decoder.js';
|
|
4
4
|
|
|
5
|
+
export { UnexpectedEof } from '../utils.js';
|
|
6
|
+
|
|
5
7
|
/** 编码为 UBJSON */
|
|
6
8
|
export function encode(value: unknown): Readable {
|
|
7
9
|
if (value == null) {
|
|
@@ -48,11 +48,11 @@ export class StreamEncoderHelper extends EncoderBase {
|
|
|
48
48
|
encode(): void {
|
|
49
49
|
if (typeof this.value != 'object' || this.value == null || ArrayBuffer.isView(this.value)) {
|
|
50
50
|
this.ensureCapacity(20);
|
|
51
|
-
this.
|
|
51
|
+
this.writeValue();
|
|
52
52
|
this.ensureCapacity(-1);
|
|
53
53
|
} else {
|
|
54
54
|
this.ensureCapacity(BLOCK_SIZE);
|
|
55
|
-
this.
|
|
55
|
+
this.writeValue();
|
|
56
56
|
this.ensureCapacity(-1);
|
|
57
57
|
}
|
|
58
58
|
}
|
package/src/utils.ts
CHANGED
|
@@ -1,10 +1,20 @@
|
|
|
1
1
|
/** 支持的数据转为 Uint8Array */
|
|
2
|
-
export function toUint8Array(data: BinaryData): Uint8Array {
|
|
2
|
+
export function toUint8Array(data: BinaryData, exact: boolean): Uint8Array {
|
|
3
3
|
if (data == null || typeof data != 'object' || typeof data.byteLength != 'number') {
|
|
4
4
|
throw new TypeError('Invalid data');
|
|
5
5
|
}
|
|
6
|
-
if (ArrayBuffer.isView(data)) {
|
|
7
|
-
return new Uint8Array(data
|
|
6
|
+
if (!ArrayBuffer.isView(data)) {
|
|
7
|
+
return new Uint8Array(data);
|
|
8
|
+
}
|
|
9
|
+
if (exact && (data.byteOffset !== 0 || data.buffer.byteLength !== data.byteLength)) {
|
|
10
|
+
return new Uint8Array(data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength));
|
|
11
|
+
}
|
|
12
|
+
return new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** 未结束的流 */
|
|
16
|
+
export class UnexpectedEof extends Error {
|
|
17
|
+
constructor() {
|
|
18
|
+
super('Unexpected EOF');
|
|
8
19
|
}
|
|
9
|
-
return new Uint8Array(data, 0, data.byteLength);
|
|
10
20
|
}
|
package/tests/decode.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Tests from https://bitbucket.org/shelacek/ubjson
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { decode } from '../dist/index.js';
|
|
5
|
+
import { decode, UnexpectedEof } from '../dist/index.js';
|
|
6
6
|
import { toBuffer } from './.utils.js';
|
|
7
7
|
|
|
8
8
|
test('decode unsupported type', () => {
|
|
@@ -66,14 +66,50 @@ test('decode float64', () => {
|
|
|
66
66
|
expect(decode(toBuffer('D', 0x40, 0xf8, 0x6a, 0x00, 0x10, 0x00, 0x00, 0x00))).toBe(100_000.003_906_25);
|
|
67
67
|
});
|
|
68
68
|
|
|
69
|
+
test('decode int8 [unexpected eof]', () => {
|
|
70
|
+
expect(() => decode(toBuffer('i'))).toThrowError(UnexpectedEof);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test('decode uint8 [unexpected eof]', () => {
|
|
74
|
+
expect(() => decode(toBuffer('U'))).toThrowError(UnexpectedEof);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('decode int16 [unexpected eof]', () => {
|
|
78
|
+
expect(() => decode(toBuffer('I', 0x12))).toThrowError(UnexpectedEof);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('decode int32 [unexpected eof]', () => {
|
|
82
|
+
expect(() => decode(toBuffer('l', 0x12, 0x34, 0x56))).toThrowError(UnexpectedEof);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test('decode int64 [unexpected eof]', () => {
|
|
86
|
+
expect(() => decode(toBuffer('L', 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde))).toThrowError(UnexpectedEof);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test('decode float32 [unexpected eof]', () => {
|
|
90
|
+
expect(() => decode(toBuffer('d', 0x3f, 0x80, 0x80))).toThrowError(UnexpectedEof);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test('decode float64 [unexpected eof]', () => {
|
|
94
|
+
expect(() => decode(toBuffer('D', 0x40, 0xf8, 0x6a, 0x00, 0x10, 0x00, 0x00))).toThrowError(UnexpectedEof);
|
|
95
|
+
});
|
|
96
|
+
|
|
69
97
|
test('decode high-precision number [error]', () => {
|
|
70
98
|
expect(() => decode(toBuffer('H', 'i', 3, '1', '.', '1'))).toThrow();
|
|
71
99
|
});
|
|
72
100
|
|
|
101
|
+
test('decode high-precision number [unexpected eof]', () => {
|
|
102
|
+
expect(() => decode(toBuffer('H', 'i', 3, '1', '.'))).toThrowError(UnexpectedEof);
|
|
103
|
+
});
|
|
104
|
+
|
|
73
105
|
test('decode char', () => {
|
|
74
106
|
expect(decode(toBuffer('C', 'a'))).toBe('a');
|
|
75
107
|
});
|
|
76
108
|
|
|
109
|
+
test('decode char [unexpected eof]', () => {
|
|
110
|
+
expect(() => decode(toBuffer('C'))).toThrowError(UnexpectedEof);
|
|
111
|
+
});
|
|
112
|
+
|
|
77
113
|
test('decode string', () => {
|
|
78
114
|
expect(decode(toBuffer('S', 'i', 6, 'u', 'b', 'j', 's', 'o', 'n'))).toBe('ubjson');
|
|
79
115
|
});
|
|
@@ -90,8 +126,16 @@ test('decode string (bad size) [error]', () => {
|
|
|
90
126
|
expect(() => decode(toBuffer('S', 'i', 0xff, 'x'))).toThrow(/Invalid length/);
|
|
91
127
|
});
|
|
92
128
|
|
|
93
|
-
test('decode string (unexpected eof) [error]', () => {
|
|
94
|
-
expect(() => decode(toBuffer('S', 'i', 2, 'x'))).toThrow(
|
|
129
|
+
test('decode short string (unexpected eof) [error]', () => {
|
|
130
|
+
expect(() => decode(toBuffer('S', 'i', 2, 'x'))).toThrow(UnexpectedEof);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test('decode long string (unexpected eof) [error]', () => {
|
|
134
|
+
expect(() => decode(toBuffer('S', 'i', 20, 'x'))).toThrow(UnexpectedEof);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test('decode huge string (unexpected eof) [error]', () => {
|
|
138
|
+
expect(() => decode(toBuffer('S', 'U', 200, 'x'))).toThrow(UnexpectedEof);
|
|
95
139
|
});
|
|
96
140
|
|
|
97
141
|
test('decode ascii string', () => {
|
|
@@ -276,8 +320,24 @@ test('decode array of objects of arrays (optimized)', () => {
|
|
|
276
320
|
]);
|
|
277
321
|
});
|
|
278
322
|
|
|
279
|
-
test('decode array (strongly typed, unexpected eof, optimized)', () => {
|
|
280
|
-
expect(() => decode(toBuffer('[', '$', 'i', '#', 'i', 3, 1, 2))).toThrow();
|
|
323
|
+
test('decode array (strongly typed i8, unexpected eof, optimized)', () => {
|
|
324
|
+
expect(() => decode(toBuffer('[', '$', 'i', '#', 'i', 3, 1, 2))).toThrow(UnexpectedEof);
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
test('decode array (strongly typed u8, unexpected eof, optimized)', () => {
|
|
328
|
+
expect(() => decode(toBuffer('[', '$', 'U', '#', 'i', 3, 1, 2))).toThrow(UnexpectedEof);
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
test('decode array (strongly typed i16, unexpected eof, optimized)', () => {
|
|
332
|
+
expect(() => decode(toBuffer('[', '$', 'I', '#', 'i', 3, 1, 2))).toThrow(UnexpectedEof);
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
test('decode array (strongly typed i32, unexpected eof, optimized)', () => {
|
|
336
|
+
expect(() => decode(toBuffer('[', '$', 'l', '#', 'i', 3, 1, 2))).toThrow(UnexpectedEof);
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
test('decode array (strongly typed i64, unexpected eof, optimized)', () => {
|
|
340
|
+
expect(() => decode(toBuffer('[', '$', 'L', '#', 'i', 3, 1, 2))).toThrow(UnexpectedEof);
|
|
281
341
|
});
|
|
282
342
|
|
|
283
343
|
test('decode array (strongly typed, invalid length value, optimized)', () => {
|
|
@@ -334,6 +394,10 @@ test('decode array (int64, strongly typed, optimized) [use typed array]', () =>
|
|
|
334
394
|
).toThrow();
|
|
335
395
|
});
|
|
336
396
|
|
|
397
|
+
test('decode array (int64, strongly typed, optimized, empty) [use typed array]', () => {
|
|
398
|
+
expect(() => decode(toBuffer('[', '$', 'L', '#', 'i', 0))).toThrow();
|
|
399
|
+
});
|
|
400
|
+
|
|
337
401
|
test('decode array (float32, strongly typed, optimized) [use typed array]', () => {
|
|
338
402
|
const actual = decode(toBuffer('[', '$', 'd', '#', 'i', 2, 0x3e, 0x80, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00));
|
|
339
403
|
expect(actual).toBeInstanceOf(Float32Array);
|
package/tests/e2e.js
CHANGED
|
@@ -299,7 +299,6 @@ test('encode/decode model', () => {
|
|
|
299
299
|
test('encode/decode complex object (no encodeInto)', () => {
|
|
300
300
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
301
301
|
const encodeInto = TextEncoder.prototype.encodeInto;
|
|
302
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
303
302
|
// @ts-expect-error 移除 encodeInto 以测试兼容性
|
|
304
303
|
TextEncoder.prototype.encodeInto = undefined;
|
|
305
304
|
const expected = {
|
|
@@ -399,3 +398,15 @@ test('decode Int8Array', () => {
|
|
|
399
398
|
const obj = { a: 1, b: undefined, c: { d: 2, e: undefined, f: null } };
|
|
400
399
|
expect(decode(new Int8Array(encode(obj).buffer))).toEqual(obj);
|
|
401
400
|
});
|
|
401
|
+
|
|
402
|
+
test('decode invalid __proto__', () => {
|
|
403
|
+
const obj = decode(encode(JSON.parse('{"__proto__":"xxx"}')));
|
|
404
|
+
expect(obj).toEqual(JSON.parse('{"__proto__":"xxx"}'));
|
|
405
|
+
expect(Object.getPrototypeOf(obj)).toBe(Object.prototype);
|
|
406
|
+
});
|
|
407
|
+
|
|
408
|
+
test('decode null __proto__', () => {
|
|
409
|
+
const obj = decode(encode(JSON.parse('{"__proto__":null}')));
|
|
410
|
+
expect(obj).toEqual(JSON.parse('{"__proto__":null}'));
|
|
411
|
+
expect(Object.getPrototypeOf(obj)).toBe(Object.prototype);
|
|
412
|
+
});
|