This is an automated email from the ASF dual-hosted git repository.
wangweipeng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fory.git
The following commit(s) were added to refs/heads/main by this push:
new cd2e0a47a feat(JavaScript): Align testcases (#3319)
cd2e0a47a is described below
commit cd2e0a47a00780d4a49b7e022cff1116c45247fb
Author: weipeng <[email protected]>
AuthorDate: Tue Feb 10 16:02:00 2026 +0800
feat(JavaScript): Align testcases (#3319)
## Why?
1. More testcases were passed, current progress 29/39
## What does this PR do?
1. Support `TAGGED_INT64` and `TAGGED_UINT64`
2. Fix float16 decode when the number is NaN or Infinity
## Related issues
#3133
## Does this PR introduce any user-facing change?
- [ ] Does this PR introduce any public API change?
- [ ] Does this PR introduce any binary protocol compatibility change?
## Benchmark
---
javascript/packages/fory/lib/gen/builder.ts | 16 ++++
javascript/packages/fory/lib/gen/number.ts | 23 ++---
javascript/packages/fory/lib/reader/index.ts | 52 ++++++++++
javascript/packages/fory/lib/writer/index.ts | 63 +++++++++++-
javascript/packages/fory/lib/writer/number.ts | 2 +-
javascript/test/crossLanguage.test.ts | 132 ++++++++++++++++++--------
javascript/test/number.test.ts | 30 ++++++
7 files changed, 268 insertions(+), 50 deletions(-)
diff --git a/javascript/packages/fory/lib/gen/builder.ts
b/javascript/packages/fory/lib/gen/builder.ts
index c03c9aba3..8138220f4 100644
--- a/javascript/packages/fory/lib/gen/builder.ts
+++ b/javascript/packages/fory/lib/gen/builder.ts
@@ -51,6 +51,14 @@ export class BinaryReaderBuilder {
return `${this.holder}.varInt32()`;
}
+ readTaggedInt64() {
+ return `${this.holder}.readTaggedInt64()`;
+ }
+
+ readTaggedUInt64() {
+ return `${this.holder}.readTaggedUInt64()`;
+ }
+
varInt64() {
return `${this.holder}.varInt64()`;
}
@@ -205,6 +213,14 @@ class BinaryWriterBuilder {
return `${this.holder}.varUInt64(${v})`;
}
+ writeTaggedInt64(v: number | string) {
+ return `${this.holder}.writeTaggedInt64(${v})`;
+ }
+
+ writeTaggedUInt64(v: number | string) {
+ return `${this.holder}.writeTaggedUInt64(${v})`;
+ }
+
varInt64(v: number | string) {
return `${this.holder}.varInt64(${v})`;
}
diff --git a/javascript/packages/fory/lib/gen/number.ts
b/javascript/packages/fory/lib/gen/number.ts
index 83c3ffe27..dd3380d5a 100644
--- a/javascript/packages/fory/lib/gen/number.ts
+++ b/javascript/packages/fory/lib/gen/number.ts
@@ -77,17 +77,25 @@ CodegenRegistry.register(TypeId.VARINT32,
CodegenRegistry.register(TypeId.INT64,
buildNumberSerializer(
- (builder, accessor) => builder.writer.sliInt64(accessor),
- builder => builder.reader.sliInt64()
+ (builder, accessor) => builder.writer.int64(accessor),
+ builder => builder.reader.int64()
)
);
CodegenRegistry.register(TypeId.TAGGED_INT64,
buildNumberSerializer(
- (builder, accessor) => builder.writer.sliInt64(accessor),
- builder => builder.reader.sliInt64()
+ (builder, accessor) => builder.writer.writeTaggedInt64(accessor),
+ builder => builder.reader.readTaggedInt64()
)
);
+
+CodegenRegistry.register(TypeId.TAGGED_UINT64,
+ buildNumberSerializer(
+ (builder, accessor) => builder.writer.writeTaggedUInt64(accessor),
+ builder => builder.reader.readTaggedUInt64()
+ )
+);
+
CodegenRegistry.register(TypeId.FLOAT16,
buildNumberSerializer(
(builder, accessor) => builder.writer.float16(accessor),
@@ -149,13 +157,6 @@ CodegenRegistry.register(TypeId.VAR_UINT64,
)
);
-CodegenRegistry.register(TypeId.TAGGED_UINT64,
- buildNumberSerializer(
- (builder, accessor) => builder.writer.varUInt64(accessor),
- builder => builder.reader.varUInt64()
- )
-);
-
CodegenRegistry.register(TypeId.VARINT64,
buildNumberSerializer(
(builder, accessor) => builder.writer.varInt64(accessor),
diff --git a/javascript/packages/fory/lib/reader/index.ts
b/javascript/packages/fory/lib/reader/index.ts
index 1fb970b71..d63cd2f15 100644
--- a/javascript/packages/fory/lib/reader/index.ts
+++ b/javascript/packages/fory/lib/reader/index.ts
@@ -104,6 +104,58 @@ export class BinaryReader {
return this.varInt64();
}
+ /**
+ * Read signed fory Tagged(Small Long as Int) encoded long.
+ * If the first bit is 0, it's a 4-byte int shifted left by 1 bit.
+ * If the first bit is 1, it's a 9-byte format with 0b1 flag + 8-byte long.
+ */
+ readTaggedInt64(): bigint {
+ const readIdx = this.cursor;
+ if (this.byteLength - readIdx < 4) {
+ throw new Error("Insufficient bytes for tagged int64");
+ }
+
+ const i = this.dataView.getInt32(readIdx, true);
+ if ((i & 0b1) !== 0b1) {
+ // Small long encoded as int
+ this.cursor = readIdx + 4;
+ return BigInt(i >> 1);
+ } else {
+ // Big long encoded as 8 bytes
+ if (this.byteLength - readIdx < 9) {
+ throw new Error("Insufficient bytes for big tagged int64");
+ }
+ this.cursor = readIdx + 1; // Skip the flag byte
+ return this.int64();
+ }
+ }
+
+ /**
+ * Read unsigned fory Tagged(Small Long as Int) encoded long.
+ * If the first bit is 0, it's a 4-byte uint shifted left by 1 bit.
+ * If the first bit is 1, it's a 9-byte format with 0b1 flag + 8-byte ulong.
+ */
+ readTaggedUInt64(): bigint {
+ const readIdx = this.cursor;
+ if (this.byteLength - readIdx < 4) {
+ throw new Error("Insufficient bytes for tagged uint64");
+ }
+
+ const i = this.dataView.getUint32(readIdx, true);
+ if ((i & 0b1) !== 0b1) {
+ // Small ulong encoded as uint
+ this.cursor = readIdx + 4;
+ return BigInt(i >>> 1); // unsigned right shift
+ } else {
+ // Big ulong encoded as 8 bytes
+ if (this.byteLength - readIdx < 9) {
+ throw new Error("Insufficient bytes for big tagged uint64");
+ }
+ this.cursor = readIdx + 1; // Skip the flag byte
+ return this.uint64();
+ }
+ }
+
float32() {
const result = this.dataView.getFloat32(this.cursor, true);
this.cursor += 4;
diff --git a/javascript/packages/fory/lib/writer/index.ts
b/javascript/packages/fory/lib/writer/index.ts
index 0ebd139c7..5c4036602 100644
--- a/javascript/packages/fory/lib/writer/index.ts
+++ b/javascript/packages/fory/lib/writer/index.ts
@@ -125,7 +125,11 @@ export class BinaryWriter {
}
int64(v: bigint) {
- this.dataView.setBigInt64(this.cursor, v, true);
+ if (typeof v !== "bigint") {
+ this.dataView.setBigInt64(this.cursor, BigInt(v), true);
+ } else {
+ this.dataView.setBigInt64(this.cursor, v, true);
+ }
this.cursor += 8;
}
@@ -147,6 +151,63 @@ export class BinaryWriter {
}
}
+ /**
+ * Write signed long using fory Tagged(Small long as int) encoding.
+ * If long is in [0xc0000000, 0x3fffffff], encode as 4 bytes int: |
little-endian: ((int) value) << 1 |
+ * Otherwise write as 9 bytes: | 0b1 | little-endian 8bytes long |
+ */
+ writeTaggedInt64(value: bigint | number): number {
+ if (typeof value !== "bigint") {
+ value = BigInt(value);
+ }
+
+ const halfMaxInt32 = 0x3fffffffn; // 0x3fffffff
+ const halfMinInt32 = -0x40000000n; // 0xc0000000 as signed
+
+ if (value >= halfMinInt32 && value <= halfMaxInt32) {
+ // Small long encoded as int
+ const v = Number(value) << 1; // bit 0 unset, means int
+ this.dataView.setInt32(this.cursor, v, true);
+ this.cursor += 4;
+ return 4;
+ } else {
+ // Big long encoded as 8 bytes
+ const BIG_LONG_FLAG = 0b1; // bit 0 set, means big long
+ this.dataView.setUint8(this.cursor, BIG_LONG_FLAG);
+ this.dataView.setBigInt64(this.cursor + 1, value, true);
+ this.cursor += 9;
+ return 9;
+ }
+ }
+
+ /**
+ * Write unsigned long using fory Tagged(Small long as int) encoding.
+ * If long is in [0, 0x7fffffff], encode as 4 bytes int: | little-endian:
((int) value) << 1 |
+ * Otherwise write as 9 bytes: | 0b1 | little-endian 8bytes long |
+ */
+ writeTaggedUInt64(value: bigint | number): number {
+ if (typeof value !== "bigint") {
+ value = BigInt(value);
+ }
+
+ const maxUInt32 = 0x7fffffffn; // 0x7fffffff
+
+ if (value >= 0n && value <= maxUInt32) {
+ // Small ulong encoded as uint
+ const v = Number(value) << 1; // bit 0 unset, means int
+ this.dataView.setUint32(this.cursor, v, true);
+ this.cursor += 4;
+ return 4;
+ } else {
+ // Big ulong encoded as 8 bytes
+ const BIG_LONG_FLAG = 0b1; // bit 0 set, means big long
+ this.dataView.setUint8(this.cursor, BIG_LONG_FLAG);
+ this.dataView.setBigUint64(this.cursor + 1, value, true);
+ this.cursor += 9;
+ return 9;
+ }
+ }
+
float32(v: number) {
this.dataView.setFloat32(this.cursor, v, true);
this.cursor += 4;
diff --git a/javascript/packages/fory/lib/writer/number.ts
b/javascript/packages/fory/lib/writer/number.ts
index 55b81883d..8729dc372 100644
--- a/javascript/packages/fory/lib/writer/number.ts
+++ b/javascript/packages/fory/lib/writer/number.ts
@@ -28,7 +28,7 @@ export function toFloat16(value: number) {
const significand = floatValue & 0x7fffff; // extract significand from
floatValue
if (exponent === 128) { // floatValue is NaN or Infinity
- return sign | ((exponent === 128) ? 0x7c00 : 0x7fff);
+ return sign | 0x7c00 | (significand !== 0 ? 0x0200 : 0);
}
if (exponent > 15) {
diff --git a/javascript/test/crossLanguage.test.ts
b/javascript/test/crossLanguage.test.ts
index 3f4f0b2a7..bd4c5ed5a 100644
--- a/javascript/test/crossLanguage.test.ts
+++ b/javascript/test/crossLanguage.test.ts
@@ -1483,23 +1483,21 @@ describe("bool", () => {
});
test("test_unsigned_schema_consistent_simple", () => {
- if (Boolean("1")) { return; }
const fory = new Fory({
- mode: Mode.Compatible
+ mode: Mode.SchemaConsistent
});
@Type.struct(1, {
- u64Tagged: Type.int64(),
- u64TaggedNullable: Type.int64()
+ u64Tagged: Type.taggedUInt64(),
+ u64TaggedNullable: Type.taggedUInt64()
})
- class UnsignedStruct {
+ class UnsignedSchemaConsistentSimple {
u64Tagged: bigint = 0n;
+
+ @ForyField({nullable: true})
u64TaggedNullable: bigint | null = null;
}
- fory.registerSerializer(UnsignedStruct);
-
- const reader = new BinaryReader({});
- reader.reset(content);
+ fory.registerSerializer(UnsignedSchemaConsistentSimple);
// Deserialize struct from Java
let cursor = 0;
@@ -1512,27 +1510,57 @@ describe("bool", () => {
});
test("test_unsigned_schema_consistent", () => {
- if (Boolean("1")) { return; }
const fory = new Fory({
- mode: Mode.Compatible
+ mode: Mode.SchemaConsistent
});
@Type.struct(501, {
u8Field: Type.uint8(),
u16Field: Type.uint16(),
- u32Field: Type.uint32(),
- u64Field: Type.uint64()
+ u32VarField: Type.varUInt32(),
+ u32FixedField: Type.uint32(),
+ u64VarField: Type.varUInt64(),
+ u64FixedField: Type.uint64(),
+ u64TaggedField: Type.taggedUInt64(),
})
- class UnsignedStruct {
+ class UnsignedSchemaConsistent {
u8Field: number = 0;
u16Field: number = 0;
- u32Field: number = 0;
- u64Field: bigint = 0n;
- }
- fory.registerSerializer(UnsignedStruct);
+ u32VarField: number = 0;
+ u32FixedField: bigint = 0n;
+ u64VarField: bigint = 0n;
+ u64FixedField: bigint = 0n;
+ u64TaggedField: bigint = 0n;
- const reader = new BinaryReader({});
- reader.reset(content);
+ @ForyField({nullable: true})
+ @Type.uint8()
+ u8NullableField: number = 0;
+
+ @ForyField({nullable: true})
+ @Type.uint16()
+ u16NullableField: number = 0;
+
+ @ForyField({nullable: true})
+ @Type.varUInt32()
+ u32VarNullableField: number = 0;
+
+ @ForyField({nullable: true})
+ @Type.uint32()
+ u32FixedNullableField: number = 0;
+
+ @ForyField({nullable: true})
+ @Type.varUInt64()
+ u64VarNullableField: bigint = 0n;
+
+ @ForyField({nullable: true})
+ @Type.uint64()
+ u64FixedNullableField: bigint = 0n;
+
+ @ForyField({nullable: true})
+ @Type.taggedUInt64()
+ u64TaggedNullableField: bigint = 0n;
+ }
+ fory.registerSerializer(UnsignedSchemaConsistent);
// Deserialize struct from Java
let cursor = 0;
@@ -1540,32 +1568,62 @@ describe("bool", () => {
cursor += fory.binaryReader.getCursor();
// Serialize the deserialized struct back
- const serializedData = fory.serialize(deserializedStruct);
- writeToFile(serializedData as Buffer);
+ const serializedBackData = fory.serialize(deserializedStruct);
+ writeToFile(serializedBackData as Buffer);
});
test("test_unsigned_schema_compatible", () => {
- if (Boolean("1")) { return; }
const fory = new Fory({
mode: Mode.Compatible
});
@Type.struct(502, {
- u8Field: Type.uint8(),
- u16Field: Type.uint16(),
- u32Field: Type.uint32(),
- u64Field: Type.uint64()
+ u8Field1: Type.uint8(),
+ u16Field1: Type.uint16(),
+ u32VarField1: Type.varUInt32(),
+ u32FixedField1: Type.uint32(),
+ u64VarField1: Type.varUInt64(),
+ u64FixedField1: Type.uint64(),
+ u64TaggedField1: Type.taggedUInt64(),
})
- class UnsignedStruct {
- u8Field: number = 0;
- u16Field: number = 0;
- u32Field: number = 0;
- u64Field: bigint = 0n;
- }
- fory.registerSerializer(UnsignedStruct);
+ class UnsignedSchemaCompatible {
+ u8Field1: number = 0;
+ u16Field1: number = 0;
+ u32VarField1: number = 0;
+ u32FixedField1: bigint = 0n;
+ u64VarField1: bigint = 0n;
+ u64FixedField1: bigint = 0n;
+ u64TaggedField1: bigint = 0n;
- const reader = new BinaryReader({});
- reader.reset(content);
+ @ForyField({nullable: true})
+ @Type.uint8()
+ u8Field2: number = 0;
+
+ @ForyField({nullable: true})
+ @Type.uint16()
+ u16Field2: number = 0;
+
+ @ForyField({nullable: true})
+ @Type.varUInt32()
+ u32VarField2: number = 0;
+
+ @ForyField({nullable: true})
+ @Type.uint32()
+ u32FixedField2: number = 0;
+
+ @ForyField({nullable: true})
+ @Type.varUInt64()
+ u64VarField2: bigint = 0n;
+
+ @ForyField({nullable: true})
+ @Type.uint64()
+ u64FixedField2: bigint = 0n;
+
+ @ForyField({nullable: true})
+ @Type.taggedUInt64()
+ u64TaggedField2: bigint = 0n;
+ }
+ fory.registerSerializer(UnsignedSchemaCompatible);
// Deserialize struct from Java
let cursor = 0;
@@ -1574,6 +1632,6 @@ describe("bool", () => {
// Serialize the deserialized struct back
const serializedData = fory.serialize(deserializedStruct);
- // writeToFile(serializedData as Buffer);
+ writeToFile(serializedData as Buffer);
});
});
diff --git a/javascript/test/number.test.ts b/javascript/test/number.test.ts
index 37c15a9e4..a13185be9 100644
--- a/javascript/test/number.test.ts
+++ b/javascript/test/number.test.ts
@@ -123,6 +123,36 @@ describe('number', () => {
expect(result.a).toBeCloseTo(1.2, 1)
});
+ test('should float16 NAN work', () => {
+
+ const fory = new Fory({ refTracking: true });
+ const serializer = fory.registerSerializer(Type.struct({
+ typeName: "example.foo"
+ }, {
+ a: Type.float16()
+ })).serializer;
+ const input = fory.serialize({ a: NaN }, serializer);
+ const result = fory.deserialize(
+ input
+ );
+ expect(result.a).toBe(NaN)
+ });
+
+ test('should float16 Infinity work', () => {
+
+ const fory = new Fory({ refTracking: true });
+ const serializer = fory.registerSerializer(Type.struct({
+ typeName: "example.foo"
+ }, {
+ a: Type.float16()
+ })).serializer;
+ const input = fory.serialize({ a: Infinity }, serializer);
+ const result = fory.deserialize(
+ input
+ );
+ expect(result.a).toBeCloseTo(Infinity)
+ });
+
test('should uint8 work', () => {
const fory = new Fory({ refTracking: true });
const serializer = fory.registerSerializer(Type.struct({
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]