@cloudpss/ubjson 0.4.31 → 0.4.33
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 +4 -52
- package/dist/common/encoder.js +216 -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 +4 -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/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 +201 -275
- package/src/common/string-decoder.ts +173 -41
- package/src/common/string-encoder.ts +6 -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/utils.ts +14 -4
- package/tests/decode.js +69 -5
- package/tests/e2e.js +12 -1
- package/tests/encode.js +33 -16
- package/tests/rxjs/decode.js +3 -2
- package/tests/rxjs/encode.js +2 -3
- package/tests/stream/decode.js +2 -1
- package/tests/stream/encode.js +2 -3
- 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,10 @@ function getCharCodeByteLength(string: string): number {
|
|
|
53
53
|
return byteLength;
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
private readonly cache = new Map<string, Uint8Array>();
|
|
56
|
+
export const getStringByteLength = (): ((v: string) => number) =>
|
|
57
|
+
typeof Buffer?.byteLength == 'function' ? (v) => Buffer.byteLength(v, 'utf8') : getCharCodeByteLength;
|
|
59
58
|
|
|
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
|
-
}
|
|
59
|
+
/* c8 ignore next 1 */
|
|
60
|
+
const encoder = typeof TextEncoder == 'function' ? new TextEncoder() : undefined;
|
|
61
|
+
export const getEncodeInto = (): ((v: string, buf: Uint8Array, offset: number) => number) | undefined =>
|
|
62
|
+
encoder?.encodeInto ? (v, buf, offset) => encoder.encodeInto(v, buf.subarray(offset)).written : 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.write(this.value);
|
|
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) {
|
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
|
+
});
|
package/tests/encode.js
CHANGED
|
@@ -69,8 +69,8 @@ test('encode char', () => {
|
|
|
69
69
|
expect(toArray(encode('a'))).toEqual(toArray('C', 'a'));
|
|
70
70
|
});
|
|
71
71
|
|
|
72
|
-
test('encode char
|
|
73
|
-
expect(toArray(encode('\
|
|
72
|
+
test('encode char 127', () => {
|
|
73
|
+
expect(toArray(encode('\u007F'))).toEqual(toArray('C', '\u007F'));
|
|
74
74
|
});
|
|
75
75
|
|
|
76
76
|
test('encode char 257', () => {
|
|
@@ -85,15 +85,27 @@ test('encode string', () => {
|
|
|
85
85
|
expect(toArray(encode('ubjson'))).toEqual(toArray('S', 'i', 6, ...'ubjson'));
|
|
86
86
|
});
|
|
87
87
|
|
|
88
|
-
test('encode string (
|
|
88
|
+
test('encode string (len = 60)', () => {
|
|
89
89
|
// 不生成 u8
|
|
90
|
-
|
|
90
|
+
const str = 'ubjson'.repeat(10);
|
|
91
|
+
expect(toArray(encode(str))).toEqual(toArray('S', 'i', str.length, ...str));
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test('encode string (len = 120)', () => {
|
|
95
|
+
// 不生成 u8
|
|
96
|
+
const str = 'ubjson'.repeat(20);
|
|
97
|
+
expect(toArray(encode(str))).toEqual(toArray('S', 'i', str.length, ...str));
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test('encode string (len = 180)', () => {
|
|
101
|
+
// 不生成 u8
|
|
102
|
+
const str = 'ubjson'.repeat(30);
|
|
103
|
+
expect(toArray(encode(str))).toEqual(toArray('S', 'I', 0, str.length, ...str));
|
|
91
104
|
});
|
|
92
105
|
|
|
93
106
|
test('encode string (no encodeInto)', () => {
|
|
94
107
|
// eslint-disable-next-line @typescript-eslint/unbound-method
|
|
95
108
|
const encodeInto = TextEncoder.prototype.encodeInto;
|
|
96
|
-
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
97
109
|
// @ts-expect-error 移除 encodeInto 以测试兼容性
|
|
98
110
|
TextEncoder.prototype.encodeInto = undefined;
|
|
99
111
|
expect(toArray(encode('ubjson'))).toEqual(toArray('S', 'i', 6, ...'ubjson'));
|
|
@@ -357,31 +369,36 @@ test('encode huge data (~128M)', () => {
|
|
|
357
369
|
);
|
|
358
370
|
});
|
|
359
371
|
|
|
360
|
-
test('encode huge data (128M)', () => {
|
|
361
|
-
const obj = [new Uint8Array(128 * 1024 * 1024)];
|
|
372
|
+
test('encode huge data (128M - 9)', () => {
|
|
373
|
+
const obj = [new Uint8Array(128 * 1024 * 1024 - 9)];
|
|
362
374
|
obj[0][0] = 0x12;
|
|
363
375
|
obj[0][1] = 0x34;
|
|
364
376
|
const encoded = encode(obj);
|
|
365
|
-
expect(encoded.length).toBe(128 * 1024 * 1024 +
|
|
366
|
-
|
|
377
|
+
expect(encoded.length).toBe(128 * 1024 * 1024 + 2);
|
|
378
|
+
// (128 * 1024 * 1024 - 9) === 0x7fffff7
|
|
379
|
+
expect(toArray(encoded.slice(0, 12))).toEqual(
|
|
380
|
+
toArray('[', '[', '$', 'U', '#', 'l', 0x7, 0xff, 0xff, 0xf7, 0x12, 0x34),
|
|
381
|
+
);
|
|
367
382
|
});
|
|
368
383
|
|
|
369
|
-
test('encode huge data (128M
|
|
370
|
-
const obj = [new Uint8Array(128 * 1024 * 1024
|
|
384
|
+
test('encode huge data (128M - 8) [error]', () => {
|
|
385
|
+
const obj = [new Uint8Array(128 * 1024 * 1024 - 8)];
|
|
371
386
|
expect(() => encode(obj)).toThrow(/Buffer has exceed max size/);
|
|
372
387
|
});
|
|
373
388
|
|
|
374
389
|
test('encode huge data (256M)', () => {
|
|
375
|
-
const obj = [new Uint8Array(128 * 1024 * 1024), new Uint8Array(128 * 1024 * 1024)];
|
|
390
|
+
const obj = [new Uint8Array(128 * 1024 * 1024 - 9), new Uint8Array(128 * 1024 * 1024 - 9)];
|
|
376
391
|
obj[0][0] = 0x12;
|
|
377
392
|
obj[0][1] = 0x34;
|
|
378
393
|
obj[1][0] = 0x56;
|
|
379
394
|
obj[1][1] = 0x78;
|
|
380
395
|
const encoded = encode(obj);
|
|
381
|
-
expect(encoded.length).toBe(128 * 1024 * 1024 * 2 +
|
|
382
|
-
expect(toArray(encoded.slice(0, 12))).toEqual(
|
|
383
|
-
|
|
384
|
-
|
|
396
|
+
expect(encoded.length).toBe(128 * 1024 * 1024 * 2 + 2);
|
|
397
|
+
expect(toArray(encoded.slice(0, 12))).toEqual(
|
|
398
|
+
toArray('[', '[', '$', 'U', '#', 'l', 0x7, 0xff, 0xff, 0xf7, 0x12, 0x34),
|
|
399
|
+
);
|
|
400
|
+
expect(toArray(encoded.slice(128 * 1024 * 1024 + 1, 128 * 1024 * 1024 + 1 + 11))).toEqual(
|
|
401
|
+
toArray('[', '$', 'U', '#', 'l', 0x7, 0xff, 0xff, 0xf7, 0x56, 0x78),
|
|
385
402
|
);
|
|
386
403
|
expect(toArray(encoded.slice(-1))).toEqual(toArray(']'));
|
|
387
404
|
});
|