This is an automated email from the ASF dual-hosted git repository.

chaokunyang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fury.git


The following commit(s) were added to refs/heads/main by this push:
     new 5e0b8a9c perf(java): optimize pojo copy performance (#1739)
5e0b8a9c is described below

commit 5e0b8a9c535c954132baad1cd0bf580824f7226d
Author: Shawn Yang <[email protected]>
AuthorDate: Sat Jul 20 16:40:18 2024 +0800

    perf(java): optimize pojo copy performance (#1739)
    
    ## What does this PR do?
     optimize pojo copy performance by 1~4X and add copy benchmark
    
    ## Related issues
    
    <!--
    Is there any related issue? Please attach here.
    
    - #xxxx0
    - #xxxx1
    - #xxxx2
    -->
    
    
    ## Does this PR introduce any user-facing change?
    
    <!--
    If any user-facing interface changes, please [open an
    issue](https://github.com/apache/fury/issues/new/choose) describing the
    need to do so and update the document if necessary.
    -->
    
    - [ ] Does this PR introduce any public API change?
    - [ ] Does this PR introduce any binary protocol compatibility change?
    
    
    ## Benchmark
    
    Before:
    ```java
    Benchmark                (bufferType)   (objectType)  (references)   Mode  
Cnt        Score         Error  Units
    CopyBenchmark.fury_copy         array  MEDIA_CONTENT         false  thrpt   
 3  1294614.644 ± 2103796.392  ops/s
    CopyBenchmark.fury_copy         array         SAMPLE         false  thrpt   
 3  1909071.799 ± 2343118.356  ops/s
    CopyBenchmark.fury_copy         array         STRUCT         false  thrpt   
 3  1220680.635 ± 1019806.837  ops/s
    CopyBenchmark.fury_copy         array        STRUCT2         false  thrpt   
 3   584429.541 ±  111229.502  ops/s
    CopyBenchmark.kryo_copy         array  MEDIA_CONTENT         false  thrpt   
 3  1008490.635 ±  309047.316  ops/s
    CopyBenchmark.kryo_copy         array         SAMPLE         false  thrpt   
 3   921863.274 ± 1082442.180  ops/s
    CopyBenchmark.kryo_copy         array         STRUCT         false  thrpt   
 3  1336939.990 ±  795836.830  ops/s
    CopyBenchmark.kryo_copy         array        STRUCT2         false  thrpt   
 3   168367.000 ±  236966.711  ops/s
    ```
    
    Java
    ```java
    Benchmark                (bufferType)   (objectType)  (references)   Mode  
Cnt        Score         Error  Units
    CopyBenchmark.fury_copy         array  MEDIA_CONTENT         false  thrpt   
 3  2201830.808 ± 4640532.805  ops/s
    CopyBenchmark.fury_copy         array         SAMPLE         false  thrpt   
 3  4945272.027 ± 5429361.187  ops/s
    CopyBenchmark.fury_copy         array         STRUCT         false  thrpt   
 3  4809373.970 ± 6803285.896  ops/s
    CopyBenchmark.fury_copy         array        STRUCT2         false  thrpt   
 3  2577391.052 ± 6682601.210  ops/s
    CopyBenchmark.kryo_copy         array  MEDIA_CONTENT         false  thrpt   
 3   830059.189 ± 2509547.599  ops/s
    CopyBenchmark.kryo_copy         array         SAMPLE         false  thrpt   
 3   696901.072 ±  525070.309  ops/s
    CopyBenchmark.kryo_copy         array         STRUCT         false  thrpt   
 3   980635.311 ± 2495689.418  ops/s
    CopyBenchmark.kryo_copy         array        STRUCT2         false  thrpt   
 3   141996.627 ±  343339.930  ops/s
    ```
---
 .../org/apache/fury/benchmark/CopyBenchmark.java   |  55 ++++
 .../src/main/java/org/apache/fury/Fury.java        |  49 ++-
 .../fury/serializer/AbstractObjectSerializer.java  | 337 ++++++++++++++++++---
 .../fury/serializer/MetaSharedSerializer.java      |   2 +-
 .../serializer/NonexistentClassSerializers.java    |   2 +-
 .../apache/fury/serializer/ObjectSerializer.java   | 158 ----------
 6 files changed, 396 insertions(+), 207 deletions(-)

diff --git 
a/java/benchmark/src/main/java/org/apache/fury/benchmark/CopyBenchmark.java 
b/java/benchmark/src/main/java/org/apache/fury/benchmark/CopyBenchmark.java
new file mode 100644
index 00000000..1940871f
--- /dev/null
+++ b/java/benchmark/src/main/java/org/apache/fury/benchmark/CopyBenchmark.java
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+package org.apache.fury.benchmark;
+
+import java.io.IOException;
+import org.apache.fury.benchmark.state.FuryState;
+import org.apache.fury.benchmark.state.KryoState;
+import org.openjdk.jmh.Main;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.CompilerControl;
+import org.openjdk.jmh.annotations.Mode;
+
+@BenchmarkMode(Mode.Throughput)
+@CompilerControl(value = CompilerControl.Mode.INLINE)
+public class CopyBenchmark {
+
+  @Benchmark
+  public Object fury_copy(FuryState.FuryUserTypeState state) {
+    return state.fury.copy(state.object);
+  }
+
+  @Benchmark
+  public Object kryo_copy(KryoState.KryoUserTypeState state) {
+    return state.kryo.copy(state.object);
+  }
+
+  public static void main(String[] args) throws IOException {
+    if (args.length == 0) {
+      String commandLine =
+          "org.apache.fury.*CopyBenchmark.* -f 1 -wi 3 -i 3 -t 1 -w 2s -r 2s 
-rf csv "
+              + "-p bufferType=array -p references=false";
+      System.out.println(commandLine);
+      args = commandLine.split(" ");
+    }
+    Main.main(args);
+  }
+}
diff --git a/java/fury-core/src/main/java/org/apache/fury/Fury.java 
b/java/fury-core/src/main/java/org/apache/fury/Fury.java
index 4cb45b21..87c635d7 100644
--- a/java/fury-core/src/main/java/org/apache/fury/Fury.java
+++ b/java/fury-core/src/main/java/org/apache/fury/Fury.java
@@ -1273,14 +1273,6 @@ public final class Fury implements BaseFury {
     Object copy;
     ClassInfo classInfo = classResolver.getOrUpdateClassInfo(obj.getClass());
     switch (classInfo.getClassId()) {
-      case ClassResolver.BOOLEAN_CLASS_ID:
-      case ClassResolver.BYTE_CLASS_ID:
-      case ClassResolver.CHAR_CLASS_ID:
-      case ClassResolver.SHORT_CLASS_ID:
-      case ClassResolver.INTEGER_CLASS_ID:
-      case ClassResolver.FLOAT_CLASS_ID:
-      case ClassResolver.LONG_CLASS_ID:
-      case ClassResolver.DOUBLE_CLASS_ID:
       case ClassResolver.PRIMITIVE_BOOLEAN_CLASS_ID:
       case ClassResolver.PRIMITIVE_BYTE_CLASS_ID:
       case ClassResolver.PRIMITIVE_CHAR_CLASS_ID:
@@ -1289,6 +1281,14 @@ public final class Fury implements BaseFury {
       case ClassResolver.PRIMITIVE_FLOAT_CLASS_ID:
       case ClassResolver.PRIMITIVE_LONG_CLASS_ID:
       case ClassResolver.PRIMITIVE_DOUBLE_CLASS_ID:
+      case ClassResolver.BOOLEAN_CLASS_ID:
+      case ClassResolver.BYTE_CLASS_ID:
+      case ClassResolver.CHAR_CLASS_ID:
+      case ClassResolver.SHORT_CLASS_ID:
+      case ClassResolver.INTEGER_CLASS_ID:
+      case ClassResolver.FLOAT_CLASS_ID:
+      case ClassResolver.LONG_CLASS_ID:
+      case ClassResolver.DOUBLE_CLASS_ID:
       case ClassResolver.STRING_CLASS_ID:
         return obj;
       case ClassResolver.PRIMITIVE_BOOLEAN_ARRAY_CLASS_ID:
@@ -1319,22 +1319,49 @@ public final class Fury implements BaseFury {
         String[] stringArr = (String[]) obj;
         return (T) Arrays.copyOf(stringArr, stringArr.length);
       case ClassResolver.ARRAYLIST_CLASS_ID:
-        copyDepth++;
         copy = arrayListSerializer.copy((ArrayList) obj);
         break;
       case ClassResolver.HASHMAP_CLASS_ID:
-        copyDepth++;
         copy = hashMapSerializer.copy((HashMap) obj);
         break;
         // todo: add fastpath for other types.
       default:
         copyDepth++;
         copy = classInfo.getSerializer().copy(obj);
+        copyDepth--;
     }
-    copyDepth--;
     return (T) copy;
   }
 
+  public <T> T copyObject(T obj, int classId) {
+    if (obj == null) {
+      return null;
+    }
+    // Fast path to avoid cost of query class map.
+    switch (classId) {
+      case ClassResolver.PRIMITIVE_BOOLEAN_CLASS_ID:
+      case ClassResolver.PRIMITIVE_BYTE_CLASS_ID:
+      case ClassResolver.PRIMITIVE_CHAR_CLASS_ID:
+      case ClassResolver.PRIMITIVE_SHORT_CLASS_ID:
+      case ClassResolver.PRIMITIVE_INT_CLASS_ID:
+      case ClassResolver.PRIMITIVE_FLOAT_CLASS_ID:
+      case ClassResolver.PRIMITIVE_LONG_CLASS_ID:
+      case ClassResolver.PRIMITIVE_DOUBLE_CLASS_ID:
+      case ClassResolver.BOOLEAN_CLASS_ID:
+      case ClassResolver.BYTE_CLASS_ID:
+      case ClassResolver.CHAR_CLASS_ID:
+      case ClassResolver.SHORT_CLASS_ID:
+      case ClassResolver.INTEGER_CLASS_ID:
+      case ClassResolver.FLOAT_CLASS_ID:
+      case ClassResolver.LONG_CLASS_ID:
+      case ClassResolver.DOUBLE_CLASS_ID:
+      case ClassResolver.STRING_CLASS_ID:
+        return obj;
+      default:
+        return (T) 
classResolver.getOrUpdateClassInfo(obj.getClass()).getSerializer().copy(obj);
+    }
+  }
+
   /**
    * Track ref for copy.
    *
diff --git 
a/java/fury-core/src/main/java/org/apache/fury/serializer/AbstractObjectSerializer.java
 
b/java/fury-core/src/main/java/org/apache/fury/serializer/AbstractObjectSerializer.java
index c609ce30..8591c6c8 100644
--- 
a/java/fury-core/src/main/java/org/apache/fury/serializer/AbstractObjectSerializer.java
+++ 
b/java/fury-core/src/main/java/org/apache/fury/serializer/AbstractObjectSerializer.java
@@ -19,34 +19,47 @@
 
 package org.apache.fury.serializer;
 
+import static org.apache.fury.type.DescriptorGrouper.createDescriptorGrouper;
+import static org.apache.fury.type.TypeUtils.getRawType;
+
 import java.lang.invoke.MethodHandle;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.List;
 import org.apache.fury.Fury;
+import org.apache.fury.collection.Tuple2;
+import org.apache.fury.collection.Tuple3;
 import org.apache.fury.memory.Platform;
 import org.apache.fury.reflect.FieldAccessor;
 import org.apache.fury.reflect.ReflectionUtils;
+import org.apache.fury.reflect.TypeRef;
+import org.apache.fury.resolver.ClassInfo;
+import org.apache.fury.resolver.ClassInfoHolder;
 import org.apache.fury.resolver.ClassResolver;
-import org.apache.fury.resolver.FieldResolver.FieldInfo;
 import org.apache.fury.resolver.RefResolver;
+import org.apache.fury.type.Descriptor;
+import org.apache.fury.type.DescriptorGrouper;
+import org.apache.fury.type.FinalObjectTypeStub;
+import org.apache.fury.type.GenericType;
+import org.apache.fury.util.record.RecordComponent;
 import org.apache.fury.util.record.RecordUtils;
 
 public abstract class AbstractObjectSerializer<T> extends Serializer<T> {
-
   protected final RefResolver refResolver;
   protected final ClassResolver classResolver;
   protected final boolean isRecord;
   protected final MethodHandle constructor;
+  private InternalFieldInfo[] fieldInfos;
 
   public AbstractObjectSerializer(Fury fury, Class<T> type) {
-    super(fury, type);
-    this.refResolver = fury.getRefResolver();
-    this.classResolver = fury.getClassResolver();
-    this.isRecord = RecordUtils.isRecord(type);
-    if (isRecord) {
-      this.constructor = RecordUtils.getRecordConstructor(type).f1;
-    } else {
-      this.constructor = ReflectionUtils.getCtrHandle(type, false);
-    }
+    this(
+        fury,
+        type,
+        RecordUtils.isRecord(type)
+            ? RecordUtils.getRecordConstructor(type).f1
+            : ReflectionUtils.getCtrHandle(type, false));
   }
 
   public AbstractObjectSerializer(Fury fury, Class<T> type, MethodHandle 
constructor) {
@@ -71,68 +84,167 @@ public abstract class AbstractObjectSerializer<T> extends 
Serializer<T> {
       }
       return originObj;
     }
-    T newObj = newBean();
+    T newObj;
     if (needToCopyRef) {
       T copyObject = (T) fury.getCopyObject(originObj);
       if (copyObject != null) {
         return copyObject;
       }
+      newObj = newBean();
       fury.reference(originObj, newObj);
+    } else {
+      newObj = newBean();
     }
     copyFields(originObj, newObj);
     return newObj;
   }
 
   private Object[] copyFields(T originObj) {
-    return classResolver.getFieldResolver(type).getAllFieldsList().stream()
-        .map(
-            fieldInfo -> {
-              FieldAccessor fieldAccessor = fieldInfo.getFieldAccessor();
-              if (classResolver.isPrimitive(fieldInfo.getEmbeddedClassId())) {
-                return fieldAccessor.get(originObj);
-              }
-              return fury.copyObject(fieldAccessor.get(originObj));
-            })
-        .toArray();
+    InternalFieldInfo[] fieldInfos = this.fieldInfos;
+    if (fieldInfos == null) {
+      fieldInfos = buildFieldsInfo();
+    }
+    Object[] fieldValues = new Object[fieldInfos.length];
+    for (int i = 0; i < fieldInfos.length; i++) {
+      InternalFieldInfo fieldInfo = fieldInfos[i];
+      FieldAccessor fieldAccessor = fieldInfo.fieldAccessor;
+      long fieldOffset = fieldAccessor.getFieldOffset();
+      if (fieldOffset != -1) {
+        fieldValues[i] = copyField(originObj, fieldOffset, fieldInfo.classId);
+      } else {
+        // field in record class has offset -1
+        Object fieldValue = fieldAccessor.get(originObj);
+        fieldValues[i] = fury.copyObject(fieldValue, fieldInfo.classId);
+      }
+    }
+    return fieldValues;
   }
 
   private void copyFields(T originObj, T newObj) {
-    List<FieldInfo> fieldsList = 
classResolver.getFieldResolver(type).getAllFieldsList();
-    for (FieldInfo info : fieldsList) {
-      FieldAccessor fieldAccessor = info.getFieldAccessor();
-      long offset = fieldAccessor.getFieldOffset();
-      switch (info.getEmbeddedClassId()) {
+    InternalFieldInfo[] fieldInfos = this.fieldInfos;
+    if (fieldInfos == null) {
+      fieldInfos = buildFieldsInfo();
+    }
+    for (InternalFieldInfo fieldInfo : fieldInfos) {
+      FieldAccessor fieldAccessor = fieldInfo.fieldAccessor;
+      long fieldOffset = fieldAccessor.getFieldOffset();
+      // record class won't go to this path;
+      assert fieldOffset != -1;
+      switch (fieldInfo.classId) {
         case ClassResolver.PRIMITIVE_BYTE_CLASS_ID:
-          Platform.putByte(newObj, offset, Platform.getByte(originObj, 
offset));
+          Platform.putByte(newObj, fieldOffset, Platform.getByte(originObj, 
fieldOffset));
           break;
         case ClassResolver.PRIMITIVE_CHAR_CLASS_ID:
-          Platform.putChar(newObj, offset, Platform.getChar(originObj, 
offset));
+          Platform.putChar(newObj, fieldOffset, Platform.getChar(originObj, 
fieldOffset));
           break;
         case ClassResolver.PRIMITIVE_SHORT_CLASS_ID:
-          Platform.putShort(newObj, offset, Platform.getShort(originObj, 
offset));
+          Platform.putShort(newObj, fieldOffset, Platform.getShort(originObj, 
fieldOffset));
           break;
         case ClassResolver.PRIMITIVE_INT_CLASS_ID:
-          Platform.putInt(newObj, offset, Platform.getInt(originObj, offset));
+          Platform.putInt(newObj, fieldOffset, Platform.getInt(originObj, 
fieldOffset));
           break;
         case ClassResolver.PRIMITIVE_LONG_CLASS_ID:
-          Platform.putLong(newObj, offset, Platform.getLong(originObj, 
offset));
+          Platform.putLong(newObj, fieldOffset, Platform.getLong(originObj, 
fieldOffset));
           break;
         case ClassResolver.PRIMITIVE_FLOAT_CLASS_ID:
-          Platform.putFloat(newObj, offset, Platform.getFloat(originObj, 
offset));
+          Platform.putFloat(newObj, fieldOffset, Platform.getFloat(originObj, 
fieldOffset));
           break;
         case ClassResolver.PRIMITIVE_DOUBLE_CLASS_ID:
-          Platform.putDouble(newObj, offset, Platform.getDouble(originObj, 
offset));
+          Platform.putDouble(newObj, fieldOffset, 
Platform.getDouble(originObj, fieldOffset));
           break;
         case ClassResolver.PRIMITIVE_BOOLEAN_CLASS_ID:
-          Platform.putBoolean(newObj, offset, Platform.getBoolean(originObj, 
offset));
+          Platform.putBoolean(newObj, fieldOffset, 
Platform.getBoolean(originObj, fieldOffset));
+          break;
+        case ClassResolver.BOOLEAN_CLASS_ID:
+        case ClassResolver.BYTE_CLASS_ID:
+        case ClassResolver.CHAR_CLASS_ID:
+        case ClassResolver.SHORT_CLASS_ID:
+        case ClassResolver.INTEGER_CLASS_ID:
+        case ClassResolver.FLOAT_CLASS_ID:
+        case ClassResolver.LONG_CLASS_ID:
+        case ClassResolver.DOUBLE_CLASS_ID:
+        case ClassResolver.STRING_CLASS_ID:
+          Platform.putObject(newObj, fieldOffset, 
Platform.getObject(originObj, fieldOffset));
           break;
         default:
           Platform.putObject(
-              newObj, offset, fury.copyObject(Platform.getObject(originObj, 
offset)));
+              newObj, fieldOffset, 
fury.copyObject(Platform.getObject(originObj, fieldOffset)));
       }
     }
   }
 
+  private Object copyField(Object targetObject, long fieldOffset, short 
classId) {
+    switch (classId) {
+      case ClassResolver.PRIMITIVE_BOOLEAN_CLASS_ID:
+        return Platform.getBoolean(targetObject, fieldOffset);
+      case ClassResolver.PRIMITIVE_BYTE_CLASS_ID:
+        return Platform.getByte(targetObject, fieldOffset);
+      case ClassResolver.PRIMITIVE_CHAR_CLASS_ID:
+        return Platform.getChar(targetObject, fieldOffset);
+      case ClassResolver.PRIMITIVE_SHORT_CLASS_ID:
+        return Platform.getShort(targetObject, fieldOffset);
+      case ClassResolver.PRIMITIVE_INT_CLASS_ID:
+        return Platform.getInt(targetObject, fieldOffset);
+      case ClassResolver.PRIMITIVE_FLOAT_CLASS_ID:
+        return Platform.getFloat(targetObject, fieldOffset);
+      case ClassResolver.PRIMITIVE_LONG_CLASS_ID:
+        return Platform.getLong(targetObject, fieldOffset);
+      case ClassResolver.PRIMITIVE_DOUBLE_CLASS_ID:
+        return Platform.getDouble(targetObject, fieldOffset);
+      case ClassResolver.BOOLEAN_CLASS_ID:
+      case ClassResolver.BYTE_CLASS_ID:
+      case ClassResolver.CHAR_CLASS_ID:
+      case ClassResolver.SHORT_CLASS_ID:
+      case ClassResolver.INTEGER_CLASS_ID:
+      case ClassResolver.FLOAT_CLASS_ID:
+      case ClassResolver.LONG_CLASS_ID:
+      case ClassResolver.DOUBLE_CLASS_ID:
+      case ClassResolver.STRING_CLASS_ID:
+        return Platform.getObject(targetObject, fieldOffset);
+      default:
+        return fury.copyObject(Platform.getObject(targetObject, fieldOffset));
+    }
+  }
+
+  private InternalFieldInfo[] buildFieldsInfo() {
+    List<Descriptor> descriptors = new ArrayList<>();
+    if (RecordUtils.isRecord(type)) {
+      RecordComponent[] components = RecordUtils.getRecordComponents(type);
+      assert components != null;
+      try {
+        for (RecordComponent component : components) {
+          Field field = type.getDeclaredField(component.getName());
+          descriptors.add(
+              new Descriptor(
+                  field, TypeRef.of(field.getGenericType()), 
component.getAccessor(), null));
+        }
+      } catch (NoSuchFieldException e) {
+        // impossible
+        Platform.throwException(e);
+      }
+    } else {
+      for (Field field : ReflectionUtils.getFields(type, true)) {
+        if (!Modifier.isStatic(field.getModifiers())) {
+          descriptors.add(new Descriptor(field, 
TypeRef.of(field.getGenericType()), null, null));
+        }
+      }
+    }
+    DescriptorGrouper descriptorGrouper =
+        createDescriptorGrouper(
+            fury.getClassResolver()::isMonomorphic,
+            descriptors,
+            false,
+            fury.compressInt(),
+            fury.compressLong());
+    Tuple3<Tuple2<FinalTypeField[], boolean[]>, GenericTypeField[], 
GenericTypeField[]> infos =
+        buildFieldInfos(fury, descriptorGrouper);
+    fieldInfos = new InternalFieldInfo[descriptors.size()];
+    System.arraycopy(infos.f0.f0, 0, fieldInfos, 0, infos.f0.f0.length);
+    System.arraycopy(infos.f1, 0, fieldInfos, infos.f0.f0.length, 
infos.f1.length);
+    System.arraycopy(infos.f2, 0, fieldInfos, fieldInfos.length - 
infos.f2.length, infos.f2.length);
+    return fieldInfos;
+  }
+
   protected T newBean() {
     if (constructor != null) {
       try {
@@ -143,4 +255,157 @@ public abstract class AbstractObjectSerializer<T> extends 
Serializer<T> {
     }
     return Platform.newInstance(type);
   }
+
+  static Tuple3<Tuple2<FinalTypeField[], boolean[]>, GenericTypeField[], 
GenericTypeField[]>
+      buildFieldInfos(Fury fury, DescriptorGrouper grouper) {
+    // When a type is both Collection/Map and final, add it to collection/map 
fields to keep
+    // consistent with jit.
+    Collection<Descriptor> primitives = grouper.getPrimitiveDescriptors();
+    Collection<Descriptor> boxed = grouper.getBoxedDescriptors();
+    Collection<Descriptor> finals = grouper.getFinalDescriptors();
+    FinalTypeField[] finalFields =
+        new FinalTypeField[primitives.size() + boxed.size() + finals.size()];
+    int cnt = 0;
+    for (Descriptor d : primitives) {
+      finalFields[cnt++] = buildFinalTypeField(fury, d);
+    }
+    for (Descriptor d : boxed) {
+      finalFields[cnt++] = buildFinalTypeField(fury, d);
+    }
+    // TODO(chaokunyang) Support Pojo<T> generics besides Map/Collection 
subclass
+    //  when it's supported in BaseObjectCodecBuilder.
+    for (Descriptor d : finals) {
+      finalFields[cnt++] = buildFinalTypeField(fury, d);
+    }
+    boolean[] isFinal = new boolean[finalFields.length];
+    for (int i = 0; i < isFinal.length; i++) {
+      ClassInfo classInfo = finalFields[i].classInfo;
+      isFinal[i] = classInfo != null && 
fury.getClassResolver().isMonomorphic(classInfo.getCls());
+    }
+    cnt = 0;
+    GenericTypeField[] otherFields = new 
GenericTypeField[grouper.getOtherDescriptors().size()];
+    for (Descriptor descriptor : grouper.getOtherDescriptors()) {
+      GenericTypeField genericTypeField =
+          new GenericTypeField(
+              descriptor.getRawType(),
+              descriptor.getDeclaringClass() + "." + descriptor.getName(),
+              descriptor.getField() != null
+                  ? FieldAccessor.createAccessor(descriptor.getField())
+                  : null,
+              fury);
+      otherFields[cnt++] = genericTypeField;
+    }
+    cnt = 0;
+    Collection<Descriptor> collections = grouper.getCollectionDescriptors();
+    Collection<Descriptor> maps = grouper.getMapDescriptors();
+    GenericTypeField[] containerFields = new 
GenericTypeField[collections.size() + maps.size()];
+    for (Descriptor d : collections) {
+      containerFields[cnt++] = buildContainerField(fury, d);
+    }
+    for (Descriptor d : maps) {
+      containerFields[cnt++] = buildContainerField(fury, d);
+    }
+    return Tuple3.of(Tuple2.of(finalFields, isFinal), otherFields, 
containerFields);
+  }
+
+  private static FinalTypeField buildFinalTypeField(Fury fury, Descriptor d) {
+    return new FinalTypeField(
+        d.getRawType(),
+        d.getDeclaringClass() + "." + d.getName(),
+        // `d.getField()` will be null when peer class doesn't have this field.
+        d.getField() != null ? FieldAccessor.createAccessor(d.getField()) : 
null,
+        fury);
+  }
+
+  private static GenericTypeField buildContainerField(Fury fury, Descriptor d) 
{
+    return new GenericTypeField(
+        d.getTypeRef(),
+        d.getDeclaringClass() + "." + d.getName(),
+        d.getField() != null ? FieldAccessor.createAccessor(d.getField()) : 
null,
+        fury);
+  }
+
+  static class InternalFieldInfo {
+    protected final short classId;
+    protected final String qualifiedFieldName;
+    protected final FieldAccessor fieldAccessor;
+
+    private InternalFieldInfo(
+        short classId, String qualifiedFieldName, FieldAccessor fieldAccessor) 
{
+      this.classId = classId;
+      this.qualifiedFieldName = qualifiedFieldName;
+      this.fieldAccessor = fieldAccessor;
+    }
+
+    @Override
+    public String toString() {
+      return "InternalFieldInfo{"
+          + "classId="
+          + classId
+          + ", fieldName="
+          + qualifiedFieldName
+          + ", field="
+          + (fieldAccessor != null ? fieldAccessor.getField() : null)
+          + '}';
+    }
+  }
+
+  static final class FinalTypeField extends InternalFieldInfo {
+    final ClassInfo classInfo;
+
+    private FinalTypeField(Class<?> type, String fieldName, FieldAccessor 
accessor, Fury fury) {
+      super(getRegisteredClassId(fury, type), fieldName, accessor);
+      // invoke `copy` to avoid ObjectSerializer construct clear serializer by 
`clearSerializer`.
+      if (type == FinalObjectTypeStub.class) {
+        // `FinalObjectTypeStub` has no fields, using its `classInfo`
+        // will make deserialization failed.
+        classInfo = null;
+      } else {
+        classInfo = fury.getClassResolver().getClassInfo(type);
+      }
+    }
+  }
+
+  static final class GenericTypeField extends InternalFieldInfo {
+    final GenericType genericType;
+    final ClassInfoHolder classInfoHolder;
+    final boolean trackingRef;
+
+    private GenericTypeField(
+        Class<?> cls, String qualifiedFieldName, FieldAccessor accessor, Fury 
fury) {
+      super(getRegisteredClassId(fury, cls), qualifiedFieldName, accessor);
+      // TODO support generics <T> in Pojo<T>, see 
ComplexObjectSerializer.getGenericTypes
+      genericType = fury.getClassResolver().buildGenericType(cls);
+      classInfoHolder = fury.getClassResolver().nilClassInfoHolder();
+      trackingRef = fury.getClassResolver().needToWriteRef(cls);
+    }
+
+    private GenericTypeField(
+        TypeRef<?> typeRef, String qualifiedFieldName, FieldAccessor accessor, 
Fury fury) {
+      super(getRegisteredClassId(fury, getRawType(typeRef)), 
qualifiedFieldName, accessor);
+      // TODO support generics <T> in Pojo<T>, see 
ComplexObjectSerializer.getGenericTypes
+      genericType = fury.getClassResolver().buildGenericType(typeRef);
+      classInfoHolder = fury.getClassResolver().nilClassInfoHolder();
+      trackingRef = 
fury.getClassResolver().needToWriteRef(getRawType(typeRef));
+    }
+
+    @Override
+    public String toString() {
+      return "GenericTypeField{"
+          + "genericType="
+          + genericType
+          + ", classId="
+          + classId
+          + ", qualifiedFieldName="
+          + qualifiedFieldName
+          + ", field="
+          + (fieldAccessor != null ? fieldAccessor.getField() : null)
+          + '}';
+    }
+  }
+
+  private static short getRegisteredClassId(Fury fury, Class<?> cls) {
+    Short classId = fury.getClassResolver().getRegisteredClassId(cls);
+    return classId == null ? ClassResolver.NO_CLASS_ID : classId;
+  }
 }
diff --git 
a/java/fury-core/src/main/java/org/apache/fury/serializer/MetaSharedSerializer.java
 
b/java/fury-core/src/main/java/org/apache/fury/serializer/MetaSharedSerializer.java
index 77647d8e..1292fcbd 100644
--- 
a/java/fury-core/src/main/java/org/apache/fury/serializer/MetaSharedSerializer.java
+++ 
b/java/fury-core/src/main/java/org/apache/fury/serializer/MetaSharedSerializer.java
@@ -96,7 +96,7 @@ public class MetaSharedSerializer<T> extends 
AbstractObjectSerializer<T> {
             Tuple2<ObjectSerializer.FinalTypeField[], boolean[]>,
             ObjectSerializer.GenericTypeField[],
             ObjectSerializer.GenericTypeField[]>
-        infos = ObjectSerializer.buildFieldInfos(fury, descriptorGrouper);
+        infos = AbstractObjectSerializer.buildFieldInfos(fury, 
descriptorGrouper);
     finalFields = infos.f0.f0;
     isFinal = infos.f0.f1;
     otherFields = infos.f1;
diff --git 
a/java/fury-core/src/main/java/org/apache/fury/serializer/NonexistentClassSerializers.java
 
b/java/fury-core/src/main/java/org/apache/fury/serializer/NonexistentClassSerializers.java
index 974bef46..fa8768b9 100644
--- 
a/java/fury-core/src/main/java/org/apache/fury/serializer/NonexistentClassSerializers.java
+++ 
b/java/fury-core/src/main/java/org/apache/fury/serializer/NonexistentClassSerializers.java
@@ -167,7 +167,7 @@ public final class NonexistentClassSerializers {
                 Tuple2<ObjectSerializer.FinalTypeField[], boolean[]>,
                 ObjectSerializer.GenericTypeField[],
                 ObjectSerializer.GenericTypeField[]>
-            tuple = ObjectSerializer.buildFieldInfos(fury, descriptorGrouper);
+            tuple = AbstractObjectSerializer.buildFieldInfos(fury, 
descriptorGrouper);
         int classVersionHash = 0;
         if (fury.checkClassVersion()) {
           classVersionHash = ObjectSerializer.computeVersionHash(descriptors);
diff --git 
a/java/fury-core/src/main/java/org/apache/fury/serializer/ObjectSerializer.java 
b/java/fury-core/src/main/java/org/apache/fury/serializer/ObjectSerializer.java
index 9edd5fb8..2c72fb2e 100644
--- 
a/java/fury-core/src/main/java/org/apache/fury/serializer/ObjectSerializer.java
+++ 
b/java/fury-core/src/main/java/org/apache/fury/serializer/ObjectSerializer.java
@@ -20,7 +20,6 @@
 package org.apache.fury.serializer;
 
 import static org.apache.fury.type.DescriptorGrouper.createDescriptorGrouper;
-import static org.apache.fury.type.TypeUtils.getRawType;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -36,15 +35,11 @@ import org.apache.fury.memory.MemoryBuffer;
 import org.apache.fury.memory.Platform;
 import org.apache.fury.meta.ClassDef;
 import org.apache.fury.reflect.FieldAccessor;
-import org.apache.fury.reflect.TypeRef;
 import org.apache.fury.resolver.ClassInfo;
-import org.apache.fury.resolver.ClassInfoHolder;
 import org.apache.fury.resolver.ClassResolver;
 import org.apache.fury.resolver.RefResolver;
 import org.apache.fury.type.Descriptor;
 import org.apache.fury.type.DescriptorGrouper;
-import org.apache.fury.type.FinalObjectTypeStub;
-import org.apache.fury.type.GenericType;
 import org.apache.fury.type.Generics;
 import org.apache.fury.util.record.RecordInfo;
 import org.apache.fury.util.record.RecordUtils;
@@ -127,75 +122,6 @@ public final class ObjectSerializer<T> extends 
AbstractObjectSerializer<T> {
     containerFields = infos.f2;
   }
 
-  static Tuple3<Tuple2<FinalTypeField[], boolean[]>, GenericTypeField[], 
GenericTypeField[]>
-      buildFieldInfos(Fury fury, DescriptorGrouper grouper) {
-    // When a type is both Collection/Map and final, add it to collection/map 
fields to keep
-    // consistent with jit.
-    Collection<Descriptor> primitives = grouper.getPrimitiveDescriptors();
-    Collection<Descriptor> boxed = grouper.getBoxedDescriptors();
-    Collection<Descriptor> finals = grouper.getFinalDescriptors();
-    FinalTypeField[] finalFields =
-        new FinalTypeField[primitives.size() + boxed.size() + finals.size()];
-    int cnt = 0;
-    for (Descriptor d : primitives) {
-      finalFields[cnt++] = buildFinalTypeField(fury, d);
-    }
-    for (Descriptor d : boxed) {
-      finalFields[cnt++] = buildFinalTypeField(fury, d);
-    }
-    // TODO(chaokunyang) Support Pojo<T> generics besides Map/Collection 
subclass
-    //  when it's supported in BaseObjectCodecBuilder.
-    for (Descriptor d : finals) {
-      finalFields[cnt++] = buildFinalTypeField(fury, d);
-    }
-    boolean[] isFinal = new boolean[finalFields.length];
-    for (int i = 0; i < isFinal.length; i++) {
-      ClassInfo classInfo = finalFields[i].classInfo;
-      isFinal[i] = classInfo != null && 
fury.getClassResolver().isMonomorphic(classInfo.getCls());
-    }
-    cnt = 0;
-    GenericTypeField[] otherFields = new 
GenericTypeField[grouper.getOtherDescriptors().size()];
-    for (Descriptor descriptor : grouper.getOtherDescriptors()) {
-      GenericTypeField genericTypeField =
-          new GenericTypeField(
-              descriptor.getRawType(),
-              descriptor.getDeclaringClass() + "." + descriptor.getName(),
-              descriptor.getField() != null
-                  ? FieldAccessor.createAccessor(descriptor.getField())
-                  : null,
-              fury);
-      otherFields[cnt++] = genericTypeField;
-    }
-    cnt = 0;
-    Collection<Descriptor> collections = grouper.getCollectionDescriptors();
-    Collection<Descriptor> maps = grouper.getMapDescriptors();
-    GenericTypeField[] containerFields = new 
GenericTypeField[collections.size() + maps.size()];
-    for (Descriptor d : collections) {
-      containerFields[cnt++] = buildContainerField(fury, d);
-    }
-    for (Descriptor d : maps) {
-      containerFields[cnt++] = buildContainerField(fury, d);
-    }
-    return Tuple3.of(Tuple2.of(finalFields, isFinal), otherFields, 
containerFields);
-  }
-
-  private static FinalTypeField buildFinalTypeField(Fury fury, Descriptor d) {
-    return new FinalTypeField(
-        d.getRawType(),
-        d.getDeclaringClass() + "." + d.getName(),
-        // `d.getField()` will be null when peer class doesn't have this field.
-        d.getField() != null ? FieldAccessor.createAccessor(d.getField()) : 
null,
-        fury);
-  }
-
-  private static GenericTypeField buildContainerField(Fury fury, Descriptor d) 
{
-    return new GenericTypeField(
-        d.getTypeRef(),
-        d.getDeclaringClass() + "." + d.getName(),
-        d.getField() != null ? FieldAccessor.createAccessor(d.getField()) : 
null,
-        fury);
-  }
-
   @Override
   public void write(MemoryBuffer buffer, T value) {
     Fury fury = this.fury;
@@ -856,90 +782,6 @@ public final class ObjectSerializer<T> extends 
AbstractObjectSerializer<T> {
     }
   }
 
-  static class InternalFieldInfo {
-    protected final short classId;
-    protected final String qualifiedFieldName;
-    protected final FieldAccessor fieldAccessor;
-
-    private InternalFieldInfo(
-        short classId, String qualifiedFieldName, FieldAccessor fieldAccessor) 
{
-      this.classId = classId;
-      this.qualifiedFieldName = qualifiedFieldName;
-      this.fieldAccessor = fieldAccessor;
-    }
-
-    @Override
-    public String toString() {
-      return "InternalFieldInfo{"
-          + "classId="
-          + classId
-          + ", fieldName="
-          + qualifiedFieldName
-          + ", field="
-          + (fieldAccessor != null ? fieldAccessor.getField() : null)
-          + '}';
-    }
-  }
-
-  static final class FinalTypeField extends InternalFieldInfo {
-    final ClassInfo classInfo;
-
-    private FinalTypeField(Class<?> type, String fieldName, FieldAccessor 
accessor, Fury fury) {
-      super(getRegisteredClassId(fury, type), fieldName, accessor);
-      // invoke `copy` to avoid ObjectSerializer construct clear serializer by 
`clearSerializer`.
-      if (type == FinalObjectTypeStub.class) {
-        // `FinalObjectTypeStub` has no fields, using its `classInfo`
-        // will make deserialization failed.
-        classInfo = null;
-      } else {
-        classInfo = fury.getClassResolver().getClassInfo(type);
-      }
-    }
-  }
-
-  static final class GenericTypeField extends InternalFieldInfo {
-    private final GenericType genericType;
-    final ClassInfoHolder classInfoHolder;
-    final boolean trackingRef;
-
-    private GenericTypeField(
-        Class<?> cls, String qualifiedFieldName, FieldAccessor accessor, Fury 
fury) {
-      super(getRegisteredClassId(fury, cls), qualifiedFieldName, accessor);
-      // TODO support generics <T> in Pojo<T>, see 
ComplexObjectSerializer.getGenericTypes
-      genericType = fury.getClassResolver().buildGenericType(cls);
-      classInfoHolder = fury.getClassResolver().nilClassInfoHolder();
-      trackingRef = fury.getClassResolver().needToWriteRef(cls);
-    }
-
-    private GenericTypeField(
-        TypeRef<?> typeRef, String qualifiedFieldName, FieldAccessor accessor, 
Fury fury) {
-      super(getRegisteredClassId(fury, getRawType(typeRef)), 
qualifiedFieldName, accessor);
-      // TODO support generics <T> in Pojo<T>, see 
ComplexObjectSerializer.getGenericTypes
-      genericType = fury.getClassResolver().buildGenericType(typeRef);
-      classInfoHolder = fury.getClassResolver().nilClassInfoHolder();
-      trackingRef = 
fury.getClassResolver().needToWriteRef(getRawType(typeRef));
-    }
-
-    @Override
-    public String toString() {
-      return "GenericTypeField{"
-          + "genericType="
-          + genericType
-          + ", classId="
-          + classId
-          + ", qualifiedFieldName="
-          + qualifiedFieldName
-          + ", field="
-          + (fieldAccessor != null ? fieldAccessor.getField() : null)
-          + '}';
-    }
-  }
-
-  private static short getRegisteredClassId(Fury fury, Class<?> cls) {
-    Short classId = fury.getClassResolver().getRegisteredClassId(cls);
-    return classId == null ? ClassResolver.NO_CLASS_ID : classId;
-  }
-
   public static int computeVersionHash(Collection<Descriptor> descriptors) {
     // TODO(chaokunyang) use murmurhash
     List<Integer> list = new ArrayList<>();


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


Reply via email to