@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
package/src/common/encoder.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import { constants } from './constants.js';
|
|
2
|
+
import { getStringByteLength, getEncodeInto } from './string-encoder.js';
|
|
3
3
|
|
|
4
4
|
/** 编码至 ubjson */
|
|
5
5
|
export abstract class EncoderBase {
|
|
6
|
-
|
|
7
|
-
protected
|
|
6
|
+
protected readonly stringByteLength = getStringByteLength();
|
|
7
|
+
protected readonly encodeInto = getEncodeInto();
|
|
8
8
|
/** 当前写指针位置 */
|
|
9
9
|
protected length = 0;
|
|
10
10
|
/** 数据 */
|
|
@@ -20,301 +20,227 @@ export abstract class EncoderBase {
|
|
|
20
20
|
/** 写入一个对象 */
|
|
21
21
|
protected write(value: unknown): void {
|
|
22
22
|
switch (typeof value) {
|
|
23
|
-
case 'undefined':
|
|
24
|
-
return this.writeNoOp();
|
|
25
|
-
case 'boolean':
|
|
26
|
-
return this.writeBoolean(value);
|
|
27
|
-
case 'number':
|
|
28
|
-
return this.writeNumber(value);
|
|
29
23
|
case 'string':
|
|
30
|
-
if (value.length === 1 && value.charCodeAt(0) <
|
|
31
|
-
// 1 byte
|
|
32
|
-
|
|
24
|
+
if (value.length === 1 && value.charCodeAt(0) < 128) {
|
|
25
|
+
// 1 byte ascii char
|
|
26
|
+
this.ensureCapacity(2);
|
|
27
|
+
this.buffer[this.length++] = constants.CHAR;
|
|
28
|
+
this.buffer[this.length++] = value.charCodeAt(0);
|
|
29
|
+
} else {
|
|
30
|
+
this.ensureCapacity(2);
|
|
31
|
+
this.buffer[this.length++] = constants.STRING;
|
|
32
|
+
this.writeStringData(value);
|
|
33
|
+
}
|
|
34
|
+
return;
|
|
35
|
+
case 'number':
|
|
36
|
+
// eslint-disable-next-line unicorn/prefer-math-trunc
|
|
37
|
+
if ((value | 0) === value) {
|
|
38
|
+
if (value >= 0 && value <= 255) {
|
|
39
|
+
this.ensureCapacity(2);
|
|
40
|
+
this.buffer[this.length++] = constants.UINT8;
|
|
41
|
+
this.buffer[this.length++] = value;
|
|
42
|
+
} else if (value >= -128 && value <= 127) {
|
|
43
|
+
this.ensureCapacity(2);
|
|
44
|
+
this.buffer[this.length++] = constants.INT8;
|
|
45
|
+
this.buffer[this.length++] = value;
|
|
46
|
+
} else if (value >= -32768 && value <= 32767) {
|
|
47
|
+
this.ensureCapacity(3);
|
|
48
|
+
this.buffer[this.length++] = constants.INT16;
|
|
49
|
+
this.buffer[this.length++] = value >> 8;
|
|
50
|
+
this.buffer[this.length++] = value & 0xff;
|
|
51
|
+
} else {
|
|
52
|
+
this.ensureCapacity(5);
|
|
53
|
+
this.buffer[this.length++] = constants.INT32;
|
|
54
|
+
this.view.setInt32(this.length, value);
|
|
55
|
+
this.length += 4;
|
|
56
|
+
}
|
|
57
|
+
} else if (Number.isNaN(value) || Math.fround(value) === value) {
|
|
58
|
+
// 如果不会损失精度,使用 32 位浮点
|
|
59
|
+
this.ensureCapacity(5);
|
|
60
|
+
this.buffer[this.length++] = constants.FLOAT32;
|
|
61
|
+
this.view.setFloat32(this.length, value);
|
|
62
|
+
this.length += 4;
|
|
63
|
+
} else {
|
|
64
|
+
this.ensureCapacity(9);
|
|
65
|
+
this.buffer[this.length++] = constants.FLOAT64;
|
|
66
|
+
this.view.setFloat64(this.length, value);
|
|
67
|
+
this.length += 8;
|
|
33
68
|
}
|
|
34
|
-
return
|
|
69
|
+
return;
|
|
35
70
|
case 'object': {
|
|
36
71
|
if (value === null) {
|
|
37
|
-
|
|
72
|
+
this.ensureCapacity(1);
|
|
73
|
+
this.buffer[this.length++] = constants.NULL;
|
|
74
|
+
} else if (Array.isArray(value)) {
|
|
75
|
+
this.ensureCapacity(1);
|
|
76
|
+
this.buffer[this.length++] = constants.ARRAY;
|
|
77
|
+
for (const v of value) {
|
|
78
|
+
// 在数组中 undefined 也被视作 null 进行序列化
|
|
79
|
+
if (v == null) {
|
|
80
|
+
this.ensureCapacity(1);
|
|
81
|
+
this.buffer[this.length++] = constants.NULL;
|
|
82
|
+
} else {
|
|
83
|
+
this.write(v);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
this.ensureCapacity(1);
|
|
87
|
+
this.buffer[this.length++] = constants.ARRAY_END;
|
|
88
|
+
} else if (!ArrayBuffer.isView(value)) {
|
|
89
|
+
this.ensureCapacity(1);
|
|
90
|
+
this.buffer[this.length++] = constants.OBJECT;
|
|
91
|
+
// 生成稳定的结果以便 hash 计算
|
|
92
|
+
for (const key of Object.keys(value).sort()) {
|
|
93
|
+
const element = (value as Record<string, unknown>)[key];
|
|
94
|
+
if (element === undefined) continue;
|
|
95
|
+
this.writeStringData(key);
|
|
96
|
+
this.write(element);
|
|
97
|
+
}
|
|
98
|
+
this.ensureCapacity(1);
|
|
99
|
+
this.buffer[this.length++] = constants.OBJECT_END;
|
|
100
|
+
} else {
|
|
101
|
+
// ARRAY(1) + TYPE_MARKER(1) + TYPE(1) + COUNT_MARKER(1) + COUNT(MAX5) + DATA
|
|
102
|
+
this.ensureCapacity(9 + value.byteLength);
|
|
103
|
+
this.buffer[this.length++] = constants.ARRAY;
|
|
104
|
+
this.buffer[this.length++] = constants.TYPE_MARKER;
|
|
105
|
+
if (value instanceof Uint8Array || value instanceof Int8Array) {
|
|
106
|
+
// fast path for typed arrays with `BYTES_PER_ELEMENT` of 1
|
|
107
|
+
this.buffer[this.length++] = value instanceof Uint8Array ? constants.UINT8 : constants.INT8;
|
|
108
|
+
this.buffer[this.length++] = constants.COUNT_MARKER;
|
|
109
|
+
this.setLength(value.length);
|
|
110
|
+
this.buffer.set(value, this.length);
|
|
111
|
+
this.length += value.byteLength;
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
/** 用于写入的 setter */
|
|
115
|
+
let setValue;
|
|
116
|
+
if (value instanceof Int16Array) {
|
|
117
|
+
this.buffer[this.length++] = constants.INT16;
|
|
118
|
+
/* eslint-disable-next-line @typescript-eslint/unbound-method */
|
|
119
|
+
setValue = this.view.setInt16;
|
|
120
|
+
} else if (value instanceof Int32Array) {
|
|
121
|
+
this.buffer[this.length++] = constants.INT32;
|
|
122
|
+
/* eslint-disable-next-line @typescript-eslint/unbound-method */
|
|
123
|
+
setValue = this.view.setInt32;
|
|
124
|
+
} else if (value instanceof Float32Array) {
|
|
125
|
+
this.buffer[this.length++] = constants.FLOAT32;
|
|
126
|
+
/* eslint-disable-next-line @typescript-eslint/unbound-method */
|
|
127
|
+
setValue = this.view.setFloat32;
|
|
128
|
+
} else if (value instanceof Float64Array) {
|
|
129
|
+
this.buffer[this.length++] = constants.FLOAT64;
|
|
130
|
+
/* eslint-disable-next-line @typescript-eslint/unbound-method */
|
|
131
|
+
setValue = this.view.setFloat64;
|
|
132
|
+
} else {
|
|
133
|
+
throw new TypeError(`Unsupported typed array type ${Object.prototype.toString.call(value)}`);
|
|
134
|
+
}
|
|
135
|
+
this.buffer[this.length++] = constants.COUNT_MARKER;
|
|
136
|
+
this.setLength(value.length);
|
|
137
|
+
for (const v of value) {
|
|
138
|
+
setValue.call(this.view, this.length, v);
|
|
139
|
+
this.length += value.BYTES_PER_ELEMENT;
|
|
140
|
+
}
|
|
38
141
|
}
|
|
39
|
-
|
|
40
|
-
return this.writeArray(value);
|
|
41
|
-
}
|
|
42
|
-
if (ArrayBuffer.isView(value)) {
|
|
43
|
-
return this.writeTypedArray(value);
|
|
44
|
-
}
|
|
45
|
-
return this.writeObject(value as Record<string, unknown>);
|
|
142
|
+
return;
|
|
46
143
|
}
|
|
144
|
+
case 'undefined':
|
|
145
|
+
this.ensureCapacity(1);
|
|
146
|
+
this.buffer[this.length++] = constants.NO_OP;
|
|
147
|
+
return;
|
|
148
|
+
case 'boolean':
|
|
149
|
+
this.ensureCapacity(1);
|
|
150
|
+
this.buffer[this.length++] = value ? constants.TRUE : constants.FALSE;
|
|
151
|
+
return;
|
|
47
152
|
default:
|
|
48
153
|
throw new Error(`Unsupported type ${Object.prototype.toString.call(value)}`);
|
|
49
154
|
}
|
|
50
155
|
}
|
|
51
|
-
/** 写入 marker */
|
|
52
|
-
protected writeMarker(marker: number): void {
|
|
53
|
-
this.ensureCapacity(1);
|
|
54
|
-
this.view.setUint8(this.length, marker);
|
|
55
|
-
this.length += 1;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/** 写入 marker */
|
|
59
|
-
protected writeNull(): void {
|
|
60
|
-
this.writeMarker(constants.NULL);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/** writeNoOp */
|
|
64
|
-
protected writeNoOp(): void {
|
|
65
|
-
this.writeMarker(constants.NO_OP);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/** writeBoolean */
|
|
69
|
-
protected writeBoolean(value: boolean): void {
|
|
70
|
-
this.writeMarker(value ? constants.TRUE : constants.FALSE);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/** writeInt8 */
|
|
74
|
-
protected writeInt8(value: number): void {
|
|
75
|
-
this.writeMarker(constants.INT8);
|
|
76
|
-
this.writeInt8Data(value);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/** writeInt8Data */
|
|
80
|
-
protected writeInt8Data(value: number): void {
|
|
81
|
-
this.ensureCapacity(1);
|
|
82
|
-
this.view.setInt8(this.length, value);
|
|
83
|
-
this.length += 1;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/** writeUint8 */
|
|
87
|
-
protected writeUint8(value: number): void {
|
|
88
|
-
this.writeMarker(constants.UINT8);
|
|
89
|
-
this.writeUint8Data(value);
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/** writeUint8Data */
|
|
93
|
-
protected writeUint8Data(value: number): void {
|
|
94
|
-
this.ensureCapacity(1);
|
|
95
|
-
this.view.setUint8(this.length, value);
|
|
96
|
-
this.length += 1;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/** writeInt16 */
|
|
100
|
-
protected writeInt16(value: number): void {
|
|
101
|
-
this.writeMarker(constants.INT16);
|
|
102
|
-
this.writeInt16Data(value);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/** writeInt16Data */
|
|
106
|
-
protected writeInt16Data(value: number): void {
|
|
107
|
-
this.ensureCapacity(2);
|
|
108
|
-
this.view.setInt16(this.length, value);
|
|
109
|
-
this.length += 2;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/** writeInt32 */
|
|
113
|
-
protected writeInt32(value: number): void {
|
|
114
|
-
this.writeMarker(constants.INT32);
|
|
115
|
-
this.writeInt32Data(value);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/** writeInt32Data */
|
|
119
|
-
protected writeInt32Data(value: number): void {
|
|
120
|
-
this.ensureCapacity(4);
|
|
121
|
-
this.view.setInt32(this.length, value);
|
|
122
|
-
this.length += 4;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/** writeFloat32 */
|
|
126
|
-
protected writeFloat32(value: number): void {
|
|
127
|
-
this.writeMarker(constants.FLOAT32);
|
|
128
|
-
this.writeFloat32Data(value);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/** writeFloat32Data */
|
|
132
|
-
protected writeFloat32Data(value: number): void {
|
|
133
|
-
this.ensureCapacity(4);
|
|
134
|
-
this.view.setFloat32(this.length, value);
|
|
135
|
-
this.length += 4;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/** writeFloat64 */
|
|
139
|
-
protected writeFloat64(value: number): void {
|
|
140
|
-
this.writeMarker(constants.FLOAT64);
|
|
141
|
-
this.writeFloat64Data(value);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/** writeFloat64Data */
|
|
145
|
-
protected writeFloat64Data(value: number): void {
|
|
146
|
-
this.ensureCapacity(8);
|
|
147
|
-
this.view.setFloat64(this.length, value);
|
|
148
|
-
this.length += 8;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/** writeChar */
|
|
152
|
-
protected writeChar(value: string): void {
|
|
153
|
-
this.writeMarker(constants.CHAR);
|
|
154
|
-
this.writeCharData(value);
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/** writeCharData */
|
|
158
|
-
protected writeCharData(value: string): void {
|
|
159
|
-
this.ensureCapacity(1);
|
|
160
|
-
this.view.setUint8(this.length, value.charCodeAt(0));
|
|
161
|
-
this.length += 1;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/** writeString */
|
|
165
|
-
protected writeString(value: string): void {
|
|
166
|
-
this.writeMarker(constants.STRING);
|
|
167
|
-
this.writeStringData(value);
|
|
168
|
-
}
|
|
169
156
|
|
|
170
157
|
/** writeStringData */
|
|
171
158
|
protected writeStringData(value: string): void {
|
|
159
|
+
const strLength = value.length;
|
|
160
|
+
const maxUsage =
|
|
161
|
+
strLength < 65536
|
|
162
|
+
? // 对于短字符串,直接计算最大使用空间
|
|
163
|
+
strLength * 3
|
|
164
|
+
: this.stringByteLength(value);
|
|
165
|
+
// 一次性分配 setLength 和 encodeInto 的空间,避免无法回溯
|
|
166
|
+
// 额外分配 3 字节,避免 encodeInto 无法写入最后一个字符
|
|
167
|
+
this.ensureCapacity(maxUsage + 5 + 3);
|
|
168
|
+
|
|
169
|
+
// 预估头部大小
|
|
170
|
+
const headerSize = strLength < 128 ? 2 : strLength < 32768 ? 3 : 5;
|
|
171
|
+
const headerPos = this.length;
|
|
172
|
+
let bufLength;
|
|
172
173
|
// 优化小字符串
|
|
173
|
-
if (
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
this.
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
174
|
+
if (strLength < 0x40 || !this.encodeInto) {
|
|
175
|
+
let c1, c2;
|
|
176
|
+
let strPosition = headerPos + headerSize;
|
|
177
|
+
const target = this.buffer;
|
|
178
|
+
for (let i = 0; i < strLength; i++) {
|
|
179
|
+
c1 = value.charCodeAt(i);
|
|
180
|
+
if (c1 < 0x80) {
|
|
181
|
+
target[strPosition++] = c1;
|
|
182
|
+
} else if (c1 < 0x800) {
|
|
183
|
+
target[strPosition++] = (c1 >> 6) | 0xc0;
|
|
184
|
+
target[strPosition++] = (c1 & 0x3f) | 0x80;
|
|
185
|
+
} else if ((c1 & 0xfc00) === 0xd800 && ((c2 = value.charCodeAt(i + 1)) & 0xfc00) === 0xdc00) {
|
|
186
|
+
c1 = 0x1_0000 + ((c1 & 0x03ff) << 10) + (c2 & 0x03ff);
|
|
187
|
+
i++;
|
|
188
|
+
target[strPosition++] = (c1 >> 18) | 0xf0;
|
|
189
|
+
target[strPosition++] = ((c1 >> 12) & 0x3f) | 0x80;
|
|
190
|
+
target[strPosition++] = ((c1 >> 6) & 0x3f) | 0x80;
|
|
191
|
+
target[strPosition++] = (c1 & 0x3f) | 0x80;
|
|
192
|
+
} else {
|
|
193
|
+
target[strPosition++] = (c1 >> 12) | 0xe0;
|
|
194
|
+
target[strPosition++] = ((c1 >> 6) & 0x3f) | 0x80;
|
|
195
|
+
target[strPosition++] = (c1 & 0x3f) | 0x80;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
bufLength = strPosition - headerPos - headerSize;
|
|
185
199
|
} else {
|
|
186
|
-
|
|
200
|
+
bufLength = this.encodeInto(value, this.buffer, headerPos + headerSize);
|
|
187
201
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
202
|
+
if (bufLength < 128) {
|
|
203
|
+
this.buffer[this.length++] = constants.INT8;
|
|
204
|
+
this.buffer[this.length++] = bufLength;
|
|
205
|
+
} else if (bufLength < 32768) {
|
|
206
|
+
if (headerSize < 3) {
|
|
207
|
+
this.buffer.copyWithin(headerPos + 3, headerPos + headerSize, headerPos + headerSize + bufLength);
|
|
208
|
+
}
|
|
209
|
+
this.buffer[this.length++] = constants.INT16;
|
|
210
|
+
this.buffer[this.length++] = bufLength >> 8;
|
|
211
|
+
this.buffer[this.length++] = bufLength & 0xff;
|
|
212
|
+
} else {
|
|
213
|
+
if (headerSize < 5) {
|
|
214
|
+
this.buffer.copyWithin(headerPos + 5, headerPos + headerSize, headerPos + headerSize + bufLength);
|
|
215
|
+
}
|
|
216
|
+
this.buffer[this.length++] = constants.INT32;
|
|
217
|
+
this.view.setInt32(this.length, bufLength);
|
|
218
|
+
this.length += 4;
|
|
199
219
|
}
|
|
200
|
-
|
|
201
|
-
this.length = currentPos;
|
|
202
|
-
lengthWriter.call(this, written!);
|
|
203
|
-
// 移动指针到写入末尾
|
|
204
|
-
this.length += written!;
|
|
220
|
+
this.length += bufLength;
|
|
205
221
|
}
|
|
206
222
|
|
|
207
223
|
/**
|
|
208
|
-
*
|
|
209
|
-
* @throws 无法在 int32 范围内表示
|
|
224
|
+
* 写入整形数字,选取合适的大小,需提前分配空间
|
|
210
225
|
*/
|
|
211
|
-
protected
|
|
226
|
+
protected setLength(value: number): number {
|
|
227
|
+
// eslint-disable-next-line unicorn/prefer-math-trunc
|
|
212
228
|
value = value | 0;
|
|
213
|
-
if (value
|
|
214
|
-
this.
|
|
215
|
-
|
|
216
|
-
return
|
|
229
|
+
if ((value & 0x7f) === value) {
|
|
230
|
+
this.buffer[this.length++] = constants.INT8;
|
|
231
|
+
this.buffer[this.length++] = value;
|
|
232
|
+
return 1;
|
|
217
233
|
}
|
|
218
234
|
// 不使用 uint8 以保持兼容
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
if (value >= -32768 && value <= 32767) {
|
|
225
|
-
this.writeInt16(value);
|
|
226
|
-
/* eslint-disable-next-line @typescript-eslint/unbound-method */
|
|
227
|
-
return this.writeInt16;
|
|
228
|
-
}
|
|
229
|
-
if (value >= -2_147_483_648 && value <= 2_147_483_647) {
|
|
230
|
-
this.writeInt32(value);
|
|
231
|
-
/* eslint-disable-next-line @typescript-eslint/unbound-method */
|
|
232
|
-
return this.writeInt32;
|
|
233
|
-
}
|
|
234
|
-
/* c8 ignore next 2 */
|
|
235
|
-
throw new Error(`Unsupported int value ${value}: out of range`);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
/** 写入数字,选取合适的大小 */
|
|
239
|
-
protected writeNumber(value: number): void {
|
|
240
|
-
if (Number.isInteger(value) && value >= -2_147_483_648 && value <= 2_147_483_647) {
|
|
241
|
-
if (value >= 0 && value <= 255) return this.writeUint8(value);
|
|
242
|
-
this.writeInt(value);
|
|
243
|
-
return;
|
|
244
|
-
}
|
|
245
|
-
if (!(Number.isNaN(value) || Math.fround(value) === value)) {
|
|
246
|
-
return this.writeFloat64(value);
|
|
247
|
-
}
|
|
248
|
-
// 如果不会损失精度,则使用 32 位浮点
|
|
249
|
-
return this.writeFloat32(value);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
/** writeObject */
|
|
253
|
-
protected writeObject(value: Record<string, unknown>): void {
|
|
254
|
-
this.writeMarker(constants.OBJECT);
|
|
255
|
-
// 生成稳定的结果以便 hash 计算
|
|
256
|
-
for (const key of Object.keys(value).sort()) {
|
|
257
|
-
const element = value[key];
|
|
258
|
-
if (element === undefined) continue;
|
|
259
|
-
this.writeStringData(key);
|
|
260
|
-
this.write(element);
|
|
261
|
-
}
|
|
262
|
-
this.writeMarker(constants.OBJECT_END);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
/** writeArray */
|
|
266
|
-
protected writeArray(value: unknown[]): void {
|
|
267
|
-
this.writeMarker(constants.ARRAY);
|
|
268
|
-
for (const v of value) {
|
|
269
|
-
// 在数组中 undefined 也被视作 null 进行序列化
|
|
270
|
-
if (v == null) this.writeNull();
|
|
271
|
-
else this.write(v);
|
|
272
|
-
}
|
|
273
|
-
this.writeMarker(constants.ARRAY_END);
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
/** writeArray */
|
|
277
|
-
protected writeTypedArray(value: ArrayBufferView): void {
|
|
278
|
-
this.writeMarker(constants.ARRAY);
|
|
279
|
-
this.writeMarker(constants.TYPE_MARKER);
|
|
280
|
-
if (value instanceof Uint8Array || value instanceof Int8Array) {
|
|
281
|
-
// fast path for typed arrays with `BYTES_PER_ELEMENT` of 1
|
|
282
|
-
this.writeMarker(value instanceof Uint8Array ? constants.UINT8 : constants.INT8);
|
|
283
|
-
this.writeMarker(constants.COUNT_MARKER);
|
|
284
|
-
this.writeInt(value.length);
|
|
285
|
-
this.ensureCapacity(value.byteLength);
|
|
286
|
-
this.buffer.set(value, this.length);
|
|
287
|
-
this.length += value.byteLength;
|
|
288
|
-
return;
|
|
289
|
-
}
|
|
290
|
-
/** 用于写入的 setter */
|
|
291
|
-
let setValue;
|
|
292
|
-
if (value instanceof Int16Array) {
|
|
293
|
-
this.writeMarker(constants.INT16);
|
|
294
|
-
/* eslint-disable-next-line @typescript-eslint/unbound-method */
|
|
295
|
-
setValue = this.view.setInt16;
|
|
296
|
-
} else if (value instanceof Int32Array) {
|
|
297
|
-
this.writeMarker(constants.INT32);
|
|
298
|
-
/* eslint-disable-next-line @typescript-eslint/unbound-method */
|
|
299
|
-
setValue = this.view.setInt32;
|
|
300
|
-
} else if (value instanceof Float32Array) {
|
|
301
|
-
this.writeMarker(constants.FLOAT32);
|
|
302
|
-
/* eslint-disable-next-line @typescript-eslint/unbound-method */
|
|
303
|
-
setValue = this.view.setFloat32;
|
|
304
|
-
} else if (value instanceof Float64Array) {
|
|
305
|
-
this.writeMarker(constants.FLOAT64);
|
|
306
|
-
/* eslint-disable-next-line @typescript-eslint/unbound-method */
|
|
307
|
-
setValue = this.view.setFloat64;
|
|
308
|
-
} else {
|
|
309
|
-
throw new TypeError(`Unsupported typed array type ${Object.prototype.toString.call(value)}`);
|
|
310
|
-
}
|
|
311
|
-
this.writeMarker(constants.COUNT_MARKER);
|
|
312
|
-
this.writeInt(value.length);
|
|
313
|
-
this.ensureCapacity(value.byteLength);
|
|
314
|
-
// 不要在前面 bind,this.ensureCapacity 有可能导致 this.view 指向新的对象
|
|
315
|
-
for (const v of value) {
|
|
316
|
-
setValue.call(this.view, this.length, v);
|
|
317
|
-
this.length += value.BYTES_PER_ELEMENT;
|
|
235
|
+
if ((value & 0x7fff) === value) {
|
|
236
|
+
this.buffer[this.length++] = constants.INT16;
|
|
237
|
+
this.buffer[this.length++] = value >> 8;
|
|
238
|
+
this.buffer[this.length++] = value & 0xff;
|
|
239
|
+
return 2;
|
|
318
240
|
}
|
|
241
|
+
this.buffer[this.length++] = constants.INT32;
|
|
242
|
+
this.view.setInt32(this.length, value);
|
|
243
|
+
this.length += 4;
|
|
244
|
+
return 4;
|
|
319
245
|
}
|
|
320
246
|
}
|