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