diff --git a/java/fury-core/src/main/java/org/apache/fury/collection/ObjectArray.java b/java/fury-core/src/main/java/org/apache/fury/collection/ObjectArray.java index ff70d74027..94c08865f8 100644 --- a/java/fury-core/src/main/java/org/apache/fury/collection/ObjectArray.java +++ b/java/fury-core/src/main/java/org/apache/fury/collection/ObjectArray.java @@ -25,7 +25,8 @@ * An auto-growing array which avoid checks in {@code ArrayList} and faster for {@code clear} * method. */ -public final class ObjectArray { +@SuppressWarnings("unchecked") +public final class ObjectArray { private static final int COPY_THRESHOLD = 128; private static final int NIL_ARRAY_SIZE = 1024; private static final Object[] NIL_ARRAY = new Object[NIL_ARRAY_SIZE]; @@ -41,7 +42,7 @@ public ObjectArray(int initialCapacity) { objects = new Object[initialCapacity]; } - public void add(Object element) { + public void add(T element) { Object[] objects = this.objects; int size = this.size++; if (objects.length <= size) { @@ -53,22 +54,22 @@ public void add(Object element) { objects[size] = element; } - public void set(int index, Object element) { + public void set(int index, T element) { objects[index] = element; } - public Object get(int index) { - return objects[index]; + public T get(int index) { + return (T) objects[index]; } /** Returns tail item or null if no available item in the array. */ - public Object popOrNull() { + public T popOrNull() { int size = this.size; if (size == 0) { return null; } this.size = --size; - return objects[size]; + return (T) objects[size]; } public void clear() { diff --git a/java/fury-core/src/main/java/org/apache/fury/memory/MemoryBuffer.java b/java/fury-core/src/main/java/org/apache/fury/memory/MemoryBuffer.java index d57009eb54..de3c64e0b2 100644 --- a/java/fury-core/src/main/java/org/apache/fury/memory/MemoryBuffer.java +++ b/java/fury-core/src/main/java/org/apache/fury/memory/MemoryBuffer.java @@ -233,6 +233,14 @@ public boolean isOffHeap() { return heapMemory == null; } + /** + * Returns true, if the memory buffer is backed by heap memory and memory buffer can + * write to the whole memory region of underlying byte array. + */ + public boolean isHeapFullyWriteable() { + return heapMemory != null && heapOffset == 0; + } + /** * Get the heap byte array object. * diff --git a/java/fury-core/src/main/java/org/apache/fury/meta/ClassDef.java b/java/fury-core/src/main/java/org/apache/fury/meta/ClassDef.java index 400626604f..cd10f1d10d 100644 --- a/java/fury-core/src/main/java/org/apache/fury/meta/ClassDef.java +++ b/java/fury-core/src/main/java/org/apache/fury/meta/ClassDef.java @@ -197,7 +197,7 @@ public String toString() { /** Write class definition to buffer. */ public void writeClassDef(MemoryBuffer buffer) { - buffer.writeBytes(encoded); + buffer.writeBytes(encoded, 0, encoded.length); } /** Read class definition from buffer. */ diff --git a/java/fury-core/src/main/java/org/apache/fury/resolver/ClassInfo.java b/java/fury-core/src/main/java/org/apache/fury/resolver/ClassInfo.java index 79237eebe2..c08f8f86a7 100644 --- a/java/fury-core/src/main/java/org/apache/fury/resolver/ClassInfo.java +++ b/java/fury-core/src/main/java/org/apache/fury/resolver/ClassInfo.java @@ -48,7 +48,7 @@ public class ClassInfo { // class id must be less than Integer.MAX_VALUE/2 since we use bit 0 as class id flag. short classId; ClassDef classDef; - public boolean needToWriteClassDef; + boolean needToWriteClassDef; ClassInfo( Class cls, diff --git a/java/fury-core/src/main/java/org/apache/fury/resolver/ClassResolver.java b/java/fury-core/src/main/java/org/apache/fury/resolver/ClassResolver.java index d7aff7a45e..a755acd3e5 100644 --- a/java/fury-core/src/main/java/org/apache/fury/resolver/ClassResolver.java +++ b/java/fury-core/src/main/java/org/apache/fury/resolver/ClassResolver.java @@ -88,6 +88,7 @@ import org.apache.fury.collection.IdentityMap; import org.apache.fury.collection.IdentityObjectIntMap; import org.apache.fury.collection.LongMap; +import org.apache.fury.collection.ObjectArray; import org.apache.fury.collection.ObjectMap; import org.apache.fury.collection.Tuple2; import org.apache.fury.config.CompatibleMode; @@ -204,7 +205,9 @@ public class ClassResolver { private static final float loadFactor = 0.25f; private static final float furyMapLoadFactor = 0.25f; private static final int estimatedNumRegistered = 150; - private static final String META_SHARE_FIELDS_INFO_KEY = "shareFieldsInfo"; + private static final String SET_META__CONTEXT_MSG = + "Meta context must be set before serialization, " + + "please set meta context by SerializationContext.setMetaContext"; private static final ClassInfo NIL_CLASS_INFO = new ClassInfo(null, null, null, null, false, null, null, ClassResolver.NO_CLASS_ID); @@ -1285,10 +1288,7 @@ public void writeClassWithMetaShare(MemoryBuffer buffer, ClassInfo classInfo) { return; } MetaContext metaContext = fury.getSerializationContext().getMetaContext(); - Preconditions.checkNotNull( - metaContext, - "Meta context must be set before serialization, " - + "please set meta context by SerializationContext.setMetaContext"); + assert metaContext != null : SET_META__CONTEXT_MSG; IdentityObjectIntMap> classMap = metaContext.classMap; int newId = classMap.size; int id = classMap.putOrGet(classInfo.cls, newId); @@ -1332,19 +1332,16 @@ boolean needToWriteClassDef(Serializer serializer) { } private ClassInfo readClassInfoWithMetaShare(MemoryBuffer buffer, MetaContext metaContext) { - Preconditions.checkNotNull( - metaContext, - "Meta context must be set before serialization," - + " please set meta context by SerializationContext.setMetaContext"); + assert metaContext != null : SET_META__CONTEXT_MSG; int header = buffer.readVarUint32Small14(); int id = header >>> 1; if ((header & 0b1) == 0) { return getOrUpdateClassInfo((short) id); } - List readClassInfos = metaContext.readClassInfos; + ObjectArray readClassInfos = metaContext.readClassInfos; ClassInfo classInfo = readClassInfos.get(id); if (classInfo == null) { - List readClassDefs = metaContext.readClassDefs; + ObjectArray readClassDefs = metaContext.readClassDefs; ClassDef classDef = readClassDefs.get(id); Tuple2 classDefTuple = extRegistry.classIdToDef.get(classDef.getId()); if (classDefTuple == null || classDefTuple.f1 == null) { @@ -1433,11 +1430,31 @@ private ClassInfo getMetaSharedClassInfo(ClassDef classDef, Class clz) { */ public void writeClassDefs(MemoryBuffer buffer) { MetaContext metaContext = fury.getSerializationContext().getMetaContext(); - buffer.writeVarUint32Small7(metaContext.writingClassDefs.size()); - for (ClassDef classDef : metaContext.writingClassDefs) { - classDef.writeClassDef(buffer); + ObjectArray writingClassDefs = metaContext.writingClassDefs; + final int size = writingClassDefs.size; + buffer.writeVarUint32Small7(size); + if (buffer.isHeapFullyWriteable()) { + writeClassDefs(buffer, writingClassDefs, size); + } else { + for (int i = 0; i < size; i++) { + writingClassDefs.get(i).writeClassDef(buffer); + } + } + metaContext.writingClassDefs.size = 0; + } + + private void writeClassDefs( + MemoryBuffer buffer, ObjectArray writingClassDefs, int size) { + int writerIndex = buffer.writerIndex(); + for (int i = 0; i < size; i++) { + byte[] encoded = writingClassDefs.get(i).getEncoded(); + int bytesLen = encoded.length; + buffer.ensure(writerIndex + bytesLen); + final byte[] targetArray = buffer.getHeapMemory(); + System.arraycopy(encoded, 0, targetArray, writerIndex, bytesLen); + writerIndex += bytesLen; } - metaContext.writingClassDefs.clear(); + buffer.writerIndex(writerIndex); } /** diff --git a/java/fury-core/src/main/java/org/apache/fury/resolver/MetaContext.java b/java/fury-core/src/main/java/org/apache/fury/resolver/MetaContext.java index 13ae1853df..e55b80cc55 100644 --- a/java/fury-core/src/main/java/org/apache/fury/resolver/MetaContext.java +++ b/java/fury-core/src/main/java/org/apache/fury/resolver/MetaContext.java @@ -19,9 +19,8 @@ package org.apache.fury.resolver; -import java.util.ArrayList; -import java.util.List; import org.apache.fury.collection.IdentityObjectIntMap; +import org.apache.fury.collection.ObjectArray; import org.apache.fury.memory.MemoryBuffer; import org.apache.fury.meta.ClassDef; @@ -34,9 +33,9 @@ public class MetaContext { public final IdentityObjectIntMap> classMap = new IdentityObjectIntMap<>(8, 0.4f); /** Class definitions read from peer. */ - public final List readClassDefs = new ArrayList<>(); + public final ObjectArray readClassDefs = new ObjectArray<>(); - public final List readClassInfos = new ArrayList<>(); + public final ObjectArray readClassInfos = new ObjectArray<>(); /** * New class definition which needs sending to peer. This will be filled up when there are new @@ -44,5 +43,5 @@ public class MetaContext { * * @see ClassResolver#writeClassDefs(MemoryBuffer) */ - public final List writingClassDefs = new ArrayList<>(); + public final ObjectArray writingClassDefs = new ObjectArray<>(); } diff --git a/java/fury-core/src/main/java/org/apache/fury/resolver/SerializationContext.java b/java/fury-core/src/main/java/org/apache/fury/resolver/SerializationContext.java index ea4f8fcf66..b85cd56426 100644 --- a/java/fury-core/src/main/java/org/apache/fury/resolver/SerializationContext.java +++ b/java/fury-core/src/main/java/org/apache/fury/resolver/SerializationContext.java @@ -74,7 +74,7 @@ public void resetWrite() { } if (scopedMetaShareEnabled) { metaContext.classMap.clear(); - metaContext.writingClassDefs.clear(); + metaContext.writingClassDefs.size = 0; } else { metaContext = null; } @@ -85,8 +85,8 @@ public void resetRead() { objects.clear(); } if (scopedMetaShareEnabled) { - metaContext.readClassInfos.clear(); - metaContext.readClassDefs.clear(); + metaContext.readClassInfos.size = 0; + metaContext.readClassDefs.size = 0; } else { metaContext = null; } @@ -98,9 +98,9 @@ public void reset() { } if (scopedMetaShareEnabled) { metaContext.classMap.clear(); - metaContext.writingClassDefs.clear(); - metaContext.readClassInfos.clear(); - metaContext.readClassDefs.clear(); + metaContext.writingClassDefs.size = 0; + metaContext.readClassInfos.size = 0; + metaContext.readClassDefs.size = 0; } else { metaContext = null; } diff --git a/java/fury-core/src/test/java/org/apache/fury/resolver/MetaContextTest.java b/java/fury-core/src/test/java/org/apache/fury/resolver/MetaContextTest.java index e75a0f7542..03a4735277 100644 --- a/java/fury-core/src/test/java/org/apache/fury/resolver/MetaContextTest.java +++ b/java/fury-core/src/test/java/org/apache/fury/resolver/MetaContextTest.java @@ -79,7 +79,7 @@ private void checkMetaShared(Fury fury, Object o) { Assert.assertEquals(fury.deserialize(bytes1), o); fury.getSerializationContext().setMetaContext(new MetaContext()); Assert.assertEquals(fury.serialize(o), bytes); - Assert.assertThrows(NullPointerException.class, () -> fury.serialize(o)); + Assert.assertThrows(AssertionError.class, () -> fury.serialize(o)); } // final InnerPojo will be taken as non-final for writing class def.