diff --git a/packages/core/package.json b/packages/core/package.json index 8c1c71cd..b12338e5 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -78,7 +78,6 @@ "directory": "packages/core" }, "dependencies": { - "fflate": "^0.8.2", "tslib": "^2.8.1" } } diff --git a/packages/core/src/Utils/BinarySerializer.ts b/packages/core/src/Utils/BinarySerializer.ts index 63884004..0cb01415 100644 --- a/packages/core/src/Utils/BinarySerializer.ts +++ b/packages/core/src/Utils/BinarySerializer.ts @@ -1,25 +1,104 @@ -import { strToU8, strFromU8, zlibSync, unzlibSync } from 'fflate'; - /** * 二进制序列化器 - * 使用zlib压缩JSON数据 + * 将对象转换为UTF8字节数组 */ export class BinarySerializer { /** - * 将对象编码为压缩的二进制数据 + * 将字符串编码为UTF8字节数组 */ - public static encode(value: any): Uint8Array { - const jsonString = JSON.stringify(value); - const utf8Bytes = strToU8(jsonString); - return zlibSync(utf8Bytes); + private static stringToUtf8(str: string): Uint8Array { + const len = str.length; + let pos = 0; + 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 { - const decompressed = unzlibSync(bytes); - const jsonString = strFromU8(decompressed); + const jsonString = this.utf8ToString(bytes); return JSON.parse(jsonString); } } diff --git a/packages/core/tests/ECS/Serialization/IncrementalSerialization.test.ts b/packages/core/tests/ECS/Serialization/IncrementalSerialization.test.ts index 8f4ba701..3160d010 100644 --- a/packages/core/tests/ECS/Serialization/IncrementalSerialization.test.ts +++ b/packages/core/tests/ECS/Serialization/IncrementalSerialization.test.ts @@ -487,7 +487,7 @@ describe('Incremental Serialization System', () => { expect(prettyJson).toContain(' '); }); - it('二进制格式应该比JSON格式更小', () => { + it('二进制格式应该可以正常序列化', () => { const entities = []; for (let i = 0; i < 50; i++) { const entity = scene.createEntity(`Entity_${i}`); @@ -506,13 +506,10 @@ describe('Incremental Serialization System', () => { const incremental = scene.serializeIncremental(); - const jsonData = IncrementalSerializer.serializeIncremental(incremental, { format: 'json' }); const binaryData = IncrementalSerializer.serializeIncremental(incremental, { format: 'binary' }); - const jsonSize = Buffer.byteLength(jsonData as string); - const binarySize = (binaryData as Buffer).length; - - expect(binarySize).toBeLessThan(jsonSize); + expect(binaryData).toBeInstanceOf(Uint8Array); + expect(binaryData.length).toBeGreaterThan(0); }); it('二进制和JSON格式应该包含相同的数据', () => { diff --git a/packages/core/tests/ECS/Serialization/Serialization.test.ts b/packages/core/tests/ECS/Serialization/Serialization.test.ts index e4d829d7..acedd0ac 100644 --- a/packages/core/tests/ECS/Serialization/Serialization.test.ts +++ b/packages/core/tests/ECS/Serialization/Serialization.test.ts @@ -559,15 +559,7 @@ describe('ECS Serialization System', () => { // 验证是Uint8Array类型 expect(binaryData instanceof Uint8Array).toBe(true); - - // 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); + expect((binaryData as Uint8Array).length).toBeGreaterThan(0); // 新场景反序列化二进制数据 const scene2 = new Scene({ name: 'LoadTest' }); diff --git a/packages/core/tests/Utils/BinarySerializer.test.ts b/packages/core/tests/Utils/BinarySerializer.test.ts new file mode 100644 index 00000000..dd381818 --- /dev/null +++ b/packages/core/tests/Utils/BinarySerializer.test.ts @@ -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); + }); + }); +});