fix(core): 移除fflate依赖,修复TextEncoder兼容性问题 (#217)
* fix(core): 移除fflate依赖,修复TextEncoder兼容性问题 * fix(core): 移除fflate依赖,修复TextEncoder兼容性问题
This commit is contained in:
@@ -78,7 +78,6 @@
|
|||||||
"directory": "packages/core"
|
"directory": "packages/core"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fflate": "^0.8.2",
|
|
||||||
"tslib": "^2.8.1"
|
"tslib": "^2.8.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,104 @@
|
|||||||
import { strToU8, strFromU8, zlibSync, unzlibSync } from 'fflate';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 二进制序列化器
|
* 二进制序列化器
|
||||||
* 使用zlib压缩JSON数据
|
* 将对象转换为UTF8字节数组
|
||||||
*/
|
*/
|
||||||
export class BinarySerializer {
|
export class BinarySerializer {
|
||||||
/**
|
/**
|
||||||
* 将对象编码为压缩的二进制数据
|
* 将字符串编码为UTF8字节数组
|
||||||
*/
|
*/
|
||||||
public static encode(value: any): Uint8Array {
|
private static stringToUtf8(str: string): Uint8Array {
|
||||||
const jsonString = JSON.stringify(value);
|
const len = str.length;
|
||||||
const utf8Bytes = strToU8(jsonString);
|
let pos = 0;
|
||||||
return zlibSync(utf8Bytes);
|
const bytes: number[] = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < len; i++) {
|
||||||
|
let code = str.charCodeAt(i);
|
||||||
|
|
||||||
|
if (code >= 0xD800 && code <= 0xDBFF && i + 1 < len) {
|
||||||
|
const high = code;
|
||||||
|
const low = str.charCodeAt(i + 1);
|
||||||
|
if (low >= 0xDC00 && low <= 0xDFFF) {
|
||||||
|
code = 0x10000 + ((high - 0xD800) << 10) + (low - 0xDC00);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (code < 0x80) {
|
||||||
|
bytes[pos++] = code;
|
||||||
|
} else if (code < 0x800) {
|
||||||
|
bytes[pos++] = 0xC0 | (code >> 6);
|
||||||
|
bytes[pos++] = 0x80 | (code & 0x3F);
|
||||||
|
} else if (code < 0x10000) {
|
||||||
|
bytes[pos++] = 0xE0 | (code >> 12);
|
||||||
|
bytes[pos++] = 0x80 | ((code >> 6) & 0x3F);
|
||||||
|
bytes[pos++] = 0x80 | (code & 0x3F);
|
||||||
|
} else {
|
||||||
|
bytes[pos++] = 0xF0 | (code >> 18);
|
||||||
|
bytes[pos++] = 0x80 | ((code >> 12) & 0x3F);
|
||||||
|
bytes[pos++] = 0x80 | ((code >> 6) & 0x3F);
|
||||||
|
bytes[pos++] = 0x80 | (code & 0x3F);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Uint8Array(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将压缩的二进制数据解码为对象
|
* 将UTF8字节数组解码为字符串
|
||||||
|
*/
|
||||||
|
private static utf8ToString(bytes: Uint8Array): string {
|
||||||
|
const len = bytes.length;
|
||||||
|
let str = '';
|
||||||
|
let i = 0;
|
||||||
|
|
||||||
|
while (i < len) {
|
||||||
|
const byte1 = bytes[i++];
|
||||||
|
if (byte1 === undefined) break;
|
||||||
|
|
||||||
|
if (byte1 < 0x80) {
|
||||||
|
str += String.fromCharCode(byte1);
|
||||||
|
} else if ((byte1 & 0xE0) === 0xC0) {
|
||||||
|
const byte2 = bytes[i++];
|
||||||
|
if (byte2 === undefined) break;
|
||||||
|
str += String.fromCharCode(((byte1 & 0x1F) << 6) | (byte2 & 0x3F));
|
||||||
|
} else if ((byte1 & 0xF0) === 0xE0) {
|
||||||
|
const byte2 = bytes[i++];
|
||||||
|
const byte3 = bytes[i++];
|
||||||
|
if (byte2 === undefined || byte3 === undefined) break;
|
||||||
|
str += String.fromCharCode(
|
||||||
|
((byte1 & 0x0F) << 12) | ((byte2 & 0x3F) << 6) | (byte3 & 0x3F)
|
||||||
|
);
|
||||||
|
} else if ((byte1 & 0xF8) === 0xF0) {
|
||||||
|
const byte2 = bytes[i++];
|
||||||
|
const byte3 = bytes[i++];
|
||||||
|
const byte4 = bytes[i++];
|
||||||
|
if (byte2 === undefined || byte3 === undefined || byte4 === undefined) break;
|
||||||
|
let code = ((byte1 & 0x07) << 18) | ((byte2 & 0x3F) << 12) |
|
||||||
|
((byte3 & 0x3F) << 6) | (byte4 & 0x3F);
|
||||||
|
code -= 0x10000;
|
||||||
|
str += String.fromCharCode(
|
||||||
|
0xD800 + (code >> 10),
|
||||||
|
0xDC00 + (code & 0x3FF)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将对象编码为二进制数据
|
||||||
|
*/
|
||||||
|
public static encode(value: any): Uint8Array {
|
||||||
|
const jsonString = JSON.stringify(value);
|
||||||
|
return this.stringToUtf8(jsonString);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将二进制数据解码为对象
|
||||||
*/
|
*/
|
||||||
public static decode(bytes: Uint8Array): any {
|
public static decode(bytes: Uint8Array): any {
|
||||||
const decompressed = unzlibSync(bytes);
|
const jsonString = this.utf8ToString(bytes);
|
||||||
const jsonString = strFromU8(decompressed);
|
|
||||||
return JSON.parse(jsonString);
|
return JSON.parse(jsonString);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -487,7 +487,7 @@ describe('Incremental Serialization System', () => {
|
|||||||
expect(prettyJson).toContain(' ');
|
expect(prettyJson).toContain(' ');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('二进制格式应该比JSON格式更小', () => {
|
it('二进制格式应该可以正常序列化', () => {
|
||||||
const entities = [];
|
const entities = [];
|
||||||
for (let i = 0; i < 50; i++) {
|
for (let i = 0; i < 50; i++) {
|
||||||
const entity = scene.createEntity(`Entity_${i}`);
|
const entity = scene.createEntity(`Entity_${i}`);
|
||||||
@@ -506,13 +506,10 @@ describe('Incremental Serialization System', () => {
|
|||||||
|
|
||||||
const incremental = scene.serializeIncremental();
|
const incremental = scene.serializeIncremental();
|
||||||
|
|
||||||
const jsonData = IncrementalSerializer.serializeIncremental(incremental, { format: 'json' });
|
|
||||||
const binaryData = IncrementalSerializer.serializeIncremental(incremental, { format: 'binary' });
|
const binaryData = IncrementalSerializer.serializeIncremental(incremental, { format: 'binary' });
|
||||||
|
|
||||||
const jsonSize = Buffer.byteLength(jsonData as string);
|
expect(binaryData).toBeInstanceOf(Uint8Array);
|
||||||
const binarySize = (binaryData as Buffer).length;
|
expect(binaryData.length).toBeGreaterThan(0);
|
||||||
|
|
||||||
expect(binarySize).toBeLessThan(jsonSize);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('二进制和JSON格式应该包含相同的数据', () => {
|
it('二进制和JSON格式应该包含相同的数据', () => {
|
||||||
|
|||||||
@@ -559,15 +559,7 @@ describe('ECS Serialization System', () => {
|
|||||||
|
|
||||||
// 验证是Uint8Array类型
|
// 验证是Uint8Array类型
|
||||||
expect(binaryData instanceof Uint8Array).toBe(true);
|
expect(binaryData instanceof Uint8Array).toBe(true);
|
||||||
|
expect((binaryData as Uint8Array).length).toBeGreaterThan(0);
|
||||||
// JSON序列化对比
|
|
||||||
const jsonData = scene1.serialize({ format: 'json', pretty: false });
|
|
||||||
|
|
||||||
// 二进制应该更小
|
|
||||||
const binarySize = (binaryData as Uint8Array).length;
|
|
||||||
const jsonSize = (jsonData as string).length;
|
|
||||||
console.log(`Binary size: ${binarySize} bytes, JSON size: ${jsonSize} bytes`);
|
|
||||||
expect(binarySize).toBeLessThan(jsonSize);
|
|
||||||
|
|
||||||
// 新场景反序列化二进制数据
|
// 新场景反序列化二进制数据
|
||||||
const scene2 = new Scene({ name: 'LoadTest' });
|
const scene2 = new Scene({ name: 'LoadTest' });
|
||||||
|
|||||||
139
packages/core/tests/Utils/BinarySerializer.test.ts
Normal file
139
packages/core/tests/Utils/BinarySerializer.test.ts
Normal file
@@ -0,0 +1,139 @@
|
|||||||
|
import { BinarySerializer } from '../../src/Utils/BinarySerializer';
|
||||||
|
|
||||||
|
describe('BinarySerializer', () => {
|
||||||
|
describe('encode and decode', () => {
|
||||||
|
it('应该正确编码和解码简单对象', () => {
|
||||||
|
const data = { name: 'test', value: 123 };
|
||||||
|
const encoded = BinarySerializer.encode(data);
|
||||||
|
const decoded = BinarySerializer.decode(encoded);
|
||||||
|
|
||||||
|
expect(decoded).toEqual(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('应该正确编码和解码包含中文的对象', () => {
|
||||||
|
const data = {
|
||||||
|
name: '测试',
|
||||||
|
description: '这是一个包含中文的测试对象',
|
||||||
|
value: 456
|
||||||
|
};
|
||||||
|
const encoded = BinarySerializer.encode(data);
|
||||||
|
const decoded = BinarySerializer.decode(encoded);
|
||||||
|
|
||||||
|
expect(decoded).toEqual(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('应该正确编码和解码数组', () => {
|
||||||
|
const data = {
|
||||||
|
items: [1, 2, 3, 4, 5],
|
||||||
|
names: ['Alice', 'Bob', 'Charlie']
|
||||||
|
};
|
||||||
|
const encoded = BinarySerializer.encode(data);
|
||||||
|
const decoded = BinarySerializer.decode(encoded);
|
||||||
|
|
||||||
|
expect(decoded).toEqual(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('应该正确编码和解码嵌套对象', () => {
|
||||||
|
const data = {
|
||||||
|
user: {
|
||||||
|
name: 'John',
|
||||||
|
age: 30,
|
||||||
|
address: {
|
||||||
|
city: 'Beijing',
|
||||||
|
street: 'Main St'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
scores: [90, 85, 95]
|
||||||
|
};
|
||||||
|
const encoded = BinarySerializer.encode(data);
|
||||||
|
const decoded = BinarySerializer.decode(encoded);
|
||||||
|
|
||||||
|
expect(decoded).toEqual(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('应该正确编码和解码包含特殊字符的字符串', () => {
|
||||||
|
const data = {
|
||||||
|
text: 'Hello\nWorld\t!@#$%^&*()',
|
||||||
|
emoji: '😀🎉🚀',
|
||||||
|
special: 'a\u0000b'
|
||||||
|
};
|
||||||
|
const encoded = BinarySerializer.encode(data);
|
||||||
|
const decoded = BinarySerializer.decode(encoded);
|
||||||
|
|
||||||
|
expect(decoded).toEqual(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('应该正确编码和解码空对象', () => {
|
||||||
|
const data = {};
|
||||||
|
const encoded = BinarySerializer.encode(data);
|
||||||
|
const decoded = BinarySerializer.decode(encoded);
|
||||||
|
|
||||||
|
expect(decoded).toEqual(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('应该正确编码和解码空数组', () => {
|
||||||
|
const data: any[] = [];
|
||||||
|
const encoded = BinarySerializer.encode(data);
|
||||||
|
const decoded = BinarySerializer.decode(encoded);
|
||||||
|
|
||||||
|
expect(decoded).toEqual(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('应该正确编码和解码包含null和undefined的对象', () => {
|
||||||
|
const data = {
|
||||||
|
nullValue: null,
|
||||||
|
undefinedValue: undefined,
|
||||||
|
normalValue: 'test'
|
||||||
|
};
|
||||||
|
const encoded = BinarySerializer.encode(data);
|
||||||
|
const decoded = BinarySerializer.decode(encoded);
|
||||||
|
|
||||||
|
expect(decoded.nullValue).toBeNull();
|
||||||
|
expect(decoded.undefinedValue).toBeUndefined();
|
||||||
|
expect(decoded.normalValue).toBe('test');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('应该正确编码和解码布尔值', () => {
|
||||||
|
const data = {
|
||||||
|
isTrue: true,
|
||||||
|
isFalse: false
|
||||||
|
};
|
||||||
|
const encoded = BinarySerializer.encode(data);
|
||||||
|
const decoded = BinarySerializer.decode(encoded);
|
||||||
|
|
||||||
|
expect(decoded).toEqual(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('应该正确编码和解码数字类型', () => {
|
||||||
|
const data = {
|
||||||
|
integer: 42,
|
||||||
|
float: 3.14159,
|
||||||
|
negative: -100,
|
||||||
|
zero: 0,
|
||||||
|
large: 1234567890
|
||||||
|
};
|
||||||
|
const encoded = BinarySerializer.encode(data);
|
||||||
|
const decoded = BinarySerializer.decode(encoded);
|
||||||
|
|
||||||
|
expect(decoded).toEqual(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('应该返回Uint8Array类型', () => {
|
||||||
|
const data = { test: 'value' };
|
||||||
|
const encoded = BinarySerializer.encode(data);
|
||||||
|
|
||||||
|
expect(encoded).toBeInstanceOf(Uint8Array);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('应该正确处理包含emoji的复杂字符串', () => {
|
||||||
|
const data = {
|
||||||
|
text: '🌟 测试 Test 👍',
|
||||||
|
emoji: '🎮🎯🎲'
|
||||||
|
};
|
||||||
|
const encoded = BinarySerializer.encode(data);
|
||||||
|
const decoded = BinarySerializer.decode(encoded);
|
||||||
|
|
||||||
|
expect(decoded).toEqual(data);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user