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 96684971c feat(JavaScript): Support EXT,NAMED_EXT (#3312)
96684971c is described below

commit 96684971cdbf6238717223fcccf88426227b19e4
Author: weipeng <[email protected]>
AuthorDate: Mon Feb 9 14:53:28 2026 +0800

    feat(JavaScript): Support EXT,NAMED_EXT (#3312)
    
    ## Why?
    
    
    
    ## What does this PR do?
    1. support EXT,NAMED_EXT and classVersionHash
    2. Add `computeStructFingerprint`
    3. More xlang testcases in crossLanguage.test.ts were passed.
    4. Support null chunk in Map
    
    ## 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/fory.ts           |  27 +-
 javascript/packages/fory/lib/gen/collection.ts |   4 +-
 javascript/packages/fory/lib/gen/ext.ts        | 185 ++++++++++++++
 javascript/packages/fory/lib/gen/index.ts      |   1 +
 javascript/packages/fory/lib/gen/map.ts        | 172 ++++++++-----
 javascript/packages/fory/lib/gen/struct.ts     |  15 +-
 javascript/packages/fory/lib/meta/TypeMeta.ts  | 122 +++++----
 javascript/packages/fory/lib/type.ts           |   9 +
 javascript/packages/fory/lib/typeInfo.ts       |  79 +++++-
 javascript/packages/fory/lib/typeResolver.ts   |  12 +
 javascript/test/crossLanguage.test.ts          | 328 ++++++++++++++++---------
 11 files changed, 724 insertions(+), 230 deletions(-)

diff --git a/javascript/packages/fory/lib/fory.ts 
b/javascript/packages/fory/lib/fory.ts
index 88ab42a6f..fbef0e3ff 100644
--- a/javascript/packages/fory/lib/fory.ts
+++ b/javascript/packages/fory/lib/fory.ts
@@ -21,7 +21,7 @@ import TypeResolver from "./typeResolver";
 import { BinaryWriter } from "./writer";
 import { BinaryReader } from "./reader";
 import { ReferenceResolver } from "./referenceResolver";
-import { ConfigFlags, Serializer, Config, Mode, ForyTypeInfoSymbol, 
WithForyClsInfo, TypeId } from "./type";
+import { ConfigFlags, Serializer, Config, Mode, ForyTypeInfoSymbol, 
WithForyClsInfo, TypeId, CustomSerializer } from "./type";
 import { OwnershipError } from "./error";
 import { InputType, ResultType, StructTypeInfo, TypeInfo } from "./typeInfo";
 import { Gen } from "./gen";
@@ -59,6 +59,7 @@ export default class {
       useSliceString: Boolean(config?.useSliceString),
       hooks: config?.hooks || {},
       mode: config?.mode || Mode.SchemaConsistent,
+      classVersionHash: config?.classVersionHash || false,
     };
   }
 
@@ -66,14 +67,14 @@ export default class {
     return this.config.mode === Mode.Compatible;
   }
 
-  registerSerializer<T extends new () => any>(constructor: T): {
+  registerSerializer<T>(constructor: new () => T, customSerializer: 
CustomSerializer<T>): {
     serializer: Serializer;
-    serialize(data: Partial<InstanceType<T>> | null): PlatformBuffer;
-    serializeVolatile(data: Partial<InstanceType<T>>): {
+    serialize(data: InputType<T> | null): PlatformBuffer;
+    serializeVolatile(data: InputType<T>): {
       get: () => Uint8Array;
       dispose: () => void;
     };
-    deserialize(bytes: Uint8Array): InstanceType<T> | null;
+    deserialize(bytes: Uint8Array): ResultType<T>;
   };
   registerSerializer<T extends TypeInfo>(typeInfo: T): {
     serializer: Serializer;
@@ -84,12 +85,21 @@ export default class {
     };
     deserialize(bytes: Uint8Array): ResultType<T>;
   };
-  registerSerializer(constructor: any) {
+  registerSerializer<T extends new () => any>(constructor: T): {
+    serializer: Serializer;
+    serialize(data: Partial<InstanceType<T>> | null): PlatformBuffer;
+    serializeVolatile(data: Partial<InstanceType<T>>): {
+      get: () => Uint8Array;
+      dispose: () => void;
+    };
+    deserialize(bytes: Uint8Array): InstanceType<T> | null;
+  };
+  registerSerializer(constructor: any, customSerializer?: 
CustomSerializer<any>) {
     let serializer: Serializer;
     TypeInfo.attach(this);
     if (constructor.prototype?.[ForyTypeInfoSymbol]) {
       const typeInfo: TypeInfo = 
(<WithForyClsInfo>(constructor.prototype[ForyTypeInfoSymbol])).structTypeInfo;
-      serializer = new Gen(this, { constructor }).generateSerializer(typeInfo);
+      serializer = new Gen(this, { creator: constructor, customSerializer 
}).generateSerializer(typeInfo);
       this.typeResolver.registerSerializer(typeInfo, serializer);
     } else {
       const typeInfo = constructor;
@@ -116,7 +126,7 @@ export default class {
 
   replaceSerializerReader(typeInfo: TypeInfo) {
     TypeInfo.attach(this);
-    const serializer = new Gen(this, { constroctor: (typeInfo as 
StructTypeInfo).options.constructor }).reGenerateSerializer(typeInfo);
+    const serializer = new Gen(this, { creator: (typeInfo as 
StructTypeInfo).options.creator }).reGenerateSerializer(typeInfo);
     const result = this.typeResolver.registerSerializer(typeInfo, {
       getHash: serializer.getHash,
       read: serializer.read,
@@ -158,6 +168,7 @@ export default class {
       throw e;
     }
     this.referenceResolver.reset();
+    this.metaStringResolver.reset();
     let bitmap = 0;
     if (data === null) {
       bitmap |= ConfigFlags.isNullFlag;
diff --git a/javascript/packages/fory/lib/gen/collection.ts 
b/javascript/packages/fory/lib/gen/collection.ts
index 2d7941bfa..808d92546 100644
--- a/javascript/packages/fory/lib/gen/collection.ts
+++ b/javascript/packages/fory/lib/gen/collection.ts
@@ -257,7 +257,7 @@ export abstract class CollectionSerializerGenerator extends 
BaseSerializerGenera
             
${this.builder.writer.reserve(`${this.innerGenerator.getFixedSize()} * 
${accessor}.${this.sizeProp()}`)};
             if (${flags} & ${CollectionFlags.TRACKING_REF}) {
                 for (const ${item} of ${accessor}) {
-                    if (${accessor} !== null && ${accessor} !== undefined) {
+                    if (${item} !== null && ${item} !== undefined) {
                         const ${existsId} = 
${this.builder.referenceResolver.existsWriteObject(item)};
                         if (typeof ${existsId} === "number") {
                             ${this.builder.writer.int8(RefFlags.RefFlag)}
@@ -273,7 +273,7 @@ export abstract class CollectionSerializerGenerator extends 
BaseSerializerGenera
                 }
             } else if (${flags} & ${CollectionFlags.HAS_NULL}) {
                 for (const ${item} of ${accessor}) {
-                    if (${accessor} !== null && ${accessor} !== undefined) {
+                    if (${item} !== null && ${item} !== undefined) {
                         ${this.builder.writer.int8(RefFlags.NotNullValueFlag)};
                         ${this.innerGenerator.writeEmbed().write(item)}
                     } else {
diff --git a/javascript/packages/fory/lib/gen/ext.ts 
b/javascript/packages/fory/lib/gen/ext.ts
new file mode 100644
index 000000000..835e1f525
--- /dev/null
+++ b/javascript/packages/fory/lib/gen/ext.ts
@@ -0,0 +1,185 @@
+/*
+ * 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 { TypeId, Mode } from "../type";
+import { Scope } from "./scope";
+import { CodecBuilder } from "./builder";
+import { StructTypeInfo, TypeInfo } from "../typeInfo";
+import { CodegenRegistry } from "./router";
+import { BaseSerializerGenerator } from "./serializer";
+import { TypeMeta } from "../meta/TypeMeta";
+
+class ExtSerializerGenerator extends BaseSerializerGenerator {
+  typeInfo: StructTypeInfo;
+  typeMeta: TypeMeta;
+
+  constructor(typeInfo: TypeInfo, builder: CodecBuilder, scope: Scope) {
+    super(typeInfo, builder, scope);
+    this.typeInfo = <StructTypeInfo>typeInfo;
+    this.typeMeta = TypeMeta.fromTypeInfo(this.typeInfo);
+  }
+
+  write(accessor: string): string {
+    return `
+      ${this.builder.getOptions("customSerializer")}.write(${accessor}, 
${this.builder.writer.ownName()}, ${this.builder.getForyName()}, )
+    `;
+  }
+
+  read(accessor: (expr: string) => string, refState: string): string {
+    const result = this.scope.uniqueName("result");
+    return `
+      ${this.typeInfo.options.withConstructor
+        ? `
+          const ${result} = new ${this.builder.getOptions("creator")}();
+        `
+        : `
+          const ${result} = {};
+        `
+      }
+      ${this.maybeReference(result, refState)}
+      ${this.builder.getOptions("customSerializer")}.read(${result}, 
${this.builder.reader.ownName()}, ${this.builder.getForyName()})
+      ${
+        accessor(result)
+      }
+    `;
+  }
+
+  readNoRef(assignStmt: (v: string) => string, refState: string): string {
+    return `
+      ${this.readTypeInfo()}
+      ${this.read(assignStmt, refState)};
+    `;
+  }
+
+  readTypeInfo(): string {
+    const typeMeta = this.scope.uniqueName("typeMeta");
+    const internalTypeId = this.getInternalTypeId();
+    let namesStmt = "";
+    let typeMetaStmt = "";
+    let readUserTypeIdStmt = "";
+    switch (internalTypeId) {
+      case TypeId.EXT:
+        readUserTypeIdStmt = `${this.builder.reader.readVarUint32Small7()};`;
+        break;
+      case TypeId.NAMED_EXT:
+        if (!this.builder.fory.isCompatible()) {
+          namesStmt = `
+            ${
+              
this.builder.metaStringResolver.readNamespace(this.builder.reader.ownName())
+            };
+            ${
+              
this.builder.metaStringResolver.readTypeName(this.builder.reader.ownName())
+            };
+          `;
+        } else {
+          typeMetaStmt = `
+          const ${typeMeta} = 
${this.builder.typeMetaResolver.readTypeMeta(this.builder.reader.ownName())};
+          `;
+        }
+        break;
+    }
+    return `
+      ${
+        this.builder.reader.uint8()
+      };
+      ${readUserTypeIdStmt}
+      ${
+        namesStmt
+      }
+      ${
+        typeMetaStmt
+      }
+    `;
+  }
+
+  readEmbed() {
+    return new Proxy({}, {
+      get: (target, prop: string) => {
+        return (accessor: (expr: string) => string, ...args: string[]) => {
+          const name = this.scope.declare(
+            "ext_ser",
+            TypeId.isNamedType(this.typeInfo.typeId)
+              ? 
this.builder.typeResolver.getSerializerByName(CodecBuilder.replaceBackslashAndQuote(this.typeInfo.named!))
+              : 
this.builder.typeResolver.getSerializerById(this.typeInfo.typeId, 
this.typeInfo.userTypeId)
+          );
+          return accessor(`${name}.${prop}(${args.join(",")})`);
+        };
+      },
+    });
+  }
+
+  writeEmbed() {
+    return new Proxy({}, {
+      get: (target, prop: string) => {
+        return (accessor: string) => {
+          const name = this.scope.declare(
+            "ext_ser",
+            TypeId.isNamedType(this.typeInfo.typeId)
+              ? 
this.builder.typeResolver.getSerializerByName(CodecBuilder.replaceBackslashAndQuote(this.typeInfo.named!))
+              : 
this.builder.typeResolver.getSerializerById(this.typeInfo.typeId, 
this.typeInfo.userTypeId)
+          );
+          return `${name}.${prop}(${accessor})`;
+        };
+      },
+    });
+  }
+
+  writeTypeInfo(): string {
+    const internalTypeId = this.getInternalTypeId();
+    let typeMeta = "";
+    let writeUserTypeIdStmt = "";
+    switch (internalTypeId) {
+      case TypeId.EXT:
+        writeUserTypeIdStmt = 
this.builder.writer.writeVarUint32Small7(this.typeInfo.userTypeId);
+        break;
+      case TypeId.NAMED_EXT:
+        if (this.builder.fory.config.mode !== Mode.Compatible) {
+          const typeInfo = this.typeInfo;
+          const nsBytes = this.scope.declare("nsBytes", 
this.builder.metaStringResolver.encodeNamespace(CodecBuilder.replaceBackslashAndQuote(typeInfo.namespace)));
+          const typeNameBytes = this.scope.declare("typeNameBytes", 
this.builder.metaStringResolver.encodeTypeName(CodecBuilder.replaceBackslashAndQuote(typeInfo.typeName)));
+          typeMeta = `
+            
${this.builder.metaStringResolver.writeBytes(this.builder.writer.ownName(), 
nsBytes)}
+            
${this.builder.metaStringResolver.writeBytes(this.builder.writer.ownName(), 
typeNameBytes)}
+          `;
+        } else {
+          const bytes = this.scope.declare("typeInfoBytes", `new 
Uint8Array([${TypeMeta.fromTypeInfo(<StructTypeInfo> 
this.typeInfo).toBytes().join(",")}])`);
+          typeMeta = 
this.builder.typeMetaResolver.writeTypeMeta(this.builder.getTypeInfo(), 
this.builder.writer.ownName(), bytes);
+        }
+        break;
+      default:
+        break;
+    }
+    return ` 
+      ${this.builder.writer.uint8(this.getTypeId())};
+      ${writeUserTypeIdStmt}
+      ${typeMeta}
+    `;
+  }
+
+  getFixedSize(): number {
+    return 5;
+  }
+
+  getHash(): string {
+    return "0";
+  }
+}
+
+CodegenRegistry.register(TypeId.EXT, ExtSerializerGenerator);
+CodegenRegistry.register(TypeId.NAMED_EXT, ExtSerializerGenerator);
diff --git a/javascript/packages/fory/lib/gen/index.ts 
b/javascript/packages/fory/lib/gen/index.ts
index 1234148ae..64529f060 100644
--- a/javascript/packages/fory/lib/gen/index.ts
+++ b/javascript/packages/fory/lib/gen/index.ts
@@ -35,6 +35,7 @@ import "./struct";
 import "./typedArray";
 import "./enum";
 import "./any";
+import "./ext";
 import Fory from "../fory";
 
 export class Gen {
diff --git a/javascript/packages/fory/lib/gen/map.ts 
b/javascript/packages/fory/lib/gen/map.ts
index 89066af8d..e7f2a2d19 100644
--- a/javascript/packages/fory/lib/gen/map.ts
+++ b/javascript/packages/fory/lib/gen/map.ts
@@ -89,13 +89,19 @@ class MapChunkWriter {
     return flag;
   }
 
-  private writeHead(keyInfo: number, valueInfo: number) {
-    // chunkSize, max 255
-    this.chunkOffset = this.fory.binaryWriter.getCursor();
+  private writeHead(keyInfo: number, valueInfo: number, withOutSize = false) {
     // KV header
     const header = this.getHead(keyInfo, valueInfo);
     // chunkSize default 0 | KV header
-    this.fory.binaryWriter.uint16(header);
+    this.fory.binaryWriter.uint8(header);
+
+    if (!withOutSize) {
+      // chunkSize, max 255
+      this.chunkOffset = this.fory.binaryWriter.getCursor();
+      this.fory.binaryWriter.uint8(0);
+    } else {
+      this.chunkOffset = 0;
+    }
     if (this.keySerializer) {
       this.keySerializer.writeTypeInfo(null);
     }
@@ -105,7 +111,18 @@ class MapChunkWriter {
     return header;
   }
 
+  public isFirst() {
+    return this.chunkSize === 0 || this.chunkSize === 1;
+  }
+
   next(keyInfo: number, valueInfo: number) {
+    if (keyInfo & MapFlags.HAS_NULL || valueInfo & MapFlags.HAS_NULL) {
+      this.endChunk();
+      this.header = this.writeHead(keyInfo, valueInfo, true);
+      this.preKeyInfo = keyInfo;
+      this.preValueInfo = valueInfo;
+      return this.header;
+    }
     // max size of chunk is 255
     if (this.chunkSize == 255
       || this.chunkOffset == 0
@@ -125,7 +142,7 @@ class MapChunkWriter {
 
   endChunk() {
     if (this.chunkOffset > 0) {
-      this.fory.binaryWriter.setUint8Position(this.chunkOffset + 1, 
this.chunkSize);
+      this.fory.binaryWriter.setUint8Position(this.chunkOffset, 
this.chunkSize);
       this.chunkSize = 0;
     }
   }
@@ -145,26 +162,17 @@ class MapAnySerializer {
   }
 
   private writeFlag(header: number, v: any) {
+    if (header & MapFlags.HAS_NULL) {
+      return true;
+    }
     if (header & MapFlags.TRACKING_REF) {
-      if (v === null || v === undefined) {
-        this.fory.binaryWriter.uint8(RefFlags.NullFlag);
-        return true;
-      }
       const keyRef = this.fory.referenceResolver.existsWriteObject(v);
       if (keyRef !== undefined) {
-        this.fory.binaryWriter.uint8(RefFlags.RefFlag);
+        this.fory.binaryWriter.int8(RefFlags.RefFlag);
         this.fory.binaryWriter.uint16(keyRef);
         return true;
       } else {
-        this.fory.binaryWriter.uint8(RefFlags.RefValueFlag);
-        return false;
-      }
-    } else if (header & MapFlags.HAS_NULL) {
-      if (v === null || v === undefined) {
-        this.fory.binaryWriter.uint8(RefFlags.NullFlag);
-        return true;
-      } else {
-        this.fory.binaryWriter.uint8(RefFlags.NotNullValueFlag);
+        this.fory.binaryWriter.int8(RefFlags.RefValueFlag);
         return false;
       }
     }
@@ -179,18 +187,32 @@ class MapAnySerializer {
       const valueSerializer = this.valueSerializer !== null ? 
this.valueSerializer : this.fory.typeResolver.getSerializerByData(v);
 
       const header = mapChunkWriter.next(
-        MapHeadUtil.elementInfo(keySerializer!.getTypeId()!, k == null ? 1 : 
0, keySerializer!.needToWriteRef() ? 1 : 0),
-        MapHeadUtil.elementInfo(valueSerializer!.getTypeId()!, v == null ? 1 : 
0, valueSerializer!.needToWriteRef() ? 1 : 0)
+        MapHeadUtil.elementInfo(keySerializer?.getTypeId() ?? 0, k == null ? 1 
: 0, keySerializer?.needToWriteRef() ? 1 : 0),
+        MapHeadUtil.elementInfo(valueSerializer?.getTypeId() ?? 0, v == null ? 
1 : 0, valueSerializer?.needToWriteRef() ? 1 : 0)
       );
-      if (!this.writeFlag(header & 0b00001111, k)) {
-        if (this.keySerializer) {
+      const keyHeader = header & 0b111;
+      const valueHeader = (header >> 3);
+      if (mapChunkWriter.isFirst()) {
+        if (!(keyHeader & MapFlags.HAS_NULL) && !(valueHeader & 
MapFlags.HAS_NULL)) {
+          if (!(keyHeader & MapFlags.DECL_ELEMENT_TYPE)) {
+            keySerializer?.writeTypeInfo(null);
+          }
+          if (!(valueHeader & MapFlags.DECL_ELEMENT_TYPE)) {
+            valueSerializer?.writeTypeInfo(null);
+          }
+        }
+      }
+
+      const includeNone = (keyHeader & MapFlags.HAS_NULL) || (valueHeader & 
MapFlags.HAS_NULL);
+      if (!this.writeFlag(keyHeader, k)) {
+        if (!includeNone) {
           keySerializer!.write(k);
         } else {
           keySerializer!.writeNoRef(k);
         }
       }
-      if (!this.writeFlag(header >> 4, v)) {
-        if (this.valueSerializer) {
+      if (!this.writeFlag(valueHeader, v)) {
+        if (!includeNone) {
           valueSerializer!.write(v);
         } else {
           valueSerializer!.writeNoRef(v);
@@ -201,25 +223,28 @@ class MapAnySerializer {
   }
 
   private readElement(header: number, serializer: Serializer | null) {
-    const declared = header & MapFlags.DECL_ELEMENT_TYPE;
     const includeNone = header & MapFlags.HAS_NULL;
     const trackingRef = header & MapFlags.TRACKING_REF;
 
-    if (!declared) {
-      serializer = AnyHelper.detectSerializer(this.fory);
+    if (includeNone) {
+      return null;
     }
-    if (!trackingRef && !includeNone) {
+    if (!trackingRef) {
+      serializer = serializer == null ? AnyHelper.detectSerializer(this.fory) 
: serializer;
       return serializer!.read(false);
     }
-    const flag = this.fory.binaryReader.uint8();
+
+    const flag = this.fory.binaryReader.int8();
     switch (flag) {
       case RefFlags.RefValueFlag:
+        serializer = serializer == null ? 
AnyHelper.detectSerializer(this.fory) : serializer;
         return serializer!.read(true);
       case RefFlags.RefFlag:
         return 
this.fory.referenceResolver.getReadObject(this.fory.binaryReader.varUInt32());
       case RefFlags.NullFlag:
         return null;
       case RefFlags.NotNullValueFlag:
+        serializer = serializer == null ? 
AnyHelper.detectSerializer(this.fory) : serializer;
         return serializer!.read(false);
     }
   }
@@ -231,20 +256,28 @@ class MapAnySerializer {
       this.fory.referenceResolver.reference(result);
     }
     while (count > 0) {
-      const header = this.fory.binaryReader.uint16();
+      const header = this.fory.binaryReader.uint8();
       const valueHeader = (header >> 3) & 0b111;
       const keyHeader = header & 0b111;
-      const chunkSize = header >> 8;
+      let chunkSize = 0;
+      if ((valueHeader & MapFlags.HAS_NULL) || (keyHeader & 
MapFlags.HAS_NULL)) {
+        chunkSize = 1;
+      } else {
+        chunkSize = this.fory.binaryReader.uint8();
+      }
+      let keySerializer = this.keySerializer;
+      let valueSerializer = this.valueSerializer;
 
-      let keySerializer = null;
-      let valueSerializer = null;
+      if (!(keyHeader & MapFlags.HAS_NULL) && !(valueHeader & 
MapFlags.HAS_NULL)) {
+        if (!(keyHeader & MapFlags.DECL_ELEMENT_TYPE)) {
+          keySerializer = AnyHelper.detectSerializer(this.fory);
+        }
 
-      if (keyHeader & MapFlags.DECL_ELEMENT_TYPE) {
-        keySerializer = AnyHelper.detectSerializer(this.fory);
-      }
-      if (valueHeader & MapFlags.DECL_ELEMENT_TYPE) {
-        valueSerializer = AnyHelper.detectSerializer(this.fory);
+        if (!(valueHeader & MapFlags.DECL_ELEMENT_TYPE)) {
+          valueSerializer = AnyHelper.detectSerializer(this.fory);
+        }
       }
+
       for (let index = 0; index < chunkSize; index++) {
         const key = this.readElement(keyHeader, keySerializer);
         const value = this.readElement(valueHeader, valueSerializer);
@@ -299,57 +332,61 @@ export class MapSerializerGenerator extends 
BaseSerializerGenerator {
       for (const [${k}, ${v}] of ${accessor}.entries()) {
         let keyIsNull = ${k} === null || ${k} === undefined;
         let valueIsNull = ${v} === null || ${v} === undefined;
-        if (${lastKeyIsNull} !== keyIsNull || ${lastValueIsNull} !== 
valueIsNull || ${chunkSize} === 0 || ${chunkSize} === 255) {
+        if (${lastKeyIsNull} !== keyIsNull || ${lastValueIsNull} !== 
valueIsNull || ${chunkSize} === 0 || ${chunkSize} === 255 || keyIsNull || 
valueIsNull) {
           if (${chunkSize} > 0) {
-            ${this.builder.writer.setUint8Position(`${chunkSizeOffset} + 1`, 
chunkSize)};
+            ${this.builder.writer.setUint8Position(`${chunkSizeOffset}`, 
chunkSize)};
             ${chunkSize} = 0;
           }
-          ${chunkSizeOffset} = ${this.builder.writer.getCursor()}
-          ${this.builder.writer.uint16(
-              `((${valueHeader} | (valueIsNull ? ${MapFlags.HAS_NULL} : 0)) << 
3) | (${keyHeader} | (keyIsNull ? ${MapFlags.HAS_NULL} : 0))`
-            )
+          if (keyIsNull || valueIsNull) {
+            ${this.builder.writer.uint8(
+                `((${valueHeader} | (valueIsNull ? ${MapFlags.HAS_NULL} : 0)) 
<< 3) | (${keyHeader} | (keyIsNull ? ${MapFlags.HAS_NULL} : 0))`
+              )
+            }
+          } else {
+            ${chunkSizeOffset} = ${this.builder.writer.getCursor()} + 1;
+            ${this.builder.writer.uint16(
+                `((${valueHeader} | (valueIsNull ? ${MapFlags.HAS_NULL} : 0)) 
<< 3) | (${keyHeader} | (keyIsNull ? ${MapFlags.HAS_NULL} : 0))`
+              )
+            }
           }
           ${lastKeyIsNull} = keyIsNull;
           ${lastValueIsNull} = valueIsNull;
         }
-        if (keyIsNull) {
-          ${this.builder.writer.uint8(RefFlags.NullFlag)}
-        } else {
+        if (!keyIsNull) {
           ${this.keyGenerator.needToWriteRef()
           ? `
               const ${keyRef} = 
${this.builder.referenceResolver.existsWriteObject(v)};
               if (${keyRef} !== undefined) {
-                ${this.builder.writer.uint8(RefFlags.RefFlag)};
+                ${this.builder.writer.int8(RefFlags.RefFlag)};
                 ${this.builder.writer.uint16(keyRef)};
               } else {
-                ${this.builder.writer.uint8(RefFlags.RefValueFlag)};
+                ${this.builder.writer.int8(RefFlags.RefValueFlag)};
                 ${this.keyGenerator.writeEmbed().write(k)}
               }
           `
           : this.keyGenerator.writeEmbed().write(k)}
         }
 
-        if (valueIsNull) {
-          ${this.builder.writer.uint8(RefFlags.NullFlag)}
-        } else {
+        if (!valueIsNull) {
           ${this.valueGenerator.needToWriteRef()
           ? `
               const ${valueRef} = 
${this.builder.referenceResolver.existsWriteObject(v)};
               if (${valueRef} !== undefined) {
-                ${this.builder.writer.uint8(RefFlags.RefFlag)};
+                ${this.builder.writer.int8(RefFlags.RefFlag)};
                 ${this.builder.writer.uint16(valueRef)};
               } else {
-                ${this.builder.writer.uint8(RefFlags.RefValueFlag)};
+                ${this.builder.writer.int8(RefFlags.RefValueFlag)};
                 ${this.valueGenerator.writeEmbed().write(v)};
               }
           `
           : this.valueGenerator.writeEmbed().write(v)}
         }
-
-        ${chunkSize}++;
+        if (!keyIsNull && !valueIsNull) {
+          ${chunkSize}++;
+        }
       }
       if (${chunkSize} > 0) {
-        ${this.builder.writer.setUint8Position(`${chunkSizeOffset} + 1`, 
chunkSize)};
+        ${this.builder.writer.setUint8Position(`${chunkSizeOffset}`, 
chunkSize)};
       }
     `;
   }
@@ -375,19 +412,24 @@ export class MapSerializerGenerator extends 
BaseSerializerGenerator {
         ${this.builder.referenceResolver.reference(result)}
       }
       while (${count} > 0) {
-        const header = ${this.builder.reader.uint16()};
+        const header = ${this.builder.reader.uint8()};
         const keyHeader = header & 0b111;
         const valueHeader = (header >> 3) & 0b111;
-        const chunkSize = header >> 8;
         const keyIncludeNone = keyHeader & ${MapFlags.HAS_NULL};
         const keyTrackingRef = keyHeader & ${MapFlags.TRACKING_REF};
         const valueIncludeNone = valueHeader & ${MapFlags.HAS_NULL};
         const valueTrackingRef = valueHeader & ${MapFlags.TRACKING_REF};
+        let chunkSize = 1;
+        if (!keyIncludeNone && !valueIncludeNone) {
+          chunkSize = ${this.builder.reader.uint8()};
+        }
         for (let index = 0; index < chunkSize; index++) {
           let key;
           let value;
-          if (keyTrackingRef || keyIncludeNone) {
-            const flag = ${this.builder.reader.uint8()};
+          if (keyIncludeNone) {
+             key = null;
+          } else if (keyTrackingRef) {
+            const flag = ${this.builder.reader.int8()};
             switch (flag) {
               case ${RefFlags.RefValueFlag}:
                 ${this.keyGenerator.read(x => `key = ${x}`, "true")}
@@ -406,8 +448,10 @@ export class MapSerializerGenerator extends 
BaseSerializerGenerator {
               ${this.keyGenerator.read(x => `key = ${x}`, "false")}
           }
           
-          if (valueTrackingRef || valueIncludeNone) {
-            const flag = ${this.builder.reader.uint8()};
+          if (valueIncludeNone) {
+            value = null;
+          } else if (valueTrackingRef) {
+            const flag = ${this.builder.reader.int8()};
             switch (flag) {
               case ${RefFlags.RefValueFlag}:
                 ${this.valueGenerator.read(x => `value = ${x}`, "true")}
diff --git a/javascript/packages/fory/lib/gen/struct.ts 
b/javascript/packages/fory/lib/gen/struct.ts
index b18173dea..f86ab2180 100644
--- a/javascript/packages/fory/lib/gen/struct.ts
+++ b/javascript/packages/fory/lib/gen/struct.ts
@@ -62,11 +62,14 @@ class StructSerializerGenerator extends 
BaseSerializerGenerator {
   typeInfo: StructTypeInfo;
   sortedProps: { key: string; typeInfo: TypeInfo }[];
   metaChangedSerializer: string;
+  typeMeta: TypeMeta;
+
   constructor(typeInfo: TypeInfo, builder: CodecBuilder, scope: Scope) {
     super(typeInfo, builder, scope);
     this.typeInfo = <StructTypeInfo>typeInfo;
     this.sortedProps = sortProps(this.typeInfo);
     this.metaChangedSerializer = 
this.scope.declareVar("metaChangedSerializer", "null");
+    this.typeMeta = TypeMeta.fromTypeInfo(this.typeInfo);
   }
 
   readField(fieldName: string, fieldTypeInfo: TypeInfo, assignStmt: (expr: 
string) => string, embedGenerator: SerializerGenerator, needToWriteRef: 
boolean) {
@@ -159,7 +162,9 @@ class StructSerializerGenerator extends 
BaseSerializerGenerator {
   }
 
   write(accessor: string): string {
+    const hash = this.typeMeta.computeStructHash();
     return `
+      ${this.builder.fory.config.classVersionHash ? 
this.builder.writer.int32(hash) : ""}
       ${this.sortedProps.map(({ key, typeInfo }) => {
       const InnerGeneratorClass = CodegenRegistry.get(typeInfo.typeId);
       if (!InnerGeneratorClass) {
@@ -175,10 +180,18 @@ class StructSerializerGenerator extends 
BaseSerializerGenerator {
 
   read(accessor: (expr: string) => string, refState: string): string {
     const result = this.scope.uniqueName("result");
+    const hash = this.typeMeta.computeStructHash();
     return `
+      ${this.builder.fory.config.classVersionHash
+? `
+        if(${this.builder.reader.int32()} !== ${hash}) {
+          throw new Error("Read class version is not consistent with ${hash} ")
+        }
+      `
+: ""}
       ${this.typeInfo.options.withConstructor
         ? `
-          const ${result} = new ${this.builder.getOptions("constructor")}();
+          const ${result} = new ${this.builder.getOptions("creator")}();
         `
         : `
           const ${result} = {
diff --git a/javascript/packages/fory/lib/meta/TypeMeta.ts 
b/javascript/packages/fory/lib/meta/TypeMeta.ts
index 985710c6f..8b5dce8d3 100644
--- a/javascript/packages/fory/lib/meta/TypeMeta.ts
+++ b/javascript/packages/fory/lib/meta/TypeMeta.ts
@@ -20,9 +20,10 @@
 import { BinaryWriter } from "../writer";
 import { BinaryReader } from "../reader";
 import { Encoding, MetaStringDecoder, MetaStringEncoder } from "./MetaString";
-import { StructTypeInfo } from "../typeInfo";
+import { StructTypeInfo, TypeInfo } from "../typeInfo";
 import { TypeId } from "../type";
 import { x64hash128 } from "../murmurHash3";
+import { fromString } from "../platformBuffer";
 
 const fieldEncoder = new MetaStringEncoder("$", ".");
 const fieldDecoder = new MetaStringDecoder("$", ".");
@@ -227,26 +228,61 @@ export class TypeMeta {
     return this.fields.length;
   }
 
-  static fromTypeInfo(typeInfo: StructTypeInfo) {
-    const structTypeInfo = typeInfo;
-    let fieldInfo = Object.entries(typeInfo.options.props!).map(([fieldName, 
typeInfo]) => {
-      let fieldTypeId = typeInfo.typeId;
-      if (fieldTypeId === TypeId.NAMED_ENUM) {
-        fieldTypeId = TypeId.ENUM;
-      } else if (fieldTypeId === TypeId.NAMED_UNION || fieldTypeId === 
TypeId.TYPED_UNION) {
-        fieldTypeId = TypeId.UNION;
+  computeStructFingerprint(fields: FieldInfo[]) {
+    let fieldInfos = [];
+    for (const field of fields) {
+      const typeId = field.getTypeId();
+      let fieldIdentifier = "";
+      if (field.getFieldId()) {
+        fieldIdentifier = `${field.getFieldId()}`;
+      } else {
+        fieldIdentifier = TypeMeta.toSnakeCase(field.getFieldName());
       }
-      const { trackingRef, nullable } = 
structTypeInfo.options.fieldInfo?.[fieldName] || {};
-      return new FieldInfo(
-        fieldName,
-        fieldTypeId,
-        typeInfo.userTypeId,
-        trackingRef,
-        nullable,
-        typeInfo.options,
-      );
-    });
+      const ref = field.trackingRef ? "1" : "0";
+      const nullable = field.nullable ? "1" : "0";
+      fieldInfos.push([fieldIdentifier, `${typeId}`, ref, nullable]);
+    }
+    fieldInfos = fieldInfos.sort((a, b) => a[0].localeCompare(b[0]));
+    let result = "";
+    for (const fieldInfo of fieldInfos) {
+      result += [fieldInfo[0], fieldInfo[1], fieldInfo[2], 
fieldInfo[3]].join(",");
+      result += ";";
+    }
+    return result;
+  }
+
+  computeStructHash() {
+    const fields = TypeMeta.groupFieldsByType(this.fields);
+    const fingerprint = this.computeStructFingerprint(fields);
+    const bytes = fromString(fingerprint);
+    const hashLong = x64hash128(bytes, 47).getBigInt64(0);
+    return Number(BigInt.asIntN(32, hashLong));
+  }
+
+  static fromTypeInfo(typeInfo: TypeInfo) {
+    let fieldInfo: FieldInfo[] = [];
+    if (TypeId.structType(typeInfo.typeId)) {
+      const structTypeInfo = typeInfo as StructTypeInfo;
+      fieldInfo = 
Object.entries(structTypeInfo.options.props!).map(([fieldName, typeInfo]) => {
+        let fieldTypeId = typeInfo.typeId;
+        if (fieldTypeId === TypeId.NAMED_ENUM) {
+          fieldTypeId = TypeId.ENUM;
+        } else if (fieldTypeId === TypeId.NAMED_UNION || fieldTypeId === 
TypeId.TYPED_UNION) {
+          fieldTypeId = TypeId.UNION;
+        }
+        const { trackingRef, nullable } = 
structTypeInfo.options.fieldInfo?.[fieldName] || {};
+        return new FieldInfo(
+          fieldName,
+          fieldTypeId,
+          typeInfo.userTypeId,
+          trackingRef,
+          nullable,
+          typeInfo.options,
+        );
+      });
+    }
     fieldInfo = TypeMeta.groupFieldsByType(fieldInfo);
+
     return new TypeMeta(fieldInfo, {
       typeId: typeInfo.typeId,
       namespace: typeInfo.namespace,
@@ -561,6 +597,29 @@ export class TypeMeta {
     return writer.dump();
   }
 
+  static toSnakeCase(name: string) {
+    const result = [];
+    const chars = Array.from(name);
+
+    for (let i = 0; i < chars.length; i++) {
+      const c = chars[i];
+      if (c >= "A" && c <= "Z") {
+        if (i > 0) {
+          const prevUpper = chars[i - 1] >= "A" && chars[i - 1] <= "Z";
+          const nextUpperOrEnd = i + 1 >= chars.length || (chars[i + 1] >= "A" 
&& chars[i + 1] <= "Z");
+
+          if (!prevUpper || !nextUpperOrEnd) {
+            result.push("_");
+          }
+        }
+        result.push(c.toLowerCase());
+      } else {
+        result.push(c);
+      }
+    }
+    return result.join("");
+  }
+
   static groupFieldsByType<T extends { fieldName: string; nullable?: boolean; 
typeId: number }>(typeInfos: Array<T>): Array<T> {
     const primitiveFields: Array<T> = [];
     const nullablePrimitiveFields: Array<T> = [];
@@ -570,29 +629,6 @@ export class TypeMeta {
     const mapFields: Array<T> = [];
     const otherFields: Array<T> = [];
 
-    const toSnakeCase = (name: string) => {
-      const result = [];
-      const chars = Array.from(name);
-
-      for (let i = 0; i < chars.length; i++) {
-        const c = chars[i];
-        if (c >= "A" && c <= "Z") {
-          if (i > 0) {
-            const prevUpper = chars[i - 1] >= "A" && chars[i - 1] <= "Z";
-            const nextUpperOrEnd = i + 1 >= chars.length || (chars[i + 1] >= 
"A" && chars[i + 1] <= "Z");
-
-            if (!prevUpper || !nextUpperOrEnd) {
-              result.push("_");
-            }
-          }
-          result.push(c.toLowerCase());
-        } else {
-          result.push(c);
-        }
-      }
-      return result.join("");
-    };
-
     for (const typeInfo of typeInfos) {
       const typeId = typeInfo.typeId;
 
@@ -644,7 +680,7 @@ export class TypeMeta {
     };
 
     const nameSorter = (a: T, b: T) => {
-      return toSnakeCase(a.fieldName).localeCompare(toSnakeCase(b.fieldName));
+      return 
TypeMeta.toSnakeCase(a.fieldName).localeCompare(TypeMeta.toSnakeCase(b.fieldName));
     };
 
     // Sort each group
diff --git a/javascript/packages/fory/lib/type.ts 
b/javascript/packages/fory/lib/type.ts
index 9cbdf5759..278157732 100644
--- a/javascript/packages/fory/lib/type.ts
+++ b/javascript/packages/fory/lib/type.ts
@@ -17,7 +17,10 @@
  * under the License.
  */
 
+import Fory from "./fory";
+import { BinaryReader } from "./reader";
 import { StructTypeInfo } from "./typeInfo";
+import { BinaryWriter } from "./writer";
 
 export const TypeId = {
   // Unknown/polymorphic type marker.
@@ -186,6 +189,11 @@ export enum ConfigFlags {
   isOutOfBandFlag = 1 << 2,
 }
 
+export type CustomSerializer<T> = {
+  read: (result: T, reader: BinaryReader, fory: Fory) => void;
+  write: (v: T, writer: BinaryWriter, fory: Fory) => void;
+};
+
 // read, write
 export type Serializer<T = any> = {
   fixedSize: number;
@@ -245,6 +253,7 @@ export interface Config {
     afterCodeGenerated?: (code: string) => string;
   };
   mode: Mode;
+  classVersionHash?: boolean;
 }
 
 export interface WithForyClsInfo {
diff --git a/javascript/packages/fory/lib/typeInfo.ts 
b/javascript/packages/fory/lib/typeInfo.ts
index 9a1b519cb..f3b846f9a 100644
--- a/javascript/packages/fory/lib/typeInfo.ts
+++ b/javascript/packages/fory/lib/typeInfo.ts
@@ -28,7 +28,10 @@ const initMeta = (target: new () => any, typeInfo: TypeInfo) 
=> {
     typeInfo.options = {};
   }
   typeInfo.options.withConstructor = true;
-  typeInfo.options.constructor = target;
+  typeInfo.options.creator = target;
+  if (!typeInfo.options.props) {
+    typeInfo.options.props = {}
+  }
   Object.assign(typeInfo.options.props, targetFields.get(target) || {})
   Object.defineProperties(target.prototype, {
     [ForyTypeInfoSymbol]: {
@@ -163,6 +166,60 @@ export class TypeInfo<T = unknown> extends 
ExtensibleFunction {
     }>(typeId);
   }
 
+  static fromExt<T = any>(nameInfo: {
+    typeId?: number;
+    namespace?: string;
+    typeName?: string;
+  } | string | number, {
+    withConstructor = false,
+  }: {
+    withConstructor?: boolean;
+  } = {}) {
+    let typeId: number | undefined;
+    let namespace: string | undefined;
+    let typeName: string | undefined;
+    if (typeof nameInfo === "string") {
+      typeName = nameInfo;
+    } else if (typeof nameInfo === "number") {
+      typeId = nameInfo;
+    } else {
+      namespace = nameInfo.namespace;
+      typeName = nameInfo.typeName;
+      typeId = nameInfo.typeId;
+    }
+    if (typeId !== undefined && typeName !== undefined) {
+      throw new Error(`type name ${typeName} and id ${typeId} should not be 
set at the same time`);
+    }
+    if (!typeId) {
+      if (!typeName) {
+        throw new Error(`type name and type id should be set at least one`);
+      }
+    }
+    if (!namespace && typeName) {
+      const splits = typeName!.split(".");
+      if (splits.length > 1) {
+        namespace = splits[0];
+        typeName = splits.slice(1).join(".");
+      }
+    }
+    let finalTypeId = 0;
+    let userTypeId = -1;
+    if (typeId !== undefined) {
+      finalTypeId = TypeId.EXT;
+      userTypeId = typeId;
+    } else {
+      finalTypeId = TypeId.NAMED_EXT;
+    }
+    const typeInfo = new TypeInfo<T>(finalTypeId, 
userTypeId).cast<StructTypeInfo>();
+    typeInfo.options = {
+      withConstructor,
+    };
+    typeInfo.namespace = namespace || "";
+    typeInfo.typeName = typeId !== undefined ? "" : typeName!;
+    typeInfo.named = `${typeInfo.namespace}$${typeInfo.typeName}`;
+    return typeInfo as TypeInfo<T>;
+  }
+
   static fromStruct<T = any>(nameInfo: {
     typeId?: number;
     namespace?: string;
@@ -289,7 +346,7 @@ export interface StructTypeInfo extends TypeInfo {
     props?: { [key: string]: TypeInfo };
     fieldInfo?: {[key: string]: StructFieldInfo};
     withConstructor?: boolean;
-    constructor?: Function;
+    creator?: Function;
   };
 }
 
@@ -545,6 +602,24 @@ export const Type = {
       };
     }>(nameInfo, t1);
   },
+  ext<T extends { [key: string]: TypeInfo }>(nameInfo: {
+    typeId?: number;
+    namespace?: string;
+    typeName?: string;
+  } | string | number, {
+    withConstructor = false,
+  }: {
+    withConstructor?: boolean;
+  } = {}) {
+    return TypeInfo.fromExt<{
+      type: typeof TypeId.EXT;
+      options: {
+        props: T;
+      };
+    }>(nameInfo, {
+      withConstructor,
+    });
+  },
   struct<T extends { [key: string]: TypeInfo }>(nameInfo: {
     typeId?: number;
     namespace?: string;
diff --git a/javascript/packages/fory/lib/typeResolver.ts 
b/javascript/packages/fory/lib/typeResolver.ts
index f4c5f5423..71a03be25 100644
--- a/javascript/packages/fory/lib/typeResolver.ts
+++ b/javascript/packages/fory/lib/typeResolver.ts
@@ -94,6 +94,15 @@ export default class TypeResolver {
     registerSerializer(Type.varUInt64());
     registerSerializer(Type.varInt64());
     registerSerializer(Type.int64());
+    registerSerializer(Type.uint8());
+    registerSerializer(Type.uint16());
+    registerSerializer(Type.uint32());
+    registerSerializer(Type.uint64());
+    registerSerializer(Type.varInt32());
+    registerSerializer(Type.varUInt32());
+    registerSerializer(Type.varUInt64());
+    registerSerializer(Type.varInt64());
+    registerSerializer(Type.taggedUInt64());
     registerSerializer(Type.sliInt64());
     registerSerializer(Type.float16());
     registerSerializer(Type.float32());
@@ -246,6 +255,9 @@ export default class TypeResolver {
   }
 
   getSerializerByData(v: any) {
+    if (v === null || v === undefined) {
+      return null;
+    }
     // internal types
     if (typeof v === "number") {
       if (Number.isInteger(v)) {
diff --git a/javascript/test/crossLanguage.test.ts 
b/javascript/test/crossLanguage.test.ts
index bc1e8aa67..f5650b91e 100644
--- a/javascript/test/crossLanguage.test.ts
+++ b/javascript/test/crossLanguage.test.ts
@@ -239,8 +239,9 @@ describe("bool", () => {
     writeToFile(Buffer.concat(bfs));
   });
   test("test_cross_language_serializer", () => {
-    if (Boolean("1")) { return; }
-    const fory = new Fory();
+    const fory = new Fory({
+      mode: Mode.Compatible
+    });
 
     // Define and register Color enum
     const Color = {
@@ -250,27 +251,24 @@ describe("bool", () => {
       White: 3,
     };
     fory.registerSerializer(Type.enum(101, Color));
-
-    const reader = new BinaryReader({});
-    reader.reset(content);
-
     // Deserialize various data types from Java
     const deserializedData = [];
+    let cursor = 0;
     for (let i = 0; i < 28; i++) { // 28 serialized items from Java
-      const deserializedItem = 
fory.deserialize(reader.buffer(reader.varUInt32()));
+      const deserializedItem = fory.deserialize(content.subarray(cursor));
+      cursor += fory.binaryReader.getCursor();
       deserializedData.push(deserializedItem);
     }
 
-    const writer = new BinaryWriter();
-    writer.reserve(1024);
 
+    const bfs = []
     // Serialize each deserialized item back
     for (const item of deserializedData) {
       const serializedData = fory.serialize(item);
-      writer.buffer(serializedData);
+      bfs.push(serializedData);
     }
 
-    writeToFile(writer.dump() as Buffer);
+    writeToFile(Buffer.concat(bfs));
   });
   test("test_simple_struct", () => {
     const fory = new Fory({
@@ -426,8 +424,9 @@ describe("bool", () => {
   });
 
   test("test_map", () => {
-    if (Boolean("1")) { return; }
-    const fory = new Fory();
+    const fory = new Fory({
+      mode: Mode.Compatible
+    });
 
     @Type.struct(102, {
       name: Type.string()
@@ -438,26 +437,24 @@ describe("bool", () => {
 
     fory.registerSerializer(Item);
 
-    const reader = new BinaryReader({});
-    reader.reset(content);
-
     // Deserialize maps from Java
     const deserializedMaps = [];
+    let cursor = 0;
     for (let i = 0; i < 2; i++) { // 2 maps
-      const deserializedMap = 
fory.deserialize(reader.buffer(reader.varUInt32()));
+      const deserializedMap = fory.deserialize(content.subarray(cursor));
+      cursor += fory.binaryReader.getCursor();
       deserializedMaps.push(deserializedMap);
     }
 
-    const writer = new BinaryWriter();
-    writer.reserve(512);
-
+    const bfs = []
     // Serialize each deserialized map back
     for (const map of deserializedMaps) {
       const serializedData = fory.serialize(map);
-      writer.buffer(serializedData);
+      fory.deserialize(serializedData);
+      bfs.push(serializedData);
     }
 
-    writeToFile(writer.dump() as Buffer);
+    writeToFile(Buffer.concat(bfs));
   });
 
   test("test_integer", () => {
@@ -505,8 +502,9 @@ describe("bool", () => {
   });
 
   test("test_item", () => {
-    if (Boolean("1")) { return; }
-    const fory = new Fory();
+    const fory = new Fory({
+      mode: Mode.Compatible
+    });
 
     @Type.struct(102, {
       name: Type.string()
@@ -521,26 +519,27 @@ describe("bool", () => {
 
     // Deserialize items from Java
     const deserializedItems = [];
+    let cursor = 0;
     for (let i = 0; i < 3; i++) { // 3 items
-      const deserializedItem = 
fory.deserialize(reader.buffer(reader.varUInt32()));
+      const deserializedItem = fory.deserialize(content.subarray(cursor));
+      cursor += fory.binaryReader.getCursor();
       deserializedItems.push(deserializedItem);
     }
 
-    const writer = new BinaryWriter();
-    writer.reserve(256);
-
+    const bfs = []
     // Serialize each deserialized item back
     for (const item of deserializedItems) {
       const serializedData = fory.serialize(item);
-      writer.buffer(serializedData);
+      bfs.push(serializedData);
     }
 
-    writeToFile(writer.dump() as Buffer);
+    writeToFile(Buffer.concat(bfs));
   });
 
   test("test_color", () => {
-    if (Boolean("1")) { return; }
-    const fory = new Fory();
+    const fory = new Fory({
+      mode: Mode.Compatible
+    });
 
     // Define and register Color enum
     const Color = {
@@ -549,15 +548,17 @@ describe("bool", () => {
       Blue: 2,
       White: 3,
     };
-    fory.registerSerializer(Type.enum(101, Color));
+    const { serialize: enumSerialize } = 
fory.registerSerializer(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(reader.buffer(reader.varUInt32()));
+      const deserializedColor = fory.deserialize(content.subarray(cursor));
+      cursor += fory.binaryReader.getCursor();
       deserializedColors.push(deserializedColor);
     }
 
@@ -566,15 +567,16 @@ describe("bool", () => {
 
     // Serialize each deserialized color back
     for (const color of deserializedColors) {
-      const serializedData = fory.serialize(color);
+      const serializedData = enumSerialize(color);
       writer.buffer(serializedData);
     }
 
     writeToFile(writer.dump() as Buffer);
   });
   test("test_struct_with_list", () => {
-    if (Boolean("1")) { return; }
-    const fory = new Fory();
+    const fory = new Fory({
+      mode: Mode.Compatible
+    });
 
     @Type.struct(201, {
       items: Type.array(Type.string())
@@ -589,8 +591,10 @@ describe("bool", () => {
 
     // Deserialize structs from Java
     const deserializedStructs = [];
+    let cursor = 0;
     for (let i = 0; i < 2; i++) { // 2 structs
-      const deserializedStruct = 
fory.deserialize(reader.buffer(reader.varUInt32()));
+      const deserializedStruct = fory.deserialize(content.subarray(cursor));
+      cursor += fory.binaryReader.getCursor();
       deserializedStructs.push(deserializedStruct);
     }
 
@@ -607,8 +611,9 @@ describe("bool", () => {
   });
 
   test("test_struct_with_map", () => {
-    if (Boolean("1")) { return; }
-    const fory = new Fory();
+    const fory = new Fory({
+      mode: Mode.Compatible
+    });
 
     @Type.struct(202, {
       data: Type.map(Type.string(), Type.string())
@@ -623,8 +628,10 @@ describe("bool", () => {
 
     // Deserialize structs from Java
     const deserializedStructs = [];
+    let cursor = 0;
     for (let i = 0; i < 2; i++) { // 2 structs
-      const deserializedStruct = 
fory.deserialize(reader.buffer(reader.varUInt32()));
+      const deserializedStruct = fory.deserialize(content.subarray(cursor));
+      cursor += fory.binaryReader.getCursor();
       deserializedStructs.push(deserializedStruct);
     }
 
@@ -642,7 +649,9 @@ describe("bool", () => {
 
   test("test_skip_id_custom", () => {
     if (Boolean("1")) { return; }
-    const fory = new Fory();
+    const fory = new Fory({
+      mode: Mode.Compatible
+    });
 
     // Define empty wrapper for deserialization
     @Type.struct(104)
@@ -653,7 +662,9 @@ describe("bool", () => {
     reader.reset(content);
 
     // Deserialize empty wrapper from Java
-    const deserializedWrapper = 
fory.deserialize(reader.buffer(reader.varUInt32()));
+    let cursor = 0;
+    const deserializedWrapper = fory.deserialize(content.subarray(cursor));
+    cursor += fory.binaryReader.getCursor();
 
     // Serialize the deserialized wrapper back
     const serializedData = fory.serialize(deserializedWrapper);
@@ -662,7 +673,9 @@ describe("bool", () => {
 
   test("test_skip_name_custom", () => {
     if (Boolean("1")) { return; }
-    const fory = new Fory();
+    const fory = new Fory({
+      mode: Mode.Compatible
+    });
 
     // Define empty wrapper for deserialization
     @Type.struct({ namespace: "", typeName: "my_wrapper" })
@@ -673,7 +686,9 @@ describe("bool", () => {
     reader.reset(content);
 
     // Deserialize empty wrapper from Java
-    const deserializedWrapper = 
fory.deserialize(reader.buffer(reader.varUInt32()));
+    let cursor = 0;
+    const deserializedWrapper = fory.deserialize(content.subarray(cursor));
+    cursor += fory.binaryReader.getCursor();
 
     // Serialize the deserialized wrapper back
     const serializedData = fory.serialize(deserializedWrapper);
@@ -681,8 +696,10 @@ describe("bool", () => {
   });
 
   test("test_consistent_named", () => {
-    if (Boolean("1")) { return; }
-    const fory = new Fory();
+    const fory = new Fory({
+      mode: Mode.SchemaConsistent,
+      classVersionHash: true,
+    });
 
     // Define and register Color enum
     const Color = {
@@ -691,45 +708,48 @@ describe("bool", () => {
       Blue: 2,
       White: 3,
     };
-    fory.registerSerializer(Type.enum({ namespace: "", typeName: "color" }, 
Color));
+    const { serialize: enumSerialize } = fory.registerSerializer(Type.enum({ 
namespace: "", typeName: "color" }, Color));
 
     @Type.struct({ namespace: "", typeName: "my_struct" }, {
-      id: Type.int32()
+      id: Type.varInt32()
     })
     class MyStruct {
       id: number = 0;
-      constructor(id: number = 0) {
-        this.id = id;
-      }
     }
     fory.registerSerializer(MyStruct);
 
-    @Type.struct({ namespace: "", typeName: "my_ext" }, {
-      id: Type.int32()
-    })
+    @Type.ext({ namespace: "", typeName: "my_ext" })
     class MyExt {
       id: number = 0;
-      constructor(id: number = 0) {
-        this.id = id;
-      }
     }
-    fory.registerSerializer(MyExt);
-
-    const reader = new BinaryReader({});
-    reader.reset(content);
+    fory.registerSerializer(MyExt, {
+      write: (value: MyExt, writer: BinaryWriter, fory: Fory) => {
+        writer.varInt32(value.id);
+      },
+      read: (result: MyExt, reader: BinaryReader, fory: Fory) => {
+        result.id = reader.varInt32();
+      }
+    });
 
     // 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(reader.buffer(reader.varUInt32()));
+      const deserializedItem = fory.deserialize(content.subarray(cursor));
+      cursor += fory.binaryReader.getCursor();
       deserializedData.push(deserializedItem);
     }
 
     const writer = new BinaryWriter();
     writer.reserve(256);
 
-    // Serialize each deserialized item back
-    for (const item of deserializedData) {
+    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);
     }
@@ -739,7 +759,9 @@ describe("bool", () => {
 
   test("test_struct_version_check", () => {
     if (Boolean("1")) { return; }
-    const fory = new Fory();
+    const fory = new Fory({
+      mode: Mode.Compatible
+    });
 
     @Type.struct(201, {
       f1: Type.int32(),
@@ -757,7 +779,9 @@ describe("bool", () => {
     reader.reset(content);
 
     // Deserialize struct from Java
-    const deserializedStruct = 
fory.deserialize(reader.buffer(reader.varUInt32()));
+    let cursor = 0;
+    const deserializedStruct = fory.deserialize(content.subarray(cursor));
+    cursor += fory.binaryReader.getCursor();
 
     // Serialize the deserialized struct back
     const serializedData = fory.serialize(deserializedStruct);
@@ -766,7 +790,9 @@ describe("bool", () => {
 
   test("test_polymorphic_list", () => {
     if (Boolean("1")) { return; }
-    const fory = new Fory();
+    const fory = new Fory({
+      mode: Mode.Compatible
+    });
 
     // Define Animal interface implementations
     @Type.struct(302, {
@@ -808,8 +834,10 @@ describe("bool", () => {
 
     // Deserialize polymorphic data from Java
     const deserializedData = [];
+    let cursor = 0;
     for (let i = 0; i < 2; i++) { // animals array + holder
-      const deserializedItem = 
fory.deserialize(reader.buffer(reader.varUInt32()));
+      const deserializedItem = fory.deserialize(content.subarray(cursor));
+      cursor += fory.binaryReader.getCursor();
       deserializedData.push(deserializedItem);
     }
 
@@ -827,7 +855,9 @@ describe("bool", () => {
 
   test("test_polymorphic_map", () => {
     if (Boolean("1")) { return; }
-    const fory = new Fory();
+    const fory = new Fory({
+      mode: Mode.Compatible
+    });
 
     // Define Animal interface implementations
     @Type.struct(302, {
@@ -869,8 +899,10 @@ describe("bool", () => {
 
     // Deserialize polymorphic data from Java
     const deserializedData = [];
+    let cursor = 0;
     for (let i = 0; i < 2; i++) { // animal map + holder
-      const deserializedItem = 
fory.deserialize(reader.buffer(reader.varUInt32()));
+      const deserializedItem = fory.deserialize(content.subarray(cursor));
+      cursor += fory.binaryReader.getCursor();
       deserializedData.push(deserializedItem);
     }
 
@@ -887,7 +919,9 @@ describe("bool", () => {
   });
   test("test_one_string_field_schema", () => {
     if (Boolean("1")) { return; }
-    const fory = new Fory();
+    const fory = new Fory({
+      mode: Mode.Compatible
+    });
 
     @Type.struct(200, {
       f1: Type.string()
@@ -901,7 +935,9 @@ describe("bool", () => {
     reader.reset(content);
 
     // Deserialize struct from Java
-    const deserializedStruct = 
fory.deserialize(reader.buffer(reader.varUInt32()));
+    let cursor = 0;
+    const deserializedStruct = fory.deserialize(content.subarray(cursor));
+    cursor += fory.binaryReader.getCursor();
 
     // Serialize the deserialized struct back
     const serializedData = fory.serialize(deserializedStruct);
@@ -909,7 +945,9 @@ describe("bool", () => {
   });
   test("test_one_string_field_compatible", () => {
     if (Boolean("1")) { return; }
-    const fory = new Fory();
+    const fory = new Fory({
+      mode: Mode.Compatible
+    });
 
     @Type.struct(200, {
       f1: Type.string()
@@ -923,7 +961,9 @@ describe("bool", () => {
     reader.reset(content);
 
     // Deserialize struct from Java
-    const deserializedStruct = 
fory.deserialize(reader.buffer(reader.varUInt32()));
+    let cursor = 0;
+    const deserializedStruct = fory.deserialize(content.subarray(cursor));
+    cursor += fory.binaryReader.getCursor();
 
     // Serialize the deserialized struct back
     const serializedData = fory.serialize(deserializedStruct);
@@ -932,7 +972,9 @@ describe("bool", () => {
 
   test("test_two_string_field_compatible", () => {
     if (Boolean("1")) { return; }
-    const fory = new Fory();
+    const fory = new Fory({
+      mode: Mode.Compatible
+    });
 
     @Type.struct(201, {
       f1: Type.string(),
@@ -948,7 +990,9 @@ describe("bool", () => {
     reader.reset(content);
 
     // Deserialize struct from Java
-    const deserializedStruct = 
fory.deserialize(reader.buffer(reader.varUInt32()));
+    let cursor = 0;
+    const deserializedStruct = fory.deserialize(content.subarray(cursor));
+    cursor += fory.binaryReader.getCursor();
 
     // Serialize the deserialized struct back
     const serializedData = fory.serialize(deserializedStruct);
@@ -957,7 +1001,9 @@ describe("bool", () => {
 
   test("test_schema_evolution_compatible", () => {
     if (Boolean("1")) { return; }
-    const fory = new Fory();
+    const fory = new Fory({
+      mode: Mode.Compatible
+    });
 
     @Type.struct(200)
     class EmptyStruct { }
@@ -967,7 +1013,9 @@ describe("bool", () => {
     reader.reset(content);
 
     // Deserialize empty struct from Java
-    const deserializedStruct = 
fory.deserialize(reader.buffer(reader.varUInt32()));
+    let cursor = 0;
+    const deserializedStruct = fory.deserialize(content.subarray(cursor));
+    cursor += fory.binaryReader.getCursor();
 
     // Serialize the deserialized struct back
     const serializedData = fory.serialize(deserializedStruct);
@@ -975,7 +1023,9 @@ describe("bool", () => {
   });
   test("test_one_enum_field_schema", () => {
     if (Boolean("1")) { return; }
-    const fory = new Fory();
+    const fory = new Fory({
+      mode: Mode.Compatible
+    });
 
     // Define and register TestEnum
     const TestEnum = {
@@ -997,7 +1047,9 @@ describe("bool", () => {
     reader.reset(content);
 
     // Deserialize struct from Java
-    const deserializedStruct = 
fory.deserialize(reader.buffer(reader.varUInt32()));
+    let cursor = 0;
+    const deserializedStruct = fory.deserialize(content.subarray(cursor));
+    cursor += fory.binaryReader.getCursor();
 
     // Serialize the deserialized struct back
     const serializedData = fory.serialize(deserializedStruct);
@@ -1006,7 +1058,9 @@ describe("bool", () => {
 
   test("test_one_enum_field_compatible", () => {
     if (Boolean("1")) { return; }
-    const fory = new Fory();
+    const fory = new Fory({
+      mode: Mode.Compatible
+    });
 
     // Define and register TestEnum
     const TestEnum = {
@@ -1028,7 +1082,9 @@ describe("bool", () => {
     reader.reset(content);
 
     // Deserialize struct from Java
-    const deserializedStruct = 
fory.deserialize(reader.buffer(reader.varUInt32()));
+    let cursor = 0;
+    const deserializedStruct = fory.deserialize(content.subarray(cursor));
+    cursor += fory.binaryReader.getCursor();
 
     // Serialize the deserialized struct back
     const serializedData = fory.serialize(deserializedStruct);
@@ -1037,7 +1093,9 @@ describe("bool", () => {
 
   test("test_two_enum_field_compatible", () => {
     if (Boolean("1")) { return; }
-    const fory = new Fory();
+    const fory = new Fory({
+      mode: Mode.Compatible
+    });
 
     // Define and register TestEnum
     const TestEnum = {
@@ -1061,7 +1119,9 @@ describe("bool", () => {
     reader.reset(content);
 
     // Deserialize struct from Java
-    const deserializedStruct = 
fory.deserialize(reader.buffer(reader.varUInt32()));
+    let cursor = 0;
+    const deserializedStruct = fory.deserialize(content.subarray(cursor));
+    cursor += fory.binaryReader.getCursor();
 
     // Serialize the deserialized struct back
     const serializedData = fory.serialize(deserializedStruct);
@@ -1070,7 +1130,9 @@ describe("bool", () => {
 
   test("test_enum_schema_evolution_compatible", () => {
     if (Boolean("1")) { return; }
-    const fory = new Fory();
+    const fory = new Fory({
+      mode: Mode.Compatible
+    });
 
     // Register TestEnum
     const TestEnum = {
@@ -1088,7 +1150,9 @@ describe("bool", () => {
     reader.reset(content);
 
     // Deserialize empty struct from Java
-    const deserializedStruct = 
fory.deserialize(reader.buffer(reader.varUInt32()));
+    let cursor = 0;
+    const deserializedStruct = fory.deserialize(content.subarray(cursor));
+    cursor += fory.binaryReader.getCursor();
 
     // Serialize the deserialized struct back
     const serializedData = fory.serialize(deserializedStruct);
@@ -1097,7 +1161,9 @@ describe("bool", () => {
 
   test("test_nullable_field_schema_consistent_not_null", () => {
     if (Boolean("1")) { return; }
-    const fory = new Fory();
+    const fory = new Fory({
+      mode: Mode.Compatible
+    });
 
     @Type.struct(401, {
       intField: Type.int32(),
@@ -1117,7 +1183,9 @@ describe("bool", () => {
     reader.reset(content);
 
     // Deserialize struct from Java
-    const deserializedStruct = 
fory.deserialize(reader.buffer(reader.varUInt32()));
+    let cursor = 0;
+    const deserializedStruct = fory.deserialize(content.subarray(cursor));
+    cursor += fory.binaryReader.getCursor();
 
     // Serialize the deserialized struct back
     const serializedData = fory.serialize(deserializedStruct);
@@ -1126,7 +1194,9 @@ describe("bool", () => {
 
   test("test_nullable_field_schema_consistent_null", () => {
     if (Boolean("1")) { return; }
-    const fory = new Fory();
+    const fory = new Fory({
+      mode: Mode.Compatible
+    });
 
     @Type.struct(401, {
       intField: Type.int32(),
@@ -1146,7 +1216,9 @@ describe("bool", () => {
     reader.reset(content);
 
     // Deserialize struct from Java
-    const deserializedStruct = 
fory.deserialize(reader.buffer(reader.varUInt32()));
+    let cursor = 0;
+    const deserializedStruct = fory.deserialize(content.subarray(cursor));
+    cursor += fory.binaryReader.getCursor();
 
     // Serialize the deserialized struct back
     const serializedData = fory.serialize(deserializedStruct);
@@ -1155,7 +1227,9 @@ describe("bool", () => {
 
   test("test_nullable_field_compatible_not_null", () => {
     if (Boolean("1")) { return; }
-    const fory = new Fory();
+    const fory = new Fory({
+      mode: Mode.Compatible
+    });
 
     @Type.struct(402, {
       intField: Type.int32(),
@@ -1175,7 +1249,9 @@ describe("bool", () => {
     reader.reset(content);
 
     // Deserialize struct from Java
-    const deserializedStruct = 
fory.deserialize(reader.buffer(reader.varUInt32()));
+    let cursor = 0;
+    const deserializedStruct = fory.deserialize(content.subarray(cursor));
+    cursor += fory.binaryReader.getCursor();
 
     // Serialize the deserialized struct back
     const serializedData = fory.serialize(deserializedStruct);
@@ -1184,7 +1260,9 @@ describe("bool", () => {
 
   test("test_nullable_field_compatible_null", () => {
     if (Boolean("1")) { return; }
-    const fory = new Fory();
+    const fory = new Fory({
+      mode: Mode.Compatible
+    });
 
     @Type.struct(402, {
       intField: Type.int32(),
@@ -1204,7 +1282,9 @@ describe("bool", () => {
     reader.reset(content);
 
     // Deserialize struct from Java
-    const deserializedStruct = 
fory.deserialize(reader.buffer(reader.varUInt32()));
+    let cursor = 0;
+    const deserializedStruct = fory.deserialize(content.subarray(cursor));
+    cursor += fory.binaryReader.getCursor();
 
     // Serialize the deserialized struct back
     const serializedData = fory.serialize(deserializedStruct);
@@ -1213,7 +1293,9 @@ describe("bool", () => {
 
   test("test_ref_schema_consistent", () => {
     if (Boolean("1")) { return; }
-    const fory = new Fory();
+    const fory = new Fory({
+      mode: Mode.Compatible
+    });
 
     @Type.struct(501, {
       id: Type.int32(),
@@ -1239,7 +1321,9 @@ describe("bool", () => {
     reader.reset(content);
 
     // Deserialize outer struct from Java
-    const deserializedOuter = 
fory.deserialize(reader.buffer(reader.varUInt32()));
+    let cursor = 0;
+    const deserializedOuter = fory.deserialize(content.subarray(cursor));
+    cursor += fory.binaryReader.getCursor();
 
     // Serialize the deserialized outer struct back
     const serializedData = fory.serialize(deserializedOuter);
@@ -1248,7 +1332,9 @@ describe("bool", () => {
 
   test("test_ref_compatible", () => {
     if (Boolean("1")) { return; }
-    const fory = new Fory();
+    const fory = new Fory({
+      mode: Mode.Compatible
+    });
 
     @Type.struct(503, {
       id: Type.int32(),
@@ -1274,7 +1360,9 @@ describe("bool", () => {
     reader.reset(content);
 
     // Deserialize outer struct from Java
-    const deserializedOuter = 
fory.deserialize(reader.buffer(reader.varUInt32()));
+    let cursor = 0;
+    const deserializedOuter = fory.deserialize(content.subarray(cursor));
+    cursor += fory.binaryReader.getCursor();
 
     // Serialize the deserialized outer struct back
     const serializedData = fory.serialize(deserializedOuter);
@@ -1283,7 +1371,9 @@ describe("bool", () => {
 
   test("test_circular_ref_schema_consistent", () => {
     if (Boolean("1")) { return; }
-    const fory = new Fory();
+    const fory = new Fory({
+      mode: Mode.Compatible
+    });
 
     @Type.struct(601, {
       name: Type.string(),
@@ -1299,7 +1389,9 @@ describe("bool", () => {
     reader.reset(content);
 
     // Deserialize circular struct from Java
-    const deserializedStruct = 
fory.deserialize(reader.buffer(reader.varUInt32()));
+    let cursor = 0;
+    const deserializedStruct = fory.deserialize(content.subarray(cursor));
+    cursor += fory.binaryReader.getCursor();
 
     // Serialize the deserialized struct back
     const serializedData = fory.serialize(deserializedStruct);
@@ -1308,7 +1400,9 @@ describe("bool", () => {
 
   test("test_circular_ref_compatible", () => {
     if (Boolean("1")) { return; }
-    const fory = new Fory();
+    const fory = new Fory({
+      mode: Mode.Compatible
+    });
 
     @Type.struct(602, {
       name: Type.string(),
@@ -1324,7 +1418,9 @@ describe("bool", () => {
     reader.reset(content);
 
     // Deserialize circular struct from Java
-    const deserializedStruct = 
fory.deserialize(reader.buffer(reader.varUInt32()));
+    let cursor = 0;
+    const deserializedStruct = fory.deserialize(content.subarray(cursor));
+    cursor += fory.binaryReader.getCursor();
 
     // Serialize the deserialized struct back
     const serializedData = fory.serialize(deserializedStruct);
@@ -1333,7 +1429,9 @@ describe("bool", () => {
 
   test("test_unsigned_schema_consistent_simple", () => {
     if (Boolean("1")) { return; }
-    const fory = new Fory();
+    const fory = new Fory({
+      mode: Mode.Compatible
+    });
 
     @Type.struct(1, {
       u64Tagged: Type.int64(),
@@ -1349,7 +1447,9 @@ describe("bool", () => {
     reader.reset(content);
 
     // Deserialize struct from Java
-    const deserializedStruct = 
fory.deserialize(reader.buffer(reader.varUInt32()));
+    let cursor = 0;
+    const deserializedStruct = fory.deserialize(content.subarray(cursor));
+    cursor += fory.binaryReader.getCursor();
 
     // Serialize the deserialized struct back
     const serializedData = fory.serialize(deserializedStruct);
@@ -1358,7 +1458,9 @@ describe("bool", () => {
 
   test("test_unsigned_schema_consistent", () => {
     if (Boolean("1")) { return; }
-    const fory = new Fory();
+    const fory = new Fory({
+      mode: Mode.Compatible
+    });
 
     @Type.struct(501, {
       u8Field: Type.uint8(),
@@ -1378,7 +1480,9 @@ describe("bool", () => {
     reader.reset(content);
 
     // Deserialize struct from Java
-    const deserializedStruct = 
fory.deserialize(reader.buffer(reader.varUInt32()));
+    let cursor = 0;
+    const deserializedStruct = fory.deserialize(content.subarray(cursor));
+    cursor += fory.binaryReader.getCursor();
 
     // Serialize the deserialized struct back
     const serializedData = fory.serialize(deserializedStruct);
@@ -1387,7 +1491,9 @@ describe("bool", () => {
 
   test("test_unsigned_schema_compatible", () => {
     if (Boolean("1")) { return; }
-    const fory = new Fory();
+    const fory = new Fory({
+      mode: Mode.Compatible
+    });
 
     @Type.struct(502, {
       u8Field: Type.uint8(),
@@ -1407,10 +1513,12 @@ describe("bool", () => {
     reader.reset(content);
 
     // Deserialize struct from Java
-    const deserializedStruct = 
fory.deserialize(reader.buffer(reader.varUInt32()));
+    let cursor = 0;
+    const deserializedStruct = fory.deserialize(content.subarray(cursor));
+    cursor += fory.binaryReader.getCursor();
 
     // Serialize the deserialized struct back
     const serializedData = fory.serialize(deserializedStruct);
-    writeToFile(serializedData as Buffer);
+   // writeToFile(serializedData as Buffer);
   });
 });


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to