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/fory.git
The following commit(s) were added to refs/heads/main by this push:
new a3095c43e feat(java): reduce java serializer memory usage by share
across Fory instances (#3546)
a3095c43e is described below
commit a3095c43e4c70e040aa59d48c3c88ebca75ac801
Author: Shawn Yang <[email protected]>
AuthorDate: Thu Apr 9 22:06:29 2026 +0800
feat(java): reduce java serializer memory usage by share across Fory
instances (#3546)
## Why?
Reduce Java serializer memory usage by reusing safe serializer instances
across equivalent Fory runtimes, and make the shareability contract
explicit in the serializer API.
## What does this PR do?
- Replaces `Serializer.shareable()` with a new `Shareable` marker
interface and updates the serializer API/docs to use that contract.
- Migrates the eligible Java, Kotlin, and Scala serializers that are
safe to reuse to implement `Shareable`.
- Caches explicitly registered shareable serializers and their
`TypeInfo` in `SharedRegistry` so equivalent runtimes can reuse them
through `registerSerializer` and `registerInternalSerializer` without
changing lazy serializer creation.
- Keeps same-runtime serializer overrides local instead of publishing
them into `SharedRegistry`.
- Aligns xlang named-type lookup and unsigned serializer registration
with the shared serializer path.
- Adds focused Java and xlang tests for shared serializer reuse and
override behavior across equivalent runtimes.
## Related issues
N/A
## AI Contribution Checklist
- [ ] Substantial AI assistance was used in this PR: `yes` / `no`
- [ ] If `yes`, I included a completed [AI Contribution
Checklist](https://github.com/apache/fory/blob/main/AI_POLICY.md#9-contributor-checklist-for-ai-assisted-prs)
in this PR description and the required `AI Usage Disclosure`.
- [ ] If `yes`, my PR description includes the required `ai_review`
summary and screenshot evidence of the final clean AI review results
from both fresh reviewers on the current PR diff or current HEAD after
the latest code changes.
## Does this PR introduce any user-facing change?
- [x] Does this PR introduce any public API change?
- [ ] Does this PR introduce any binary protocol compatibility change?
## Benchmark
N/A
---
.agents/languages/java.md | 5 +
docs/guide/java/custom-serializers.md | 21 ++--
.../org/apache/fory/resolver/ClassResolver.java | 68 ++++++++-----
.../org/apache/fory/resolver/SharedRegistry.java | 37 ++++++-
.../org/apache/fory/resolver/TypeResolver.java | 40 +-------
.../org/apache/fory/resolver/XtypeResolver.java | 113 +++++++++++++--------
.../apache/fory/serializer/ArraySerializers.java | 8 +-
.../apache/fory/serializer/BufferSerializers.java | 8 +-
.../org/apache/fory/serializer/EnumSerializer.java | 7 +-
.../apache/fory/serializer/Float16Serializer.java | 7 +-
.../apache/fory/serializer/LocaleSerializer.java | 7 +-
.../fory/serializer/OptionalSerializers.java | 24 ++---
.../fory/serializer/PrimitiveSerializers.java | 100 ++++--------------
.../org/apache/fory/serializer/Serializer.java | 40 ++------
.../org/apache/fory/serializer/Serializers.java | 103 +++++--------------
.../java/org/apache/fory/serializer/Shareable.java | 92 +++++++++++++++++
.../apache/fory/serializer/TimeSerializers.java | 111 +++++---------------
.../fory/serializer/UnknownClassSerializers.java | 8 +-
.../fory/serializer/UnsignedSerializers.java | 32 ++----
.../collection/PrimitiveListSerializers.java | 9 +-
.../fory/type/unsigned/UnsignedSerializers.java | 103 ++-----------------
.../apache/fory/resolver/ClassResolverTest.java | 55 ++++++++++
.../java/org/apache/fory/xlang/RegisterTest.java | 50 +++++++++
.../extension/serializer/ByteStringSerializer.java | 8 +-
.../extension/serializer/ProtobufSerializer.java | 8 +-
.../fory/serializer/kotlin/DurationSerializer.kt | 5 +-
.../serializer/kotlin/UnsignedArraySerializers.kt | 5 +-
.../fory/serializer/kotlin/UnsignedSerializer.kt | 41 +-------
.../fory/serializer/kotlin/UuidSerializer.kt | 6 +-
.../serializer/scala/ToFactorySerializers.java | 15 +--
.../fory/serializer/scala/RangeSerializer.scala | 13 +--
31 files changed, 504 insertions(+), 645 deletions(-)
diff --git a/.agents/languages/java.md b/.agents/languages/java.md
index e55f12e22..a1e42dc30 100644
--- a/.agents/languages/java.md
+++ b/.agents/languages/java.md
@@ -7,6 +7,7 @@ Load this file when changing anything under `java/` or when
Java drives a cross-
- Run all Maven commands from within `java/`.
- Changes under `java/` must pass code style checks and tests.
- Fory Java requires JDK `17+`.
+- Run Java `spotless` with JDK `21+`. If the current runtime is lower than 21,
export `JAVA_HOME` to a JDK 21 installation before running `mvn spotless:check`
or `mvn spotless:apply`.
- `fory-core` targets Java 8 bytecode and `fory-format` targets Java 11
bytecode. Do not use newer APIs in those modules.
- Do not use wildcard imports.
- If you run temporary tests with `java -cp`, run `mvn -T16 install
-DskipTests` first so local Fory jars are current.
@@ -44,6 +45,10 @@ mvn -T16 spotless:check
# Apply formatting
mvn -T16 spotless:apply
+# Spotless with JDK 21 when the current runtime is older
+export JAVA_HOME=$(/usr/libexec/java_home -v 21)
+mvn -T16 spotless:check
+
# Style check
mvn -T16 checkstyle:check
diff --git a/docs/guide/java/custom-serializers.md
b/docs/guide/java/custom-serializers.md
index a1aee2f60..34bd73750 100644
--- a/docs/guide/java/custom-serializers.md
+++ b/docs/guide/java/custom-serializers.md
@@ -27,8 +27,8 @@ Custom serializers should not retain `Fory`.
- Use `Config` when the serializer only depends on immutable configuration and
can be shared.
- Use `TypeResolver` when the serializer needs type metadata, generics, or
nested dynamic dispatch.
-- If a serializer retains `TypeResolver`, it is usually not shareable and
should keep the default
- `shareable() == false`.
+- If a serializer retains `TypeResolver`, it is usually not shareable and
should not implement
+ `Shareable`.
## Basic Serializer
@@ -41,8 +41,9 @@ import org.apache.fory.context.ReadContext;
import org.apache.fory.context.WriteContext;
import org.apache.fory.memory.MemoryBuffer;
import org.apache.fory.serializer.Serializer;
+import org.apache.fory.serializer.Shareable;
-public final class FooSerializer extends Serializer<Foo> {
+public final class FooSerializer extends Serializer<Foo> implements Shareable {
public FooSerializer(Config config) {
super(config, Foo.class);
}
@@ -61,11 +62,6 @@ public final class FooSerializer extends Serializer<Foo> {
foo.f2 = readContext.readString(buffer);
return foo;
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
```
@@ -108,7 +104,7 @@ public final class EnvelopeSerializer extends
Serializer<Envelope> {
}
```
-This serializer can be shareable because it retains no runtime-local mutable
state.
+This serializer can implement `Shareable` because it retains no runtime-local
mutable state.
## Collection Serializers
@@ -218,9 +214,10 @@ fory.registerSerializer(
## Shareability
-Override `shareable()` only when the serializer can be safely reused across
equivalent runtimes and
-concurrent operations. In practice, that means the serializer must not retain
operation state,
-runtime-local mutable state, or mutable scratch buffers that are shared across
calls.
+Implement the `Shareable` marker interface when the serializer can be safely
reused across
+equivalent runtimes and concurrent operations. A shareable serializer must not
retain operation
+state, runtime-local mutable state, or mutable scratch buffers shared across
calls. Consumers can
+check shareability via `serializer instanceof Shareable`.
In practice:
diff --git
a/java/fory-core/src/main/java/org/apache/fory/resolver/ClassResolver.java
b/java/fory-core/src/main/java/org/apache/fory/resolver/ClassResolver.java
index 6e0ecd6b3..3a7dbbd2c 100644
--- a/java/fory-core/src/main/java/org/apache/fory/resolver/ClassResolver.java
+++ b/java/fory-core/src/main/java/org/apache/fory/resolver/ClassResolver.java
@@ -126,6 +126,7 @@ import
org.apache.fory.serializer.SerializedLambdaSerializer;
import org.apache.fory.serializer.Serializer;
import org.apache.fory.serializer.SerializerFactory;
import org.apache.fory.serializer.Serializers;
+import org.apache.fory.serializer.Shareable;
import org.apache.fory.serializer.TimeSerializers;
import org.apache.fory.serializer.UnknownClass;
import org.apache.fory.serializer.UnknownClass.UnknownEmptyStruct;
@@ -705,24 +706,25 @@ public class ClassResolver extends TypeResolver {
@Override
public boolean isRegistered(Class<?> cls) {
- return isRegisteredByIdLocalOrFrozen(cls) ||
isRegisteredByNameLocalOrFrozen(cls);
+ return extRegistry.registeredClassIdMap.get(cls) != null
+ || extRegistry.registeredClasses.inverse().get(cls) != null;
}
public boolean isRegisteredByName(String name) {
- return isRegisteredByNameLocalOrFrozen(name);
+ return extRegistry.registeredClasses.get(name) != null;
}
@Override
public boolean isRegisteredByName(Class<?> cls) {
- return isRegisteredByNameLocalOrFrozen(cls);
+ return extRegistry.registeredClasses.inverse().get(cls) != null;
}
public String getRegisteredName(Class<?> cls) {
- return getRegisteredNameLocalOrFrozen(cls);
+ return extRegistry.registeredClasses.inverse().get(cls);
}
public Tuple2<String, String> getRegisteredNameTuple(Class<?> cls) {
- String name = getRegisteredNameLocalOrFrozen(cls);
+ String name = extRegistry.registeredClasses.inverse().get(cls);
Preconditions.checkNotNull(name);
int index = name.lastIndexOf(".");
if (index != -1) {
@@ -734,11 +736,11 @@ public class ClassResolver extends TypeResolver {
@Override
public boolean isRegisteredById(Class<?> cls) {
- return isRegisteredByIdLocalOrFrozen(cls);
+ return extRegistry.registeredClassIdMap.get(cls) != null;
}
public Integer getRegisteredClassId(Class<?> cls) {
- return getRegisteredClassIdLocalOrFrozen(cls);
+ return extRegistry.registeredClassIdMap.get(cls);
}
public Class<?> getRegisteredClass(short id) {
@@ -752,7 +754,7 @@ public class ClassResolver extends TypeResolver {
}
public Class<?> getRegisteredClass(String className) {
- return getRegisteredClassLocalOrFrozen(className);
+ return extRegistry.registeredClasses.get(className);
}
public Class<?> getRegisteredClassByTypeId(int typeId) {
@@ -795,11 +797,11 @@ public class ClassResolver extends TypeResolver {
}
public String getTypeAlias(Class<?> cls) {
- Integer id = getRegisteredClassIdLocalOrFrozen(cls);
+ Integer id = extRegistry.registeredClassIdMap.get(cls);
if (id != null) {
return Integer.toUnsignedString(id);
}
- String name = getRegisteredNameLocalOrFrozen(cls);
+ String name = extRegistry.registeredClasses.inverse().get(cls);
if (name != null) {
return name;
}
@@ -815,7 +817,7 @@ public class ClassResolver extends TypeResolver {
if (typeInfo != null) {
return typeInfo.typeId;
}
- Integer classId = getRegisteredClassIdLocalOrFrozen(cls);
+ Integer classId = extRegistry.registeredClassIdMap.get(cls);
if (classId != null) {
typeInfo = classInfoMap.get(cls);
if (typeInfo == null) {
@@ -843,7 +845,7 @@ public class ClassResolver extends TypeResolver {
if (typeInfo != null) {
return typeInfo.userTypeId;
}
- Integer classId = getRegisteredClassIdLocalOrFrozen(cls);
+ Integer classId = extRegistry.registeredClassIdMap.get(cls);
if (classId != null && !isInternalRegisteredClassId(cls, classId)) {
return classId;
}
@@ -977,7 +979,7 @@ public class ClassResolver extends TypeResolver {
public <T> void registerSerializer(Class<T> type, Class<? extends
Serializer> serializerClass) {
checkRegisterAllowed();
checkSerializerRegistration(type, serializerClass);
- registerSerializer(type, Serializers.newSerializer(this, type,
serializerClass));
+ registerSerializerImpl(type, Serializers.newSerializer(this, type,
serializerClass));
}
@Override
@@ -998,7 +1000,7 @@ public class ClassResolver extends TypeResolver {
*/
@Override
public void registerInternalSerializer(Class<?> type, Serializer<?>
serializer) {
- Integer classId = getRegisteredClassIdLocalOrFrozen(type);
+ Integer classId = extRegistry.registeredClassIdMap.get(type);
if (classId != null && !isInternalRegisteredClassId(type, classId)) {
throw new IllegalArgumentException(
String.format(
@@ -1022,9 +1024,29 @@ public class ClassResolver extends TypeResolver {
if
(!serializer.getClass().getPackage().getName().startsWith("org.apache.fory")) {
SerializationUtils.validate(type, serializer.getClass());
}
+ TypeInfo existingTypeInfo = classInfoMap.get(type);
+ boolean localOverride = existingTypeInfo != null &&
existingTypeInfo.serializer != null;
+ boolean shareable = serializer instanceof Shareable;
+ if (shareable && !localOverride) {
+ serializer = sharedRegistry.cacheRegisteredSerializer(type, serializer);
+ }
addSerializer(type, serializer);
TypeInfo typeInfo = classInfoMap.get(type);
- classInfoMap.put(type, typeInfo);
+ if (shareable && !localOverride) {
+ TypeInfo sharedTypeInfo = sharedRegistry.cacheRegisteredTypeInfo(type,
typeInfo);
+ if (sharedTypeInfo != typeInfo) {
+ typeInfo = sharedTypeInfo;
+ updateTypeInfo(type, typeInfo);
+ if (typeInfo.namespace != null && typeInfo.typeName != null) {
+ TypeNameBytes typeNameBytes =
+ new TypeNameBytes(typeInfo.namespace.hash,
typeInfo.typeName.hash);
+ compositeNameBytes2TypeInfo.put(typeNameBytes, typeInfo);
+ }
+ if (typeInfoCache.type == type) {
+ typeInfoCache = NIL_TYPE_INFO;
+ }
+ }
+ }
// in order to support customized serializer for abstract or interface.
if (!type.isPrimitive() && (ReflectionUtils.isAbstract(type) ||
type.isInterface())) {
extRegistry.abstractTypeInfo.put(type, typeInfo);
@@ -1081,7 +1103,7 @@ public class ClassResolver extends TypeResolver {
/** Set serializer for class whose name is {@code className}. */
public void setSerializer(String className, Class<? extends Serializer>
serializer) {
for (Map.Entry<Class<?>, TypeInfo> entry : classInfoMap.iterable()) {
- if (isRegisteredByNameLocalOrFrozen(className)) {
+ if (extRegistry.registeredClasses.get(className) != null) {
LOG.warn("Skip clear serializer for registered class {}", className);
return;
}
@@ -1100,7 +1122,7 @@ public class ClassResolver extends TypeResolver {
for (Map.Entry<Class<?>, TypeInfo> entry : classInfoMap.iterable()) {
Class<?> cls = entry.getKey();
String className = cls.getName();
- if (isRegisteredByNameLocalOrFrozen(className)) {
+ if (extRegistry.registeredClasses.get(className) != null) {
continue;
}
if (className.startsWith(classNamePrefix)) {
@@ -1137,7 +1159,7 @@ public class ClassResolver extends TypeResolver {
public void addSerializer(Class<?> type, Serializer<?> serializer) {
Preconditions.checkNotNull(serializer);
TypeInfo typeInfo;
- Integer classId = getRegisteredClassIdLocalOrFrozen(type);
+ Integer classId = extRegistry.registeredClassIdMap.get(type);
boolean registered = classId != null;
if (registered) {
int id = classId;
@@ -1526,8 +1548,8 @@ public class ClassResolver extends TypeResolver {
if (!config.suppressClassRegistrationWarnings()
&& !Functions.isLambda(cls)
&& !ReflectionUtils.isJdkProxy(cls)
- && !isRegisteredByIdLocalOrFrozen(cls)
- && !isRegisteredByNameLocalOrFrozen(cls)
+ && extRegistry.registeredClassIdMap.get(cls) == null
+ && extRegistry.registeredClasses.inverse().get(cls) == null
&& !hasGraalvmSerializerClass(cls)
&& !shimDispatcher.contains(cls)
&& !extRegistry.isTypeCheckerSet()) {
@@ -1677,7 +1699,7 @@ public class ClassResolver extends TypeResolver {
}
private boolean isSecure(Class<?> cls) {
- if (isRegisteredByNameLocalOrFrozen(cls)
+ if (extRegistry.registeredClasses.inverse().get(cls) != null
|| hasGraalvmSerializerClass(cls)
|| shimDispatcher.contains(cls)) {
return true;
@@ -1696,7 +1718,7 @@ public class ClassResolver extends TypeResolver {
if (config.requireClassRegistration()) {
return Functions.isLambda(cls)
|| ReflectionUtils.isJdkProxy(cls)
- || isRegisteredByIdLocalOrFrozen(cls)
+ || extRegistry.registeredClassIdMap.get(cls) != null
|| shimDispatcher.contains(cls);
} else {
return extRegistry.typeChecker.checkType(this, cls.getName());
@@ -1797,7 +1819,7 @@ public class ClassResolver extends TypeResolver {
private TypeInfo buildClassInfo(Class<?> cls) {
TypeInfo typeInfo;
- Integer classId = getRegisteredClassIdLocalOrFrozen(cls);
+ Integer classId = extRegistry.registeredClassIdMap.get(cls);
// Don't create serializer in case the object for class is
non-serializable,
// Or class is abstract or interface.
int typeId;
diff --git
a/java/fory-core/src/main/java/org/apache/fory/resolver/SharedRegistry.java
b/java/fory-core/src/main/java/org/apache/fory/resolver/SharedRegistry.java
index c8152f6db..b7bc39378 100644
--- a/java/fory-core/src/main/java/org/apache/fory/resolver/SharedRegistry.java
+++ b/java/fory-core/src/main/java/org/apache/fory/resolver/SharedRegistry.java
@@ -37,11 +37,19 @@ import org.apache.fory.meta.Encoders;
import org.apache.fory.meta.MetaString;
import org.apache.fory.meta.MetaStringEncoder;
import org.apache.fory.meta.TypeDef;
+import org.apache.fory.serializer.Serializer;
import org.apache.fory.type.Descriptor;
import org.apache.fory.type.DescriptorGrouper;
import org.apache.fory.util.GraalvmSupport;
-/** Shared caches reused by multiple equivalent {@link org.apache.fory.Fory}
instances. */
+/**
+ * Shared caches reused by multiple equivalent {@link org.apache.fory.Fory}
instances.
+ *
+ * <p>A {@code SharedRegistry} is scoped to one effective Fory config/mode
family. Equivalent
+ * runtimes may share the same registry, but incompatible runtimes must not.
In particular, the
+ * registered serializer and registered {@link TypeInfo} caches below assume
one config scope and
+ * therefore are keyed only by class.
+ */
@Internal
public final class SharedRegistry {
private static final int MAX_CACHED_ENCODED_META_STRINGS = 32768;
@@ -67,6 +75,10 @@ public final class SharedRegistry {
new ConcurrentHashMap<>();
final ConcurrentHashMap<EncodedMetaStringKey, EncodedMetaString>
encodedMetaStringMap =
new ConcurrentHashMap<>();
+ final ConcurrentIdentityMap<Class<?>, TypeInfo> registeredTypeInfoCache =
+ new ConcurrentIdentityMap<>();
+ final ConcurrentIdentityMap<Class<?>, Serializer<?>>
registeredSerializerCache =
+ new ConcurrentIdentityMap<>();
private final Object metaStringCacheLock = new Object();
volatile IdentityHashMap<Class<?>, Integer> registeredClassIdMap;
volatile BiMap<String, Class<?>> registeredClasses;
@@ -90,6 +102,29 @@ public final class SharedRegistry {
return Objects.requireNonNull(registeredClasses);
}
+ Serializer<?> getRegisteredSerializer(Class<?> type) {
+ return registeredSerializerCache.get(type);
+ }
+
+ Serializer<?> cacheRegisteredSerializer(Class<?> type, Serializer<?>
serializer) {
+ Serializer<?> existing = registeredSerializerCache.putIfAbsent(type,
serializer);
+ if (existing == null) {
+ return serializer;
+ }
+ if (existing.getClass() != serializer.getClass()) {
+ throw new IllegalArgumentException(
+ String.format(
+ "Conflicting shareable serializer registration for %s.
Existing=%s, new=%s",
+ type.getName(), existing, serializer));
+ }
+ return existing;
+ }
+
+ TypeInfo cacheRegisteredTypeInfo(Class<?> type, TypeInfo typeInfo) {
+ TypeInfo existing = registeredTypeInfoCache.putIfAbsent(type, typeInfo);
+ return existing == null ? typeInfo : existing;
+ }
+
EncodedMetaString getPackageEncodedMetaString(String string) {
return getEncodedMetaString(
string,
diff --git
a/java/fory-core/src/main/java/org/apache/fory/resolver/TypeResolver.java
b/java/fory-core/src/main/java/org/apache/fory/resolver/TypeResolver.java
index cb2d4d23c..ff7cecb50 100644
--- a/java/fory-core/src/main/java/org/apache/fory/resolver/TypeResolver.java
+++ b/java/fory-core/src/main/java/org/apache/fory/resolver/TypeResolver.java
@@ -944,7 +944,7 @@ public abstract class TypeResolver {
}
return localTypeInfo;
}
- Integer classId = getRegisteredClassIdLocalOrFrozen(cls);
+ Integer classId = extRegistry.registeredClassIdMap.get(cls);
int typeId;
if (classId != null) {
TypeInfo registeredInfo = classInfoMap.get(cls);
@@ -1071,7 +1071,7 @@ public abstract class TypeResolver {
final Class<?> loadClass(
String className, boolean isEnum, int arrayDims, boolean
deserializeUnknownClass) {
extRegistry.typeChecker.checkType(this, className);
- Class<?> cls = getRegisteredClassLocalOrFrozen(className);
+ Class<?> cls = extRegistry.registeredClasses.get(className);
if (cls != null) {
return cls;
}
@@ -1716,42 +1716,6 @@ public abstract class TypeResolver {
return null;
}
- /**
- * Reads a registered class id from the mutable local maps before freeze or
the shared maps after
- * {@link #finishRegistration()}.
- */
- final Integer getRegisteredClassIdLocalOrFrozen(Class<?> cls) {
- return extRegistry.registeredClassIdMap.get(cls);
- }
-
- /**
- * Reads a registered class name from the mutable local maps before freeze
or the shared maps
- * after {@link #finishRegistration()}.
- */
- final String getRegisteredNameLocalOrFrozen(Class<?> cls) {
- return extRegistry.registeredClasses.inverse().get(cls);
- }
-
- /**
- * Reads a registered class lookup by name from the mutable local maps
before freeze or the shared
- * maps after {@link #finishRegistration()}.
- */
- final Class<?> getRegisteredClassLocalOrFrozen(String className) {
- return extRegistry.registeredClasses.get(className);
- }
-
- final boolean isRegisteredByIdLocalOrFrozen(Class<?> cls) {
- return getRegisteredClassIdLocalOrFrozen(cls) != null;
- }
-
- final boolean isRegisteredByNameLocalOrFrozen(Class<?> cls) {
- return getRegisteredNameLocalOrFrozen(cls) != null;
- }
-
- final boolean isRegisteredByNameLocalOrFrozen(String className) {
- return getRegisteredClassLocalOrFrozen(className) != null;
- }
-
public GenericType getObjectGenericType() {
return extRegistry.objectGenericType;
}
diff --git
a/java/fory-core/src/main/java/org/apache/fory/resolver/XtypeResolver.java
b/java/fory-core/src/main/java/org/apache/fory/resolver/XtypeResolver.java
index acc5b7b55..3fb39fb6f 100644
--- a/java/fory-core/src/main/java/org/apache/fory/resolver/XtypeResolver.java
+++ b/java/fory-core/src/main/java/org/apache/fory/resolver/XtypeResolver.java
@@ -86,6 +86,7 @@ import org.apache.fory.serializer.PrimitiveSerializers;
import org.apache.fory.serializer.SerializationUtils;
import org.apache.fory.serializer.Serializer;
import org.apache.fory.serializer.Serializers;
+import org.apache.fory.serializer.Shareable;
import org.apache.fory.serializer.TimeSerializers;
import org.apache.fory.serializer.UnionSerializer;
import org.apache.fory.serializer.UnknownClass;
@@ -129,8 +130,6 @@ public class XtypeResolver extends TypeResolver {
// Every deserialization for unregistered class will query it, performance
is important.
private final ObjectMap<TypeNameBytes, TypeInfo>
compositeClassNameBytes2TypeInfo =
new ObjectMap<>(16, loadFactor);
- private final ObjectMap<String, TypeInfo> qualifiedType2TypeInfo =
- new ObjectMap<>(16, loadFactor);
// typeDefMap is inherited from TypeResolver
private final boolean shareMeta;
private int xtypeIdGenerator = 64;
@@ -326,8 +325,12 @@ public class XtypeResolver extends TypeResolver {
}
}
typeInfo.setSerializer(this, serializer);
- qualifiedType2TypeInfo.put(qualifiedName, typeInfo);
extRegistry.registeredClasses.put(qualifiedName, type);
+ if (typeInfo.typeName != null) {
+ TypeNameBytes typeNameBytes =
+ new TypeNameBytes(typeInfo.namespace.hash, typeInfo.typeName.hash);
+ compositeClassNameBytes2TypeInfo.put(typeNameBytes, typeInfo);
+ }
registerGraalvmClass(type);
updateTypeInfo(type, typeInfo);
}
@@ -451,6 +454,11 @@ public class XtypeResolver extends TypeResolver {
if
(!serializer.getClass().getPackage().getName().startsWith("org.apache.fory")) {
SerializationUtils.validate(type, serializer.getClass());
}
+ boolean localOverride = typeInfo.serializer != null;
+ boolean shouldShare = serializer instanceof Shareable && !localOverride;
+ if (shouldShare) {
+ serializer = sharedRegistry.cacheRegisteredSerializer(type, serializer);
+ }
int oldTypeId = typeInfo.typeId;
int foryId = oldTypeId;
@@ -461,10 +469,11 @@ public class XtypeResolver extends TypeResolver {
}
typeInfo = typeInfo.copy(foryId);
typeInfo.setSerializer(this, serializer);
+ if (shouldShare) {
+ typeInfo = sharedRegistry.cacheRegisteredTypeInfo(type, typeInfo);
+ }
updateTypeInfo(type, typeInfo);
if (typeInfo.typeName != null) {
- String qualifiedName = qualifiedName(typeInfo.decodeNamespace(),
typeInfo.decodeTypeName());
- qualifiedType2TypeInfo.put(qualifiedName, typeInfo);
TypeNameBytes typeNameBytes =
new TypeNameBytes(typeInfo.namespace.hash, typeInfo.typeName.hash);
compositeClassNameBytes2TypeInfo.put(typeNameBytes, typeInfo);
@@ -482,14 +491,22 @@ public class XtypeResolver extends TypeResolver {
return;
}
TypeInfo typeInfo = classInfoMap.get(type);
- if (typeInfo != null) {
- if (typeInfo.serializer == null) {
- typeInfo.setSerializer(this, serializer);
- }
+ if (typeInfo != null && typeInfo.serializer != null) {
return;
}
- int typeId = determineTypeIdForClass(type);
- typeInfo = newTypeInfo(type, serializer, typeId);
+ boolean shareable = serializer instanceof Shareable;
+ if (shareable) {
+ serializer = sharedRegistry.cacheRegisteredSerializer(type, serializer);
+ }
+ if (typeInfo != null) {
+ typeInfo.setSerializer(this, serializer);
+ } else {
+ int typeId = determineTypeIdForClass(type);
+ typeInfo = newTypeInfo(type, serializer, typeId);
+ }
+ if (shareable) {
+ typeInfo = sharedRegistry.cacheRegisteredTypeInfo(type, typeInfo);
+ }
classInfoMap.put(type, typeInfo);
}
@@ -699,8 +716,10 @@ public class XtypeResolver extends TypeResolver {
}
public TypeInfo getUserTypeInfo(String namespace, String typeName) {
- String name = qualifiedName(namespace, typeName);
- return qualifiedType2TypeInfo.get(name);
+ EncodedMetaString namespaceBytes =
sharedRegistry.getPackageEncodedMetaString(namespace);
+ EncodedMetaString typeNameBytes =
sharedRegistry.getTypeNameEncodedMetaString(typeName);
+ return compositeClassNameBytes2TypeInfo.get(
+ new TypeNameBytes(namespaceBytes.hash, typeNameBytes.hash));
}
public TypeInfo getUserTypeInfo(int userTypeId) {
@@ -1188,39 +1207,45 @@ public class XtypeResolver extends TypeResolver {
String namespace = packageBytes.decode(PACKAGE_DECODER);
String typeName = simpleClassNameBytes.decode(TYPE_NAME_DECODER);
String qualifiedName = qualifiedName(namespace, typeName);
- TypeInfo typeInfo = qualifiedType2TypeInfo.get(qualifiedName);
- if (typeInfo == null) {
- String msg = String.format("Class %s not registered", qualifiedName);
- Class<?> type = null;
- if (config.deserializeUnknownClass()) {
- LOG.warn(msg);
- switch (typeId) {
- case Types.NAMED_ENUM:
- case Types.NAMED_STRUCT:
- case Types.NAMED_COMPATIBLE_STRUCT:
- type =
- UnknownClass.getUnknowClass(
- qualifiedName, isEnum(typeId), 0,
config.isMetaShareEnabled());
- break;
- case Types.NAMED_EXT:
- throw new SerializerUnregisteredException(qualifiedName);
- default:
- break;
- }
- } else {
- throw new ClassUnregisteredException(qualifiedName);
+ Class<?> registeredType = extRegistry.registeredClasses.get(qualifiedName);
+ if (registeredType != null) {
+ TypeInfo typeInfo = classInfoMap.get(registeredType);
+ if (typeInfo == null) {
+ typeInfo = getTypeInfo(registeredType);
}
- typeInfo =
- new TypeInfo(
- type,
- packageBytes,
- simpleClassNameBytes,
- null,
- NOT_SUPPORT_XLANG,
- INVALID_USER_TYPE_ID);
- if
(UnknownClass.class.isAssignableFrom(TypeUtils.getComponentIfArray(type))) {
- typeInfo.serializer = UnknownClassSerializers.getSerializer(this,
qualifiedName, type);
+ compositeClassNameBytes2TypeInfo.put(typeNameBytes, typeInfo);
+ return typeInfo;
+ }
+ String msg = String.format("Class %s not registered", qualifiedName);
+ Class<?> type = null;
+ if (config.deserializeUnknownClass()) {
+ LOG.warn(msg);
+ switch (typeId) {
+ case Types.NAMED_ENUM:
+ case Types.NAMED_STRUCT:
+ case Types.NAMED_COMPATIBLE_STRUCT:
+ type =
+ UnknownClass.getUnknowClass(
+ qualifiedName, isEnum(typeId), 0,
config.isMetaShareEnabled());
+ break;
+ case Types.NAMED_EXT:
+ throw new SerializerUnregisteredException(qualifiedName);
+ default:
+ break;
}
+ } else {
+ throw new ClassUnregisteredException(qualifiedName);
+ }
+ TypeInfo typeInfo =
+ new TypeInfo(
+ type,
+ packageBytes,
+ simpleClassNameBytes,
+ null,
+ NOT_SUPPORT_XLANG,
+ INVALID_USER_TYPE_ID);
+ if
(UnknownClass.class.isAssignableFrom(TypeUtils.getComponentIfArray(type))) {
+ typeInfo.serializer = UnknownClassSerializers.getSerializer(this,
qualifiedName, type);
}
compositeClassNameBytes2TypeInfo.put(typeNameBytes, typeInfo);
return typeInfo;
diff --git
a/java/fory-core/src/main/java/org/apache/fory/serializer/ArraySerializers.java
b/java/fory-core/src/main/java/org/apache/fory/serializer/ArraySerializers.java
index 0f7c6e80c..935047137 100644
---
a/java/fory-core/src/main/java/org/apache/fory/serializer/ArraySerializers.java
+++
b/java/fory-core/src/main/java/org/apache/fory/serializer/ArraySerializers.java
@@ -252,18 +252,14 @@ public class ArraySerializers {
// Implement all read/write methods in subclasses to avoid
// virtual method call cost.
- public abstract static class PrimitiveArraySerializer<T> extends
Serializer<T> {
+ public abstract static class PrimitiveArraySerializer<T> extends
Serializer<T>
+ implements Shareable {
protected final Config config;
public PrimitiveArraySerializer(TypeResolver typeResolver, Class<T> cls) {
super(typeResolver.getConfig(), cls);
this.config = typeResolver.getConfig();
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
public static final class BooleanArraySerializer extends
PrimitiveArraySerializer<boolean[]> {
diff --git
a/java/fory-core/src/main/java/org/apache/fory/serializer/BufferSerializers.java
b/java/fory-core/src/main/java/org/apache/fory/serializer/BufferSerializers.java
index 1783b0cf4..d5d52a4ce 100644
---
a/java/fory-core/src/main/java/org/apache/fory/serializer/BufferSerializers.java
+++
b/java/fory-core/src/main/java/org/apache/fory/serializer/BufferSerializers.java
@@ -35,7 +35,8 @@ public class BufferSerializers {
* doesn't implement {@link java.io.Serializable}, it's ok to only serialize
data. Also Note that
* a direct buffer may be returned if the serialized buffer is a heap buffer.
*/
- public static final class ByteBufferSerializer extends
Serializer<ByteBuffer> {
+ public static final class ByteBufferSerializer extends Serializer<ByteBuffer>
+ implements Shareable {
public ByteBufferSerializer(TypeResolver typeResolver, Class<ByteBuffer>
cls) {
super(typeResolver.getConfig(), cls);
@@ -65,11 +66,6 @@ public class BufferSerializers {
isBigEndian == (byte) 1 ? ByteOrder.BIG_ENDIAN :
ByteOrder.LITTLE_ENDIAN);
return originalBuffer;
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
// TODO(chaokunyang) add support for MemoryBuffer serialization.
diff --git
a/java/fory-core/src/main/java/org/apache/fory/serializer/EnumSerializer.java
b/java/fory-core/src/main/java/org/apache/fory/serializer/EnumSerializer.java
index 23609e4f7..e88681be5 100644
---
a/java/fory-core/src/main/java/org/apache/fory/serializer/EnumSerializer.java
+++
b/java/fory-core/src/main/java/org/apache/fory/serializer/EnumSerializer.java
@@ -28,7 +28,7 @@ import org.apache.fory.context.WriteContext;
import org.apache.fory.util.Preconditions;
@SuppressWarnings("rawtypes")
-public class EnumSerializer extends ImmutableSerializer<Enum> {
+public class EnumSerializer extends ImmutableSerializer<Enum> implements
Shareable {
private final Config config;
private final Enum[] enumConstants;
private final Map<String, Enum> stringToEnum;
@@ -110,9 +110,4 @@ public class EnumSerializer extends
ImmutableSerializer<Enum> {
String.format("Enum string %s not in %s", value,
Arrays.toString(enumConstants)));
}
}
-
- @Override
- public boolean shareable() {
- return config.forVirtualThread();
- }
}
diff --git
a/java/fory-core/src/main/java/org/apache/fory/serializer/Float16Serializer.java
b/java/fory-core/src/main/java/org/apache/fory/serializer/Float16Serializer.java
index f8f585685..d88fa4690 100644
---
a/java/fory-core/src/main/java/org/apache/fory/serializer/Float16Serializer.java
+++
b/java/fory-core/src/main/java/org/apache/fory/serializer/Float16Serializer.java
@@ -24,7 +24,7 @@ import org.apache.fory.context.ReadContext;
import org.apache.fory.context.WriteContext;
import org.apache.fory.type.Float16;
-public final class Float16Serializer extends ImmutableSerializer<Float16> {
+public final class Float16Serializer extends ImmutableSerializer<Float16>
implements Shareable {
public Float16Serializer(Config config) {
super(config, Float16.class);
@@ -39,9 +39,4 @@ public final class Float16Serializer extends
ImmutableSerializer<Float16> {
public Float16 read(ReadContext readContext) {
return Float16.fromBits(readContext.getBuffer().readInt16());
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
diff --git
a/java/fory-core/src/main/java/org/apache/fory/serializer/LocaleSerializer.java
b/java/fory-core/src/main/java/org/apache/fory/serializer/LocaleSerializer.java
index 7f7f3af5d..00ae9b088 100644
---
a/java/fory-core/src/main/java/org/apache/fory/serializer/LocaleSerializer.java
+++
b/java/fory-core/src/main/java/org/apache/fory/serializer/LocaleSerializer.java
@@ -28,7 +28,7 @@ import org.apache.fory.context.ReadContext;
import org.apache.fory.context.WriteContext;
/** Local serializer for {@link Locale}. */
-public final class LocaleSerializer extends ImmutableSerializer<Locale> {
+public final class LocaleSerializer extends ImmutableSerializer<Locale>
implements Shareable {
// Using `new HashMap<>` to ensure thread safety by java constructor
semantics.
private static final Map<Tuple3<String, String, String>, Locale>
LOCALE_CACHE =
new HashMap<>(createCacheMap());
@@ -103,9 +103,4 @@ public final class LocaleSerializer extends
ImmutableSerializer<Locale> {
&& locale.getCountry().equals(country)
&& locale.getVariant().equals(variant));
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
diff --git
a/java/fory-core/src/main/java/org/apache/fory/serializer/OptionalSerializers.java
b/java/fory-core/src/main/java/org/apache/fory/serializer/OptionalSerializers.java
index abb9dfe00..8625c1cf7 100644
---
a/java/fory-core/src/main/java/org/apache/fory/serializer/OptionalSerializers.java
+++
b/java/fory-core/src/main/java/org/apache/fory/serializer/OptionalSerializers.java
@@ -60,7 +60,8 @@ public final class OptionalSerializers {
}
}
- public static final class OptionalIntSerializer extends
ImmutableSerializer<OptionalInt> {
+ public static final class OptionalIntSerializer extends
ImmutableSerializer<OptionalInt>
+ implements Shareable {
public OptionalIntSerializer(Config config) {
super(config, OptionalInt.class);
}
@@ -82,14 +83,10 @@ public final class OptionalSerializers {
return OptionalInt.empty();
}
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static final class OptionalLongSerializer extends
ImmutableSerializer<OptionalLong> {
+ public static final class OptionalLongSerializer extends
ImmutableSerializer<OptionalLong>
+ implements Shareable {
public OptionalLongSerializer(Config config) {
super(config, OptionalLong.class);
}
@@ -111,14 +108,10 @@ public final class OptionalSerializers {
return OptionalLong.empty();
}
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static final class OptionalDoubleSerializer extends
ImmutableSerializer<OptionalDouble> {
+ public static final class OptionalDoubleSerializer extends
ImmutableSerializer<OptionalDouble>
+ implements Shareable {
public OptionalDoubleSerializer(Config config) {
super(config, OptionalDouble.class);
}
@@ -140,11 +133,6 @@ public final class OptionalSerializers {
return OptionalDouble.empty();
}
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
public static void registerDefaultSerializers(TypeResolver resolver) {
diff --git
a/java/fory-core/src/main/java/org/apache/fory/serializer/PrimitiveSerializers.java
b/java/fory-core/src/main/java/org/apache/fory/serializer/PrimitiveSerializers.java
index 1b90c9478..8537456e2 100644
---
a/java/fory-core/src/main/java/org/apache/fory/serializer/PrimitiveSerializers.java
+++
b/java/fory-core/src/main/java/org/apache/fory/serializer/PrimitiveSerializers.java
@@ -36,7 +36,8 @@ import org.apache.fory.util.Preconditions;
/** Serializers for java primitive types. */
@SuppressWarnings({"rawtypes", "unchecked"})
public class PrimitiveSerializers {
- public static final class BooleanSerializer extends
ImmutableSerializer<Boolean> {
+ public static final class BooleanSerializer extends
ImmutableSerializer<Boolean>
+ implements Shareable {
public BooleanSerializer(Config config, Class<?> cls) {
super(config, (Class) cls, false);
}
@@ -50,14 +51,9 @@ public class PrimitiveSerializers {
public Boolean read(ReadContext readContext) {
return readContext.getBuffer().readBoolean();
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static final class ByteSerializer extends ImmutableSerializer<Byte> {
+ public static final class ByteSerializer extends ImmutableSerializer<Byte>
implements Shareable {
public ByteSerializer(Config config, Class<?> cls) {
super(config, (Class) cls, false);
}
@@ -71,14 +67,10 @@ public class PrimitiveSerializers {
public Byte read(ReadContext readContext) {
return readContext.getBuffer().readByte();
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static final class Uint8Serializer extends
ImmutableSerializer<Integer> {
+ public static final class Uint8Serializer extends
ImmutableSerializer<Integer>
+ implements Shareable {
public Uint8Serializer(Config config) {
super(config, Integer.class);
}
@@ -94,14 +86,10 @@ public class PrimitiveSerializers {
int b = readContext.getBuffer().readByte();
return b >>> 24;
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static final class Uint16Serializer extends
ImmutableSerializer<Integer> {
+ public static final class Uint16Serializer extends
ImmutableSerializer<Integer>
+ implements Shareable {
public Uint16Serializer(Config config) {
super(config, Integer.class);
}
@@ -117,14 +105,10 @@ public class PrimitiveSerializers {
int b = readContext.getBuffer().readByte();
return b >>> 16;
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static final class CharSerializer extends
ImmutableSerializer<Character> {
+ public static final class CharSerializer extends
ImmutableSerializer<Character>
+ implements Shareable {
public CharSerializer(Config config, Class<?> cls) {
super(config, (Class) cls, false);
}
@@ -138,14 +122,10 @@ public class PrimitiveSerializers {
public Character read(ReadContext readContext) {
return readContext.getBuffer().readChar();
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static final class ShortSerializer extends ImmutableSerializer<Short>
{
+ public static final class ShortSerializer extends ImmutableSerializer<Short>
+ implements Shareable {
public ShortSerializer(Config config, Class<?> cls) {
super(config, (Class) cls, false);
}
@@ -159,14 +139,10 @@ public class PrimitiveSerializers {
public Short read(ReadContext readContext) {
return readContext.getBuffer().readInt16();
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static final class IntSerializer extends ImmutableSerializer<Integer>
{
+ public static final class IntSerializer extends ImmutableSerializer<Integer>
+ implements Shareable {
private final boolean compressNumber;
public IntSerializer(Config config, Class<?> cls) {
@@ -191,14 +167,9 @@ public class PrimitiveSerializers {
}
return readContext.getBuffer().readInt32();
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static final class VarUint32Serializer extends Serializer<Integer> {
+ public static final class VarUint32Serializer extends Serializer<Integer>
implements Shareable {
public VarUint32Serializer(Config config) {
super(config, Integer.class);
}
@@ -213,14 +184,9 @@ public class PrimitiveSerializers {
public Integer read(ReadContext readContext) {
return readContext.getBuffer().readVarUint32();
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static final class LongSerializer extends ImmutableSerializer<Long> {
+ public static final class LongSerializer extends ImmutableSerializer<Long>
implements Shareable {
private final LongEncoding longEncoding;
public LongSerializer(Config config, Class<?> cls) {
@@ -289,14 +255,9 @@ public class PrimitiveSerializers {
throw new UnsupportedOperationException("Unsupported long encoding "
+ longEncoding);
}
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static final class VarUint64Serializer extends Serializer<Long> {
+ public static final class VarUint64Serializer extends Serializer<Long>
implements Shareable {
public VarUint64Serializer(Config config) {
super(config, Long.class);
}
@@ -311,14 +272,10 @@ public class PrimitiveSerializers {
public Long read(ReadContext readContext) {
return readContext.getBuffer().readVarUint64();
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static final class FloatSerializer extends ImmutableSerializer<Float>
{
+ public static final class FloatSerializer extends ImmutableSerializer<Float>
+ implements Shareable {
public FloatSerializer(Config config, Class<?> cls) {
super(config, (Class) cls, false);
}
@@ -332,14 +289,10 @@ public class PrimitiveSerializers {
public Float read(ReadContext readContext) {
return readContext.getBuffer().readFloat32();
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static final class DoubleSerializer extends
ImmutableSerializer<Double> {
+ public static final class DoubleSerializer extends
ImmutableSerializer<Double>
+ implements Shareable {
public DoubleSerializer(Config config, Class<?> cls) {
super(config, (Class) cls, false);
}
@@ -353,14 +306,10 @@ public class PrimitiveSerializers {
public Double read(ReadContext readContext) {
return readContext.getBuffer().readFloat64();
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static final class Float16Serializer extends
ImmutableSerializer<Float16> {
+ public static final class Float16Serializer extends
ImmutableSerializer<Float16>
+ implements Shareable {
public Float16Serializer(Config config, Class<?> cls) {
super(config, (Class) cls, false);
}
@@ -374,11 +323,6 @@ public class PrimitiveSerializers {
public Float16 read(ReadContext readContext) {
return Float16.fromBits(readContext.getBuffer().readInt16());
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
public static void registerDefaultSerializers(TypeResolver resolver) {
diff --git
a/java/fory-core/src/main/java/org/apache/fory/serializer/Serializer.java
b/java/fory-core/src/main/java/org/apache/fory/serializer/Serializer.java
index 1ed7bce46..fdd318bed 100644
--- a/java/fory-core/src/main/java/org/apache/fory/serializer/Serializer.java
+++ b/java/fory-core/src/main/java/org/apache/fory/serializer/Serializer.java
@@ -38,10 +38,17 @@ import org.apache.fory.type.TypeUtils;
* <p>This type stays an abstract class instead of an interface so hot-path
flag checks such as
* {@link #needToWriteRef()} avoid an extra virtual dispatch.
*
- * <p>Unless a subclass overrides {@link #shareable()}, instances are
runtime-local and must not be
- * shared across equivalent runtimes or concurrent operations.
+ * <h3>Sharing across runtimes</h3>
+ *
+ * <p>By default, serializer instances are <b>runtime-local</b> and must not
be shared across
+ * equivalent Fory runtimes or concurrent operations. A serializer that is
safe to share must
+ * implement the {@link Shareable} marker interface. Consumers can check
shareability via {@code
+ * serializer instanceof Shareable}.
+ *
+ * <p>See {@link Shareable} for the full contract and guidelines on when to
implement it.
*
* @param <T> value type handled by this serializer
+ * @see Shareable
*/
@SuppressWarnings("unchecked")
public abstract class Serializer<T> {
@@ -199,33 +206,4 @@ public abstract class Serializer<T> {
public boolean isImmutable() {
return immutable;
}
-
- /**
- * Returns whether this serializer instance may be shared across equivalent
runtimes.
- *
- * <p>The default is {@code false}, which means the serializer instance is
runtime-local and must
- * only be used by the runtime that created it.
- *
- * <p>Returning {@code true} is a stronger contract than ordinary thread
safety. A shareable
- * serializer must satisfy all the following:
- *
- * <ul>
- * <li>It is safe for concurrent use by multiple threads.
- * <li>It does not retain operation-local mutable state such as active
{@link WriteContext},
- * {@link ReadContext}, {@link CopyContext}, or temporary {@link
MemoryBuffer} references.
- * <li>It does not retain runtime-local mutable state whose behavior
depends on a specific
- * runtime instance, such as mutable resolver state tied to a
particular class-loading or
- * registration session.
- * <li>Any retained fields are immutable, effectively immutable, or
otherwise concurrency-safe
- * and independent of the calling runtime.
- * </ul>
- *
- * <p>Override this only in concrete leaf serializers. Intermediate base
classes should keep the
- * default to avoid accidentally widening sharing for subclasses that carry
mutable state.
- *
- * <p>If in doubt, keep the default {@code false}.
- */
- public boolean shareable() {
- return false;
- }
}
diff --git
a/java/fory-core/src/main/java/org/apache/fory/serializer/Serializers.java
b/java/fory-core/src/main/java/org/apache/fory/serializer/Serializers.java
index 052353b8b..601523962 100644
--- a/java/fory-core/src/main/java/org/apache/fory/serializer/Serializers.java
+++ b/java/fory-core/src/main/java/org/apache/fory/serializer/Serializers.java
@@ -407,7 +407,8 @@ public class Serializers {
}
}
- public static final class BigDecimalSerializer extends
ImmutableSerializer<BigDecimal> {
+ public static final class BigDecimalSerializer extends
ImmutableSerializer<BigDecimal>
+ implements Shareable {
public BigDecimalSerializer(Config config) {
super(config, BigDecimal.class);
}
@@ -432,14 +433,10 @@ public class Serializers {
final BigInteger bigInteger = new BigInteger(bytes);
return new BigDecimal(bigInteger, scale, new MathContext(precision));
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static final class BigIntegerSerializer extends
ImmutableSerializer<BigInteger> {
+ public static final class BigIntegerSerializer extends
ImmutableSerializer<BigInteger>
+ implements Shareable {
public BigIntegerSerializer(Config config) {
super(config, BigInteger.class);
}
@@ -459,14 +456,10 @@ public class Serializers {
byte[] bytes = buffer.readBytes(len);
return new BigInteger(bytes);
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static final class AtomicBooleanSerializer extends
Serializer<AtomicBoolean> {
+ public static final class AtomicBooleanSerializer extends
Serializer<AtomicBoolean>
+ implements Shareable {
public AtomicBooleanSerializer(Config config) {
super(config, AtomicBoolean.class);
@@ -486,14 +479,10 @@ public class Serializers {
public AtomicBoolean read(ReadContext readContext) {
return new AtomicBoolean(readContext.getBuffer().readBoolean());
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static final class AtomicIntegerSerializer extends
Serializer<AtomicInteger> {
+ public static final class AtomicIntegerSerializer extends
Serializer<AtomicInteger>
+ implements Shareable {
public AtomicIntegerSerializer(Config config) {
super(config, AtomicInteger.class);
@@ -513,14 +502,10 @@ public class Serializers {
public AtomicInteger read(ReadContext readContext) {
return new AtomicInteger(readContext.getBuffer().readInt32());
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static final class AtomicLongSerializer extends
Serializer<AtomicLong> {
+ public static final class AtomicLongSerializer extends Serializer<AtomicLong>
+ implements Shareable {
public AtomicLongSerializer(Config config) {
super(config, AtomicLong.class);
@@ -540,14 +525,10 @@ public class Serializers {
public AtomicLong read(ReadContext readContext) {
return new AtomicLong(readContext.getBuffer().readInt64());
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static final class AtomicReferenceSerializer extends
Serializer<AtomicReference> {
+ public static final class AtomicReferenceSerializer extends
Serializer<AtomicReference>
+ implements Shareable {
public AtomicReferenceSerializer(Config config) {
super(config, AtomicReference.class);
@@ -567,14 +548,10 @@ public class Serializers {
public AtomicReference read(ReadContext readContext) {
return new AtomicReference(readContext.readRef());
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static final class CurrencySerializer extends
ImmutableSerializer<Currency> {
+ public static final class CurrencySerializer extends
ImmutableSerializer<Currency>
+ implements Shareable {
public CurrencySerializer(Config config) {
super(config, Currency.class);
}
@@ -588,15 +565,11 @@ public class Serializers {
public Currency read(ReadContext readContext) {
return Currency.getInstance(readContext.readString());
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
/** Serializer for {@link Charset}. */
- public static final class CharsetSerializer<T extends Charset> extends
ImmutableSerializer<T> {
+ public static final class CharsetSerializer<T extends Charset> extends
ImmutableSerializer<T>
+ implements Shareable {
public CharsetSerializer(Config config, Class<T> type) {
super(config, type);
}
@@ -608,14 +581,10 @@ public class Serializers {
public T read(ReadContext readContext) {
return (T) Charset.forName(readContext.readString());
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static final class URISerializer extends
ImmutableSerializer<java.net.URI> {
+ public static final class URISerializer extends
ImmutableSerializer<java.net.URI>
+ implements Shareable {
public URISerializer(Config config) {
super(config, URI.class);
@@ -630,14 +599,10 @@ public class Serializers {
public URI read(ReadContext readContext) {
return URI.create(readContext.readString());
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static final class RegexSerializer extends
ImmutableSerializer<Pattern> {
+ public static final class RegexSerializer extends
ImmutableSerializer<Pattern>
+ implements Shareable {
public RegexSerializer(Config config) {
super(config, Pattern.class);
}
@@ -656,14 +621,9 @@ public class Serializers {
int flags = buffer.readInt32();
return Pattern.compile(regex, flags);
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static final class UUIDSerializer extends ImmutableSerializer<UUID> {
+ public static final class UUIDSerializer extends ImmutableSerializer<UUID>
implements Shareable {
public UUIDSerializer(Config config) {
super(config, UUID.class);
@@ -681,14 +641,10 @@ public class Serializers {
MemoryBuffer buffer = readContext.getBuffer();
return new UUID(buffer.readInt64(), buffer.readInt64());
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static final class ClassSerializer extends ImmutableSerializer<Class>
{
+ public static final class ClassSerializer extends ImmutableSerializer<Class>
+ implements Shareable {
public ClassSerializer(Config config) {
super(config, Class.class);
}
@@ -702,11 +658,6 @@ public class Serializers {
public Class read(ReadContext readContext) {
return ((ClassResolver)
readContext.getTypeResolver()).readClassInternal(readContext);
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
/**
@@ -716,7 +667,8 @@ public class Serializers {
* serializable or class registration checks.
*/
// Use a separate serializer to avoid codegen for empty object.
- public static final class EmptyObjectSerializer extends
ImmutableSerializer<Object> {
+ public static final class EmptyObjectSerializer extends
ImmutableSerializer<Object>
+ implements Shareable {
public EmptyObjectSerializer(Config config) {
super(config, Object.class);
@@ -729,11 +681,6 @@ public class Serializers {
public Object read(ReadContext readContext) {
return new Object();
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
public static void registerDefaultSerializers(TypeResolver resolver) {
diff --git
a/java/fory-core/src/main/java/org/apache/fory/serializer/Shareable.java
b/java/fory-core/src/main/java/org/apache/fory/serializer/Shareable.java
new file mode 100644
index 000000000..eb5cfdfe4
--- /dev/null
+++ b/java/fory-core/src/main/java/org/apache/fory/serializer/Shareable.java
@@ -0,0 +1,92 @@
+/*
+ * 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.fory.serializer;
+
+import org.apache.fory.context.CopyContext;
+import org.apache.fory.context.ReadContext;
+import org.apache.fory.context.WriteContext;
+import org.apache.fory.memory.MemoryBuffer;
+
+/**
+ * Marker interface for {@link Serializer} instances that can be shared across
equivalent Fory
+ * runtimes and concurrent operations.
+ *
+ * <p>Implementing this interface is a stronger contract than ordinary thread
safety. A shareable
+ * serializer must satisfy <b>all</b> of the following conditions:
+ *
+ * <ul>
+ * <li><b>Thread-safe:</b> the serializer is safe for concurrent use by
multiple threads with no
+ * external synchronization.
+ * <li><b>No operation-local mutable state:</b> the serializer does not
retain active {@link
+ * WriteContext}, {@link ReadContext}, {@link CopyContext}, or temporary
{@link MemoryBuffer}
+ * references across calls. All operation state must be passed through
the context parameters.
+ * <li><b>No runtime-local mutable state:</b> the serializer does not depend
on mutable state tied
+ * to a specific runtime instance, such as mutable resolver state from a
particular
+ * class-loading or registration session.
+ * <li><b>Immutable or concurrency-safe retained fields:</b> any fields
retained by the serializer
+ * are immutable, effectively immutable, or otherwise concurrency-safe
and independent of the
+ * calling runtime.
+ * </ul>
+ *
+ * <h3>When to implement</h3>
+ *
+ * <ul>
+ * <li>Serializers that only depend on {@link org.apache.fory.config.Config}
(immutable
+ * configuration) are typically shareable.
+ * <li>Serializers for immutable value types (primitives, time types, enums,
UUIDs, etc.) are
+ * almost always shareable.
+ * <li>Serializers whose fields are derived entirely from the target type
and configuration at
+ * construction time are shareable.
+ * </ul>
+ *
+ * <h3>When NOT to implement</h3>
+ *
+ * <ul>
+ * <li>Serializers that retain a {@link
org.apache.fory.resolver.TypeResolver} reference are
+ * usually <b>not</b> shareable because the resolver carries
runtime-local mutable state.
+ * <li>Serializers that cache intermediate results per-operation (scratch
buffers, temporary
+ * collections, etc.) in instance fields are not shareable.
+ * <li>When in doubt, do not implement this interface. The default
assumption is that a serializer
+ * is runtime-local.
+ * </ul>
+ *
+ * <h3>Usage</h3>
+ *
+ * <p>Implement this interface on concrete leaf serializer classes only. Do
not implement it on
+ * intermediate abstract base classes to avoid accidentally widening sharing
for subclasses that
+ * carry mutable state.
+ *
+ * <pre>
+ * public final class FooSerializer extends Serializer<Foo> implements
Shareable {
+ * public FooSerializer(Config config) {
+ * super(config, Foo.class);
+ * }
+ *
+ * public void write(WriteContext ctx, Foo value) { ... }
+ *
+ * public Foo read(ReadContext ctx) { ... }
+ * }
+ * </pre>
+ *
+ * <p>Consumers can check shareability via {@code serializer instanceof
Shareable}.
+ *
+ * @see Serializer
+ */
+public interface Shareable {}
diff --git
a/java/fory-core/src/main/java/org/apache/fory/serializer/TimeSerializers.java
b/java/fory-core/src/main/java/org/apache/fory/serializer/TimeSerializers.java
index 32039a1ed..95ce87e0a 100644
---
a/java/fory-core/src/main/java/org/apache/fory/serializer/TimeSerializers.java
+++
b/java/fory-core/src/main/java/org/apache/fory/serializer/TimeSerializers.java
@@ -204,7 +204,8 @@ public class TimeSerializers {
}
}
- public static final class LocalDateSerializer extends
ImmutableTimeSerializer<LocalDate> {
+ public static final class LocalDateSerializer extends
ImmutableTimeSerializer<LocalDate>
+ implements Shareable {
public LocalDateSerializer(Config config) {
super(config, LocalDate.class);
}
@@ -245,14 +246,10 @@ public class TimeSerializers {
int dayOfMonth = buffer.readByte();
return LocalDate.of(year, month, dayOfMonth);
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static final class InstantSerializer extends
ImmutableTimeSerializer<Instant> {
+ public static final class InstantSerializer extends
ImmutableTimeSerializer<Instant>
+ implements Shareable {
public InstantSerializer(Config config) {
super(config, Instant.class);
}
@@ -275,14 +272,10 @@ public class TimeSerializers {
int nanos = buffer.readInt32();
return Instant.ofEpochSecond(seconds, nanos);
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static class DurationSerializer extends
ImmutableTimeSerializer<Duration> {
+ public static class DurationSerializer extends
ImmutableTimeSerializer<Duration>
+ implements Shareable {
public DurationSerializer(Config config) {
super(config, Duration.class);
}
@@ -305,14 +298,10 @@ public class TimeSerializers {
int nanos = buffer.readInt32();
return Duration.ofSeconds(seconds, nanos);
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static class LocalDateTimeSerializer extends
ImmutableTimeSerializer<LocalDateTime> {
+ public static class LocalDateTimeSerializer extends
ImmutableTimeSerializer<LocalDateTime>
+ implements Shareable {
public LocalDateTimeSerializer(Config config) {
super(config, LocalDateTime.class);
}
@@ -335,14 +324,10 @@ public class TimeSerializers {
LocalTime time = LocalTimeSerializer.readLocalTime(buffer);
return LocalDateTime.of(date, time);
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static class LocalTimeSerializer extends
ImmutableTimeSerializer<LocalTime> {
+ public static class LocalTimeSerializer extends
ImmutableTimeSerializer<LocalTime>
+ implements Shareable {
public LocalTimeSerializer(Config config) {
super(config, LocalTime.class);
}
@@ -407,11 +392,6 @@ public class TimeSerializers {
}
return LocalTime.of(hour, minute, second, nano);
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
public static class TimeZoneSerializer extends TimeSerializer<TimeZone> {
@@ -496,7 +476,8 @@ public class TimeSerializers {
}
}
- public static class ZoneIdSerializer extends ImmutableTimeSerializer<ZoneId>
{
+ public static class ZoneIdSerializer extends ImmutableTimeSerializer<ZoneId>
+ implements Shareable {
public ZoneIdSerializer(Config config, Class<ZoneId> type) {
super(config, type);
}
@@ -514,14 +495,10 @@ public class TimeSerializers {
public ZoneId read(ReadContext readContext) {
return ZoneId.of(readContext.readString());
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static class ZoneOffsetSerializer extends
ImmutableTimeSerializer<ZoneOffset> {
+ public static class ZoneOffsetSerializer extends
ImmutableTimeSerializer<ZoneOffset>
+ implements Shareable {
// cached zone offsets for the single byte representation, using this
overrides the JDK zone
// offset caching which uses a concurrent hash map for zone offsets that
causes a noticeable
@@ -569,14 +546,10 @@ public class TimeSerializers {
}
return COMPRESSED_ZONE_OFFSETS[offsetByte + 72];
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static class ZonedDateTimeSerializer extends
ImmutableTimeSerializer<ZonedDateTime> {
+ public static class ZonedDateTimeSerializer extends
ImmutableTimeSerializer<ZonedDateTime>
+ implements Shareable {
public ZonedDateTimeSerializer(Config config) {
super(config, ZonedDateTime.class);
@@ -600,14 +573,9 @@ public class TimeSerializers {
ZoneId zone = ZoneId.of(readContext.readString());
return ZonedDateTime.of(date, time, zone);
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static class YearSerializer extends ImmutableTimeSerializer<Year> {
+ public static class YearSerializer extends ImmutableTimeSerializer<Year>
implements Shareable {
public YearSerializer(Config config) {
super(config, Year.class);
}
@@ -627,14 +595,10 @@ public class TimeSerializers {
MemoryBuffer buffer = readContext.getBuffer();
return Year.of(buffer.readInt32());
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static class YearMonthSerializer extends
ImmutableTimeSerializer<YearMonth> {
+ public static class YearMonthSerializer extends
ImmutableTimeSerializer<YearMonth>
+ implements Shareable {
public YearMonthSerializer(Config config) {
super(config, YearMonth.class);
}
@@ -657,14 +621,10 @@ public class TimeSerializers {
byte month = buffer.readByte();
return YearMonth.of(year, month);
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static class MonthDaySerializer extends
ImmutableTimeSerializer<MonthDay> {
+ public static class MonthDaySerializer extends
ImmutableTimeSerializer<MonthDay>
+ implements Shareable {
public MonthDaySerializer(Config config) {
super(config, MonthDay.class);
}
@@ -685,14 +645,10 @@ public class TimeSerializers {
byte day = buffer.readByte();
return MonthDay.of(month, day);
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static class PeriodSerializer extends ImmutableTimeSerializer<Period>
{
+ public static class PeriodSerializer extends ImmutableTimeSerializer<Period>
+ implements Shareable {
public PeriodSerializer(Config config) {
super(config, Period.class);
}
@@ -715,14 +671,10 @@ public class TimeSerializers {
int days = buffer.readInt32();
return Period.of(years, months, days);
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static class OffsetTimeSerializer extends
ImmutableTimeSerializer<OffsetTime> {
+ public static class OffsetTimeSerializer extends
ImmutableTimeSerializer<OffsetTime>
+ implements Shareable {
public OffsetTimeSerializer(Config config) {
super(config, OffsetTime.class);
}
@@ -743,14 +695,10 @@ public class TimeSerializers {
ZoneOffset offset = ZoneOffsetSerializer.readZoneOffset(buffer);
return OffsetTime.of(time, offset);
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static class OffsetDateTimeSerializer extends
ImmutableTimeSerializer<OffsetDateTime> {
+ public static class OffsetDateTimeSerializer extends
ImmutableTimeSerializer<OffsetDateTime>
+ implements Shareable {
public OffsetDateTimeSerializer(Config config) {
super(config, OffsetDateTime.class);
}
@@ -773,11 +721,6 @@ public class TimeSerializers {
ZoneOffset offset = ZoneOffsetSerializer.readZoneOffset(buffer);
return OffsetDateTime.of(date, time, offset);
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
public static void registerDefaultSerializers(TypeResolver resolver) {
diff --git
a/java/fory-core/src/main/java/org/apache/fory/serializer/UnknownClassSerializers.java
b/java/fory-core/src/main/java/org/apache/fory/serializer/UnknownClassSerializers.java
index e4804d35b..08ce7dc64 100644
---
a/java/fory-core/src/main/java/org/apache/fory/serializer/UnknownClassSerializers.java
+++
b/java/fory-core/src/main/java/org/apache/fory/serializer/UnknownClassSerializers.java
@@ -254,7 +254,8 @@ public final class UnknownClassSerializers {
}
}
- public static final class UnknownEnumSerializer extends
ImmutableSerializer<UnknownEnum> {
+ public static final class UnknownEnumSerializer extends
ImmutableSerializer<UnknownEnum>
+ implements Shareable {
private final Config config;
private final UnknownEnum[] enumConstants;
@@ -286,11 +287,6 @@ public final class UnknownClassSerializers {
}
return enumConstants[ordinal];
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
public static Serializer getSerializer(
diff --git
a/java/fory-core/src/main/java/org/apache/fory/serializer/UnsignedSerializers.java
b/java/fory-core/src/main/java/org/apache/fory/serializer/UnsignedSerializers.java
index 5f959152e..8adeb979d 100644
---
a/java/fory-core/src/main/java/org/apache/fory/serializer/UnsignedSerializers.java
+++
b/java/fory-core/src/main/java/org/apache/fory/serializer/UnsignedSerializers.java
@@ -32,7 +32,8 @@ import org.apache.fory.type.unsigned.Uint8;
@SuppressWarnings({"rawtypes", "unchecked"})
public class UnsignedSerializers {
- public static final class Uint8Serializer extends ImmutableSerializer<Uint8>
{
+ public static final class Uint8Serializer extends ImmutableSerializer<Uint8>
+ implements Shareable {
public Uint8Serializer(Config config) {
super(config, Uint8.class, false);
}
@@ -46,14 +47,10 @@ public class UnsignedSerializers {
public Uint8 read(ReadContext readContext) {
return new Uint8(readContext.getBuffer().readByte());
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static final class Uint16Serializer extends
ImmutableSerializer<Uint16> {
+ public static final class Uint16Serializer extends
ImmutableSerializer<Uint16>
+ implements Shareable {
public Uint16Serializer(Config config) {
super(config, Uint16.class, false);
}
@@ -67,14 +64,10 @@ public class UnsignedSerializers {
public Uint16 read(ReadContext readContext) {
return new Uint16(readContext.getBuffer().readInt16());
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static final class Uint32Serializer extends
ImmutableSerializer<Uint32> {
+ public static final class Uint32Serializer extends
ImmutableSerializer<Uint32>
+ implements Shareable {
public Uint32Serializer(Config config) {
super(config, Uint32.class, false);
}
@@ -88,14 +81,10 @@ public class UnsignedSerializers {
public Uint32 read(ReadContext readContext) {
return new Uint32(readContext.getBuffer().readInt32());
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static final class Uint64Serializer extends
ImmutableSerializer<Uint64> {
+ public static final class Uint64Serializer extends
ImmutableSerializer<Uint64>
+ implements Shareable {
public Uint64Serializer(Config config) {
super(config, Uint64.class, false);
}
@@ -109,11 +98,6 @@ public class UnsignedSerializers {
public Uint64 read(ReadContext readContext) {
return new Uint64(readContext.getBuffer().readInt64());
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
public static void registerDefaultSerializers(TypeResolver resolver) {
diff --git
a/java/fory-core/src/main/java/org/apache/fory/serializer/collection/PrimitiveListSerializers.java
b/java/fory-core/src/main/java/org/apache/fory/serializer/collection/PrimitiveListSerializers.java
index a7cbf3c83..a0143717c 100644
---
a/java/fory-core/src/main/java/org/apache/fory/serializer/collection/PrimitiveListSerializers.java
+++
b/java/fory-core/src/main/java/org/apache/fory/serializer/collection/PrimitiveListSerializers.java
@@ -39,21 +39,18 @@ import org.apache.fory.context.WriteContext;
import org.apache.fory.memory.MemoryBuffer;
import org.apache.fory.memory.Platform;
import org.apache.fory.resolver.TypeResolver;
+import org.apache.fory.serializer.Shareable;
/** Serializers for primitive list types. */
@SuppressWarnings({"rawtypes", "unchecked"})
public class PrimitiveListSerializers {
- private abstract static class PrimitiveListSerializer<T> extends
CollectionLikeSerializer<T> {
+ private abstract static class PrimitiveListSerializer<T> extends
CollectionLikeSerializer<T>
+ implements Shareable {
private PrimitiveListSerializer(TypeResolver typeResolver, Class<T> cls) {
super(typeResolver, cls, false, false);
}
- @Override
- public boolean shareable() {
- return true;
- }
-
@Override
public final Collection onCollectionWrite(WriteContext writeContext, T
value) {
throw new IllegalStateException("supportCodegenHook is disabled for " +
type.getName());
diff --git
a/java/fory-core/src/main/java/org/apache/fory/type/unsigned/UnsignedSerializers.java
b/java/fory-core/src/main/java/org/apache/fory/type/unsigned/UnsignedSerializers.java
index 926dd570b..1eb1b3505 100644
---
a/java/fory-core/src/main/java/org/apache/fory/type/unsigned/UnsignedSerializers.java
+++
b/java/fory-core/src/main/java/org/apache/fory/type/unsigned/UnsignedSerializers.java
@@ -20,10 +20,7 @@
package org.apache.fory.type.unsigned;
import org.apache.fory.config.Config;
-import org.apache.fory.context.ReadContext;
-import org.apache.fory.context.WriteContext;
import org.apache.fory.resolver.TypeResolver;
-import org.apache.fory.serializer.ImmutableSerializer;
/** Utility class for registering Uint wrapper type serializers. */
public class UnsignedSerializers {
@@ -31,97 +28,13 @@ public class UnsignedSerializers {
/** Register all Uint wrapper type serializers. */
public static void registerSerializers(TypeResolver resolver) {
Config config = resolver.getConfig();
- resolver.registerSerializer(Uint8.class, new Uint8Serializer(config));
- resolver.registerSerializer(Uint16.class, new Uint16Serializer(config));
- resolver.registerSerializer(Uint32.class, new Uint32Serializer(config));
- resolver.registerSerializer(Uint64.class, new Uint64Serializer(config));
- }
-
- /** Serializer for Uint8 wrapper type. */
- public static class Uint8Serializer extends ImmutableSerializer<Uint8> {
- public Uint8Serializer(Config config) {
- super(config, Uint8.class);
- }
-
- @Override
- public void write(WriteContext writeContext, Uint8 value) {
- writeContext.getBuffer().writeByte(value.byteValue());
- }
-
- @Override
- public Uint8 read(ReadContext readContext) {
- return Uint8.valueOf(readContext.getBuffer().readByte());
- }
-
- @Override
- public boolean shareable() {
- return true;
- }
- }
-
- /** Serializer for Uint16 wrapper type. */
- public static class Uint16Serializer extends ImmutableSerializer<Uint16> {
- public Uint16Serializer(Config config) {
- super(config, Uint16.class);
- }
-
- @Override
- public void write(WriteContext writeContext, Uint16 value) {
- writeContext.getBuffer().writeInt16((short) value.toInt());
- }
-
- @Override
- public Uint16 read(ReadContext readContext) {
- return Uint16.valueOf(readContext.getBuffer().readInt16());
- }
-
- @Override
- public boolean shareable() {
- return true;
- }
- }
-
- /** Serializer for Uint32 wrapper type. */
- public static class Uint32Serializer extends ImmutableSerializer<Uint32> {
- public Uint32Serializer(Config config) {
- super(config, Uint32.class);
- }
-
- @Override
- public void write(WriteContext writeContext, Uint32 value) {
- writeContext.getBuffer().writeInt32((int) value.toLong());
- }
-
- @Override
- public Uint32 read(ReadContext readContext) {
- return Uint32.valueOf(readContext.getBuffer().readInt32());
- }
-
- @Override
- public boolean shareable() {
- return true;
- }
- }
-
- /** Serializer for Uint64 wrapper type. */
- public static class Uint64Serializer extends ImmutableSerializer<Uint64> {
- public Uint64Serializer(Config config) {
- super(config, Uint64.class);
- }
-
- @Override
- public void write(WriteContext writeContext, Uint64 value) {
- writeContext.getBuffer().writeInt64(value.toLong());
- }
-
- @Override
- public Uint64 read(ReadContext readContext) {
- return Uint64.valueOf(readContext.getBuffer().readInt64());
- }
-
- @Override
- public boolean shareable() {
- return true;
- }
+ resolver.registerSerializer(
+ Uint8.class, new
org.apache.fory.serializer.UnsignedSerializers.Uint8Serializer(config));
+ resolver.registerSerializer(
+ Uint16.class, new
org.apache.fory.serializer.UnsignedSerializers.Uint16Serializer(config));
+ resolver.registerSerializer(
+ Uint32.class, new
org.apache.fory.serializer.UnsignedSerializers.Uint32Serializer(config));
+ resolver.registerSerializer(
+ Uint64.class, new
org.apache.fory.serializer.UnsignedSerializers.Uint64Serializer(config));
}
}
diff --git
a/java/fory-core/src/test/java/org/apache/fory/resolver/ClassResolverTest.java
b/java/fory-core/src/test/java/org/apache/fory/resolver/ClassResolverTest.java
index d46bfc022..83b13ba78 100644
---
a/java/fory-core/src/test/java/org/apache/fory/resolver/ClassResolverTest.java
+++
b/java/fory-core/src/test/java/org/apache/fory/resolver/ClassResolverTest.java
@@ -20,6 +20,7 @@
package org.apache.fory.resolver;
import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertNotSame;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertSame;
@@ -65,6 +66,7 @@ import org.apache.fory.resolver.longlongpkg.C3;
import org.apache.fory.serializer.ObjectSerializer;
import org.apache.fory.serializer.Serializer;
import org.apache.fory.serializer.Serializers;
+import org.apache.fory.serializer.Shareable;
import org.apache.fory.serializer.collection.CollectionSerializer;
import org.apache.fory.serializer.collection.CollectionSerializers;
import org.apache.fory.serializer.collection.MapSerializers;
@@ -654,6 +656,25 @@ public class ClassResolverTest extends ForyTestBase {
}
}
+ static class ShareableFooSerializer extends Serializer<Foo> implements
Shareable {
+
+ public ShareableFooSerializer(TypeResolver typeResolver, Class<Foo> type) {
+ super(typeResolver.getConfig(), type);
+ }
+
+ @Override
+ public void write(WriteContext writeContext, Foo value) {
+ writeContext.getBuffer().writeInt32(value.f1);
+ }
+
+ @Override
+ public Foo read(ReadContext readContext) {
+ Foo foo = new Foo();
+ foo.f1 = readContext.getBuffer().readInt32();
+ return foo;
+ }
+ }
+
@Test
public void testFooCustomSerializer() {
Fory fory = Fory.builder().withLanguage(Language.JAVA).build();
@@ -667,6 +688,40 @@ public class ClassResolverTest extends ForyTestBase {
fory.getTypeResolver().getSerializer(foo.getClass()).getClass(),
FooCustomSerializer.class);
}
+ @Test
+ public void testShareableSerializerSharedAcrossRuntimes() {
+ ForyBuilder builder =
Fory.builder().withLanguage(Language.JAVA).requireClassRegistration(true);
+ finishBuilder(builder);
+ SharedRegistry sharedRegistry = new SharedRegistry();
+ Fory fory1 = new Fory(builder, ClassResolverTest.class.getClassLoader(),
sharedRegistry);
+ Fory fory2 = new Fory(builder, ClassResolverTest.class.getClassLoader(),
sharedRegistry);
+
+ ClassResolver resolver1 = (ClassResolver) fory1.getTypeResolver();
+ ClassResolver resolver2 = (ClassResolver) fory2.getTypeResolver();
+
+ resolver1.register(Foo.class, 101);
+ resolver2.register(Foo.class, 101);
+ resolver1.registerSerializer(Foo.class, ShareableFooSerializer.class);
+ resolver2.registerSerializer(Foo.class, ShareableFooSerializer.class);
+ resolver1.finishRegistration();
+ resolver2.finishRegistration();
+
+ Serializer<?> serializer1 = resolver1.getSerializer(Foo.class);
+ TypeInfo sharedTypeInfo =
sharedRegistry.registeredTypeInfoCache.get(Foo.class);
+ assertNotNull(sharedTypeInfo);
+ assertSame(sharedRegistry.getRegisteredSerializer(Foo.class), serializer1);
+ assertSame(sharedTypeInfo, resolver1.getTypeInfo(Foo.class, false));
+
+ TypeInfo sharedTypeInfo2 = resolver2.getTypeInfo(Foo.class, false);
+ assertNotNull(sharedTypeInfo2);
+ assertSame(sharedTypeInfo2, sharedTypeInfo);
+ assertSame(sharedTypeInfo2.getSerializer(), serializer1);
+
+ Foo foo = new Foo();
+ foo.f1 = 123;
+ assertEquals(fory2.deserialize(fory1.serialize(foo)), foo);
+ }
+
@Test
public void testRejectIncompatibleCollectionAndMapSerializerRegistration() {
Fory fory =
Fory.builder().withLanguage(Language.JAVA).requireClassRegistration(false).build();
diff --git
a/java/fory-core/src/test/java/org/apache/fory/xlang/RegisterTest.java
b/java/fory-core/src/test/java/org/apache/fory/xlang/RegisterTest.java
index 45f348c64..4f21613bc 100644
--- a/java/fory-core/src/test/java/org/apache/fory/xlang/RegisterTest.java
+++ b/java/fory-core/src/test/java/org/apache/fory/xlang/RegisterTest.java
@@ -31,8 +31,10 @@ import org.apache.fory.context.ReadContext;
import org.apache.fory.context.WriteContext;
import org.apache.fory.memory.MemoryBuffer;
import org.apache.fory.memory.MemoryUtils;
+import org.apache.fory.resolver.SharedRegistry;
import org.apache.fory.resolver.TypeResolver;
import org.apache.fory.serializer.Serializer;
+import org.apache.fory.serializer.Shareable;
import org.apache.fory.type.Types;
import org.testng.Assert;
import org.testng.annotations.Test;
@@ -83,6 +85,13 @@ public class RegisterTest extends ForyTestBase {
}
}
+ private static class ShareableMyExtSerializer extends MyExtSerializer
implements Shareable {
+
+ public ShareableMyExtSerializer(TypeResolver typeResolver, Class<MyExt>
cls) {
+ super(typeResolver, cls);
+ }
+ }
+
@Data
static class MyWrapper {
Color color;
@@ -146,6 +155,47 @@ public class RegisterTest extends ForyTestBase {
Assert.assertEquals(intValue, 1);
}
+ @Test
+ public void testShareableSerializerOverrideStaysLocal() {
+ ForyBuilder builder =
+
Fory.builder().withLanguage(Language.XLANG).requireClassRegistration(true);
+ finishBuilder(builder);
+ SharedRegistry sharedRegistry = new SharedRegistry();
+ Fory fory1 = new Fory(builder, RegisterTest.class.getClassLoader(),
sharedRegistry);
+ Fory fory2 = new Fory(builder, RegisterTest.class.getClassLoader(),
sharedRegistry);
+
+ TypeResolver resolver1 = fory1.getTypeResolver();
+ TypeResolver resolver2 = fory2.getTypeResolver();
+
+ fory1.register(MyExt.class, 103);
+ fory2.register(MyExt.class, 103);
+ resolver1.registerSerializer(MyExt.class, ShareableMyExtSerializer.class);
+ resolver2.registerSerializer(MyExt.class, ShareableMyExtSerializer.class);
+
+ Assert.assertNotSame(
+ resolver1.getSerializer(MyExt.class),
resolver2.getSerializer(MyExt.class));
+ }
+
+ @Test
+ public void testShareableInternalSerializerSharedAcrossRuntimes() {
+ ForyBuilder builder =
+
Fory.builder().withLanguage(Language.XLANG).requireClassRegistration(true);
+ finishBuilder(builder);
+ SharedRegistry sharedRegistry = new SharedRegistry();
+ Fory fory1 = new Fory(builder, RegisterTest.class.getClassLoader(),
sharedRegistry);
+ Fory fory2 = new Fory(builder, RegisterTest.class.getClassLoader(),
sharedRegistry);
+
+ TypeResolver resolver1 = fory1.getTypeResolver();
+ TypeResolver resolver2 = fory2.getTypeResolver();
+
+ resolver1.registerInternalSerializer(
+ MyExt.class, new ShareableMyExtSerializer(resolver1, MyExt.class));
+ resolver2.registerInternalSerializer(
+ MyExt.class, new ShareableMyExtSerializer(resolver2, MyExt.class));
+
+ Assert.assertSame(resolver1.getSerializer(MyExt.class),
resolver2.getSerializer(MyExt.class));
+ }
+
private static void finishBuilder(ForyBuilder builder) {
try {
Method finish = ForyBuilder.class.getDeclaredMethod("finish");
diff --git
a/java/fory-extensions/src/main/java/org/apache/fory/extension/serializer/ByteStringSerializer.java
b/java/fory-extensions/src/main/java/org/apache/fory/extension/serializer/ByteStringSerializer.java
index 7b291607a..1e5b94071 100644
---
a/java/fory-extensions/src/main/java/org/apache/fory/extension/serializer/ByteStringSerializer.java
+++
b/java/fory-extensions/src/main/java/org/apache/fory/extension/serializer/ByteStringSerializer.java
@@ -25,8 +25,9 @@ import org.apache.fory.context.ReadContext;
import org.apache.fory.context.WriteContext;
import org.apache.fory.memory.MemoryBuffer;
import org.apache.fory.serializer.Serializer;
+import org.apache.fory.serializer.Shareable;
-public class ByteStringSerializer extends Serializer<ByteString> {
+public class ByteStringSerializer extends Serializer<ByteString> implements
Shareable {
public ByteStringSerializer(Config config, Class<ByteString> type) {
super(config, type, true);
}
@@ -63,9 +64,4 @@ public class ByteStringSerializer extends
Serializer<ByteString> {
return bytes;
}
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
diff --git
a/java/fory-extensions/src/main/java/org/apache/fory/extension/serializer/ProtobufSerializer.java
b/java/fory-extensions/src/main/java/org/apache/fory/extension/serializer/ProtobufSerializer.java
index 8ac442fe0..3fe85cec3 100644
---
a/java/fory-extensions/src/main/java/org/apache/fory/extension/serializer/ProtobufSerializer.java
+++
b/java/fory-extensions/src/main/java/org/apache/fory/extension/serializer/ProtobufSerializer.java
@@ -34,10 +34,11 @@ import org.apache.fory.context.WriteContext;
import org.apache.fory.memory.MemoryBuffer;
import org.apache.fory.memory.Platform;
import org.apache.fory.serializer.Serializer;
+import org.apache.fory.serializer.Shareable;
import org.apache.fory.util.unsafe._JDKAccess;
@SuppressWarnings({"rawtypes", "unchecked"})
-public class ProtobufSerializer extends Serializer<Message> {
+public class ProtobufSerializer extends Serializer<Message> implements
Shareable {
private static final int SMALL_BYTES_THRESHOLD = 32;
private static final ClassValue<MethodHandle[]> parseFromMethodCache =
@@ -123,9 +124,4 @@ public class ProtobufSerializer extends Serializer<Message>
{
throw new RuntimeException(e);
}
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
diff --git
a/kotlin/src/main/kotlin/org/apache/fory/serializer/kotlin/DurationSerializer.kt
b/kotlin/src/main/kotlin/org/apache/fory/serializer/kotlin/DurationSerializer.kt
index 9b2f18a58..3854ee888 100644
---
a/kotlin/src/main/kotlin/org/apache/fory/serializer/kotlin/DurationSerializer.kt
+++
b/kotlin/src/main/kotlin/org/apache/fory/serializer/kotlin/DurationSerializer.kt
@@ -33,9 +33,10 @@ import org.apache.fory.context.ReadContext
import org.apache.fory.context.WriteContext
import org.apache.fory.serializer.ImmutableSerializer
import org.apache.fory.serializer.Serializer
+import org.apache.fory.serializer.Shareable
public class DurationSerializer(config: Config) :
- ImmutableSerializer<Duration>(config, Duration::class.java,
!config.isTimeRefIgnored) {
+ ImmutableSerializer<Duration>(config, Duration::class.java,
!config.isTimeRefIgnored), Shareable {
@Suppress("UNCHECKED_CAST")
private fun durationUnitSerializer(writeContext: WriteContext):
Serializer<DurationUnit> {
@@ -121,6 +122,4 @@ public class DurationSerializer(config: Config) :
return DurationUnit.DAYS
}
}
-
- override fun shareable(): Boolean = true
}
diff --git
a/kotlin/src/main/kotlin/org/apache/fory/serializer/kotlin/UnsignedArraySerializers.kt
b/kotlin/src/main/kotlin/org/apache/fory/serializer/kotlin/UnsignedArraySerializers.kt
index f93efc1ab..e012ddb57 100644
---
a/kotlin/src/main/kotlin/org/apache/fory/serializer/kotlin/UnsignedArraySerializers.kt
+++
b/kotlin/src/main/kotlin/org/apache/fory/serializer/kotlin/UnsignedArraySerializers.kt
@@ -26,13 +26,14 @@ import org.apache.fory.context.ReadContext
import org.apache.fory.context.WriteContext
import org.apache.fory.resolver.TypeResolver
import org.apache.fory.serializer.Serializer
+import org.apache.fory.serializer.Shareable
import org.apache.fory.serializer.collection.CollectionLikeSerializer
public abstract class AbstractDelegatingArraySerializer<T, T_Delegate>(
typeResolver: TypeResolver,
cls: Class<T>,
private val delegateClass: Class<T_Delegate>
-) : CollectionLikeSerializer<T>(typeResolver, cls, false) {
+) : CollectionLikeSerializer<T>(typeResolver, cls, false), Shareable {
@Suppress("UNCHECKED_CAST")
private fun delegatingSerializer(writeContext: WriteContext):
Serializer<T_Delegate> {
@@ -64,8 +65,6 @@ public abstract class AbstractDelegatingArraySerializer<T,
T_Delegate>(
override fun onCollectionRead(collection: Collection<*>): T {
throw IllegalStateException("supportCodegenHook is disabled for
${type.name}")
}
-
- override fun shareable(): Boolean = true
}
public class UByteArraySerializer(
diff --git
a/kotlin/src/main/kotlin/org/apache/fory/serializer/kotlin/UnsignedSerializer.kt
b/kotlin/src/main/kotlin/org/apache/fory/serializer/kotlin/UnsignedSerializer.kt
index b13b449b3..9f7f3c661 100644
---
a/kotlin/src/main/kotlin/org/apache/fory/serializer/kotlin/UnsignedSerializer.kt
+++
b/kotlin/src/main/kotlin/org/apache/fory/serializer/kotlin/UnsignedSerializer.kt
@@ -23,6 +23,7 @@ import org.apache.fory.config.Config
import org.apache.fory.context.ReadContext
import org.apache.fory.context.WriteContext
import org.apache.fory.serializer.ImmutableSerializer
+import org.apache.fory.serializer.Shareable
/**
* UByteSerializer
@@ -31,13 +32,7 @@ import org.apache.fory.serializer.ImmutableSerializer
*/
public class UByteSerializer(
config: Config,
-) :
- ImmutableSerializer<UByte>(
- config,
- UByte::class.java,
- false,
- true
- ) {
+) : ImmutableSerializer<UByte>(config, UByte::class.java, false, true),
Shareable {
override fun write(writeContext: WriteContext, value: UByte) {
writeContext.buffer.writeByte(value.toInt())
@@ -46,8 +41,6 @@ public class UByteSerializer(
override fun read(readContext: ReadContext): UByte {
return readContext.buffer.readByte().toUByte()
}
-
- override fun shareable(): Boolean = true
}
/**
@@ -57,13 +50,7 @@ public class UByteSerializer(
*/
public class UShortSerializer(
config: Config,
-) :
- ImmutableSerializer<UShort>(
- config,
- UShort::class.java,
- false,
- true
- ) {
+) : ImmutableSerializer<UShort>(config, UShort::class.java, false, true),
Shareable {
override fun write(writeContext: WriteContext, value: UShort) {
writeContext.buffer.writeVarUint32(value.toInt())
}
@@ -71,8 +58,6 @@ public class UShortSerializer(
override fun read(readContext: ReadContext): UShort {
return readContext.buffer.readVarUint32().toUShort()
}
-
- override fun shareable(): Boolean = true
}
/**
@@ -82,13 +67,7 @@ public class UShortSerializer(
*/
public class UIntSerializer(
config: Config,
-) :
- ImmutableSerializer<UInt>(
- config,
- UInt::class.java,
- false,
- true
- ) {
+) : ImmutableSerializer<UInt>(config, UInt::class.java, false, true),
Shareable {
override fun write(writeContext: WriteContext, value: UInt) {
writeContext.buffer.writeVarUint32(value.toInt())
@@ -97,8 +76,6 @@ public class UIntSerializer(
override fun read(readContext: ReadContext): UInt {
return readContext.buffer.readVarUint32().toUInt()
}
-
- override fun shareable(): Boolean = true
}
/**
@@ -108,13 +85,7 @@ public class UIntSerializer(
*/
public class ULongSerializer(
config: Config,
-) :
- ImmutableSerializer<ULong>(
- config,
- ULong::class.java,
- false,
- true
- ) {
+) : ImmutableSerializer<ULong>(config, ULong::class.java, false, true),
Shareable {
override fun write(writeContext: WriteContext, value: ULong) {
writeContext.buffer.writeVarUint64(value.toLong())
}
@@ -122,6 +93,4 @@ public class ULongSerializer(
override fun read(readContext: ReadContext): ULong {
return readContext.buffer.readVarUint64().toULong()
}
-
- override fun shareable(): Boolean = true
}
diff --git
a/kotlin/src/main/kotlin/org/apache/fory/serializer/kotlin/UuidSerializer.kt
b/kotlin/src/main/kotlin/org/apache/fory/serializer/kotlin/UuidSerializer.kt
index b4ca00920..94871abf0 100644
--- a/kotlin/src/main/kotlin/org/apache/fory/serializer/kotlin/UuidSerializer.kt
+++ b/kotlin/src/main/kotlin/org/apache/fory/serializer/kotlin/UuidSerializer.kt
@@ -25,9 +25,11 @@ import org.apache.fory.config.Config
import org.apache.fory.context.ReadContext
import org.apache.fory.context.WriteContext
import org.apache.fory.serializer.ImmutableSerializer
+import org.apache.fory.serializer.Shareable
@OptIn(ExperimentalUuidApi::class)
-public class UuidSerializer(config: Config) :
ImmutableSerializer<Uuid>(config, Uuid::class.java) {
+public class UuidSerializer(config: Config) :
+ ImmutableSerializer<Uuid>(config, Uuid::class.java), Shareable {
override fun write(writeContext: WriteContext, value: Uuid) {
val buffer = writeContext.buffer
value.toLongs { msb, lsb ->
@@ -40,6 +42,4 @@ public class UuidSerializer(config: Config) :
ImmutableSerializer<Uuid>(config,
val buffer = readContext.buffer
return Uuid.fromLongs(buffer.readInt64(), buffer.readInt64())
}
-
- override fun shareable(): Boolean = true
}
diff --git
a/scala/src/main/java/org/apache/fory/serializer/scala/ToFactorySerializers.java
b/scala/src/main/java/org/apache/fory/serializer/scala/ToFactorySerializers.java
index 51097c496..b4f0901c3 100644
---
a/scala/src/main/java/org/apache/fory/serializer/scala/ToFactorySerializers.java
+++
b/scala/src/main/java/org/apache/fory/serializer/scala/ToFactorySerializers.java
@@ -25,6 +25,7 @@ import org.apache.fory.context.WriteContext;
import org.apache.fory.memory.MemoryBuffer;
import org.apache.fory.memory.Platform;
import org.apache.fory.reflect.ReflectionUtils;
+import org.apache.fory.serializer.Shareable;
import org.apache.fory.serializer.Serializer;
import java.lang.reflect.Field;
@@ -35,7 +36,7 @@ public class ToFactorySerializers {
static final Class<?> MapToFactoryClass = ReflectionUtils.loadClass(
"scala.collection.MapFactory$ToFactory");
- public static class IterableToFactorySerializer extends Serializer {
+ public static class IterableToFactorySerializer extends Serializer
implements Shareable {
private static final long fieldOffset;
static {
@@ -63,14 +64,9 @@ public class ToFactorySerializers {
Platform.putObject(o, fieldOffset, readContext.readRef());
return o;
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
- public static class MapToFactorySerializer extends Serializer {
+ public static class MapToFactorySerializer extends Serializer implements
Shareable {
private static final long fieldOffset;
static {
@@ -98,10 +94,5 @@ public class ToFactorySerializers {
Platform.putObject(o, fieldOffset, readContext.readRef());
return o;
}
-
- @Override
- public boolean shareable() {
- return true;
- }
}
}
diff --git
a/scala/src/main/scala/org/apache/fory/serializer/scala/RangeSerializer.scala
b/scala/src/main/scala/org/apache/fory/serializer/scala/RangeSerializer.scala
index 1739e76eb..70d5fed92 100644
---
a/scala/src/main/scala/org/apache/fory/serializer/scala/RangeSerializer.scala
+++
b/scala/src/main/scala/org/apache/fory/serializer/scala/RangeSerializer.scala
@@ -22,6 +22,7 @@ package org.apache.fory.serializer.scala
import org.apache.fory.context.ReadContext
import org.apache.fory.context.WriteContext
import org.apache.fory.reflect.FieldAccessor
+import org.apache.fory.serializer.Shareable
import org.apache.fory.serializer.Serializer
import org.apache.fory.serializer.collection.CollectionLikeSerializer
import org.apache.fory.resolver.TypeResolver
@@ -32,7 +33,8 @@ import java.lang.invoke.{MethodHandle, MethodHandles}
import scala.collection.immutable.NumericRange
class RangeSerializer[T <: Range](typeResolver: TypeResolver, cls: Class[T])
- extends CollectionLikeSerializer[T](typeResolver, cls, false) {
+ extends CollectionLikeSerializer[T](typeResolver, cls, false)
+ with Shareable {
private val rangeClass = cls
override def write(writeContext: WriteContext, value: T): Unit = {
@@ -52,9 +54,6 @@ class RangeSerializer[T <: Range](typeResolver: TypeResolver,
cls: Class[T])
Range.inclusive(start, end, step).asInstanceOf[T]
}
}
-
- override def shareable(): Boolean = true
-
override def onCollectionWrite(writeContext: WriteContext, value: T):
util.Collection[_] =
throw new IllegalStateException(s"supportCodegenHook is disabled for
${getType.getName}")
@@ -74,7 +73,8 @@ private object RangeUtils {
class NumericRangeSerializer[A, T <: NumericRange[A]](typeResolver:
TypeResolver, cls: Class[T])
- extends CollectionLikeSerializer[T](typeResolver, cls, false) {
+ extends CollectionLikeSerializer[T](typeResolver, cls, false)
+ with Shareable {
private val ctr = RangeUtils.lookupCache.get(cls)
private val getter =
FieldAccessor.createAccessor(
@@ -101,9 +101,6 @@ class NumericRangeSerializer[A, T <:
NumericRange[A]](typeResolver: TypeResolver
val step = serializer.read(readContext)
ctr.invoke(start, end, step, readContext.readRef()).asInstanceOf[T]
}
-
- override def shareable(): Boolean = true
-
override def onCollectionWrite(writeContext: WriteContext, value: T):
util.Collection[_] =
throw new IllegalStateException(s"supportCodegenHook is disabled for
${getType.getName}")
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]