| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| import Fory, { |
| BinaryReader, |
| BinaryWriter, |
| Decimal, |
| ReadContext, |
| Type, |
| Dynamic, |
| WriteContext, |
| } from "../packages/core/index"; |
| import { describe, expect, test } from "@jest/globals"; |
| import * as fs from "node:fs"; |
| import * as beautify from "js-beautify"; |
| import { TypeId } from "../packages/core/lib/type"; |
| |
| const Byte = { |
| MAX_VALUE: 127, |
| MIN_VALUE: -128, |
| }; |
| |
| const Short = { |
| MAX_VALUE: 32767, |
| MIN_VALUE: -32768, |
| }; |
| |
| const Integer = { |
| MAX_VALUE: 2147483647, |
| MIN_VALUE: -2147483648, |
| }; |
| |
| const Long = { |
| MAX_VALUE: BigInt("9223372036854775807"), |
| MIN_VALUE: BigInt("-9223372036854775808"), |
| }; |
| |
| function decimal( |
| unscaledValue: string | bigint | number, |
| scale: number, |
| ): Decimal { |
| return new Decimal(unscaledValue, scale); |
| } |
| |
| function decimalValues(): Decimal[] { |
| return [ |
| decimal(0n, 0), |
| decimal(0n, 3), |
| decimal(1n, 0), |
| decimal(-1n, 0), |
| decimal(12345n, 2), |
| decimal("9223372036854775807", 0), |
| decimal("-9223372036854775808", 0), |
| decimal("4611686018427387903", 0), |
| decimal("-4611686018427387904", 0), |
| decimal("9223372036854775808", 0), |
| decimal("-9223372036854775809", 0), |
| decimal("123456789012345678901234567890123456789", 37), |
| decimal("-123456789012345678901234567890123456789", -17), |
| ]; |
| } |
| |
| describe("bool", () => { |
| const dataFile = process.env["DATA_FILE"]; |
| if (!dataFile) { |
| return; |
| } |
| function writeToFile(buffer: Buffer) { |
| fs.writeFileSync(dataFile!, buffer); |
| } |
| const content = fs.readFileSync(dataFile); |
| |
| test("test_buffer", () => { |
| const buffer = new BinaryWriter(); |
| buffer.reserve(32); |
| buffer.bool(true); |
| buffer.writeUint8(Byte.MAX_VALUE); |
| buffer.writeInt16(Short.MAX_VALUE); |
| buffer.writeInt32(Integer.MAX_VALUE); |
| buffer.writeInt64(Long.MAX_VALUE); |
| buffer.writeFloat32(-1.1); |
| buffer.writeFloat64(-1.1); |
| buffer.writeVarUInt32(100); |
| const bytes = ["a".charCodeAt(0), "b".charCodeAt(0)]; |
| buffer.writeInt32(bytes.length); |
| buffer.buffer(new Uint8Array(bytes)); |
| writeToFile(buffer.dump() as Buffer); |
| }); |
| test("test_buffer_var", () => { |
| const reader = new BinaryReader({}); |
| reader.reset(content); |
| |
| const varInt32Values = [ |
| Integer.MIN_VALUE, |
| Integer.MIN_VALUE + 1, |
| -1000000, |
| -1000, |
| -128, |
| -1, |
| 0, |
| 1, |
| 127, |
| 128, |
| 16383, |
| 16384, |
| 2097151, |
| 2097152, |
| 268435455, |
| 268435456, |
| Integer.MAX_VALUE - 1, |
| Integer.MAX_VALUE, |
| ]; |
| for (const expected of varInt32Values) { |
| expect(reader.readVarInt32()).toBe(expected); |
| } |
| |
| const varUInt32Values = [ |
| 0, |
| 1, |
| 127, |
| 128, |
| 16383, |
| 16384, |
| 2097151, |
| 2097152, |
| 268435455, |
| 268435456, |
| Integer.MAX_VALUE - 1, |
| Integer.MAX_VALUE, |
| ]; |
| for (const expected of varUInt32Values) { |
| expect(reader.readVarUInt32()).toBe(expected); |
| } |
| |
| const varUInt64Values = [ |
| 0n, |
| 1n, |
| 127n, |
| 128n, |
| 16383n, |
| 16384n, |
| 2097151n, |
| 2097152n, |
| 268435455n, |
| 268435456n, |
| 34359738367n, |
| 34359738368n, |
| 4398046511103n, |
| 4398046511104n, |
| 562949953421311n, |
| 562949953421312n, |
| 72057594037927935n, |
| 72057594037927936n, |
| Long.MAX_VALUE, |
| ]; |
| for (const expected of varUInt64Values) { |
| expect(reader.readVarUInt64()).toBe(expected); |
| } |
| |
| const varInt64Values = [ |
| Long.MIN_VALUE, |
| Long.MIN_VALUE + 1n, |
| -1000000000000n, |
| -1000000n, |
| -1000n, |
| -128n, |
| -1n, |
| 0n, |
| 1n, |
| 127n, |
| 1000n, |
| 1000000n, |
| 1000000000000n, |
| Long.MAX_VALUE - 1n, |
| Long.MAX_VALUE, |
| ]; |
| for (const expected of varInt64Values) { |
| expect(reader.readVarInt64()).toBe(expected); |
| } |
| |
| const writer = new BinaryWriter(); |
| writer.reserve(256); |
| for (const value of varInt32Values) { |
| writer.writeVarInt32(value); |
| } |
| for (const value of varUInt32Values) { |
| writer.writeVarUInt32(value); |
| } |
| for (const value of varUInt64Values) { |
| writer.writeVarUInt64(value); |
| } |
| for (const value of varInt64Values) { |
| writer.writeVarInt64(value); |
| } |
| writeToFile(writer.dump() as Buffer); |
| }); |
| test("test_murmurhash3", () => { |
| const { x64hash128 } = require("../packages/core/lib/murmurHash3"); |
| const reader = new BinaryReader({}); |
| reader.reset(content); |
| let dataview = x64hash128(new Uint8Array([1, 2, 8]), 47); |
| expect(reader.readInt64()).toEqual(dataview.getBigInt64(0)); |
| expect(reader.readInt64()).toEqual(dataview.getBigInt64(8)); |
| }); |
| test("test_string_serializer", () => { |
| const fory = new Fory({ |
| compatible: true, |
| }); |
| // Deserialize strings from Java |
| const deserializedStrings = []; |
| let cursor = 0; |
| for (let i = 0; i < 7; i++) { |
| // 7 test strings |
| const deserializedString = fory.deserialize(content.subarray(cursor)); |
| cursor += fory.readContext.reader.readGetCursor(); |
| deserializedStrings.push(deserializedString); |
| } |
| const bfs = []; |
| // Serialize each deserialized string back |
| for (const testString of deserializedStrings) { |
| const serializedData = fory.serialize(testString); |
| bfs.push(serializedData); |
| } |
| |
| writeToFile(Buffer.concat(bfs)); |
| }); |
| test("test_cross_language_serializer", () => { |
| const fory = new Fory({ |
| compatible: true, |
| }); |
| |
| // Define and register Color enum |
| const Color = { |
| Green: 0, |
| Red: 1, |
| Blue: 2, |
| White: 3, |
| }; |
| const { serialize: colorSerialize } = fory.register(Type.enum(101, Color)); |
| // Deserialize various data types from Java |
| const deserializedData = []; |
| let cursor = 0; |
| for (let i = 0; i < 27; i++) { |
| // 28 serialized items from Java |
| const deserializedItem = fory.deserialize(content.subarray(cursor)); |
| cursor += fory.readContext.reader.readGetCursor(); |
| deserializedData.push(deserializedItem); |
| } |
| |
| const bfs = []; |
| // Serialize each deserialized item back |
| for (let index = 0; index < deserializedData.length; index++) { |
| const item = deserializedData[index]; |
| let serializedData; |
| if (index === 11) { |
| serializedData = fory.serialize( |
| item, |
| fory.typeResolver.getSerializerById(TypeId.FLOAT32), |
| ); |
| } else if (index === 12) { |
| serializedData = fory.serialize( |
| item, |
| fory.typeResolver.getSerializerById(TypeId.FLOAT64), |
| ); |
| } else if (index === 14) { |
| serializedData = fory.serialize( |
| item, |
| fory.typeResolver.getSerializerById(TypeId.DATE), |
| ); |
| } else if (index === 15) { |
| serializedData = fory.serialize( |
| item, |
| fory.typeResolver.getSerializerById(TypeId.TIMESTAMP), |
| ); |
| } else if (index === 16) { |
| serializedData = fory.serialize( |
| item, |
| fory.typeResolver.getSerializerById(TypeId.BOOL_ARRAY), |
| ); |
| } else if (index === 17) { |
| serializedData = fory.serialize( |
| item, |
| fory.typeResolver.getSerializerById(TypeId.BINARY), |
| ); |
| } else if (index === 26) { |
| serializedData = colorSerialize(item); |
| } else { |
| serializedData = fory.serialize(item); |
| } |
| bfs.push(serializedData); |
| } |
| |
| writeToFile(Buffer.concat(bfs)); |
| }); |
| test("test_simple_struct", () => { |
| const fory = new Fory({ |
| compatible: true, |
| hooks: { |
| afterCodeGenerated: (code) => { |
| return beautify.js(code, { |
| indent_size: 2, |
| space_in_empty_paren: true, |
| indent_empty_lines: true, |
| }); |
| }, |
| }, |
| }); |
| |
| // Define Color enum |
| const Color = { |
| Green: 0, |
| Red: 1, |
| Blue: 2, |
| White: 3, |
| }; |
| fory.register(Type.enum(101, Color)); |
| |
| // Define Item class with field type registration |
| @Type.struct(102, { |
| name: Type.string(), |
| }) |
| class Item { |
| name: string = ""; |
| } |
| fory.register(Item); |
| |
| // Define SimpleStruct class with field type registration |
| @Type.struct(103, { |
| f1: Type.map(Type.int32(), Type.float64()), |
| f2: Type.int32(), |
| f3: Type.struct(102), |
| f4: Type.string(), |
| f5: Type.enum(101, Color), |
| f6: Type.list(Type.string()), |
| f7: Type.int32(), |
| f8: Type.int32(), |
| last: Type.int32(), |
| }) |
| class SimpleStruct { |
| f2: number = 0; |
| f3: Item | null = null; |
| f4: string = ""; |
| f5: number = 0; // Color enum value |
| f1 = new Map([ |
| [1, 1.0], |
| [2, 2.0], |
| ]); |
| f6 = ["f6"]; |
| f7: number = 0; |
| f8: number = 0; |
| last: number = 0; |
| } |
| fory.register(SimpleStruct); |
| |
| const reader = new BinaryReader({}); |
| reader.reset(content); |
| |
| // Deserialize the object from Java |
| const deserializedObj = fory.deserialize(content); |
| |
| // Serialize the deserialized object back |
| const serializedData = fory.serialize(deserializedObj); |
| |
| writeToFile(serializedData as Buffer); |
| }); |
| test("test_named_simple_struct", () => { |
| // Same as test_simple_struct but with named registration |
| const fory = new Fory({ |
| compatible: true, |
| }); |
| |
| // Define Color enum |
| const Color = { |
| Green: 0, |
| Red: 1, |
| Blue: 2, |
| White: 3, |
| }; |
| fory.register(Type.enum({ namespace: "demo", typeName: "color" }, Color)); |
| |
| // Define Item class with field type registration |
| @Type.struct( |
| { namespace: "demo", typeName: "item" }, |
| { |
| name: Type.string(), |
| }, |
| ) |
| class Item { |
| name: string = ""; |
| } |
| fory.register(Item); |
| |
| // Define SimpleStruct class with field type registration |
| @Type.struct( |
| { namespace: "demo", typeName: "simple_struct" }, |
| { |
| f1: Type.map(Type.int32(), Type.float64()), |
| f2: Type.int32(), |
| f3: Type.struct({ namespace: "demo", typeName: "item" }), |
| f4: Type.string(), |
| f5: Type.enum({ namespace: "demo", typeName: "color" }, Color), |
| f6: Type.list(Type.string()), |
| f7: Type.int32(), |
| f8: Type.int32(), |
| last: Type.int32(), |
| }, |
| ) |
| class SimpleStruct { |
| f1: Map<number, number> = new Map(); |
| f2: number = 0; |
| f3: Item | null = null; |
| f4: string = ""; |
| f5: number = 0; // Color enum value |
| f6: string[] = []; |
| f7: number = 0; |
| f8: number = 0; |
| last: number = 0; |
| } |
| fory.register(SimpleStruct); |
| |
| // Deserialize the object from Java |
| const deserializedObj = fory.deserialize(content); |
| // Serialize the deserialized object back |
| const serializedData = fory.serialize(deserializedObj); |
| writeToFile(serializedData as Buffer); |
| }); |
| |
| test("test_struct_evolving_override", () => { |
| const fory = new Fory({ |
| compatible: true, |
| }); |
| |
| @Type.struct( |
| { namespace: "test", typeName: "evolving_yes" }, |
| { |
| f1: Type.string(), |
| }, |
| ) |
| class EvolvingOverrideStruct { |
| f1: string = ""; |
| } |
| fory.register(EvolvingOverrideStruct); |
| |
| @Type.struct( |
| { namespace: "test", typeName: "evolving_off", evolving: false }, |
| { |
| f1: Type.string(), |
| }, |
| ) |
| class FixedOverrideStruct { |
| f1: string = ""; |
| } |
| fory.register(FixedOverrideStruct); |
| |
| let cursor = 0; |
| const evolving = fory.deserialize(content.subarray(cursor)); |
| cursor += fory.readContext.reader.readGetCursor(); |
| const fixed = fory.deserialize(content.subarray(cursor)); |
| cursor += fory.readContext.reader.readGetCursor(); |
| |
| expect(evolving).toEqual({ f1: "payload" }); |
| expect(fixed).toEqual({ f1: "payload" }); |
| |
| const serializedData = Buffer.concat([ |
| fory.serialize(evolving) as Buffer, |
| fory.serialize(fixed) as Buffer, |
| ]); |
| writeToFile(serializedData); |
| }); |
| |
| test("test_list", () => { |
| const fory = new Fory({ |
| compatible: true, |
| }); |
| |
| @Type.struct(102, { |
| name: Type.string(), |
| }) |
| class Item { |
| name: string = ""; |
| } |
| fory.register(Item); |
| |
| // Deserialize all lists from Java |
| const deserializedLists = []; |
| let cursor = 0; |
| for (let i = 0; i < 4; i++) { |
| // 4 lists |
| const deserializedList = fory.deserialize(content.subarray(cursor)); |
| cursor += fory.readContext.reader.readGetCursor(); |
| deserializedLists.push(deserializedList); |
| } |
| |
| const bfs = []; |
| |
| // Serialize each deserialized list back |
| for (const list of deserializedLists) { |
| const serializedData = fory.serialize(list); |
| bfs.push(serializedData); |
| } |
| writeToFile(Buffer.concat(bfs)); |
| }); |
| |
| test("test_map", () => { |
| const fory = new Fory({ |
| compatible: true, |
| }); |
| |
| @Type.struct(102, { |
| name: Type.string(), |
| }) |
| class Item { |
| name: string = ""; |
| } |
| |
| fory.register(Item); |
| |
| // Deserialize maps from Java |
| const deserializedMaps = []; |
| let cursor = 0; |
| for (let i = 0; i < 2; i++) { |
| // 2 maps |
| const deserializedMap = fory.deserialize(content.subarray(cursor)); |
| cursor += fory.readContext.reader.readGetCursor(); |
| deserializedMaps.push(deserializedMap); |
| } |
| |
| const bfs = []; |
| // Serialize each deserialized map back |
| for (const map of deserializedMaps) { |
| const serializedData = fory.serialize(map); |
| fory.deserialize(serializedData); |
| bfs.push(serializedData); |
| } |
| |
| writeToFile(Buffer.concat(bfs)); |
| }); |
| |
| test("test_integer", () => { |
| const fory = new Fory({ |
| compatible: true, |
| }); |
| |
| @Type.struct(101, { |
| f1: Type.int32(), |
| f2: Type.int32(), |
| f3: Type.int32(), |
| f4: Type.int32(), |
| f5: Type.int32(), |
| f6: Type.int32(), |
| }) |
| class Item1 { |
| f1: number = 0; |
| f2: number = 0; |
| f3: number | null = null; |
| f4: number | null = null; |
| f5: number | null = null; |
| f6: number | null = null; |
| } |
| |
| fory.register(Item1); |
| |
| // Deserialize item and individual integers from Java |
| const deserializedData = []; |
| let cursor = 0; |
| for (let i = 0; i < 7; i++) { |
| // 1 item + 6 integers |
| const deserializedItem = fory.deserialize(content.subarray(cursor)); |
| cursor += fory.readContext.reader.readGetCursor(); |
| deserializedData.push(deserializedItem); |
| } |
| |
| const bfs = []; |
| // Serialize each deserialized item back |
| for (const item of deserializedData) { |
| const serializedData = fory.serialize(item); |
| bfs.push(serializedData); |
| } |
| |
| writeToFile(Buffer.concat(bfs)); |
| }); |
| |
| test("test_decimal", () => { |
| const fory = new Fory({ |
| compatible: true, |
| }); |
| |
| const expectedValues = decimalValues(); |
| const actualValues: Decimal[] = []; |
| let cursor = 0; |
| for (let i = 0; i < expectedValues.length; i++) { |
| const value = fory.deserialize(content.subarray(cursor)); |
| cursor += fory.readContext.reader.readGetCursor(); |
| expect(value).toBeInstanceOf(Decimal); |
| expect((value as Decimal).equals(expectedValues[i])).toBe(true); |
| actualValues.push(value as Decimal); |
| } |
| |
| const bfs = []; |
| for (const value of actualValues) { |
| bfs.push(fory.serialize(value)); |
| } |
| writeToFile(Buffer.concat(bfs)); |
| }); |
| |
| test("test_item", () => { |
| const fory = new Fory({ |
| compatible: true, |
| }); |
| |
| @Type.struct(102, { |
| name: Type.string(), |
| }) |
| class Item { |
| name: string = ""; |
| } |
| fory.register(Item); |
| |
| const reader = new BinaryReader({}); |
| reader.reset(content); |
| |
| // Deserialize items from Java |
| const deserializedItems = []; |
| let cursor = 0; |
| for (let i = 0; i < 3; i++) { |
| // 3 items |
| const deserializedItem = fory.deserialize(content.subarray(cursor)); |
| cursor += fory.readContext.reader.readGetCursor(); |
| deserializedItems.push(deserializedItem); |
| } |
| |
| const bfs = []; |
| // Serialize each deserialized item back |
| for (const item of deserializedItems) { |
| const serializedData = fory.serialize(item); |
| bfs.push(serializedData); |
| } |
| |
| writeToFile(Buffer.concat(bfs)); |
| }); |
| |
| test("test_color", () => { |
| const fory = new Fory({ |
| compatible: true, |
| }); |
| |
| // Define and register Color enum |
| const Color = { |
| Green: 0, |
| Red: 1, |
| Blue: 2, |
| White: 3, |
| }; |
| const { serialize: enumSerialize } = fory.register(Type.enum(101, Color)); |
| |
| const reader = new BinaryReader({}); |
| reader.reset(content); |
| |
| // Deserialize colors from Java |
| const deserializedColors = []; |
| let cursor = 0; |
| for (let i = 0; i < 4; i++) { |
| // 4 colors |
| const deserializedColor = fory.deserialize(content.subarray(cursor)); |
| cursor += fory.readContext.reader.readGetCursor(); |
| deserializedColors.push(deserializedColor); |
| } |
| |
| const writer = new BinaryWriter(); |
| writer.reserve(128); |
| |
| // Serialize each deserialized color back |
| for (const color of deserializedColors) { |
| const serializedData = enumSerialize(color); |
| writer.buffer(serializedData); |
| } |
| |
| writeToFile(writer.dump() as Buffer); |
| }); |
| test("test_struct_with_list", () => { |
| const fory = new Fory({ |
| compatible: true, |
| }); |
| |
| @Type.struct(201, { |
| items: Type.list(Type.string()), |
| }) |
| class StructWithList { |
| items: (string | null)[] = []; |
| } |
| fory.register(StructWithList); |
| |
| const reader = new BinaryReader({}); |
| reader.reset(content); |
| |
| // Deserialize structs from Java |
| const deserializedStructs = []; |
| let cursor = 0; |
| for (let i = 0; i < 2; i++) { |
| // 2 structs |
| const deserializedStruct = fory.deserialize(content.subarray(cursor)); |
| cursor += fory.readContext.reader.readGetCursor(); |
| deserializedStructs.push(deserializedStruct); |
| } |
| |
| const writer = new BinaryWriter(); |
| writer.reserve(256); |
| |
| // Serialize each deserialized struct back |
| for (const struct of deserializedStructs) { |
| const serializedData = fory.serialize(struct); |
| writer.buffer(serializedData); |
| } |
| |
| writeToFile(writer.dump() as Buffer); |
| }); |
| |
| test("test_struct_with_map", () => { |
| const fory = new Fory({ |
| compatible: true, |
| }); |
| |
| @Type.struct(202, { |
| data: Type.map(Type.string(), Type.string()), |
| }) |
| class StructWithMap { |
| data: Map<string | null, string | null> = new Map(); |
| } |
| fory.register(StructWithMap); |
| |
| const reader = new BinaryReader({}); |
| reader.reset(content); |
| |
| // Deserialize structs from Java |
| const deserializedStructs = []; |
| let cursor = 0; |
| for (let i = 0; i < 2; i++) { |
| // 2 structs |
| const deserializedStruct = fory.deserialize(content.subarray(cursor)); |
| cursor += fory.readContext.reader.readGetCursor(); |
| deserializedStructs.push(deserializedStruct); |
| } |
| |
| const writer = new BinaryWriter(); |
| writer.reserve(256); |
| |
| // Serialize each deserialized struct back |
| for (const struct of deserializedStructs) { |
| const serializedData = fory.serialize(struct); |
| writer.buffer(serializedData); |
| } |
| |
| writeToFile(writer.dump() as Buffer); |
| }); |
| |
| test("test_collection_element_ref_override", () => { |
| const fory = new Fory({ |
| compatible: false, |
| ref: true, |
| hooks: { |
| afterCodeGenerated: (code) => { |
| return beautify.js(code, { |
| indent_size: 2, |
| space_in_empty_paren: true, |
| indent_empty_lines: true, |
| }); |
| }, |
| }, |
| }); |
| |
| const Type701 = Type.struct(701, { |
| id: Type.int32(), |
| name: Type.string(), |
| }); |
| |
| @Type701 |
| class RefOverrideElement { |
| id: number = 0; |
| name: string = ""; |
| } |
| |
| @Type.struct(702, { |
| list_field: Type.list(Type701.setTrackingRef(true)), |
| set_field: Type.set(Type701.setTrackingRef(true)), |
| map_field: Type.map(Type.string(), Type701.setTrackingRef(true)), |
| }) |
| class RefOverrideContainer { |
| list_field: RefOverrideElement[] = []; |
| set_field: Set<RefOverrideElement> = new Set(); |
| map_field: Map<string, RefOverrideElement> = new Map(); |
| } |
| |
| fory.register(RefOverrideElement); |
| fory.register(RefOverrideContainer); |
| |
| const outer = fory.deserialize(content); |
| console.log("Deserialized:", outer); |
| |
| expect(outer.list_field).toBeTruthy(); |
| expect(outer.list_field.length).toBeGreaterThan(0); |
| const shared = outer.list_field[0]; |
| const setShared = outer.set_field.values().next() |
| .value as RefOverrideElement; |
| expect(outer.list_field[1]).not.toBe(shared); |
| expect(setShared).not.toBe(shared); |
| expect(outer.map_field.get("k1")).not.toBe(shared); |
| expect(outer.map_field.get("k2")).not.toBe(shared); |
| expect(outer.map_field.get("k1")).not.toBe(setShared); |
| expect(outer.map_field.get("k2")).not.toBe(setShared); |
| const newOuter = new RefOverrideContainer(); |
| newOuter.list_field = [shared, shared]; |
| newOuter.set_field = new Set([shared]); |
| newOuter.map_field = new Map([ |
| ["k1", shared], |
| ["k2", shared], |
| ]); |
| |
| const newBytes = fory.serialize(newOuter); |
| writeToFile(newBytes as Buffer); |
| }); |
| |
| test("test_collection_element_ref_remote_tracking", () => { |
| const fory = new Fory({ |
| compatible: false, |
| ref: true, |
| hooks: { |
| afterCodeGenerated: (code) => { |
| return beautify.js(code, { |
| indent_size: 2, |
| space_in_empty_paren: true, |
| indent_empty_lines: true, |
| }); |
| }, |
| }, |
| }); |
| |
| const Type701 = Type.struct(701, { |
| id: Type.int32(), |
| name: Type.string(), |
| }); |
| |
| @Type701 |
| class RefOverrideElement { |
| id: number = 0; |
| name: string = ""; |
| } |
| |
| @Type.struct(702, { |
| list_field: Type.list(Type701.setTrackingRef(true)), |
| set_field: Type.set(Type701.setTrackingRef(true)), |
| map_field: Type.map(Type.string(), Type701.setTrackingRef(true)), |
| }) |
| class RefOverrideContainer { |
| list_field: RefOverrideElement[] = []; |
| set_field: Set<RefOverrideElement> = new Set(); |
| map_field: Map<string, RefOverrideElement> = new Map(); |
| } |
| |
| fory.register(RefOverrideElement); |
| fory.register(RefOverrideContainer); |
| |
| const shared = new RefOverrideElement(); |
| shared.id = 7; |
| shared.name = "shared_element"; |
| |
| // IMPORTANT: this peer intentionally writes a shared-reference payload with |
| // its default local ref-tracked schema. The Java reader uses ref-disabled |
| // element annotations and must still honor the wire metadata. DO NOT REMOVE |
| // this comment. |
| const outer = new RefOverrideContainer(); |
| outer.list_field = [shared, shared]; |
| outer.set_field = new Set([shared]); |
| outer.map_field = new Map([ |
| ["k1", shared], |
| ["k2", shared], |
| ]); |
| |
| const newBytes = fory.serialize(outer); |
| writeToFile(newBytes as Buffer); |
| }); |
| |
| test("test_skip_id_custom", () => { |
| const fory1 = new Fory({ |
| compatible: true, |
| }); |
| |
| @Type.ext(103) |
| class MyExt { |
| id: number = 0; |
| } |
| fory1.register(MyExt, { |
| write: (writeContext: WriteContext, value: MyExt) => { |
| writeContext.writeVarInt32(value.id); |
| }, |
| read: (readContext: ReadContext, result: MyExt) => { |
| result.id = readContext.readVarInt32(); |
| }, |
| }); |
| |
| // Define empty wrapper for deserialization |
| @Type.struct(104) |
| class Empty {} |
| fory1.register(Empty); |
| |
| const fory2 = new Fory({ |
| compatible: true, |
| }); |
| |
| // Define Color enum |
| const Color = { |
| Green: 0, |
| Red: 1, |
| Blue: 2, |
| White: 3, |
| }; |
| fory2.register(Type.enum(101, Color)); |
| |
| @Type.struct(102, { |
| id: Type.int32(), |
| }) |
| class MyStruct { |
| id: number = 0; |
| } |
| fory2.register(MyStruct); |
| |
| fory2.register(MyExt, { |
| write: (writeContext: WriteContext, value: MyExt) => { |
| writeContext.writeVarInt32(value.id); |
| }, |
| read: (readContext: ReadContext, result: MyExt) => { |
| result.id = readContext.readVarInt32(); |
| }, |
| }); |
| |
| @Type.struct(104, { |
| color: Type.enum(101, Color), |
| myStruct: Type.struct(102), |
| myExt: Type.ext(103), |
| }) |
| class MyWrapper { |
| color: number = 0; |
| myStruct: MyStruct = new MyStruct(); |
| myExt: MyExt = new MyExt(); |
| } |
| fory2.register(MyWrapper); |
| |
| // Deserialize empty from Java |
| let cursor = 0; |
| const deserializedEmpty = fory1.deserialize(content.subarray(cursor)); |
| cursor += fory1.readContext.reader.readGetCursor(); |
| expect(deserializedEmpty instanceof Empty).toEqual(true); |
| |
| // Create wrapper object |
| const wrapper = new MyWrapper(); |
| wrapper.color = Color.White; |
| wrapper.myStruct = new MyStruct(); |
| wrapper.myStruct.id = 42; |
| wrapper.myExt = new MyExt(); |
| wrapper.myExt.id = 43; |
| |
| // Serialize wrapper |
| const serializedData = fory2.serialize(wrapper); |
| writeToFile(serializedData as Buffer); |
| }); |
| |
| test("test_skip_name_custom", () => { |
| const fory1 = new Fory({ |
| compatible: true, |
| }); |
| |
| @Type.ext("my_ext") |
| class MyExt { |
| id: number = 0; |
| } |
| fory1.register(MyExt, { |
| write: (writeContext: WriteContext, value: MyExt) => { |
| writeContext.writeVarInt32(value.id); |
| }, |
| read: (readContext: ReadContext, result: MyExt) => { |
| result.id = readContext.readVarInt32(); |
| }, |
| }); |
| |
| // Define empty wrapper for deserialization |
| @Type.struct("my_wrapper") |
| class Empty {} |
| fory1.register(Empty); |
| |
| const fory2 = new Fory({ |
| compatible: true, |
| }); |
| |
| // Define Color enum |
| const Color = { |
| Green: 0, |
| Red: 1, |
| Blue: 2, |
| White: 3, |
| }; |
| fory2.register(Type.enum("color", Color)); |
| |
| @Type.struct("my_struct", { |
| id: Type.int32(), |
| }) |
| class MyStruct { |
| id: number = 0; |
| } |
| fory2.register(MyStruct); |
| |
| fory2.register(MyExt, { |
| write: (writeContext: WriteContext, value: MyExt) => { |
| writeContext.writeVarInt32(value.id); |
| }, |
| read: (readContext: ReadContext, result: MyExt) => { |
| result.id = readContext.readVarInt32(); |
| }, |
| }); |
| |
| @Type.struct("my_wrapper", { |
| color: Type.enum("color", Color), |
| myStruct: Type.struct("my_struct"), |
| myExt: Type.ext("my_ext"), |
| }) |
| class MyWrapper { |
| color: number = 0; |
| myStruct: MyStruct = new MyStruct(); |
| myExt: MyExt = new MyExt(); |
| } |
| fory2.register(MyWrapper); |
| |
| // Deserialize empty from Java |
| let cursor = 0; |
| const deserializedEmpty = fory1.deserialize(content.subarray(cursor)); |
| cursor += fory1.readContext.reader.readGetCursor(); |
| expect(deserializedEmpty instanceof Empty).toEqual(true); |
| |
| // Create wrapper object |
| const wrapper = new MyWrapper(); |
| wrapper.color = Color.White; |
| wrapper.myStruct = new MyStruct(); |
| wrapper.myStruct.id = 42; |
| wrapper.myExt = new MyExt(); |
| wrapper.myExt.id = 43; |
| |
| // Serialize wrapper |
| const serializedData = fory2.serialize(wrapper); |
| writeToFile(serializedData as Buffer); |
| }); |
| |
| test("test_consistent_named", () => { |
| const fory = new Fory({ |
| compatible: false, |
| }); |
| |
| // Define and register Color enum |
| const Color = { |
| Green: 0, |
| Red: 1, |
| Blue: 2, |
| White: 3, |
| }; |
| const { serialize: enumSerialize } = fory.register( |
| Type.enum({ namespace: "", typeName: "color" }, Color), |
| ); |
| |
| @Type.struct( |
| { namespace: "", typeName: "my_struct" }, |
| { |
| id: Type.int32(), |
| }, |
| ) |
| class MyStruct { |
| id: number = 0; |
| } |
| fory.register(MyStruct); |
| |
| @Type.ext({ namespace: "", typeName: "my_ext" }) |
| class MyExt { |
| id: number = 0; |
| } |
| fory.register(MyExt, { |
| write: (writeContext: WriteContext, value: MyExt) => { |
| writeContext.writeVarInt32(value.id); |
| }, |
| read: (readContext: ReadContext, result: MyExt) => { |
| result.id = readContext.readVarInt32(); |
| }, |
| }); |
| |
| // Deserialize multiple instances from Java |
| const deserializedData = []; |
| let cursor = 0; |
| for (let i = 0; i < 9; i++) { |
| // 3 colors + 3 structs + 3 exts |
| const deserializedItem = fory.deserialize(content.subarray(cursor)); |
| cursor += fory.readContext.reader.readGetCursor(); |
| deserializedData.push(deserializedItem); |
| } |
| |
| const writer = new BinaryWriter(); |
| writer.reserve(256); |
| |
| for (let index = 0; index < 3; index++) { |
| const element = deserializedData[index]; |
| const serializedData = enumSerialize(element); |
| writer.buffer(serializedData); |
| } |
| for (let index = 3; index < deserializedData.length; index++) { |
| const item = deserializedData[index]; |
| const serializedData = fory.serialize(item); |
| writer.buffer(serializedData); |
| } |
| |
| writeToFile(writer.dump() as Buffer); |
| }); |
| |
| test("test_struct_version_check", () => { |
| const fory = new Fory({ |
| compatible: false, |
| }); |
| |
| @Type.struct(201, { |
| f1: Type.int32(), |
| f2: Type.string().setNullable(true), |
| f3: Type.float64(), |
| }) |
| class VersionCheckStruct { |
| f1: number = 0; |
| f2: string | null = null; |
| f3: number = 0; |
| } |
| fory.register(VersionCheckStruct); |
| |
| const reader = new BinaryReader({}); |
| reader.reset(content); |
| |
| // Deserialize struct from Java |
| let cursor = 0; |
| const deserializedStruct = fory.deserialize(content.subarray(cursor)); |
| cursor += fory.readContext.reader.readGetCursor(); |
| |
| // Serialize the deserialized struct back |
| const serializedData = fory.serialize(deserializedStruct); |
| writeToFile(serializedData as Buffer); |
| }); |
| |
| test("test_polymorphic_list", () => { |
| const fory = new Fory({ |
| compatible: true, |
| }); |
| |
| // Define Animal interface implementations |
| @Type.struct(302, { |
| age: Type.int32(), |
| name: Type.string().setNullable(true), |
| }) |
| class Dog { |
| age: number = 0; |
| name: string | null = null; |
| } |
| fory.register(Dog); |
| |
| @Type.struct(303, { |
| age: Type.int32(), |
| lives: Type.int32(), |
| }) |
| class Cat { |
| age: number = 0; |
| lives: number = 0; |
| } |
| fory.register(Cat); |
| |
| @Type.struct(304, { |
| animals: Type.list(Type.any()), // Polymorphic list |
| }) |
| class AnimalListHolder { |
| animals: (Dog | Cat)[] = []; |
| } |
| fory.register(AnimalListHolder); |
| |
| const reader = new BinaryReader({}); |
| reader.reset(content); |
| |
| // Deserialize polymorphic data from Java |
| const deserializedData = []; |
| let cursor = 0; |
| for (let i = 0; i < 2; i++) { |
| // animals array + holder |
| const deserializedItem = fory.deserialize(content.subarray(cursor)); |
| cursor += fory.readContext.reader.readGetCursor(); |
| deserializedData.push(deserializedItem); |
| } |
| |
| const writer = new BinaryWriter(); |
| writer.reserve(512); |
| |
| // Serialize each deserialized item back |
| for (const item of deserializedData) { |
| const serializedData = fory.serialize(item); |
| const s = fory.deserialize(serializedData); |
| writer.buffer(serializedData); |
| } |
| |
| writeToFile(writer.dump() as Buffer); |
| }); |
| |
| test("test_polymorphic_map", () => { |
| const fory = new Fory({ |
| compatible: true, |
| }); |
| |
| // Define Animal interface implementations |
| @Type.struct(302, { |
| age: Type.int32(), |
| name: Type.string().setNullable(true), |
| }) |
| class Dog { |
| age: number = 0; |
| name: string | null = null; |
| } |
| fory.register(Dog); |
| |
| @Type.struct(303, { |
| age: Type.int32(), |
| lives: Type.int32(), |
| }) |
| class Cat { |
| age: number = 0; |
| lives: number = 0; |
| } |
| fory.register(Cat); |
| |
| @Type.struct(305, { |
| animal_map: Type.map(Type.string(), Type.any()), // Polymorphic map |
| }) |
| class AnimalMapHolder { |
| animal_map: Map<string, Dog | Cat> = new Map(); |
| } |
| fory.register(AnimalMapHolder); |
| |
| const reader = new BinaryReader({}); |
| reader.reset(content); |
| |
| // Deserialize polymorphic data from Java |
| const deserializedData = []; |
| let cursor = 0; |
| for (let i = 0; i < 2; i++) { |
| // animal map + holder |
| const deserializedItem = fory.deserialize(content.subarray(cursor)); |
| cursor += fory.readContext.reader.readGetCursor(); |
| deserializedData.push(deserializedItem); |
| } |
| |
| const writer = new BinaryWriter(); |
| writer.reserve(512); |
| |
| // Serialize each deserialized item back |
| for (const item of deserializedData) { |
| const serializedData = fory.serialize(item); |
| writer.buffer(serializedData); |
| } |
| |
| writeToFile(writer.dump() as Buffer); |
| }); |
| test("test_one_string_field_schema", () => { |
| const fory = new Fory({ |
| compatible: false, |
| }); |
| |
| @Type.struct(200, { |
| f1: Type.string(), |
| }) |
| class OneStringFieldStruct { |
| @(Type.string().setNullable(true)) |
| f1: string | null = null; |
| } |
| fory.register(OneStringFieldStruct); |
| |
| // Deserialize struct from Java |
| let cursor = 0; |
| const deserializedStruct = fory.deserialize(content.subarray(cursor)); |
| cursor += fory.readContext.reader.readGetCursor(); |
| |
| // Serialize the deserialized struct back |
| const serializedData = fory.serialize(deserializedStruct); |
| writeToFile(serializedData as Buffer); |
| }); |
| test("test_one_string_field_compatible", () => { |
| const fory = new Fory({ |
| compatible: true, |
| }); |
| |
| @Type.struct(200, { |
| f1: Type.string().setNullable(true), |
| }) |
| class OneStringFieldStruct { |
| f1: string | null = null; |
| } |
| fory.register(OneStringFieldStruct); |
| |
| const reader = new BinaryReader({}); |
| reader.reset(content); |
| |
| // Deserialize struct from Java |
| let cursor = 0; |
| const deserializedStruct = fory.deserialize(content.subarray(cursor)); |
| cursor += fory.readContext.reader.readGetCursor(); |
| |
| // Serialize the deserialized struct back |
| const serializedData = fory.serialize(deserializedStruct); |
| writeToFile(serializedData as Buffer); |
| }); |
| |
| test("test_two_string_field_compatible", () => { |
| const fory = new Fory({ |
| compatible: true, |
| }); |
| |
| @Type.struct(201, { |
| f1: Type.string(), |
| f2: Type.string(), |
| }) |
| class TwoStringFieldStruct { |
| f1: string = ""; |
| f2: string = ""; |
| } |
| fory.register(TwoStringFieldStruct); |
| |
| // Deserialize struct from Java |
| let cursor = 0; |
| const deserializedStruct = fory.deserialize(content.subarray(cursor)); |
| cursor += fory.readContext.reader.readGetCursor(); |
| |
| // Serialize the deserialized struct back |
| const serializedData = fory.serialize(deserializedStruct); |
| writeToFile(serializedData as Buffer); |
| }); |
| |
| test("test_schema_evolution_compatible_reverse", () => { |
| const fory = new Fory({ |
| compatible: true, |
| }); |
| |
| @Type.struct(200) |
| class TwoStringFieldStruct { |
| @Type.string() |
| f1: string = ""; |
| @Type.string() |
| f2: string = ""; |
| } |
| fory.register(TwoStringFieldStruct); |
| |
| // Deserialize empty struct from Java |
| let cursor = 0; |
| const deserializedStruct = fory.deserialize(content.subarray(cursor)); |
| cursor += fory.readContext.reader.readGetCursor(); |
| |
| // Serialize the deserialized struct back |
| const serializedData = fory.serialize(deserializedStruct); |
| writeToFile(serializedData as Buffer); |
| }); |
| |
| test("test_schema_evolution_compatible", () => { |
| const fory = new Fory({ |
| compatible: true, |
| }); |
| |
| @Type.struct(200) |
| class EmptyStruct {} |
| fory.register(EmptyStruct); |
| |
| // Deserialize empty struct from Java |
| let cursor = 0; |
| const deserializedStruct = fory.deserialize(content.subarray(cursor)); |
| cursor += fory.readContext.reader.readGetCursor(); |
| |
| // Serialize the deserialized struct back |
| const serializedData = fory.serialize(deserializedStruct); |
| writeToFile(serializedData as Buffer); |
| }); |
| |
| test("test_list_array_compatible_list_to_array", () => { |
| const fory = new Fory({ compatible: true }); |
| const serializer = fory.register( |
| Type.struct(901, { |
| values: Type.int32Array().setId(1), |
| }), |
| ); |
| |
| const value = serializer.deserialize(content); |
| writeToFile(serializer.serialize(value) as Buffer); |
| }); |
| |
| test("test_list_array_compatible_array_to_list", () => { |
| const fory = new Fory({ compatible: true }); |
| const serializer = fory.register( |
| Type.struct(901, { |
| values: Type.list(Type.int32({ encoding: "fixed" })).setId(1), |
| }), |
| ); |
| |
| const value = serializer.deserialize(content); |
| writeToFile(serializer.serialize(value) as Buffer); |
| }); |
| |
| test("test_list_array_compatible_nullable_list_to_array_error", () => { |
| const fory = new Fory({ compatible: true }); |
| const serializer = fory.register( |
| Type.struct(901, { |
| values: Type.int32Array().setId(1), |
| }), |
| ); |
| |
| expect(() => serializer.deserialize(content)).toThrow(); |
| writeToFile(content); |
| }); |
| |
| test("test_one_enum_field_schema", () => { |
| const fory = new Fory({ |
| compatible: false, |
| }); |
| |
| // Define and register TestEnum |
| const TestEnum = { |
| VALUE_A: 0, |
| VALUE_B: 1, |
| VALUE_C: 2, |
| }; |
| fory.register(Type.enum(210, TestEnum)); |
| |
| @Type.struct(211, { |
| f1: Type.enum(210, TestEnum), |
| }) |
| class OneEnumFieldStruct { |
| f1: number = 0; // enum value |
| } |
| fory.register(OneEnumFieldStruct); |
| |
| const reader = new BinaryReader({}); |
| reader.reset(content); |
| |
| // Deserialize struct from Java |
| let cursor = 0; |
| const deserializedStruct = fory.deserialize(content.subarray(cursor)); |
| cursor += fory.readContext.reader.readGetCursor(); |
| |
| // Serialize the deserialized struct back |
| const serializedData = fory.serialize(deserializedStruct); |
| writeToFile(serializedData as Buffer); |
| }); |
| |
| test("test_one_enum_field_compatible", () => { |
| const fory = new Fory({ |
| compatible: true, |
| }); |
| |
| // Define and register TestEnum |
| const TestEnum = { |
| VALUE_A: 0, |
| VALUE_B: 1, |
| VALUE_C: 2, |
| }; |
| fory.register(Type.enum(210, TestEnum)); |
| |
| @Type.struct(211, { |
| f1: Type.enum(210, TestEnum), |
| }) |
| class OneEnumFieldStruct { |
| f1: number = 0; // enum value |
| } |
| fory.register(OneEnumFieldStruct); |
| |
| // Deserialize struct from Java |
| let cursor = 0; |
| const deserializedStruct = fory.deserialize(content.subarray(cursor)); |
| cursor += fory.readContext.reader.readGetCursor(); |
| |
| // Serialize the deserialized struct back |
| const serializedData = fory.serialize(deserializedStruct); |
| writeToFile(serializedData as Buffer); |
| }); |
| |
| test("test_two_enum_field_compatible", () => { |
| const fory = new Fory({ |
| compatible: true, |
| }); |
| |
| // Define and register TestEnum |
| const TestEnum = { |
| VALUE_A: 0, |
| VALUE_B: 1, |
| VALUE_C: 2, |
| }; |
| fory.register(Type.enum(210, TestEnum)); |
| |
| @Type.struct(212, { |
| f1: Type.enum(210, TestEnum), |
| f2: Type.enum(210, TestEnum), |
| }) |
| class TwoEnumFieldStruct { |
| f1: number = 0; // enum value |
| f2: number = 0; // enum value |
| } |
| fory.register(TwoEnumFieldStruct); |
| |
| // Deserialize struct from Java |
| let cursor = 0; |
| const deserializedStruct = fory.deserialize(content.subarray(cursor)); |
| cursor += fory.readContext.reader.readGetCursor(); |
| |
| // Serialize the deserialized struct back |
| const serializedData = fory.serialize(deserializedStruct); |
| writeToFile(serializedData as Buffer); |
| }); |
| |
| test("test_enum_schema_evolution_compatible_reverse", () => { |
| const fory = new Fory({ |
| compatible: true, |
| }); |
| |
| // Define and register TestEnum |
| const TestEnum = { |
| VALUE_A: 0, |
| VALUE_B: 1, |
| VALUE_C: 2, |
| }; |
| fory.register(Type.enum(210, TestEnum)); |
| |
| @Type.struct(211, { |
| f1: Type.enum(210, TestEnum), |
| f2: Type.enum(210, TestEnum), |
| }) |
| class TwoEnumFieldStruct { |
| f1: number = 0; // enum value |
| f2: number = 0; // enum value |
| } |
| fory.register(TwoEnumFieldStruct); |
| |
| // Deserialize struct from Java |
| let cursor = 0; |
| const deserializedStruct = fory.deserialize(content.subarray(cursor)); |
| cursor += fory.readContext.reader.readGetCursor(); |
| |
| // Serialize the deserialized struct back |
| const serializedData = fory.serialize(deserializedStruct); |
| writeToFile(serializedData as Buffer); |
| }); |
| |
| test("test_enum_schema_evolution_compatible", () => { |
| const fory = new Fory({ |
| compatible: true, |
| }); |
| |
| // Register TestEnum |
| const TestEnum = { |
| VALUE_A: 0, |
| VALUE_B: 1, |
| VALUE_C: 2, |
| }; |
| fory.register(Type.enum(210, TestEnum)); |
| |
| @Type.struct(211) |
| class EmptyStruct {} |
| fory.register(EmptyStruct); |
| |
| // Deserialize empty struct from Java |
| let cursor = 0; |
| const deserializedStruct = fory.deserialize(content.subarray(cursor)); |
| cursor += fory.readContext.reader.readGetCursor(); |
| |
| // Serialize the deserialized struct back |
| const serializedData = fory.serialize(deserializedStruct); |
| writeToFile(serializedData as Buffer); |
| }); |
| |
| const buildClass = (id = 402) => { |
| @Type.struct({ typeId: id }) |
| class NullableComprehensiveCompatible { |
| // Base non-nullable primitive fields |
| @Type.int8() |
| byteField: number = 0; |
| @Type.int16() |
| shortField: number = 0; |
| @Type.int32() |
| intField: number = 0; |
| @Type.int64() |
| longField: number = 0; |
| @Type.float32() |
| floatField: number = 0; |
| @Type.float64() |
| doubleField: number = 0; |
| @Type.bool() |
| boolField: boolean = false; |
| |
| // Base non-nullable boxed fields (not nullable by default in xlang) |
| @Type.int32() |
| boxedInt: number = 0; |
| @Type.int64() |
| boxedLong: number = 0; |
| @Type.float32() |
| boxedFloat: number = 0; |
| @Type.float64() |
| boxedDouble: number = 0; |
| @Type.bool() |
| boxedBool = false; |
| |
| // Base non-nullable reference fields |
| @Type.string() |
| stringField: string = ""; |
| @Type.list(Type.string()) |
| listField: string[] = []; |
| @Type.set(Type.string()) |
| setField: Set<string> = new Set(); |
| @Type.map(Type.string(), Type.string()) |
| mapField: Map<string, string> = new Map(); |
| |
| // Nullable group 1 - boxed types with nullable type decorators |
| @(Type.int32().setNullable(true)) |
| nullableInt1: number | null = null; |
| |
| @(Type.int64().setNullable(true)) |
| nullableLong1: number | null = null; |
| |
| @(Type.float32().setNullable(true)) |
| nullableFloat1: number | null = null; |
| |
| @(Type.float64().setNullable(true)) |
| nullableDouble1: number | null = null; |
| |
| @(Type.bool().setNullable(true)) |
| nullableBool1: boolean | null = null; |
| |
| // Nullable group 2 - reference types with nullable type decorators |
| @(Type.string().setNullable(true)) |
| nullableString2: string | null = null; |
| |
| @(Type.list(Type.string()).setNullable(true)) |
| nullableList2: string[] | null = null; |
| |
| @(Type.set(Type.string()).setNullable(true)) |
| nullableSet2: Set<string> | null = null; |
| |
| @(Type.map(Type.string(), Type.string()).setNullable(true)) |
| nullableMap2: Map<string, string> | null = null; |
| } |
| return NullableComprehensiveCompatible; |
| }; |
| |
| const buildClassConsistent = (id = 401) => { |
| @Type.struct({ typeId: id }) |
| class NullableComprehensiveConsistent { |
| // Base non-nullable primitive fields |
| @Type.int8() |
| byteField: number = 0; |
| @Type.int16() |
| shortField: number = 0; |
| @Type.int32() |
| intField: number = 0; |
| @Type.int64() |
| longField: number = 0; |
| @Type.float32() |
| floatField: number = 0; |
| @Type.float64() |
| doubleField: number = 0; |
| @Type.bool() |
| boolField: boolean = false; |
| |
| // Base non-nullable reference fields |
| @Type.string() |
| stringField: string = ""; |
| @Type.list(Type.string()) |
| listField: string[] = []; |
| @Type.set(Type.string()) |
| setField: Set<string> = new Set(); |
| @Type.map(Type.string(), Type.string()) |
| mapField: Map<string, string> = new Map(); |
| |
| // Nullable group 1 - boxed types with nullable type decorators |
| @(Type.int32().setNullable(true)) |
| nullableInt: number | null = null; |
| |
| @(Type.int64().setNullable(true)) |
| nullableLong: number | null = null; |
| |
| @(Type.float32().setNullable(true)) |
| nullableFloat: number | null = null; |
| |
| @(Type.float64().setNullable(true)) |
| nullableDouble: number | null = null; |
| |
| @(Type.bool().setNullable(true)) |
| nullableBool: boolean | null = null; |
| |
| // Nullable group 2 - reference types with nullable type decorators |
| @(Type.string().setNullable(true)) |
| nullableString: string | null = null; |
| |
| @(Type.list(Type.string()).setNullable(true)) |
| nullableList: string[] | null = null; |
| |
| @(Type.set(Type.string()).setNullable(true)) |
| nullableSet: Set<string> | null = null; |
| |
| @(Type.map(Type.string(), Type.string()).setNullable(true)) |
| nullableMap: Map<string, string> | null = null; |
| } |
| return NullableComprehensiveConsistent; |
| }; |
| |
| test("test_nullable_field_schema_consistent_not_null", () => { |
| const fory = new Fory({ |
| compatible: false, |
| }); |
| |
| fory.register(buildClassConsistent(401)); |
| |
| // Deserialize struct from Java |
| let cursor = 0; |
| const deserializedStruct = fory.deserialize(content.subarray(cursor)); |
| cursor += fory.readContext.reader.readGetCursor(); |
| |
| // Serialize the deserialized struct back |
| const serializedData = fory.serialize(deserializedStruct); |
| writeToFile(serializedData as Buffer); |
| }); |
| |
| test("test_nullable_field_schema_consistent_null", () => { |
| const fory = new Fory({ |
| compatible: false, |
| }); |
| fory.register(buildClassConsistent()); |
| |
| const reader = new BinaryReader({}); |
| reader.reset(content); |
| |
| // Deserialize struct from Java |
| let cursor = 0; |
| const deserializedStruct = fory.deserialize(content.subarray(cursor)); |
| cursor += fory.readContext.reader.readGetCursor(); |
| |
| // Serialize the deserialized struct back |
| const serializedData = fory.serialize(deserializedStruct); |
| writeToFile(serializedData as Buffer); |
| }); |
| |
| test("test_nullable_field_compatible_not_null", () => { |
| const fory = new Fory({ |
| compatible: true, |
| }); |
| |
| fory.register(buildClass()); |
| |
| // Deserialize struct from Java |
| let cursor = 0; |
| const deserializedStruct = fory.deserialize(content.subarray(cursor)); |
| cursor += fory.readContext.reader.readGetCursor(); |
| |
| // Serialize the deserialized struct back |
| const serializedData = fory.serialize(deserializedStruct); |
| writeToFile(serializedData as Buffer); |
| }); |
| |
| test("test_nullable_field_compatible_null", () => { |
| const fory = new Fory({ |
| compatible: true, |
| }); |
| |
| fory.register(buildClass()); |
| |
| const reader = new BinaryReader({}); |
| reader.reset(content); |
| |
| // Deserialize struct from Java |
| let cursor = 0; |
| const deserializedStruct: InstanceType< |
| ReturnType<typeof buildClass> |
| > | null = fory.deserialize(content.subarray(cursor)); |
| cursor += fory.readContext.reader.readGetCursor(); |
| |
| if (deserializedStruct === null) { |
| throw new Error("deserializedStruct is null"); |
| } |
| // Serialize the deserialized struct back |
| const serializedData = fory.serialize(deserializedStruct); |
| writeToFile(serializedData as Buffer); |
| }); |
| |
| test("test_ref_schema_consistent", () => { |
| const fory = new Fory({ |
| compatible: false, |
| ref: true, |
| }); |
| |
| @Type.struct(501, { |
| id: Type.int32(), |
| name: Type.string(), |
| }) |
| class RefInner { |
| id: number = 0; |
| name: string = ""; |
| } |
| fory.register(RefInner); |
| |
| @Type.struct(502, { |
| inner1: Type.struct(501) |
| .setTrackingRef(true) |
| .setNullable(true) |
| .setDynamic(Dynamic.FALSE), |
| inner2: Type.struct(501) |
| .setTrackingRef(true) |
| .setNullable(true) |
| .setDynamic(Dynamic.FALSE), |
| }) |
| class RefOuter { |
| inner1: RefInner | null = null; |
| |
| inner2: RefInner | null = null; |
| } |
| fory.register(RefOuter); |
| |
| // Deserialize outer struct from Java |
| let cursor = 0; |
| const deserializedOuter = fory.deserialize(content.subarray(cursor)); |
| cursor += fory.readContext.reader.readGetCursor(); |
| |
| // Serialize the deserialized outer struct back |
| const serializedData = fory.serialize(deserializedOuter); |
| writeToFile(serializedData as Buffer); |
| }); |
| |
| test("test_ref_compatible", () => { |
| const fory = new Fory({ |
| compatible: true, |
| ref: true, |
| }); |
| |
| @Type.struct(503, { |
| id: Type.int32(), |
| name: Type.string(), |
| }) |
| class RefInner { |
| id: number = 0; |
| name: string = ""; |
| } |
| fory.register(RefInner); |
| |
| @Type.struct(504, { |
| inner1: Type.struct(503).setTrackingRef(true).setNullable(true), |
| inner2: Type.struct(503).setTrackingRef(true).setNullable(true), |
| }) |
| class RefOuter { |
| inner1: RefInner | null = null; |
| inner2: RefInner | null = null; |
| } |
| fory.register(RefOuter); |
| |
| // Deserialize outer struct from Java |
| let cursor = 0; |
| const deserializedOuter = fory.deserialize(content.subarray(cursor)); |
| cursor += fory.readContext.reader.readGetCursor(); |
| |
| // Serialize the deserialized outer struct back |
| const serializedData = fory.serialize(deserializedOuter); |
| writeToFile(serializedData as Buffer); |
| }); |
| |
| test("test_circular_ref_schema_consistent", () => { |
| const fory = new Fory({ |
| compatible: false, |
| ref: true, |
| }); |
| |
| @Type.struct(601, { |
| name: Type.string(), |
| selfRef: Type.struct(601).setNullable(true).setTrackingRef(true), |
| }) |
| class CircularRefStruct { |
| name: string = ""; |
| selfRef: CircularRefStruct | null = null; |
| } |
| fory.register(CircularRefStruct); |
| |
| const reader = new BinaryReader({}); |
| reader.reset(content); |
| |
| // Deserialize circular struct from Java |
| let cursor = 0; |
| const deserializedStruct = fory.deserialize(content.subarray(cursor)); |
| cursor += fory.readContext.reader.readGetCursor(); |
| |
| // Serialize the deserialized struct back |
| const serializedData = fory.serialize(deserializedStruct); |
| writeToFile(serializedData as Buffer); |
| }); |
| |
| test("test_circular_ref_compatible", () => { |
| const fory = new Fory({ |
| compatible: true, |
| ref: true, |
| }); |
| |
| @Type.struct(602, { |
| name: Type.string(), |
| selfRef: Type.struct(602).setNullable(true).setTrackingRef(true), |
| }) |
| class CircularRefStruct { |
| name: string = ""; |
| selfRef: CircularRefStruct | null = null; |
| } |
| fory.register(CircularRefStruct); |
| |
| // Deserialize circular struct from Java |
| let cursor = 0; |
| const deserializedStruct = fory.deserialize(content.subarray(cursor)); |
| cursor += fory.readContext.reader.readGetCursor(); |
| |
| // Serialize the deserialized struct back |
| const serializedData = fory.serialize(deserializedStruct); |
| writeToFile(serializedData as Buffer); |
| }); |
| |
| test("test_unsigned_schema_consistent_simple", () => { |
| const fory = new Fory({ |
| compatible: false, |
| }); |
| |
| @Type.struct(1, { |
| u64Tagged: Type.uint64({ encoding: "tagged" }), |
| u64TaggedNullable: Type.uint64({ encoding: "tagged" }).setNullable(true), |
| }) |
| class UnsignedSchemaConsistentSimple { |
| u64Tagged: bigint = 0n; |
| |
| u64TaggedNullable: bigint | null = null; |
| } |
| fory.register(UnsignedSchemaConsistentSimple); |
| |
| // Deserialize struct from Java |
| let cursor = 0; |
| const deserializedStruct = fory.deserialize(content.subarray(cursor)); |
| cursor += fory.readContext.reader.readGetCursor(); |
| |
| // Serialize the deserialized struct back |
| const serializedData = fory.serialize(deserializedStruct); |
| writeToFile(serializedData as Buffer); |
| }); |
| |
| test("test_unsigned_schema_consistent", () => { |
| const fory = new Fory({ |
| compatible: false, |
| }); |
| |
| @Type.struct(501, { |
| u8Field: Type.uint8(), |
| u16Field: Type.uint16(), |
| u32VarField: Type.uint32(), |
| u32FixedField: Type.uint32({ encoding: "fixed" }), |
| u64VarField: Type.uint64(), |
| u64FixedField: Type.uint64({ encoding: "fixed" }), |
| u64TaggedField: Type.uint64({ encoding: "tagged" }), |
| }) |
| class UnsignedSchemaConsistent { |
| u8Field: number = 0; |
| u16Field: number = 0; |
| u32VarField: number = 0; |
| u32FixedField: bigint = 0n; |
| u64VarField: bigint = 0n; |
| u64FixedField: bigint = 0n; |
| u64TaggedField: bigint = 0n; |
| |
| @(Type.uint8().setNullable(true)) |
| u8NullableField: number = 0; |
| |
| @(Type.uint16().setNullable(true)) |
| u16NullableField: number = 0; |
| |
| @(Type.uint32().setNullable(true)) |
| u32VarNullableField: number = 0; |
| |
| @(Type.uint32({ encoding: "fixed" }).setNullable(true)) |
| u32FixedNullableField: number = 0; |
| |
| @(Type.uint64().setNullable(true)) |
| u64VarNullableField: bigint = 0n; |
| |
| @(Type.uint64({ encoding: "fixed" }).setNullable(true)) |
| u64FixedNullableField: bigint = 0n; |
| |
| @(Type.uint64({ encoding: "tagged" }).setNullable(true)) |
| u64TaggedNullableField: bigint = 0n; |
| } |
| fory.register(UnsignedSchemaConsistent); |
| |
| // Deserialize struct from Java |
| let cursor = 0; |
| const deserializedStruct = fory.deserialize(content.subarray(cursor)); |
| cursor += fory.readContext.reader.readGetCursor(); |
| |
| // Serialize the deserialized struct back |
| const serializedBackData = fory.serialize(deserializedStruct); |
| writeToFile(serializedBackData as Buffer); |
| }); |
| |
| test("test_unsigned_schema_compatible", () => { |
| const fory = new Fory({ |
| compatible: true, |
| }); |
| |
| @Type.struct(502, { |
| u8Field1: Type.uint8(), |
| u16Field1: Type.uint16(), |
| u32VarField1: Type.uint32(), |
| u32FixedField1: Type.uint32({ encoding: "fixed" }), |
| u64VarField1: Type.uint64(), |
| u64FixedField1: Type.uint64({ encoding: "fixed" }), |
| u64TaggedField1: Type.uint64({ encoding: "tagged" }), |
| }) |
| class UnsignedSchemaCompatible { |
| u8Field1: number = 0; |
| u16Field1: number = 0; |
| u32VarField1: number = 0; |
| u32FixedField1: bigint = 0n; |
| u64VarField1: bigint = 0n; |
| u64FixedField1: bigint = 0n; |
| u64TaggedField1: bigint = 0n; |
| |
| @(Type.uint8().setNullable(true)) |
| u8Field2: number = 0; |
| |
| @(Type.uint16().setNullable(true)) |
| u16Field2: number = 0; |
| |
| @(Type.uint32().setNullable(true)) |
| u32VarField2: number = 0; |
| |
| @(Type.uint32({ encoding: "fixed" }).setNullable(true)) |
| u32FixedField2: number = 0; |
| |
| @(Type.uint64().setNullable(true)) |
| u64VarField2: bigint = 0n; |
| |
| @(Type.uint64({ encoding: "fixed" }).setNullable(true)) |
| u64FixedField2: bigint = 0n; |
| |
| @(Type.uint64({ encoding: "tagged" }).setNullable(true)) |
| u64TaggedField2: bigint = 0n; |
| } |
| fory.register(UnsignedSchemaCompatible); |
| |
| // Deserialize struct from Java |
| let cursor = 0; |
| const deserializedStruct = fory.deserialize(content.subarray(cursor)); |
| cursor += fory.readContext.reader.readGetCursor(); |
| |
| // Serialize the deserialized struct back |
| const serializedData = fory.serialize(deserializedStruct); |
| writeToFile(serializedData as Buffer); |
| }); |
| }); |