From 304830a7961027786bdd4ad6669718223d7bfc9b Mon Sep 17 00:00:00 2001 From: chaokunyang Date: Sat, 28 Mar 2026 10:28:22 +0800 Subject: [PATCH 01/16] refactor java serialization api --- .idea/vcs.xml | 11 +- ...uite.java => ThreadSafeForyPoolSuite.java} | 10 +- compiler/fory_compiler/generators/java.py | 2 +- docs/guide/graalvm_guide.md | 12 +- docs/guide/java/configuration.md | 50 +- docs/guide/java/custom-serializers.md | 716 ++++------------ docs/guide/java/index.md | 60 +- docs/guide/java/schema-evolution.md | 7 +- docs/guide/java/troubleshooting.md | 2 +- docs/guide/java/type-registration.md | 8 +- docs/guide/kotlin/fory-creation.md | 14 +- docs/guide/scala/fory-creation.md | 15 +- .../graalvm/CompatibleThreadSafeExample.java | 21 +- .../fory/graalvm/EnsureSerializerExample.java | 19 +- .../fory/graalvm/ThreadSafeExample.java | 19 +- .../apache/fory/AbstractThreadSafeFory.java | 18 +- .../main/java/org/apache/fory/BaseFory.java | 35 +- .../src/main/java/org/apache/fory/Fory.java | 763 +++++------------- .../java/org/apache/fory/ThreadLocalFory.java | 241 ------ .../java/org/apache/fory/ThreadSafeFory.java | 41 +- ...dPoolFory.java => ThreadSafeForyPool.java} | 149 ++-- .../fory/builder/BaseObjectCodecBuilder.java | 85 +- .../org/apache/fory/builder/Generated.java | 25 +- .../fory/builder/MetaSharedCodecBuilder.java | 30 +- .../builder/MetaSharedLayerCodecBuilder.java | 30 +- .../java/org/apache/fory/config/Config.java | 2 +- .../org/apache/fory/config/ForyBuilder.java | 108 +-- .../org/apache/fory/context/CopyContext.java | 194 +++++ .../org/apache/fory/context/ReadContext.java | 396 +++++++++ .../org/apache/fory/context/WriteContext.java | 463 +++++++++++ .../fory/pool/ClassLoaderForyPooled.java | 135 ---- .../org/apache/fory/pool/FastForyPool.java | 336 -------- .../fory/pool/ForyPooledObjectFactory.java | 145 ---- .../fory/resolver/AllowListChecker.java | 11 +- .../apache/fory/resolver/ClassResolver.java | 32 +- .../apache/fory/resolver/SharedRegistry.java | 30 - .../apache/fory/resolver/TypeResolver.java | 31 +- .../apache/fory/resolver/XtypeResolver.java | 249 +++--- .../serializer/AbstractObjectSerializer.java | 34 +- .../fory/serializer/ArraySerializers.java | 212 ++--- .../fory/serializer/BufferSerializers.java | 12 +- .../fory/serializer/CodegenSerializer.java | 17 +- .../serializer/CopyOnlyObjectSerializer.java | 11 +- .../serializer/DeferedLazySerializer.java | 22 +- .../fory/serializer/EnumSerializer.java | 14 +- .../serializer/ExternalizableSerializer.java | 15 +- .../apache/fory/serializer/FieldGroups.java | 3 +- .../FinalFieldReplaceResolveSerializer.java | 10 +- .../fory/serializer/Float16Serializer.java | 12 +- .../fory/serializer/ForwardSerializer.java | 280 ------- .../serializer/ForyCopyableSerializer.java | 17 +- .../fory/serializer/ImmutableSerializer.java | 34 +- .../fory/serializer/JavaSerializer.java | 15 +- .../fory/serializer/JdkProxySerializer.java | 12 +- .../fory/serializer/LambdaSerializer.java | 16 +- .../fory/serializer/LazySerializer.java | 21 +- .../fory/serializer/LocaleSerializer.java | 12 +- .../serializer/MetaSharedLayerSerializer.java | 25 +- .../MetaSharedLayerSerializerBase.java | 5 +- .../fory/serializer/MetaSharedSerializer.java | 28 +- .../fory/serializer/NoneSerializer.java | 12 +- .../fory/serializer/ObjectSerializer.java | 26 +- .../serializer/ObjectStreamSerializer.java | 39 +- .../fory/serializer/OptionalSerializers.java | 54 +- .../fory/serializer/PrimitiveSerializers.java | 174 ++-- .../serializer/ReplaceResolveSerializer.java | 26 +- .../SerializedLambdaSerializer.java | 12 +- .../apache/fory/serializer/Serializer.java | 102 ++- .../fory/serializer/SerializerFactory.java | 4 +- .../fory/serializer/SerializerRuntime.java | 341 ++++++++ .../apache/fory/serializer/Serializers.java | 336 +++++--- .../fory/serializer/StringSerializer.java | 18 +- .../fory/serializer/TimeSerializers.java | 340 ++++---- .../apache/fory/serializer/URLSerializer.java | 11 +- .../fory/serializer/UnionSerializer.java | 19 +- .../serializer/UnknownClassSerializers.java | 38 +- .../fory/serializer/UnsignedSerializers.java | 54 +- .../collection/ChildContainerSerializers.java | 40 +- .../collection/CollectionLikeSerializer.java | 92 +-- .../collection/CollectionSerializer.java | 15 +- .../collection/CollectionSerializers.java | 260 +++--- .../ConcurrentCollectionSerializer.java | 7 +- .../collection/ConcurrentMapSerializer.java | 7 +- .../collection/ForyArrayAsListSerializer.java | 5 +- .../GuavaCollectionSerializers.java | 73 +- .../ImmutableCollectionSerializers.java | 26 +- .../collection/MapLikeSerializer.java | 104 +-- .../serializer/collection/MapSerializer.java | 15 +- .../serializer/collection/MapSerializers.java | 128 +-- .../collection/PrimitiveListSerializers.java | 174 ++-- .../collection/SubListSerializers.java | 27 +- .../collection/SynchronizedSerializers.java | 25 +- .../collection/UnmodifiableSerializers.java | 25 +- .../scala/SingletonCollectionSerializer.java | 11 +- .../scala/SingletonMapSerializer.java | 11 +- .../scala/SingletonObjectSerializer.java | 11 +- .../type/unsigned/UnsignedSerializers.java | 54 +- .../org/apache/fory/util/GraalvmSupport.java | 19 +- .../org/apache/fory/util/LoaderBinding.java | 210 ----- .../fory-core/native-image.properties | 8 +- .../test/ResolverValidateSerializerTest.java | 42 +- .../java/org/apache/fory/ForyCopyTest.java | 31 +- .../test/java/org/apache/fory/ForyTest.java | 44 +- .../java/org/apache/fory/ForyTestBase.java | 31 + .../apache/fory/ThreadSafeForyPoolTest.java | 85 ++ .../org/apache/fory/ThreadSafeForyTest.java | 424 ---------- .../apache/fory/builder/CodecUtilsTest.java | 10 +- .../apache/fory/builder/JITContextTest.java | 6 +- .../ThreadSafeForyClassLoaderTest.java | 73 -- .../fory/pool/ClassLoaderForyPooledTest.java | 144 ---- .../apache/fory/pool/FastForyPoolTest.java | 51 -- .../fory/resolver/AllowListCheckerTest.java | 15 +- .../fory/resolver/ClassResolverTest.java | 65 +- .../serializer/CodegenSerializerTest.java | 21 +- .../fory/serializer/DuplicateFieldsTest.java | 20 +- ...inalFieldReplaceResolveSerializerTest.java | 4 +- .../serializer/ForwardSerializerTest.java | 199 ----- .../fory/serializer/LambdaSerializerTest.java | 2 +- .../fory/serializer/ObjectSerializerTest.java | 49 +- .../ObjectStreamSerializerTest.java | 183 +++-- .../serializer/PrimitiveSerializersTest.java | 29 +- .../apache/fory/serializer/RegisterTest.java | 23 +- .../ReplaceResolveSerializerTest.java | 12 +- .../serializer/SerializerFactoryTest.java | 15 +- .../fory/serializer/StringSerializerTest.java | 82 +- .../fory/serializer/TimeSerializersTest.java | 18 +- .../collection/CollectionSerializersTest.java | 21 +- .../collection/MapSerializersTest.java | 9 +- .../SynchronizedSerializersTest.java | 4 +- .../UnmodifiableSerializersTest.java | 4 +- .../fory/type/unsigned/UnsignedTest.java | 8 +- .../fory/xlang/PyCrossLanguageTest.java | 12 +- .../org/apache/fory/xlang/RegisterTest.java | 13 +- .../org/apache/fory/xlang/XlangTestBase.java | 13 +- .../fory/VirtualThreadSafeForyTest.java | 75 -- .../pool/VirtualThreadFastForyPoolTest.java | 74 -- kotlin/README.md | 16 +- scala/README.md | 17 +- 138 files changed, 4578 insertions(+), 5661 deletions(-) rename benchmarks/java/src/main/java/org/apache/fory/benchmark/{ThreadPoolForySuite.java => ThreadSafeForyPoolSuite.java} (88%) delete mode 100644 java/fory-core/src/main/java/org/apache/fory/ThreadLocalFory.java rename java/fory-core/src/main/java/org/apache/fory/{pool/ThreadPoolFory.java => ThreadSafeForyPool.java} (65%) create mode 100644 java/fory-core/src/main/java/org/apache/fory/context/CopyContext.java create mode 100644 java/fory-core/src/main/java/org/apache/fory/context/ReadContext.java create mode 100644 java/fory-core/src/main/java/org/apache/fory/context/WriteContext.java delete mode 100644 java/fory-core/src/main/java/org/apache/fory/pool/ClassLoaderForyPooled.java delete mode 100644 java/fory-core/src/main/java/org/apache/fory/pool/FastForyPool.java delete mode 100644 java/fory-core/src/main/java/org/apache/fory/pool/ForyPooledObjectFactory.java delete mode 100644 java/fory-core/src/main/java/org/apache/fory/serializer/ForwardSerializer.java create mode 100644 java/fory-core/src/main/java/org/apache/fory/serializer/SerializerRuntime.java delete mode 100644 java/fory-core/src/main/java/org/apache/fory/util/LoaderBinding.java create mode 100644 java/fory-core/src/test/java/org/apache/fory/ThreadSafeForyPoolTest.java delete mode 100644 java/fory-core/src/test/java/org/apache/fory/ThreadSafeForyTest.java delete mode 100644 java/fory-core/src/test/java/org/apache/fory/classloader/ThreadSafeForyClassLoaderTest.java delete mode 100644 java/fory-core/src/test/java/org/apache/fory/pool/ClassLoaderForyPooledTest.java delete mode 100644 java/fory-core/src/test/java/org/apache/fory/pool/FastForyPoolTest.java delete mode 100644 java/fory-core/src/test/java/org/apache/fory/serializer/ForwardSerializerTest.java delete mode 100644 java/fory-latest-jdk-tests/src/test/java/org/apache/fory/pool/VirtualThreadFastForyPoolTest.java diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 3c576d9d18..f77135031e 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -12,5 +12,14 @@ + + + + + + + + + - + \ No newline at end of file diff --git a/benchmarks/java/src/main/java/org/apache/fory/benchmark/ThreadPoolForySuite.java b/benchmarks/java/src/main/java/org/apache/fory/benchmark/ThreadSafeForyPoolSuite.java similarity index 88% rename from benchmarks/java/src/main/java/org/apache/fory/benchmark/ThreadPoolForySuite.java rename to benchmarks/java/src/main/java/org/apache/fory/benchmark/ThreadSafeForyPoolSuite.java index 8f3cd81b6b..e84133571b 100644 --- a/benchmarks/java/src/main/java/org/apache/fory/benchmark/ThreadPoolForySuite.java +++ b/benchmarks/java/src/main/java/org/apache/fory/benchmark/ThreadSafeForyPoolSuite.java @@ -24,8 +24,6 @@ import org.apache.fory.ThreadSafeFory; import org.apache.fory.config.CompatibleMode; import org.apache.fory.config.Language; -import org.apache.fory.logging.Logger; -import org.apache.fory.logging.LoggerFactory; import org.openjdk.jmh.Main; import org.openjdk.jmh.annotations.Benchmark; import org.openjdk.jmh.annotations.BenchmarkMode; @@ -42,9 +40,7 @@ @CompilerControl(value = CompilerControl.Mode.INLINE) @State(Scope.Benchmark) @OutputTimeUnit(java.util.concurrent.TimeUnit.MILLISECONDS) -public class ThreadPoolForySuite { - - private static final Logger LOG = LoggerFactory.getLogger(ThreadPoolForySuite.class); +public class ThreadSafeForyPoolSuite { private ThreadSafeFory fory = Fory.builder() @@ -55,7 +51,7 @@ public class ThreadPoolForySuite { .withCompatibleMode(CompatibleMode.COMPATIBLE) .withAsyncCompilation(true) .withRefTracking(true) - .buildThreadSafeForyPool(10, 60); + .buildThreadSafeForyPool(60); private static StructBenchmark.NumericStruct struct; @@ -83,7 +79,7 @@ public void tearDown() {} public static void main(String[] args) throws IOException { if (args.length == 0) { String commandLine = - "org.apache.fory.*ObjectPoolBenchmark.* -f 1 -wi 0 -i 5 -w 2s -r 2s -rf csv"; + "org.apache.fory.*ThreadSafeForyPoolSuite.* -f 1 -wi 0 -i 5 -w 2s -r 2s -rf csv"; System.out.println(commandLine); args = commandLine.split(" "); } diff --git a/compiler/fory_compiler/generators/java.py b/compiler/fory_compiler/generators/java.py index a4d02ca752..55d845163e 100644 --- a/compiler/fory_compiler/generators/java.py +++ b/compiler/fory_compiler/generators/java.py @@ -1585,7 +1585,7 @@ def generate_registration_file( lines.append("") lines.append(" private static ThreadSafeFory createFory() {") lines.append( - " ThreadSafeFory fory = Fory.builder().withXlang(true).withRefTracking(true).buildFastForyPool();" + " ThreadSafeFory fory = Fory.builder().withXlang(true).withRefTracking(true).buildThreadSafeFory();" ) imported_packages = self._collect_imported_packages() if imported_packages: diff --git a/docs/guide/graalvm_guide.md b/docs/guide/graalvm_guide.md index 7349658eba..1c17a3e244 100644 --- a/docs/guide/graalvm_guide.md +++ b/docs/guide/graalvm_guide.md @@ -150,11 +150,10 @@ public class ProxyExample { ## Thread-Safe Fory -For multi-threaded applications, use `ThreadLocalFory`: +For multi-threaded applications, use `ThreadSafeForyPool` through the builder: ```java import org.apache.fory.Fory; -import org.apache.fory.ThreadLocalFory; import org.apache.fory.ThreadSafeFory; public class ThreadSafeExample { @@ -163,12 +162,9 @@ public class ThreadSafeExample { static ThreadSafeFory fory; static { - fory = new ThreadLocalFory(classLoader -> { - Fory f = Fory.builder().build(); - f.register(Foo.class); - f.ensureSerializersCompiled(); - return f; - }); + fory = Fory.builder().buildThreadSafeFory(); + fory.register(Foo.class); + fory.ensureSerializersCompiled(); } public static void main(String[] args) { diff --git a/docs/guide/java/configuration.md b/docs/guide/java/configuration.md index 18a9d6b640..9071d423b6 100644 --- a/docs/guide/java/configuration.md +++ b/docs/guide/java/configuration.md @@ -23,31 +23,31 @@ This page documents all configuration options available through `ForyBuilder`. ## ForyBuilder Options -| Option Name | Description | Default Value | -| ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | -| `timeRefIgnored` | Whether to ignore reference tracking of all time types registered in `TimeSerializers` and subclasses of those types when ref tracking is enabled. If ignored, ref tracking of every time type can be enabled by invoking `Fory#registerSerializer(Class, Serializer)`. For example, `fory.registerSerializer(Date.class, new DateSerializer(fory, true))`. Note that enabling ref tracking should happen before serializer codegen of any types which contain time fields. Otherwise, those fields will still skip ref tracking. | `true` | -| `compressInt` | Enables or disables int compression for smaller size. | `true` | -| `compressLong` | Enables or disables long compression for smaller size. | `true` | -| `compressIntArray` | Enables or disables SIMD-accelerated compression for int arrays when values can fit in smaller data types. Requires Java 16+. | `false` | -| `compressLongArray` | Enables or disables SIMD-accelerated compression for long arrays when values can fit in smaller data types. Requires Java 16+. | `false` | -| `compressString` | Enables or disables string compression for smaller size. | `false` | -| `classLoader` | The classloader should not be updated; Fory caches class metadata. Use `LoaderBinding` or `ThreadSafeFory` for classloader updates. | `Thread.currentThread().getContextClassLoader()` | -| `compatibleMode` | Type forward/backward compatibility config. Also Related to `checkClassVersion` config. `SCHEMA_CONSISTENT`: Class schema must be consistent between serialization peer and deserialization peer. `COMPATIBLE`: Class schema can be different between serialization peer and deserialization peer. They can add/delete fields independently. [See more](schema-evolution.md). | `CompatibleMode.SCHEMA_CONSISTENT` | -| `checkClassVersion` | Determines whether to check the consistency of the class schema. If enabled, Fory checks, writes, and checks consistency using the `classVersionHash`. It will be automatically disabled when `CompatibleMode#COMPATIBLE` is enabled. Disabling is not recommended unless you can ensure the class won't evolve. | `false` | -| `checkJdkClassSerializable` | Enables or disables checking of `Serializable` interface for classes under `java.*`. If a class under `java.*` is not `Serializable`, Fory will throw an `UnsupportedOperationException`. | `true` | -| `registerGuavaTypes` | Whether to pre-register Guava types such as `RegularImmutableMap`/`RegularImmutableList`. These types are not public API, but seem pretty stable. | `true` | -| `requireClassRegistration` | Disabling may allow unknown classes to be deserialized, potentially causing security risks. | `true` | -| `maxDepth` | Set max depth for deserialization, when depth exceeds, an exception will be thrown. This can be used to refuse deserialization DDOS attack. | `50` | -| `suppressClassRegistrationWarnings` | Whether to suppress class registration warnings. The warnings can be used for security audit, but may be annoying, this suppression will be enabled by default. | `true` | -| `metaShareEnabled` | Enables or disables meta share mode. | `true` if `CompatibleMode.COMPATIBLE` is set, otherwise false. | -| `scopedMetaShareEnabled` | Scoped meta share focuses on a single serialization process. Metadata created or identified during this process is exclusive to it and is not shared with by other serializations. | `true` if `CompatibleMode.COMPATIBLE` is set, otherwise false. | -| `metaCompressor` | Set a compressor for meta compression. Note that the passed MetaCompressor should be thread-safe. By default, a `Deflater` based compressor `DeflaterMetaCompressor` will be used. Users can pass other compressor such as `zstd` for better compression rate. | `DeflaterMetaCompressor` | -| `deserializeUnknownClass` | Enables or disables deserialization/skipping of data for non-existent or unknown classes. | `true` if `CompatibleMode.COMPATIBLE` is set, otherwise false. | -| `codeGenEnabled` | Disabling may result in faster initial serialization but slower subsequent serializations. | `true` | -| `asyncCompilationEnabled` | If enabled, serialization uses interpreter mode first and switches to JIT serialization after async serializer JIT for a class is finished. | `false` | -| `scalaOptimizationEnabled` | Enables or disables Scala-specific serialization optimization. | `false` | -| `copyRef` | When disabled, the copy performance will be better. But fory deep copy will ignore circular and shared reference. Same reference of an object graph will be copied into different objects in one `Fory#copy`. | `false` | -| `serializeEnumByName` | When Enabled, fory serialize enum by name instead of ordinal. | `false` | +| Option Name | Description | Default Value | +| ----------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | +| `timeRefIgnored` | Whether to ignore reference tracking of all time types registered in `TimeSerializers` and subclasses of those types when ref tracking is enabled. If ignored, ref tracking of every time type can be enabled by invoking `Fory#registerSerializer(Class, Serializer)`. For example, `fory.registerSerializer(Date.class, new DateSerializer(fory.getConfig(), true))`. Note that enabling ref tracking should happen before serializer codegen of any types which contain time fields. Otherwise, those fields will still skip ref tracking. | `true` | +| `compressInt` | Enables or disables int compression for smaller size. | `true` | +| `compressLong` | Enables or disables long compression for smaller size. | `true` | +| `compressIntArray` | Enables or disables SIMD-accelerated compression for int arrays when values can fit in smaller data types. Requires Java 16+. | `false` | +| `compressLongArray` | Enables or disables SIMD-accelerated compression for long arrays when values can fit in smaller data types. Requires Java 16+. | `false` | +| `compressString` | Enables or disables string compression for smaller size. | `false` | +| `classLoader` | The classloader is fixed per `Fory` instance because Fory caches class metadata. To use a different loader, create a new `Fory` or `ThreadSafeFory` configured with that loader, or rely on the thread context classloader before first class resolution. | `Thread.currentThread().getContextClassLoader()` | +| `compatibleMode` | Type forward/backward compatibility config. Also Related to `checkClassVersion` config. `SCHEMA_CONSISTENT`: Class schema must be consistent between serialization peer and deserialization peer. `COMPATIBLE`: Class schema can be different between serialization peer and deserialization peer. They can add/delete fields independently. [See more](schema-evolution.md). | `CompatibleMode.SCHEMA_CONSISTENT` | +| `checkClassVersion` | Determines whether to check the consistency of the class schema. If enabled, Fory checks, writes, and checks consistency using the `classVersionHash`. It will be automatically disabled when `CompatibleMode#COMPATIBLE` is enabled. Disabling is not recommended unless you can ensure the class won't evolve. | `false` | +| `checkJdkClassSerializable` | Enables or disables checking of `Serializable` interface for classes under `java.*`. If a class under `java.*` is not `Serializable`, Fory will throw an `UnsupportedOperationException`. | `true` | +| `registerGuavaTypes` | Whether to pre-register Guava types such as `RegularImmutableMap`/`RegularImmutableList`. These types are not public API, but seem pretty stable. | `true` | +| `requireClassRegistration` | Disabling may allow unknown classes to be deserialized, potentially causing security risks. | `true` | +| `maxDepth` | Set max depth for deserialization, when depth exceeds, an exception will be thrown. This can be used to refuse deserialization DDOS attack. | `50` | +| `suppressClassRegistrationWarnings` | Whether to suppress class registration warnings. The warnings can be used for security audit, but may be annoying, this suppression will be enabled by default. | `true` | +| `metaShareEnabled` | Enables or disables meta share mode. | `true` if `CompatibleMode.COMPATIBLE` is set, otherwise false. | +| `scopedMetaShareEnabled` | Scoped meta share focuses on a single serialization process. Metadata created or identified during this process is exclusive to it and is not shared with by other serializations. | `true` if `CompatibleMode.COMPATIBLE` is set, otherwise false. | +| `metaCompressor` | Set a compressor for meta compression. Note that the passed MetaCompressor should be thread-safe. By default, a `Deflater` based compressor `DeflaterMetaCompressor` will be used. Users can pass other compressor such as `zstd` for better compression rate. | `DeflaterMetaCompressor` | +| `deserializeUnknownClass` | Enables or disables deserialization/skipping of data for non-existent or unknown classes. | `true` if `CompatibleMode.COMPATIBLE` is set, otherwise false. | +| `codeGenEnabled` | Disabling may result in faster initial serialization but slower subsequent serializations. | `true` | +| `asyncCompilationEnabled` | If enabled, serialization uses interpreter mode first and switches to JIT serialization after async serializer JIT for a class is finished. | `false` | +| `scalaOptimizationEnabled` | Enables or disables Scala-specific serialization optimization. | `false` | +| `copyRef` | When disabled, the copy performance will be better. But fory deep copy will ignore circular and shared reference. Same reference of an object graph will be copied into different objects in one `Fory#copy`. | `false` | +| `serializeEnumByName` | When Enabled, fory serialize enum by name instead of ordinal. | `false` | ## Example Configuration diff --git a/docs/guide/java/custom-serializers.md b/docs/guide/java/custom-serializers.md index 2afcac066b..415b0c0f09 100644 --- a/docs/guide/java/custom-serializers.md +++ b/docs/guide/java/custom-serializers.md @@ -19,641 +19,205 @@ license: | limitations under the License. --- -This page covers how to implement custom serializers for your types. +This page covers the current Java custom serializer API. -## Basic Custom Serializer +## Constructor Inputs -In some cases, you may want to implement a serializer for your type, especially for classes that customize serialization using JDK `writeObject/writeReplace/readObject/readResolve`, which is very inefficient. +Custom serializers should not retain `Fory`. -For example, if you don't want the following `Foo#writeObject` to be invoked, you can implement a custom serializer: +- 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 not thread-safe and should keep the default `threadSafe() == false`. -```java -class Foo { - public long f1; +## Basic Serializer - private void writeObject(ObjectOutputStream s) throws IOException { - System.out.println(f1); - s.defaultWriteObject(); - } -} +Use `WriteContext` and `ReadContext` for runtime state. Get the buffer into a local variable first +when you perform multiple reads or writes. -class FooSerializer extends Serializer { - public FooSerializer(Fory fory) { - super(fory, Foo.class); +```java +import org.apache.fory.config.Config; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; +import org.apache.fory.memory.MemoryBuffer; +import org.apache.fory.serializer.Serializer; + +public final class FooSerializer extends Serializer { + public FooSerializer(Config config) { + super(config, Foo.class); } @Override - public void write(MemoryBuffer buffer, Foo value) { + public void write(WriteContext writeContext, Foo value) { + MemoryBuffer buffer = writeContext.getBuffer(); buffer.writeInt64(value.f1); + writeContext.writeString(value.f2); } @Override - public Foo read(MemoryBuffer buffer) { + public Foo read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); Foo foo = new Foo(); foo.f1 = buffer.readInt64(); + foo.f2 = readContext.readString(buffer); return foo; } + + @Override + public boolean threadSafe() { + return true; + } } ``` -### Register the Serializer +Register it with a `Config`-based constructor when the serializer is shareable: ```java -Fory fory = getFory(); -fory.registerSerializer(Foo.class, new FooSerializer(fory)); +Fory fory = Fory.builder().build(); +fory.registerSerializer(Foo.class, new FooSerializer(fory.getConfig())); ``` -Besides registering serializers, you can also implement `java.io.Externalizable` for a class to customize serialization logic. Such types will be serialized by Fory's `ExternalizableSerializer`. - -## Collection Serializer - -When implementing a serializer for a custom Collection type, you must extend `CollectionSerializer` or `CollectionLikeSerializer`. The key difference is that `CollectionLikeSerializer` can serialize a class which has a collection-like structure but is not a Java Collection subtype. - -### supportCodegenHook Parameter - -This special parameter controls serialization behavior: - -**When `true`:** - -- Enables optimized access to collection elements and JIT compilation for better performance -- Direct serialization invocation and inline for collection items without dynamic serializer dispatch cost -- Better performance for standard collection types -- Recommended for most collections - -**When `false`:** - -- Uses interface-based element access and dynamic serializer dispatch for elements (higher cost) -- More flexible for custom collection types -- Required when collection has special serialization needs -- Handles complex collection implementations - -### Collection Serializer with JIT Support +## Nested Objects -When implementing a Collection serializer with JIT support, leverage Fory's existing binary format and collection serialization infrastructure: +If your serializer needs to write or read nested objects, use the context helpers instead of +retaining `Fory`: ```java -public class CustomCollectionSerializer extends CollectionSerializer { - public CustomCollectionSerializer(Fory fory, Class cls) { - // supportCodegenHook controls whether to use JIT compilation - super(fory, cls, true); - } - - @Override - public Collection onCollectionWrite(MemoryBuffer buffer, T value) { - // Write collection size - buffer.writeVarUint32Small7(value.size()); - // Write any additional collection metadata - return value; - } - - @Override - public Collection newCollection(MemoryBuffer buffer) { - // Create new collection instance - Collection collection = super.newCollection(buffer); - // Read and set collection size - int numElements = getAndClearNumElements(); - setNumElements(numElements); - return collection; - } -} -``` - -Note: Invoke `setNumElements` when implementing `newCollection` to let Fory know how many elements to deserialize. - -### Custom Collection Serializer without JIT - -For collections that use primitive arrays or have special requirements, implement a serializer with JIT disabled: +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; +import org.apache.fory.resolver.TypeResolver; +import org.apache.fory.serializer.Serializer; + +public final class EnvelopeSerializer extends Serializer { + public EnvelopeSerializer(TypeResolver typeResolver) { + super(typeResolver, Envelope.class); + } -```java -class IntList extends AbstractCollection { - private final int[] elements; - private final int size; - - public IntList(int size) { - this.elements = new int[size]; - this.size = size; - } - - public IntList(int[] elements, int size) { - this.elements = elements; - this.size = size; - } - - @Override - public Iterator iterator() { - return new Iterator() { - private int index = 0; - - @Override - public boolean hasNext() { - return index < size; - } - - @Override - public Integer next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - return elements[index++]; - } - }; - } - - @Override - public int size() { - return size; - } - - public int get(int index) { - if (index >= size) { - throw new IndexOutOfBoundsException(); - } - return elements[index]; - } - - public void set(int index, int value) { - if (index >= size) { - throw new IndexOutOfBoundsException(); - } - elements[index] = value; - } - - public int[] getElements() { - return elements; - } -} + @Override + public void write(WriteContext writeContext, Envelope value) { + writeContext.writeRef(value.header); + writeContext.writeRef(value.payload); + } -class IntListSerializer extends CollectionLikeSerializer { - public IntListSerializer(Fory fory) { - // Disable JIT since we're handling serialization directly - super(fory, IntList.class, false); - } - - @Override - public void write(MemoryBuffer buffer, IntList value) { - // Write size - buffer.writeVarUint32Small7(value.size()); - - // Write elements directly as primitive ints - int[] elements = value.getElements(); - for (int i = 0; i < value.size(); i++) { - buffer.writeVarInt32(elements[i]); - } - } - - @Override - public IntList read(MemoryBuffer buffer) { - // Read size - int size = buffer.readVarUint32Small7(); - - // Create array and read elements - int[] elements = new int[size]; - for (int i = 0; i < size; i++) { - elements[i] = buffer.readVarInt32(); - } - - return new IntList(elements, size); - } - - // These methods are not used when JIT is disabled - @Override - public Collection onCollectionWrite(MemoryBuffer buffer, IntList value) { - throw new UnsupportedOperationException(); - } - - @Override - public Collection newCollection(MemoryBuffer buffer) { - throw new UnsupportedOperationException(); - } - - @Override - public IntList onCollectionRead(Collection collection) { - throw new UnsupportedOperationException(); - } + @Override + public Envelope read(ReadContext readContext) { + Envelope envelope = new Envelope(); + envelope.header = (Header) readContext.readRef(); + envelope.payload = readContext.readRef(); + return envelope; + } } ``` -**When to use this approach:** +This serializer is not thread-safe because it retains `TypeResolver`. -- Working with primitive types -- Need maximum performance -- Want to minimize memory overhead -- Have special serialization requirements +## Collection Serializers -### Collection-like Type Serializer +For Java collections, extend `CollectionSerializer` or `CollectionLikeSerializer`. -For types that behave like collections but aren't standard Java Collections: +- Use `CollectionSerializer` for real `Collection` implementations. +- Use `CollectionLikeSerializer` for collection-shaped types that do not implement `Collection`. +- Keep `supportCodegenHook == true` when the collection can use the standard element codegen path. +- Set `supportCodegenHook == false` only when you need to fully control element IO. + +Example: ```java -class CustomCollectionLike { - private final Object[] elements; - private final int size; - - public CustomCollectionLike(int size) { - this.elements = new Object[size]; - this.size = 0; - } - - public CustomCollectionLike(Object[] elements, int size) { - this.elements = elements; - this.size = size; - } - - public Object get(int index) { - if (index >= size) { - throw new IndexOutOfBoundsException(); - } - return elements[index]; - } - - public int size() { - return size; - } - - public Object[] getElements() { - return elements; - } -} +import java.util.ArrayList; +import java.util.Collection; +import org.apache.fory.memory.MemoryBuffer; +import org.apache.fory.resolver.TypeResolver; +import org.apache.fory.serializer.collection.CollectionSerializer; + +public final class CustomCollectionSerializer> + extends CollectionSerializer { + public CustomCollectionSerializer(TypeResolver typeResolver, Class type) { + super(typeResolver, type, true); + } -class CollectionView extends AbstractCollection { - private final Object[] elements; - private final int size; - private int writeIndex; - - public CollectionView(CustomCollectionLike collection) { - this.elements = collection.getElements(); - this.size = collection.size(); - } - - public CollectionView(int size) { - this.size = size; - this.elements = new Object[size]; - } - - @Override - public Iterator iterator() { - return new Iterator() { - private int index = 0; - - @Override - public boolean hasNext() { - return index < size; - } - - @Override - public Object next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - return elements[index++]; - } - }; - } - - @Override - public boolean add(Object element) { - if (writeIndex >= size) { - throw new IllegalStateException("Collection is full"); - } - elements[writeIndex++] = element; - return true; - } - - @Override - public int size() { - return size; - } - - public Object[] getElements() { - return elements; - } -} + @Override + public Collection onCollectionWrite(MemoryBuffer buffer, T value) { + buffer.writeVarUint32Small7(value.size()); + return value; + } + + @Override + public T onCollectionRead(Collection collection) { + return (T) collection; + } -class CustomCollectionSerializer extends CollectionLikeSerializer { - public CustomCollectionSerializer(Fory fory) { - super(fory, CustomCollectionLike.class, true); - } - - @Override - public Collection onCollectionWrite(MemoryBuffer buffer, CustomCollectionLike value) { - buffer.writeVarUint32Small7(value.size()); - return new CollectionView(value); - } - - @Override - public Collection newCollection(MemoryBuffer buffer) { - int numElements = buffer.readVarUint32Small7(); - setNumElements(numElements); - return new CollectionView(numElements); - } - - @Override - public CustomCollectionLike onCollectionRead(Collection collection) { - CollectionView view = (CollectionView) collection; - return new CustomCollectionLike(view.getElements(), view.size()); - } + @Override + public Collection newCollection(MemoryBuffer buffer) { + int numElements = buffer.readVarUint32Small7(); + setNumElements(numElements); + return new ArrayList(numElements); + } } ``` -## Map Serializer +## Map Serializers -When implementing a serializer for a custom Map type, extend `MapSerializer` or `MapLikeSerializer`. The key difference is that `MapLikeSerializer` can serialize a class which has a map-like structure but is not a Java Map subtype. - -### Map Serializer with JIT Support +For Java maps, extend `MapSerializer` or `MapLikeSerializer`. ```java -public class CustomMapSerializer extends MapSerializer { - public CustomMapSerializer(Fory fory, Class cls) { - // supportCodegenHook is a critical parameter that determines serialization behavior - super(fory, cls, true); - } - - @Override - public Map onMapWrite(MemoryBuffer buffer, T value) { - // Write map size - buffer.writeVarUint32Small7(value.size()); - // Write any additional map metadata here - return value; - } - - @Override - public Map newMap(MemoryBuffer buffer) { - // Read map size - int numElements = buffer.readVarUint32Small7(); - setNumElements(numElements); - // Create and return new map instance - T map = (T) new HashMap(numElements); - fory.getRefResolver().reference(map); - return map; - } -} -``` - -Note: Invoke `setNumElements` when implementing `newMap` to let Fory know how many elements to deserialize. - -### Custom Map Serializer without JIT +import java.util.LinkedHashMap; +import java.util.Map; +import org.apache.fory.memory.MemoryBuffer; +import org.apache.fory.resolver.TypeResolver; +import org.apache.fory.serializer.collection.MapSerializer; + +public final class CustomMapSerializer> extends MapSerializer { + public CustomMapSerializer(TypeResolver typeResolver, Class type) { + super(typeResolver, type, true); + } -For complete control over the serialization process: + @Override + public Map onMapWrite(MemoryBuffer buffer, T value) { + buffer.writeVarUint32Small7(value.size()); + return value; + } -```java -class FixedValueMap extends AbstractMap { - private final Set keys; - private final int fixedValue; - - public FixedValueMap(Set keys, int fixedValue) { - this.keys = keys; - this.fixedValue = fixedValue; - } - - @Override - public Set> entrySet() { - Set> entries = new HashSet<>(); - for (String key : keys) { - entries.add(new SimpleEntry<>(key, fixedValue)); - } - return entries; - } - - @Override - public Integer get(Object key) { - return keys.contains(key) ? fixedValue : null; - } - - public Set getKeys() { - return keys; - } - - public int getFixedValue() { - return fixedValue; - } -} + @Override + public T onMapRead(Map map) { + return (T) map; + } -class FixedValueMapSerializer extends MapLikeSerializer { - public FixedValueMapSerializer(Fory fory) { - // Disable codegen since we're handling serialization directly - super(fory, FixedValueMap.class, false); - } - - @Override - public void write(MemoryBuffer buffer, FixedValueMap value) { - // Write the fixed value - buffer.writeInt32(value.getFixedValue()); - // Write the number of keys - buffer.writeVarUint32Small7(value.getKeys().size()); - // Write each key - for (String key : value.getKeys()) { - buffer.writeString(key); - } - } - - @Override - public FixedValueMap read(MemoryBuffer buffer) { - // Read the fixed value - int fixedValue = buffer.readInt32(); - // Read the number of keys - int size = buffer.readVarUint32Small7(); - Set keys = new HashSet<>(size); - for (int i = 0; i < size; i++) { - keys.add(buffer.readString()); - } - return new FixedValueMap(keys, fixedValue); - } - - // These methods are not used when supportCodegenHook is false - @Override - public Map onMapWrite(MemoryBuffer buffer, FixedValueMap value) { - throw new UnsupportedOperationException(); - } - - @Override - public FixedValueMap onMapRead(Map map) { - throw new UnsupportedOperationException(); - } - - @Override - public FixedValueMap onMapCopy(Map map) { - throw new UnsupportedOperationException(); - } + @Override + public Map newMap(MemoryBuffer buffer) { + int numElements = buffer.readVarUint32Small7(); + setNumElements(numElements); + return new LinkedHashMap(numElements); + } } ``` -### Map-like Type Serializer - -For types that behave like maps but aren't standard Java Maps: +## Registration ```java -class CustomMapLike { - private final Object[] keyArray; - private final Object[] valueArray; - private final int size; - - public CustomMapLike(int initialCapacity) { - this.keyArray = new Object[initialCapacity]; - this.valueArray = new Object[initialCapacity]; - this.size = 0; - } - - public CustomMapLike(Object[] keyArray, Object[] valueArray, int size) { - this.keyArray = keyArray; - this.valueArray = valueArray; - this.size = size; - } - - public Integer get(String key) { - for (int i = 0; i < size; i++) { - if (key.equals(keyArray[i])) { - return (Integer) valueArray[i]; - } - } - return null; - } - - public int size() { - return size; - } - - public Object[] getKeyArray() { - return keyArray; - } - - public Object[] getValueArray() { - return valueArray; - } -} - -class MapView extends AbstractMap { - private final Object[] keyArray; - private final Object[] valueArray; - private final int size; - private int writeIndex; - - public MapView(CustomMapLike mapLike) { - this.size = mapLike.size(); - this.keyArray = mapLike.getKeyArray(); - this.valueArray = mapLike.getValueArray(); - } - - public MapView(int size) { - this.size = size; - this.keyArray = new Object[size]; - this.valueArray = new Object[size]; - } - - @Override - public Set> entrySet() { - return new AbstractSet>() { - @Override - public Iterator> iterator() { - return new Iterator>() { - private int index = 0; - - @Override - public boolean hasNext() { - return index < size; - } - - @Override - public Entry next() { - if (!hasNext()) { - throw new NoSuchElementException(); - } - final int currentIndex = index++; - return new SimpleEntry<>( - keyArray[currentIndex], - valueArray[currentIndex] - ); - } - }; - } - - @Override - public int size() { - return size; - } - }; - } - - @Override - public Object put(Object key, Object value) { - if (writeIndex >= size) { - throw new IllegalStateException("Map is full"); - } - keyArray[writeIndex] = key; - valueArray[writeIndex] = value; - writeIndex++; - return null; - } - - public Object[] getKeyArray() { - return keyArray; - } - - public Object[] getValueArray() { - return valueArray; - } - - public int size() { - return size; - } -} - -class CustomMapLikeSerializer extends MapLikeSerializer { - public CustomMapLikeSerializer(Fory fory) { - super(fory, CustomMapLike.class, true); - } - - @Override - public Map onMapWrite(MemoryBuffer buffer, CustomMapLike value) { - buffer.writeVarUint32Small7(value.size()); - return new MapView(value); - } - - @Override - public Map newMap(MemoryBuffer buffer) { - int numElements = buffer.readVarUint32Small7(); - setNumElements(numElements); - return new MapView(numElements); - } - - @Override - public CustomMapLike onMapRead(Map map) { - MapView view = (MapView) map; - return new CustomMapLike(view.getKeyArray(), view.getValueArray(), view.size()); - } - - @Override - public CustomMapLike onMapCopy(Map map) { - MapView view = (MapView) map; - return new CustomMapLike(view.getKeyArray(), view.getValueArray(), view.size()); - } -} +Fory fory = Fory.builder().build(); + +fory.registerSerializer(Foo.class, new FooSerializer(fory.getConfig())); +fory.registerSerializer( + CustomMap.class, new CustomMapSerializer<>(fory.getTypeResolver(), CustomMap.class)); +fory.registerSerializer( + CustomCollection.class, + new CustomCollectionSerializer<>(fory.getTypeResolver(), CustomCollection.class)); ``` -## Registering Custom Serializers +If you want Fory to construct the serializer lazily, register a factory: ```java -Fory fory = Fory.builder() - .withLanguage(Language.JAVA) - .build(); - -// Register map serializer -fory.registerSerializer(CustomMap.class, new CustomMapSerializer<>(fory, CustomMap.class)); - -// Register collection serializer -fory.registerSerializer(CustomCollection.class, new CustomCollectionSerializer<>(fory, CustomCollection.class)); +fory.registerSerializer( + CustomMap.class, resolver -> new CustomMapSerializer<>(resolver, CustomMap.class)); ``` -## Key Points - -When implementing custom map or collection serializers: +## Thread Safety -1. Always extend the appropriate base class (`MapSerializer`/`MapLikeSerializer` for maps, `CollectionSerializer`/`CollectionLikeSerializer` for collections) -2. Consider the impact of `supportCodegenHook` on performance and functionality -3. Properly handle reference tracking if needed -4. Implement proper size management using `setNumElements` and `getAndClearNumElements` when `supportCodegenHook` is `true` +Override `threadSafe()` only when all retained fields are immutable and the serializer does not +retain `TypeResolver`, `RefResolver`, `Fory`, or other runtime state. -## Related Topics +In practice: -- [Type Registration](type-registration.md) - Register serializers -- [Schema Evolution](schema-evolution.md) - Compatible mode considerations -- [Configuration](configuration.md) - Serialization options +- `Config`-only serializers are often thread-safe. +- `TypeResolver`-based serializers are not thread-safe. +- Operation state belongs in `WriteContext`, `ReadContext`, and `CopyContext`, not in serializer + fields. diff --git a/docs/guide/java/index.md b/docs/guide/java/index.md index dc8856d082..5e8510031c 100644 --- a/docs/guide/java/index.md +++ b/docs/guide/java/index.md @@ -56,9 +56,6 @@ Note that Fory creation is not cheap, the **Fory instances should be reused betw ### Single-Thread Usage ```java -import java.util.List; -import java.util.Arrays; - import org.apache.fory.*; import org.apache.fory.config.*; @@ -83,9 +80,6 @@ public class Example { ### Multi-Thread Usage ```java -import java.util.List; -import java.util.Arrays; - import org.apache.fory.*; import org.apache.fory.config.*; @@ -94,12 +88,8 @@ public class Example { SomeClass object = new SomeClass(); // Note that Fory instances should be reused between // multiple serializations of different objects. - ThreadSafeFory fory = new ThreadLocalFory(classLoader -> { - Fory f = Fory.builder().withLanguage(Language.JAVA) - .withClassLoader(classLoader).build(); - f.register(SomeClass.class, 1); - return f; - }); + ThreadSafeFory fory = Fory.builder().withLanguage(Language.JAVA).buildThreadSafeFory(); + fory.register(SomeClass.class, 1); byte[] bytes = fory.serialize(object); System.out.println(fory.deserialize(bytes)); } @@ -109,20 +99,17 @@ public class Example { ### Fory Instance Reuse Pattern ```java -import java.util.List; -import java.util.Arrays; - import org.apache.fory.*; import org.apache.fory.config.*; public class Example { // reuse fory. - private static final ThreadSafeFory fory = new ThreadLocalFory(classLoader -> { - Fory f = Fory.builder().withLanguage(Language.JAVA) - .withClassLoader(classLoader).build(); - f.register(SomeClass.class, 1); - return f; - }); + private static final ThreadSafeFory fory = + Fory.builder().withLanguage(Language.JAVA).buildThreadSafeFory(); + + static { + fory.register(SomeClass.class, 1); + } public static void main(String[] args) { SomeClass object = new SomeClass(); @@ -134,19 +121,17 @@ public class Example { ## Thread Safety -Fory provides multiple options for thread-safe serialization: +Fory provides a pooled thread-safe runtime for multi-threaded serialization: -### ThreadLocalFory +### ThreadSafeForyPool -Uses thread-local storage to maintain separate Fory instances per thread: +`buildThreadSafeFory()` returns a `ThreadSafeForyPool` with a default idle-pool size: ```java -ThreadSafeFory fory = new ThreadLocalFory(classLoader -> { - Fory f = Fory.builder().withLanguage(Language.JAVA) - .withClassLoader(classLoader).build(); - f.register(SomeClass.class, 1); - return f; -}); +ThreadSafeFory fory = Fory.builder() + .withLanguage(Language.JAVA) + .buildThreadSafeFory(); +fory.register(SomeClass.class, 1); byte[] bytes = fory.serialize(object); System.out.println(fory.deserialize(bytes)); ``` @@ -166,10 +151,10 @@ ThreadSafeFory fory = Fory.builder() See more details in [Virtual Threads](virtual-threads.md). -### ThreadSafeForyPool +### Custom Pool Size -For environments where thread-local storage is not appropriate and you need the existing -time-expiring pooled runtime, use `buildThreadSafeForyPool`: +Use `buildThreadSafeForyPool(maxPoolSize)` when you want to control how many idle `Fory` +instances are retained for reuse: ```java ThreadSafeFory fory = Fory.builder() @@ -177,12 +162,11 @@ ThreadSafeFory fory = Fory.builder() .withRefTracking(false) .withCompatibleMode(CompatibleMode.SCHEMA_CONSISTENT) .withAsyncCompilation(true) - .buildThreadSafeForyPool(minPoolSize, maxPoolSize); + .buildThreadSafeForyPool(maxPoolSize); ``` -Note that calling `buildThreadSafeFory()` on `ForyBuilder` creates a `ThreadLocalFory`. This is -not a good default for virtual-thread workloads because each virtual thread can create its own -`Fory` instance. For virtual threads, prefer `buildVirtualThreadSafeFory(...)`. +`maxPoolSize` limits how many idle `Fory` instances stay in the pool after a burst. It does not +cap the number of concurrent operations. ### Builder Methods @@ -195,7 +179,7 @@ Fory fory = Fory.builder() .withAsyncCompilation(true) .build(); -// Thread-safe Fory (ThreadLocalFory) +// Thread-safe Fory (pooled) ThreadSafeFory fory = Fory.builder() .withLanguage(Language.JAVA) .withRefTracking(false) diff --git a/docs/guide/java/schema-evolution.md b/docs/guide/java/schema-evolution.md index 8dce5824c9..f62ab7529e 100644 --- a/docs/guide/java/schema-evolution.md +++ b/docs/guide/java/schema-evolution.md @@ -90,7 +90,6 @@ fory.deserialize(bytes); ```java // Thread-safe fory -fory.setClassLoader(beanA.getClass().getClassLoader()); byte[] serialized = fory.execute( f -> { f.getSerializationContext().setMetaContext(context); @@ -99,7 +98,6 @@ byte[] serialized = fory.execute( ); // Thread-safe fory -fory.setClassLoader(beanA.getClass().getClassLoader()); Object newObj = fory.execute( f -> { f.getSerializationContext().setMetaContext(context); @@ -108,7 +106,10 @@ Object newObj = fory.execute( ); ``` -**Note**: `MetaContext` is not thread-safe and cannot be reused across instances of Fory or multiple threads. In cases of multi-threading, a separate `MetaContext` must be created for each Fory instance. +**Note**: `MetaContext` is not thread-safe and cannot be reused across instances of Fory or +multiple threads. In cases of multi-threading, a separate `MetaContext` must be created for each +Fory instance. If you need a different classloader, create a separate `Fory` or `ThreadSafeFory` +configured with that loader instead of switching loaders on an existing instance. For more details, please refer to the [Meta Sharing specification](https://fory.apache.org/docs/specification/fory_java_serialization_spec#meta-share). diff --git a/docs/guide/java/troubleshooting.md b/docs/guide/java/troubleshooting.md index 7d49973cc0..ec0e778bed 100644 --- a/docs/guide/java/troubleshooting.md +++ b/docs/guide/java/troubleshooting.md @@ -147,7 +147,7 @@ Fory fory = Fory.builder() **Solution**: Register a custom serializer: ```java -fory.registerSerializer(MyClass.class, new MyClassSerializer(fory)); +fory.registerSerializer(MyClass.class, new MyClassSerializer(fory.getTypeResolver())); ``` ## Performance Issues diff --git a/docs/guide/java/type-registration.md b/docs/guide/java/type-registration.md index fdfa61774c..7e82572da2 100644 --- a/docs/guide/java/type-registration.md +++ b/docs/guide/java/type-registration.md @@ -76,12 +76,8 @@ Fory provides a `org.apache.fory.resolver.AllowListChecker` which is an allowed/ ```java AllowListChecker checker = new AllowListChecker(AllowListChecker.CheckLevel.STRICT); -ThreadSafeFory fory = new ThreadLocalFory(classLoader -> { - Fory f = Fory.builder().requireClassRegistration(true).withClassLoader(classLoader).build(); - f.getTypeResolver().setTypeChecker(checker); - checker.addListener((ClassResolver) f.getTypeResolver()); - return f; -}); +ThreadSafeFory fory = Fory.builder().requireClassRegistration(false).buildThreadSafeFory(); +fory.setTypeChecker(checker); checker.allowClass("org.example.*"); ``` diff --git a/docs/guide/kotlin/fory-creation.md b/docs/guide/kotlin/fory-creation.md index 9139346532..bb82ac8728 100644 --- a/docs/guide/kotlin/fory-creation.md +++ b/docs/guide/kotlin/fory-creation.md @@ -63,18 +63,14 @@ For multi-threaded applications, use `ThreadSafeFory`: ```kotlin import org.apache.fory.Fory import org.apache.fory.ThreadSafeFory -import org.apache.fory.ThreadLocalFory import org.apache.fory.serializer.kotlin.KotlinSerializers object ForyHolder { - val fory: ThreadSafeFory = ThreadLocalFory { classLoader -> - Fory.builder() - .withClassLoader(classLoader) - .requireClassRegistration(true) - .build().also { - KotlinSerializers.registerSerializers(it) - } - } + val fory: ThreadSafeFory = Fory.builder() + .requireClassRegistration(true) + .buildThreadSafeFory().also { + KotlinSerializers.registerSerializers(it) + } } ``` diff --git a/docs/guide/scala/fory-creation.md b/docs/guide/scala/fory-creation.md index 91473f1507..fecfd02241 100644 --- a/docs/guide/scala/fory-creation.md +++ b/docs/guide/scala/fory-creation.md @@ -98,19 +98,16 @@ object ForyHolder { For multi-threaded applications, use `ThreadSafeFory`: ```scala +import org.apache.fory.Fory import org.apache.fory.ThreadSafeFory -import org.apache.fory.ThreadLocalFory import org.apache.fory.serializer.scala.ScalaSerializers object ForyHolder { - val fory: ThreadSafeFory = new ThreadLocalFory(classLoader => { - val f = Fory.builder() - .withScalaOptimizationEnabled(true) - .withClassLoader(classLoader) - .build() - ScalaSerializers.registerSerializers(f) - f - }) + val fory: ThreadSafeFory = Fory.builder() + .withScalaOptimizationEnabled(true) + .buildThreadSafeFory() + + ScalaSerializers.registerSerializers(fory) } ``` diff --git a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/CompatibleThreadSafeExample.java b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/CompatibleThreadSafeExample.java index a63fbb4c42..69a7607926 100644 --- a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/CompatibleThreadSafeExample.java +++ b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/CompatibleThreadSafeExample.java @@ -20,7 +20,6 @@ package org.apache.fory.graalvm; import org.apache.fory.Fory; -import org.apache.fory.ThreadLocalFory; import org.apache.fory.ThreadSafeFory; import org.apache.fory.config.CompatibleMode; @@ -29,19 +28,13 @@ public class CompatibleThreadSafeExample { static { fory = - new ThreadLocalFory( - classLoader -> { - Fory f = - Fory.builder() - .withName(CompatibleThreadSafeExample.class.getName()) - .requireClassRegistration(true) - .withCompatibleMode(CompatibleMode.COMPATIBLE) - .build(); - // register and generate serializer code. - f.register(Foo.class); - f.ensureSerializersCompiled(); - return f; - }); + Fory.builder() + .withName(CompatibleThreadSafeExample.class.getName()) + .requireClassRegistration(true) + .withCompatibleMode(CompatibleMode.COMPATIBLE) + .buildThreadSafeFory(); + fory.register(Foo.class); + fory.ensureSerializersCompiled(); System.out.println("Init fory at build time"); } diff --git a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/EnsureSerializerExample.java b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/EnsureSerializerExample.java index fe6e17e786..54b8c79aea 100644 --- a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/EnsureSerializerExample.java +++ b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/EnsureSerializerExample.java @@ -20,7 +20,9 @@ package org.apache.fory.graalvm; import org.apache.fory.Fory; -import org.apache.fory.memory.MemoryBuffer; +import org.apache.fory.config.Config; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; import org.apache.fory.serializer.Serializer; import org.apache.fory.util.Preconditions; @@ -34,7 +36,7 @@ public class EnsureSerializerExample { .requireClassRegistration(true) .build(); // register and generate serializer code. - fory.registerSerializer(Custom.class, new CustomSerializer(fory)); + fory.registerSerializer(Custom.class, new CustomSerializer(fory.getConfig())); fory.register(EnsureSerializerExample.class); fory.ensureSerializersCompiled(); } @@ -57,16 +59,21 @@ public static class Custom { } private static class CustomSerializer extends Serializer { - public CustomSerializer(Fory fory) { - super(fory, Custom.class); + public CustomSerializer(Config config) { + super(config, Custom.class); } @Override - public void write(MemoryBuffer buffer, Custom value) {} + public void write(WriteContext writeContext, Custom value) {} @Override - public Custom read(MemoryBuffer buffer) { + public Custom read(ReadContext readContext) { return new Custom(); } + + @Override + public boolean threadSafe() { + return true; + } } } diff --git a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ThreadSafeExample.java b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ThreadSafeExample.java index dd45c3f290..545c8d9b7a 100644 --- a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ThreadSafeExample.java +++ b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ThreadSafeExample.java @@ -25,7 +25,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import org.apache.fory.Fory; -import org.apache.fory.ThreadLocalFory; import org.apache.fory.ThreadSafeFory; import org.apache.fory.collection.Collections; import org.apache.fory.util.Preconditions; @@ -35,18 +34,12 @@ public class ThreadSafeExample { static { fory = - new ThreadLocalFory( - classLoader -> { - Fory f = - Fory.builder() - .withName(ThreadSafeExample.class.getName()) - .requireClassRegistration(true) - .build(); - // register and generate serializer code. - f.register(Foo.class); - f.ensureSerializersCompiled(); - return f; - }); + Fory.builder() + .withName(ThreadSafeExample.class.getName()) + .requireClassRegistration(true) + .buildThreadSafeFory(); + fory.register(Foo.class); + fory.ensureSerializersCompiled(); System.out.println("Init fory at build time"); } diff --git a/java/fory-core/src/main/java/org/apache/fory/AbstractThreadSafeFory.java b/java/fory-core/src/main/java/org/apache/fory/AbstractThreadSafeFory.java index 82b792e5e1..33ac6af9ee 100644 --- a/java/fory-core/src/main/java/org/apache/fory/AbstractThreadSafeFory.java +++ b/java/fory-core/src/main/java/org/apache/fory/AbstractThreadSafeFory.java @@ -31,22 +31,11 @@ public void register(Class clz) { registerCallback(fory -> fory.register(clz)); } - @Override - public void register(Class cls, boolean createSerializer) { - registerCallback(fory -> fory.register(cls, createSerializer)); - } - @Override public void register(Class cls, int id) { registerCallback(fory -> fory.register(cls, id)); } - @Override - public void register(Class cls, int id, boolean createSerializer) { - registerCallback(fory -> fory.register(cls, id, createSerializer)); - } - - @Deprecated @Override public void register(Class cls, String typeName) { registerCallback(fory -> fory.register(cls, typeName)); @@ -96,8 +85,9 @@ public void registerSerializer(Class type, Serializer serializer) { } @Override - public void registerSerializer(Class type, Function> serializerCreator) { - registerCallback(fory -> fory.registerSerializer(type, serializerCreator.apply(fory))); + public void registerSerializer( + Class type, Function> serializerCreator) { + registerCallback(fory -> fory.registerSerializer(type, serializerCreator)); } @Override @@ -113,7 +103,7 @@ public void registerSerializerAndType(Class type, Serializer serializer) { @Override public void registerSerializerAndType( - Class type, Function> serializerCreator) { + Class type, Function> serializerCreator) { registerCallback(fory -> fory.registerSerializerAndType(type, serializerCreator)); } diff --git a/java/fory-core/src/main/java/org/apache/fory/BaseFory.java b/java/fory-core/src/main/java/org/apache/fory/BaseFory.java index fc59eae32a..5839cfa164 100644 --- a/java/fory-core/src/main/java/org/apache/fory/BaseFory.java +++ b/java/fory-core/src/main/java/org/apache/fory/BaseFory.java @@ -51,32 +51,6 @@ public interface BaseFory { */ void register(Class cls, int id); - /** - * Register class and allocate an auto-grown ID for this class. - * - *

NOTE: The registration order is important. If registration order is inconsistent, the - * allocated ID will be different, and the deserialization will failed !!! - * - * @param cls class to register. - * @param createSerializer whether to create serializer, if true and codegen enabled, this will - * generate the serializer code too. - */ - void register(Class cls, boolean createSerializer); - - /** - * Register class with specified id. This method has been deprecated, please use {@link - * #register(Class, int)} instead, and invoke {@link #ensureSerializersCompiled} after all classes - * has been registered. - * - * @param cls class to register. - * @param id id for provided class. - * @param createSerializer whether to create serializer, if true and codegen enabled, this will - * generate the serializer code too. this parameter has no effect anymore on whether to - * generate code, please use {@link #ensureSerializersCompiled} to trigger code generation - */ - @Deprecated - void register(Class cls, int id, boolean createSerializer); - /** register class with given type name which will be used for cross-language serialization. */ void register(Class cls, String typeName); @@ -139,9 +113,9 @@ public interface BaseFory { * allocated ID will be different, and the deserialization will failed !!! * * @param type class needed to be serialized/deserialized. - * @param serializerCreator serializer creator with param {@link Fory} + * @param serializerCreator serializer creator with param {@link TypeResolver} */ - void registerSerializer(Class type, Function> serializerCreator); + void registerSerializer(Class type, Function> serializerCreator); /** * Register a class (if not already registered) and then register its serializer class. @@ -171,9 +145,10 @@ public interface BaseFory { * allocated ID will be different, and the deserialization will failed !!! * * @param type class needed to be serialized/deserialized. - * @param serializerCreator serializer creator with param {@link Fory} + * @param serializerCreator serializer creator with param {@link TypeResolver} */ - void registerSerializerAndType(Class type, Function> serializerCreator); + void registerSerializerAndType( + Class type, Function> serializerCreator); void setSerializerFactory(SerializerFactory serializerFactory); diff --git a/java/fory-core/src/main/java/org/apache/fory/Fory.java b/java/fory-core/src/main/java/org/apache/fory/Fory.java index 43e90a38c7..a2397e6a6a 100644 --- a/java/fory-core/src/main/java/org/apache/fory/Fory.java +++ b/java/fory-core/src/main/java/org/apache/fory/Fory.java @@ -36,6 +36,9 @@ import org.apache.fory.config.Config; import org.apache.fory.config.ForyBuilder; import org.apache.fory.config.LongEncoding; +import org.apache.fory.context.CopyContext; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; import org.apache.fory.exception.CopyException; import org.apache.fory.exception.DeserializationException; import org.apache.fory.exception.ForyException; @@ -110,7 +113,6 @@ public final class Fory implements BaseFory { private final SharedRegistry sharedRegistry; private final ClassLoader classLoader; private final JITContext jitContext; - private MemoryBuffer buffer; private final StringSerializer stringSerializer; private final ArrayListSerializer arrayListSerializer; private final HashMapSerializer hashMapSerializer; @@ -118,16 +120,11 @@ public final class Fory implements BaseFory { private final boolean compressInt; private final LongEncoding longEncoding; private final Generics generics; - private BufferCallback bufferCallback; - private Iterator outOfBandBuffers; - private boolean peerOutOfBandEnabled; - private int depth; - private final int maxDepth; + private final WriteContext writeContext; + private final ReadContext readContext; + private final CopyContext copyContext; private boolean registrationFinished; - private final boolean forVirtualThread; - private int copyDepth; private final boolean copyRefTracking; - private final IdentityMap originToCopyMap; public Fory(ForyBuilder builder, ClassLoader classLoader) { this(builder, classLoader, new SharedRegistry()); @@ -148,14 +145,12 @@ public Fory(ForyBuilder builder, ClassLoader classLoader, SharedRegistry sharedR this.sharedRegistry = sharedRegistry; this.classLoader = classLoader; config = new Config(builder); - this.forVirtualThread = config.forVirtualThread(); crossLanguage = config.isXlang(); this.refTracking = config.trackingRef(); this.copyRefTracking = config.copyRef(); this.shareMeta = config.isMetaShareEnabled(); compressInt = config.compressInt(); longEncoding = config.longEncoding(); - maxDepth = config.maxDepth(); if (refTracking) { this.refResolver = new MapRefResolver(config.mapRefLoadFactor()); } else { @@ -164,14 +159,32 @@ public Fory(ForyBuilder builder, ClassLoader classLoader, SharedRegistry sharedR jitContext = new JITContext(this); generics = new Generics(this); // init stringSerializer first, so other places can share same StringSerializer. - stringSerializer = new StringSerializer(this); + stringSerializer = new StringSerializer(config); metaStringResolver = new MetaStringResolver(sharedRegistry); typeResolver = crossLanguage ? new XtypeResolver(this) : new ClassResolver(this); typeResolver.initialize(); serializationContext = new SerializationContext(config); - arrayListSerializer = new ArrayListSerializer(this); - hashMapSerializer = new HashMapSerializer(this); - originToCopyMap = new IdentityMap<>(2); + writeContext = + new WriteContext( + config, + generics, + typeResolver, + refResolver, + metaStringResolver, + serializationContext, + stringSerializer); + readContext = + new ReadContext( + config, + generics, + typeResolver, + refResolver, + metaStringResolver, + serializationContext, + stringSerializer); + copyContext = new CopyContext(typeResolver, copyRefTracking); + arrayListSerializer = new ArrayListSerializer(typeResolver); + hashMapSerializer = new HashMapSerializer(typeResolver); LOG.info("Created new fory {}", this); } @@ -185,18 +198,6 @@ public void register(Class cls, int id) { getTypeResolver().register(cls, Integer.toUnsignedLong(id)); } - @Deprecated - @Override - public void register(Class cls, boolean createSerializer) { - getTypeResolver().register(cls); - } - - @Deprecated - @Override - public void register(Class cls, int id, boolean createSerializer) { - getTypeResolver().register(cls, Integer.toUnsignedLong(id)); - } - /** * Register class with given type name, this method will have bigger serialization time/space cost * compared to register by id. @@ -253,8 +254,9 @@ public void registerSerializer(Class type, Serializer serializer) { } @Override - public void registerSerializer(Class type, Function> serializerCreator) { - getTypeResolver().registerSerializer(type, serializerCreator.apply(this)); + public void registerSerializer( + Class type, Function> serializerCreator) { + getTypeResolver().registerSerializer(type, serializerCreator.apply(typeResolver)); } @Override @@ -270,8 +272,8 @@ public void registerSerializerAndType(Class type, Serializer serializer) { @Override public void registerSerializerAndType( - Class type, Function> serializerCreator) { - getTypeResolver().registerSerializerAndType(type, serializerCreator.apply(this)); + Class type, Function> serializerCreator) { + getTypeResolver().registerSerializerAndType(type, serializerCreator.apply(typeResolver)); } @Override @@ -330,33 +332,36 @@ public MemoryBuffer serialize(MemoryBuffer buffer, Object obj) { @Override public MemoryBuffer serialize(MemoryBuffer buffer, Object obj, BufferCallback callback) { ensureRegistrationFinished(); - byte bitmap = 0; - if (crossLanguage) { - bitmap |= isCrossLanguageFlag; - } - if (obj == null) { - bitmap |= isNilFlag; - buffer.writeByte(bitmap); - return buffer; - } - if (callback != null) { - bitmap |= isOutOfBandFlag; - bufferCallback = callback; - } - buffer.writeByte(bitmap); - try { - jitContext.lock(); - if (depth > 0) { - throwDepthSerializationException(); - } - writeRef(buffer, obj); - return buffer; - } catch (Throwable t) { - throw processSerializationError(t); - } finally { - resetWrite(); - jitContext.unlock(); - } + return writeContext.run( + buffer, + callback, + () -> { + byte bitmap = 0; + if (crossLanguage) { + bitmap |= isCrossLanguageFlag; + } + if (obj == null) { + bitmap |= isNilFlag; + buffer.writeByte(bitmap); + return buffer; + } + if (callback != null) { + bitmap |= isOutOfBandFlag; + } + buffer.writeByte(bitmap); + try { + jitContext.lock(); + if (writeContext.getDepth() > 0) { + throwDepthSerializationException(); + } + writeContext.writeRef(obj); + return buffer; + } catch (Throwable t) { + throw processSerializationError(t); + } finally { + jitContext.unlock(); + } + }); } @Override @@ -402,99 +407,28 @@ private ForyException processCopyError(Throwable e) { } public MemoryBuffer getBuffer() { - MemoryBuffer buf = buffer; - if (buf == null) { - buf = buffer = MemoryBuffer.newHeapBuffer(64); - } - return buf; + return writeContext.getBuffer(); } public void resetBuffer() { - MemoryBuffer buf = buffer; - if (buf != null && buf.size() > config.bufferSizeLimitBytes()) { - buffer = MemoryBuffer.newHeapBuffer(config.bufferSizeLimitBytes()); - } + writeContext.resetBuffer(); } /** Serialize a nullable referencable object to buffer. */ public void writeRef(MemoryBuffer buffer, Object obj) { - if (!refResolver.writeRefOrNull(buffer, obj)) { - TypeResolver resolver = typeResolver; - TypeInfo typeInfo = resolver.getTypeInfo(obj.getClass()); - if (crossLanguage && typeInfo.getCls() == UnknownStruct.class) { - depth++; - typeInfo.getSerializer().write(buffer, obj); - depth--; - return; - } - resolver.writeTypeInfo(buffer, typeInfo); - writeData(buffer, typeInfo, obj); - } + writeContext.writeRef(obj); } public void writeRef(MemoryBuffer buffer, Object obj, TypeInfoHolder classInfoHolder) { - if (!refResolver.writeRefOrNull(buffer, obj)) { - TypeResolver resolver = typeResolver; - TypeInfo typeInfo = resolver.getTypeInfo(obj.getClass(), classInfoHolder); - if (crossLanguage && typeInfo.getCls() == UnknownStruct.class) { - depth++; - typeInfo.getSerializer().write(buffer, obj); - depth--; - return; - } - resolver.writeTypeInfo(buffer, typeInfo); - writeData(buffer, typeInfo, obj); - } + writeContext.writeRef(obj, classInfoHolder); } public void writeRef(MemoryBuffer buffer, Object obj, TypeInfo typeInfo) { - if (crossLanguage && typeInfo.getCls() == UnknownStruct.class) { - if (!refResolver.writeRefOrNull(buffer, obj)) { - depth++; - typeInfo.getSerializer().write(buffer, obj); - depth--; - } - return; - } - TypeResolver resolver = typeResolver; - Serializer serializer = typeInfo.getSerializer(); - if (serializer.needToWriteRef()) { - if (!refResolver.writeRefOrNull(buffer, obj)) { - resolver.writeTypeInfo(buffer, typeInfo); - depth++; - serializer.write(buffer, obj); - depth--; - } - } else { - if (obj == null) { - buffer.writeByte(Fory.NULL_FLAG); - } else { - buffer.writeByte(Fory.NOT_NULL_VALUE_FLAG); - resolver.writeTypeInfo(buffer, typeInfo); - depth++; - serializer.write(buffer, obj); - depth--; - } - } + writeContext.writeRef(obj, typeInfo); } public void writeRef(MemoryBuffer buffer, T obj, Serializer serializer) { - if (serializer.needToWriteRef()) { - if (!refResolver.writeRefOrNull(buffer, obj)) { - depth++; - serializer.write(buffer, obj); - depth--; - } - } else { - if (obj == null) { - buffer.writeByte(Fory.NULL_FLAG); - } else { - buffer.writeByte(Fory.NOT_NULL_VALUE_FLAG); - depth++; - serializer.write(buffer, obj); - depth--; - } - } + writeContext.writeRef(obj, serializer); } /** @@ -504,211 +438,54 @@ public void writeRef(MemoryBuffer buffer, T obj, Serializer serializer) { * object graph. */ public void writeNonRef(MemoryBuffer buffer, Object obj) { - TypeResolver resolver = typeResolver; - TypeInfo typeInfo = resolver.getTypeInfo(obj.getClass()); - if (crossLanguage && typeInfo.getCls() == UnknownStruct.class) { - depth++; - typeInfo.getSerializer().write(buffer, obj); - depth--; - return; - } - resolver.writeTypeInfo(buffer, typeInfo); - writeData(buffer, typeInfo, obj); + writeContext.writeNonRef(obj); } public void writeNonRef(MemoryBuffer buffer, Object obj, Serializer serializer) { - depth++; - serializer.write(buffer, obj); - depth--; + writeContext.writeNonRef(obj, serializer); } public void writeNonRef(MemoryBuffer buffer, Object obj, TypeInfoHolder holder) { - TypeResolver resolver = typeResolver; - TypeInfo typeInfo = resolver.getTypeInfo(obj.getClass(), holder); - if (crossLanguage && typeInfo.getCls() == UnknownStruct.class) { - depth++; - typeInfo.getSerializer().write(buffer, obj); - depth--; - return; - } - resolver.writeTypeInfo(buffer, typeInfo); - writeData(buffer, typeInfo, obj); + writeContext.writeNonRef(obj, holder); } public void writeNonRef(MemoryBuffer buffer, Object obj, TypeInfo typeInfo) { - if (crossLanguage && typeInfo.getCls() == UnknownStruct.class) { - depth++; - typeInfo.getSerializer().write(buffer, obj); - depth--; - return; - } - typeResolver.writeTypeInfo(buffer, typeInfo); - writeData(buffer, typeInfo, obj); + writeContext.writeNonRef(obj, typeInfo); } /** Class/type info should be written already. */ public void writeData(MemoryBuffer buffer, TypeInfo typeInfo, Object obj) { - int typeId = typeInfo.getTypeId(); - switch (typeId) { - case Types.BOOL: - buffer.writeBoolean((Boolean) obj); - break; - case Types.INT8: - buffer.writeByte((Byte) obj); - break; - case Types.INT16: - buffer.writeInt16((Short) obj); - break; - case ClassResolver.CHAR_ID: - buffer.writeChar((Character) obj); - break; - case Types.INT32: - case Types.VARINT32: - if (compressInt) { - buffer.writeVarInt32((Integer) obj); - } else { - buffer.writeInt32((Integer) obj); - } - break; - case Types.INT64: - LongSerializer.writeInt64(buffer, (Long) obj, longEncoding); - break; - case Types.VARINT64: - buffer.writeVarInt64((Long) obj); - break; - case Types.TAGGED_INT64: - buffer.writeTaggedInt64((Long) obj); - break; - case Types.FLOAT32: - buffer.writeFloat32((Float) obj); - break; - case Types.FLOAT64: - buffer.writeFloat64((Double) obj); - break; - case Types.STRING: - if (typeInfo.getCls() == String.class) { - stringSerializer.writeString(buffer, (String) obj); - break; - } - depth++; - typeInfo.getSerializer().write(buffer, obj); - depth--; - break; - default: - depth++; - typeInfo.getSerializer().write(buffer, obj); - depth--; - } + writeContext.writeData(typeInfo, obj); } public void writeBufferObject(MemoryBuffer buffer, BufferObject bufferObject) { - if (bufferCallback == null || bufferCallback.apply(bufferObject)) { - buffer.writeBoolean(true); - // writer length. - int totalBytes = bufferObject.totalBytes(); - // write aligned length so that later buffer copy happen on aligned offset, which will be more - // efficient - // TODO(chaokunyang) Remove branch when other languages support aligned varint. - if (!crossLanguage) { - buffer.writeVarUint32Aligned(totalBytes); - } else { - buffer.writeVarUint32(totalBytes); - } - int writerIndex = buffer.writerIndex(); - buffer.ensure(writerIndex + bufferObject.totalBytes()); - bufferObject.writeTo(buffer); - int size = buffer.writerIndex() - writerIndex; - Preconditions.checkArgument(size == totalBytes); - } else { - buffer.writeBoolean(false); - } + writeContext.writeBufferObject(bufferObject); } // duplicate for speed. public void writeBufferObject( MemoryBuffer buffer, ArraySerializers.PrimitiveArrayBufferObject bufferObject) { - if (bufferCallback == null || bufferCallback.apply(bufferObject)) { - buffer.writeBoolean(true); - int totalBytes = bufferObject.totalBytes(); - // write aligned length so that later buffer copy happen on aligned offset, which will be very - // efficient - // TODO(chaokunyang) Remove branch when other languages support aligned varint. - if (!crossLanguage) { - buffer.writeVarUint32Aligned(totalBytes); - } else { - buffer.writeVarUint32(totalBytes); - } - bufferObject.writeTo(buffer); - } else { - buffer.writeBoolean(false); - } + writeContext.writeBufferObject(bufferObject); } public MemoryBuffer readBufferObject(MemoryBuffer buffer) { - boolean inBand = buffer.readBoolean(); - if (inBand) { - int size; - // TODO(chaokunyang) Remove branch when other languages support aligned varint. - if (!crossLanguage) { - size = buffer.readAlignedVarUint32(); - } else { - size = buffer.readVarUint32(); - } - if (buffer.readerIndex() + size > buffer.size() && buffer.getStreamReader() != null) { - buffer.getStreamReader().fillBuffer(buffer.readerIndex() + size - buffer.size()); - } - MemoryBuffer slice = buffer.slice(buffer.readerIndex(), size); - buffer.readerIndex(buffer.readerIndex() + size); - return slice; - } else { - Preconditions.checkArgument(outOfBandBuffers.hasNext()); - return outOfBandBuffers.next(); - } + return readContext.readBufferObject(buffer); } public void writeString(MemoryBuffer buffer, String str) { - stringSerializer.writeString(buffer, str); + writeContext.writeString(str); } public String readString(MemoryBuffer buffer) { - return stringSerializer.readString(buffer); + return readContext.readString(buffer); } public void writeStringRef(MemoryBuffer buffer, String str) { - if (stringSerializer.needToWriteRef()) { - if (!refResolver.writeRefOrNull(buffer, str)) { - stringSerializer.writeString(buffer, str); - } - } else { - if (str == null) { - buffer.writeByte(Fory.NULL_FLAG); - } else { - buffer.writeByte(Fory.NOT_NULL_VALUE_FLAG); - stringSerializer.write(buffer, str); - } - } + writeContext.writeStringRef(str); } public String readStringRef(MemoryBuffer buffer) { - RefResolver refResolver = this.refResolver; - if (stringSerializer.needToWriteRef()) { - String obj; - int nextReadRefId = refResolver.tryPreserveRefId(buffer); - if (nextReadRefId >= NOT_NULL_VALUE_FLAG) { - obj = stringSerializer.read(buffer); - refResolver.setReadObject(nextReadRefId, obj); - return obj; - } else { - return (String) refResolver.getReadObject(); - } - } else { - byte headFlag = buffer.readByte(); - if (headFlag == Fory.NULL_FLAG) { - return null; - } else { - return stringSerializer.read(buffer); - } - } + return readContext.readStringRef(buffer); } public void writeInt64(MemoryBuffer buffer, long value) { @@ -716,7 +493,7 @@ public void writeInt64(MemoryBuffer buffer, long value) { } public long readInt64(MemoryBuffer buffer) { - return LongSerializer.readInt64(buffer, longEncoding); + return readContext.readInt64(buffer); } @Override @@ -732,24 +509,31 @@ public T deserialize(byte[] bytes, Class type) { @Override public T deserialize(MemoryBuffer buffer, Class type) { ensureRegistrationFinished(); + byte bitmap = buffer.readByte(); + if ((bitmap & isNilFlag) == isNilFlag) { + return null; + } + boolean peerOutOfBandEnabled = (bitmap & isOutOfBandFlag) == isOutOfBandFlag; + assert !peerOutOfBandEnabled : "Out of band buffers not passed in when deserializing"; + checkXlangBitmap(bitmap); try { - jitContext.lock(); - if (depth > 0) { - throwDepthDeserializationException(); - } - byte bitmap = buffer.readByte(); - if ((bitmap & isNilFlag) == isNilFlag) { - return null; - } - boolean peerOutOfBandEnabled = (bitmap & isOutOfBandFlag) == isOutOfBandFlag; - assert !peerOutOfBandEnabled : "Out of band buffers not passed in when deserializing"; - checkXlangBitmap(bitmap); - return deserializeByType(buffer, type); + return readContext.run( + buffer, + null, + false, + () -> { + try { + jitContext.lock(); + if (readContext.getDepth() > 0) { + throwDepthDeserializationException(); + } + return deserializeByType(buffer, type); + } finally { + jitContext.unlock(); + } + }); } catch (Throwable t) { throw ExceptionUtils.handleReadFailed(this, t); - } finally { - resetRead(); - jitContext.unlock(); } } @@ -798,35 +582,41 @@ public Object deserialize(MemoryBuffer buffer) { @Override public Object deserialize(MemoryBuffer buffer, Iterable outOfBandBuffers) { ensureRegistrationFinished(); + byte bitmap = buffer.readByte(); + if ((bitmap & isNilFlag) == isNilFlag) { + return null; + } + checkXlangBitmap(bitmap); + boolean peerOutOfBandEnabled = (bitmap & isOutOfBandFlag) == isOutOfBandFlag; + if (peerOutOfBandEnabled) { + Preconditions.checkNotNull( + outOfBandBuffers, + "outOfBandBuffers shouldn't be null when the serialized stream is " + + "produced with bufferCallback not null."); + } else { + Preconditions.checkArgument( + outOfBandBuffers == null, + "outOfBandBuffers should be null when the serialized stream is " + + "produced with bufferCallback null."); + } try { - jitContext.lock(); - if (depth > 0) { - throwDepthDeserializationException(); - } - byte bitmap = buffer.readByte(); - if ((bitmap & isNilFlag) == isNilFlag) { - return null; - } - checkXlangBitmap(bitmap); - peerOutOfBandEnabled = (bitmap & isOutOfBandFlag) == isOutOfBandFlag; - if (peerOutOfBandEnabled) { - Preconditions.checkNotNull( - outOfBandBuffers, - "outOfBandBuffers shouldn't be null when the serialized stream is " - + "produced with bufferCallback not null."); - this.outOfBandBuffers = outOfBandBuffers.iterator(); - } else { - Preconditions.checkArgument( - outOfBandBuffers == null, - "outOfBandBuffers should be null when the serialized stream is " - + "produced with bufferCallback null."); - } - return readRef(buffer); + return readContext.run( + buffer, + peerOutOfBandEnabled ? outOfBandBuffers : null, + peerOutOfBandEnabled, + () -> { + try { + jitContext.lock(); + if (readContext.getDepth() > 0) { + throwDepthDeserializationException(); + } + return readContext.readRef(); + } finally { + jitContext.unlock(); + } + }); } catch (Throwable t) { throw ExceptionUtils.handleReadFailed(this, t); - } finally { - resetRead(); - jitContext.unlock(); } } @@ -876,115 +666,55 @@ private T deserializeByType(MemoryBuffer buffer, Class type) { /** Deserialize nullable referencable object from buffer. */ public Object readRef(MemoryBuffer buffer) { - RefResolver refResolver = this.refResolver; - int nextReadRefId = refResolver.tryPreserveRefId(buffer); - if (nextReadRefId >= NOT_NULL_VALUE_FLAG) { - TypeInfo typeInfo = typeResolver.readTypeInfo(buffer); - Object o = readNonRef(buffer, typeInfo); - refResolver.setReadObject(nextReadRefId, o); - return o; - } - return refResolver.getReadObject(); + return readContext.readRef(); } public Object readRef(MemoryBuffer buffer, TypeInfo typeInfo) { - RefResolver refResolver = this.refResolver; - int nextReadRefId = refResolver.tryPreserveRefId(buffer); - if (nextReadRefId >= NOT_NULL_VALUE_FLAG) { - Object o = readNonRef(buffer, typeInfo); - refResolver.setReadObject(nextReadRefId, o); - return o; - } - return refResolver.getReadObject(); + return readContext.readRef(typeInfo); } public Object readRef(MemoryBuffer buffer, TypeInfoHolder classInfoHolder) { - RefResolver refResolver = this.refResolver; - int nextReadRefId = refResolver.tryPreserveRefId(buffer); - if (nextReadRefId >= NOT_NULL_VALUE_FLAG) { - TypeInfo typeInfo = typeResolver.readTypeInfo(buffer, classInfoHolder); - Object o = readNonRef(buffer, typeInfo); - refResolver.setReadObject(nextReadRefId, o); - return o; - } - return refResolver.getReadObject(); + return readContext.readRef(classInfoHolder); } @SuppressWarnings("unchecked") public T readRef(MemoryBuffer buffer, Serializer serializer) { - if (serializer.needToWriteRef()) { - RefResolver refResolver = this.refResolver; - int nextReadRefId = refResolver.tryPreserveRefId(buffer); - if (nextReadRefId >= NOT_NULL_VALUE_FLAG) { - Object o = readNonRef(buffer, serializer); - refResolver.setReadObject(nextReadRefId, o); - return (T) o; - } - return (T) refResolver.getReadObject(); - } - byte headFlag = buffer.readByte(); - if (headFlag == Fory.NULL_FLAG) { - return null; - } - return (T) readNonRef(buffer, serializer); + return readContext.readRef(serializer); } /** Deserialize not-null and non-reference object from buffer. */ public Object readNonRef(MemoryBuffer buffer) { - TypeInfo typeInfo = typeResolver.readTypeInfo(buffer); - return readNonRef(buffer, typeInfo); + return readContext.readNonRef(); } public Object readNonRef(MemoryBuffer buffer, TypeInfoHolder classInfoHolder) { - TypeInfo typeInfo = typeResolver.readTypeInfo(buffer, classInfoHolder); - return readNonRef(buffer, typeInfo); + return readContext.readNonRef(classInfoHolder); } public Object readNonRef(MemoryBuffer buffer, TypeInfo typeInfo) { - return readDataInternal(buffer, typeInfo); + return readContext.readNonRef(typeInfo); } public Object readNonRef(MemoryBuffer buffer, Serializer serializer) { - incReadDepth(); - Object o = serializer.read(buffer); - depth--; - return o; + return readContext.readNonRef(serializer); } /** Read object class and data without tracking ref. */ public Object readNullable(MemoryBuffer buffer) { - byte headFlag = buffer.readByte(); - if (headFlag == Fory.NULL_FLAG) { - return null; - } else { - return readNonRef(buffer); - } + return readContext.readNullable(); } public Object readNullable(MemoryBuffer buffer, Serializer serializer) { - byte headFlag = buffer.readByte(); - if (headFlag == Fory.NULL_FLAG) { - return null; - } else { - return serializer.read(buffer); - } + return readContext.readNullable(serializer); } public Object readNullable(MemoryBuffer buffer, TypeInfoHolder classInfoHolder) { - byte headFlag = buffer.readByte(); - if (headFlag == Fory.NULL_FLAG) { - return null; - } - return readNonRef(buffer, classInfoHolder); + return readContext.readNullable(classInfoHolder); } /** Class should be read already. */ public Object readData(MemoryBuffer buffer, TypeInfo typeInfo) { - incReadDepth(); - Serializer serializer = typeInfo.getSerializer(); - Object read = serializer.read(buffer); - depth--; - return read; + return readContext.readData(typeInfo); } private Object readDataInternal(MemoryBuffer buffer, TypeInfo typeInfo) { @@ -1021,13 +751,13 @@ private Object readDataInternal(MemoryBuffer buffer, TypeInfo typeInfo) { return stringSerializer.readString(buffer); } incReadDepth(); - Object stringLike = typeInfo.getSerializer().read(buffer); - depth--; + Object stringLike = typeInfo.getSerializer().read(readContext); + readContext.decDepth(); return stringLike; default: incReadDepth(); - Object read = typeInfo.getSerializer().read(buffer); - depth--; + Object read = typeInfo.getSerializer().read(readContext); + readContext.decDepth(); return read; } } @@ -1043,10 +773,9 @@ private void checkXlangBitmap(byte bitmap) { @Override public T copy(T obj) { - assert copyDepth == 0 : "copy should only be invoked at top level; use copyObject instead"; ensureRegistrationFinished(); try { - return copyObject(obj); + return copyContext.run(() -> copyContext.copyObject(obj)); } catch (Throwable e) { throw processCopyError(e); } finally { @@ -1061,115 +790,15 @@ public T copy(T obj) { * @return copied object */ public T copyObject(T obj) { - if (obj == null) { - return null; - } - Object copy; - TypeInfo typeInfo = typeResolver.getTypeInfo(obj.getClass(), true); - int typeId = typeInfo.getTypeId(); - switch (typeId) { - case Types.BOOL: - case Types.INT8: - case ClassResolver.CHAR_ID: - case Types.INT16: - case Types.INT32: - case Types.FLOAT32: - case Types.INT64: - case Types.FLOAT64: - return obj; - case Types.STRING: - if (typeInfo.getCls() == String.class) { - return obj; - } - copy = copyObject(obj, typeInfo.getSerializer()); - break; - case ClassResolver.PRIMITIVE_BOOLEAN_ARRAY_ID: - boolean[] boolArr = (boolean[]) obj; - return (T) Arrays.copyOf(boolArr, boolArr.length); - case ClassResolver.PRIMITIVE_BYTE_ARRAY_ID: - byte[] byteArr = (byte[]) obj; - return (T) Arrays.copyOf(byteArr, byteArr.length); - case ClassResolver.PRIMITIVE_CHAR_ARRAY_ID: - char[] charArr = (char[]) obj; - return (T) Arrays.copyOf(charArr, charArr.length); - case ClassResolver.PRIMITIVE_SHORT_ARRAY_ID: - short[] shortArr = (short[]) obj; - return (T) Arrays.copyOf(shortArr, shortArr.length); - case ClassResolver.PRIMITIVE_INT_ARRAY_ID: - int[] intArr = (int[]) obj; - return (T) Arrays.copyOf(intArr, intArr.length); - case ClassResolver.PRIMITIVE_FLOAT_ARRAY_ID: - float[] floatArr = (float[]) obj; - return (T) Arrays.copyOf(floatArr, floatArr.length); - case ClassResolver.PRIMITIVE_LONG_ARRAY_ID: - long[] longArr = (long[]) obj; - return (T) Arrays.copyOf(longArr, longArr.length); - case ClassResolver.PRIMITIVE_DOUBLE_ARRAY_ID: - double[] doubleArr = (double[]) obj; - return (T) Arrays.copyOf(doubleArr, doubleArr.length); - case ClassResolver.STRING_ARRAY_ID: - String[] stringArr = (String[]) obj; - return (T) Arrays.copyOf(stringArr, stringArr.length); - case ClassResolver.ARRAYLIST_ID: - copy = arrayListSerializer.copy((ArrayList) obj); - break; - case ClassResolver.HASHMAP_ID: - copy = hashMapSerializer.copy((HashMap) obj); - break; - // todo: add fastpath for other types. - default: - copy = copyObject(obj, typeInfo.getSerializer()); - } - return (T) copy; + return copyContext.copyObject(obj); } public T copyObject(T obj, int classId) { - if (obj == null) { - return null; - } - // Fast path to avoid cost of query class map. - switch (classId) { - case ClassResolver.PRIMITIVE_BOOL_ID: - case ClassResolver.PRIMITIVE_INT8_ID: - case ClassResolver.PRIMITIVE_CHAR_ID: - case ClassResolver.PRIMITIVE_INT16_ID: - case ClassResolver.PRIMITIVE_INT32_ID: - case ClassResolver.PRIMITIVE_FLOAT32_ID: - case ClassResolver.PRIMITIVE_INT64_ID: - case ClassResolver.PRIMITIVE_FLOAT64_ID: - case Types.BOOL: - case Types.INT8: - case ClassResolver.CHAR_ID: - case Types.INT16: - case Types.INT32: - case Types.FLOAT32: - case Types.INT64: - case Types.FLOAT64: - return obj; - case Types.STRING: - if (obj.getClass() == String.class) { - return obj; - } - return copyObject(obj, typeResolver.getTypeInfo(obj.getClass(), true).getSerializer()); - default: - return copyObject(obj, typeResolver.getTypeInfo(obj.getClass(), true).getSerializer()); - } + return copyContext.copyObject(obj, classId); } public T copyObject(T obj, Serializer serializer) { - copyDepth++; - T copyObject; - if (serializer.needToCopyRef()) { - copyObject = getCopyObject(obj); - if (copyObject == null) { - copyObject = serializer.copy(obj); - originToCopyMap.put(obj, copyObject); - } - } else { - copyObject = serializer.copy(obj); - } - copyDepth--; - return copyObject; + return copyContext.copyObject(obj, serializer); } /** @@ -1182,14 +811,12 @@ public T copyObject(T obj, Serializer serializer) { * @param o2 the copied object */ public void reference(T o1, T o2) { - if (copyRefTracking && o1 != null) { - originToCopyMap.put(o1, o2); - } + copyContext.reference(o1, o2); } @SuppressWarnings("unchecked") public T getCopyObject(T originObj) { - return (T) originToCopyMap.get(originObj); + return copyContext.getCopyObject(originObj); } private void serializeToStream(OutputStream outputStream, Consumer function) { @@ -1227,34 +854,15 @@ public void reset() { } public void resetWrite() { - refResolver.resetWrite(); - typeResolver.resetWrite(); - metaStringResolver.resetWrite(); - serializationContext.resetWrite(); - bufferCallback = null; - depth = 0; - if (forVirtualThread) { - stringSerializer.clearBuffer(config.bufferSizeLimitBytes()); - } + writeContext.reset(); } public void resetRead() { - refResolver.resetRead(); - typeResolver.resetRead(); - metaStringResolver.resetRead(); - serializationContext.resetRead(); - peerOutOfBandEnabled = false; - depth = 0; - if (forVirtualThread) { - stringSerializer.clearBuffer(config.bufferSizeLimitBytes()); - } + readContext.reset(); } public void resetCopy() { - if (copyRefTracking) { - originToCopyMap.clear(); - } - copyDepth = 0; + copyContext.reset(); } private void throwDepthSerializationException() { @@ -1283,11 +891,11 @@ public JITContext getJITContext() { } public BufferCallback getBufferCallback() { - return bufferCallback; + return writeContext.getBufferCallback(); } public boolean isPeerOutOfBandEnabled() { - return peerOutOfBandEnabled; + return readContext.isPeerOutOfBandEnabled(); } public RefResolver getRefResolver() { @@ -1318,42 +926,51 @@ public Generics getGenerics() { } public int getDepth() { - return depth; + try { + return ReadContext.current().getDepth(); + } catch (IllegalStateException ignored) { + try { + return WriteContext.current().getDepth(); + } catch (IllegalStateException ignoredWrite) { + return CopyContext.current().getDepth(); + } + } } public void setDepth(int depth) { - this.depth = depth; + try { + ReadContext.current().setDepth(depth); + } catch (IllegalStateException ignored) { + WriteContext.current().setDepth(depth); + } } public void incDepth(int diff) { - this.depth += diff; + try { + ReadContext.current().incDepth(diff); + } catch (IllegalStateException ignored) { + WriteContext.current().incDepth(diff); + } } public void incDepth() { - this.depth += 1; + WriteContext.current().incDepth(); } public void decDepth() { - this.depth -= 1; - } - - public void incReadDepth() { - if ((this.depth += 1) > maxDepth) { - throwReadDepthExceedException(); + try { + ReadContext.current().decDepth(); + } catch (IllegalStateException ignored) { + WriteContext.current().decDepth(); } } - private void throwReadDepthExceedException() { - throw new InsecureException( - String.format( - "Read depth exceed max depth %s, " - + "the deserialization data may be malicious. If it's not malicious, " - + "please increase max read depth by ForyBuilder#withMaxDepth(largerDepth)", - maxDepth)); + public void incReadDepth() { + ReadContext.current().incReadDepth(); } public void incCopyDepth(int diff) { - this.copyDepth += diff; + copyContext.incDepth(diff); } // Invoked by jit diff --git a/java/fory-core/src/main/java/org/apache/fory/ThreadLocalFory.java b/java/fory-core/src/main/java/org/apache/fory/ThreadLocalFory.java deleted file mode 100644 index fffff10075..0000000000 --- a/java/fory-core/src/main/java/org/apache/fory/ThreadLocalFory.java +++ /dev/null @@ -1,241 +0,0 @@ -/* - * 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; - -import java.io.OutputStream; -import java.nio.ByteBuffer; -import java.util.Collections; -import java.util.Map; -import java.util.WeakHashMap; -import java.util.function.Consumer; -import java.util.function.Function; -import javax.annotation.concurrent.ThreadSafe; -import org.apache.fory.annotation.Internal; -import org.apache.fory.io.ForyInputStream; -import org.apache.fory.io.ForyReadableChannel; -import org.apache.fory.memory.MemoryBuffer; -import org.apache.fory.memory.MemoryUtils; -import org.apache.fory.resolver.SharedRegistry; -import org.apache.fory.serializer.BufferCallback; -import org.apache.fory.util.LoaderBinding; -import org.apache.fory.util.LoaderBinding.StagingType; - -/** - * A thread safe serialization entrance for {@link Fory} by binding a {@link Fory} for every thread. - * Note that the thread shouldn't be created and destroyed frequently, otherwise the {@link Fory} - * will be created and destroyed frequently, which is slow. - */ -@ThreadSafe -public class ThreadLocalFory extends AbstractThreadSafeFory { - private final ThreadLocal bufferLocal = - ThreadLocal.withInitial(() -> MemoryUtils.buffer(32)); - - private final ThreadLocal bindingThreadLocal; - private final SharedRegistry sharedRegistry; - private Consumer factoryCallback; - private final Map allFory; - - private ClassLoader classLoader; - private final Object callbackLock = new Object(); - - public ThreadLocalFory(Function foryFactory) { - this(foryFactory, new SharedRegistry()); - } - - public ThreadLocalFory(Function foryFactory, SharedRegistry sharedRegistry) { - this.sharedRegistry = sharedRegistry; - factoryCallback = f -> {}; - allFory = Collections.synchronizedMap(new WeakHashMap<>()); - bindingThreadLocal = - ThreadLocal.withInitial( - () -> { - LoaderBinding binding = new LoaderBinding(foryFactory); - binding.setBindingCallback(factoryCallback); - ClassLoader cl = - classLoader == null - ? Thread.currentThread().getContextClassLoader() - : classLoader; - binding.setClassLoader(cl); - allFory.put(binding, null); - return binding; - }); - // 1. init and warm for current thread. - // Fory creation took about 1~2 ms, but first creation - // in a process load some classes which is not cheap. - // 2. Make fory generate code at graalvm build time. - Fory fory = bindingThreadLocal.get().get(); - } - - @Internal - @Override - public void registerCallback(Consumer callback) { - synchronized (callbackLock) { - factoryCallback = factoryCallback.andThen(callback); - for (LoaderBinding binding : allFory.keySet()) { - binding.visitAllFory(callback); - binding.setBindingCallback(factoryCallback); - } - } - } - - @Override - public R execute(Function action) { - Fory fory = bindingThreadLocal.get().get(); - return action.apply(fory); - } - - @Override - public byte[] serialize(Object obj) { - MemoryBuffer buffer = bufferLocal.get(); - buffer.writerIndex(0); - bindingThreadLocal.get().get().serialize(buffer, obj); - return buffer.getBytes(0, buffer.writerIndex()); - } - - @Override - public byte[] serialize(Object obj, BufferCallback callback) { - MemoryBuffer buffer = bufferLocal.get(); - buffer.writerIndex(0); - bindingThreadLocal.get().get().serialize(buffer, obj, callback); - return buffer.getBytes(0, buffer.writerIndex()); - } - - @Override - public MemoryBuffer serialize(Object obj, long address, int size) { - return bindingThreadLocal.get().get().serialize(obj, address, size); - } - - @Override - public MemoryBuffer serialize(MemoryBuffer buffer, Object obj) { - return bindingThreadLocal.get().get().serialize(buffer, obj); - } - - @Override - public MemoryBuffer serialize(MemoryBuffer buffer, Object obj, BufferCallback callback) { - return bindingThreadLocal.get().get().serialize(buffer, obj, callback); - } - - @Override - public void serialize(OutputStream outputStream, Object obj) { - bindingThreadLocal.get().get().serialize(outputStream, obj); - } - - @Override - public void serialize(OutputStream outputStream, Object obj, BufferCallback callback) { - bindingThreadLocal.get().get().serialize(outputStream, obj, callback); - } - - @Override - public Object deserialize(byte[] bytes) { - return bindingThreadLocal.get().get().deserialize(bytes); - } - - @Override - public T deserialize(byte[] bytes, Class type) { - return bindingThreadLocal.get().get().deserialize(bytes, type); - } - - @Override - public T deserialize(MemoryBuffer buffer, Class type) { - return bindingThreadLocal.get().get().deserialize(buffer, type); - } - - @Override - public T deserialize(ForyInputStream inputStream, Class type) { - return bindingThreadLocal.get().get().deserialize(inputStream, type); - } - - @Override - public T deserialize(ForyReadableChannel channel, Class type) { - return bindingThreadLocal.get().get().deserialize(channel, type); - } - - @Override - public Object deserialize(byte[] bytes, Iterable outOfBandBuffers) { - return bindingThreadLocal.get().get().deserialize(bytes, outOfBandBuffers); - } - - @Override - public Object deserialize(long address, int size) { - return bindingThreadLocal.get().get().deserialize(address, size); - } - - @Override - public Object deserialize(MemoryBuffer buffer) { - return bindingThreadLocal.get().get().deserialize(buffer); - } - - @Override - public Object deserialize(ByteBuffer byteBuffer) { - return bindingThreadLocal.get().get().deserialize(MemoryUtils.wrap(byteBuffer)); - } - - @Override - public Object deserialize(MemoryBuffer buffer, Iterable outOfBandBuffers) { - return bindingThreadLocal.get().get().deserialize(buffer, outOfBandBuffers); - } - - @Override - public Object deserialize(ForyInputStream inputStream) { - return bindingThreadLocal.get().get().deserialize(inputStream); - } - - @Override - public Object deserialize(ForyInputStream inputStream, Iterable outOfBandBuffers) { - return bindingThreadLocal.get().get().deserialize(inputStream, outOfBandBuffers); - } - - @Override - public Object deserialize(ForyReadableChannel channel) { - return bindingThreadLocal.get().get().deserialize(channel); - } - - @Override - public Object deserialize(ForyReadableChannel channel, Iterable outOfBandBuffers) { - return bindingThreadLocal.get().get().deserialize(channel, outOfBandBuffers); - } - - @Override - public T copy(T obj) { - return bindingThreadLocal.get().get().copy(obj); - } - - @Override - public void setClassLoader(ClassLoader classLoader) { - setClassLoader(classLoader, StagingType.STRONG_STAGING); - } - - @Override - public void setClassLoader(ClassLoader classLoader, StagingType stagingType) { - this.classLoader = classLoader; - bindingThreadLocal.get().setClassLoader(classLoader, stagingType); - } - - @Override - public ClassLoader getClassLoader() { - return bindingThreadLocal.get().getClassLoader(); - } - - @Override - public void clearClassLoader(ClassLoader loader) { - bindingThreadLocal.get().clearClassLoader(loader); - sharedRegistry.clearClassLoader(loader); - } -} diff --git a/java/fory-core/src/main/java/org/apache/fory/ThreadSafeFory.java b/java/fory-core/src/main/java/org/apache/fory/ThreadSafeFory.java index 68b37495e1..97c65b8304 100644 --- a/java/fory-core/src/main/java/org/apache/fory/ThreadSafeFory.java +++ b/java/fory-core/src/main/java/org/apache/fory/ThreadSafeFory.java @@ -23,13 +23,12 @@ import java.util.function.Consumer; import java.util.function.Function; import org.apache.fory.annotation.Internal; -import org.apache.fory.resolver.TypeChecker; import org.apache.fory.serializer.SerializerFactory; -import org.apache.fory.util.LoaderBinding; +import org.apache.fory.resolver.TypeChecker; /** * Thread safe serializer interface. {@link Fory} is not thread-safe, the implementation of this - * interface will be thread-safe. And support switch classloader dynamically. + * interface will be thread-safe. */ public interface ThreadSafeFory extends BaseFory { @@ -42,31 +41,6 @@ public interface ThreadSafeFory extends BaseFory { /** Deserialize obj from a {@link ByteBuffer}. */ Object deserialize(ByteBuffer byteBuffer); - /** - * Set classLoader of serializer for current thread only. - * - * @see LoaderBinding#setClassLoader(ClassLoader) - */ - void setClassLoader(ClassLoader classLoader); - - /** - * Set classLoader of serializer for current thread only. - * - *

If staging is true, a cached {@link Fory} instance will be returned if not - * null, and the previous classloader and associated {@link Fory} instance won't be gc unless - * {@link #clearClassLoader} is called explicitly. If false, and the passed classLoader - * is different, a new {@link Fory} instance will be created, previous classLoader and - * associated {@link Fory} instance will be cleared. - * - * @param classLoader {@link ClassLoader} for resolving unregistered class name to class - * @param stagingType Whether cache previous classloader and associated {@link Fory} instance. - * @see LoaderBinding#setClassLoader(ClassLoader, LoaderBinding.StagingType) - */ - void setClassLoader(ClassLoader classLoader, LoaderBinding.StagingType stagingType); - - /** Returns classLoader of serializer for current thread. */ - ClassLoader getClassLoader(); - /** * Set TypeChecker of serializer for current thread only. * @@ -81,17 +55,6 @@ public interface ThreadSafeFory extends BaseFory { */ void setSerializerFactory(SerializerFactory serializerFactory); - /** - * Clean up classloader set by {@link #setClassLoader(ClassLoader, LoaderBinding.StagingType)}, - * - * classLoader - * won't be referenced by {@link Fory} after this call and can be gc if it's not - * referenced by other objects. - * - * @see LoaderBinding#clearClassLoader(ClassLoader) - */ - void clearClassLoader(ClassLoader loader); - @Internal void registerCallback(Consumer callback); } diff --git a/java/fory-core/src/main/java/org/apache/fory/pool/ThreadPoolFory.java b/java/fory-core/src/main/java/org/apache/fory/ThreadSafeForyPool.java similarity index 65% rename from java/fory-core/src/main/java/org/apache/fory/pool/ThreadPoolFory.java rename to java/fory-core/src/main/java/org/apache/fory/ThreadSafeForyPool.java index cbef000550..dce4c38b99 100644 --- a/java/fory-core/src/main/java/org/apache/fory/pool/ThreadPoolFory.java +++ b/java/fory-core/src/main/java/org/apache/fory/ThreadSafeForyPool.java @@ -17,79 +17,86 @@ * under the License. */ -package org.apache.fory.pool; +package org.apache.fory; import java.io.OutputStream; import java.nio.ByteBuffer; -import java.util.concurrent.TimeUnit; +import java.util.Objects; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.Semaphore; import java.util.function.Consumer; import java.util.function.Function; -import javax.annotation.concurrent.ThreadSafe; -import org.apache.fory.AbstractThreadSafeFory; -import org.apache.fory.Fory; -import org.apache.fory.annotation.Internal; +import org.apache.fory.config.ForyBuilder; import org.apache.fory.io.ForyInputStream; import org.apache.fory.io.ForyReadableChannel; -import org.apache.fory.logging.Logger; -import org.apache.fory.logging.LoggerFactory; import org.apache.fory.memory.MemoryBuffer; import org.apache.fory.memory.MemoryUtils; +import org.apache.fory.resolver.SharedRegistry; import org.apache.fory.serializer.BufferCallback; -import org.apache.fory.util.LoaderBinding; -@ThreadSafe -public class ThreadPoolFory extends AbstractThreadSafeFory { - - private static final Logger LOG = LoggerFactory.getLogger(ThreadPoolFory.class); +/** + * A lightweight thread-safe {@link Fory} pool that reuses equivalent {@link Fory} instances built + * from a single configuration and classloader. + */ +public class ThreadSafeForyPool extends AbstractThreadSafeFory { + private static final int DEFAULT_POOL_SIZE = 1024; - private final ForyPooledObjectFactory foryPooledObjectFactory; - private Consumer factoryCallback = f -> {}; + private final Function foryFactory; + private final SharedRegistry sharedRegistry; + private final ConcurrentLinkedQueue pool = new ConcurrentLinkedQueue<>(); private final Object callbackLock = new Object(); + private final int maxPoolSize; + private final Semaphore idleCapacity; + private Consumer factoryCallback = f -> {}; + private volatile boolean used; - public ThreadPoolFory( - Function foryFactory, - int minPoolSize, - int maxPoolSize, - long expireTime, - TimeUnit timeUnit) { - this.foryPooledObjectFactory = - new ForyPooledObjectFactory( - foryFactory, - minPoolSize, - maxPoolSize, - expireTime, - timeUnit, - fory -> factoryCallback.accept(fory)); + public ThreadSafeForyPool(Function foryFactory) { + this(foryFactory, new SharedRegistry(), DEFAULT_POOL_SIZE); } - @Internal - @Override - public void registerCallback(Consumer callback) { - synchronized (callbackLock) { - factoryCallback = factoryCallback.andThen(callback); - for (ClassLoaderForyPooled foryPooled : - foryPooledObjectFactory.classLoaderForyPooledCache.asMap().values()) { - foryPooled.allFory.keySet().forEach(callback); - } + public ThreadSafeForyPool(Function foryFactory, SharedRegistry sharedRegistry) { + this(foryFactory, sharedRegistry, DEFAULT_POOL_SIZE); + } + + public ThreadSafeForyPool( + Function foryFactory, SharedRegistry sharedRegistry, int maxPoolSize) { + this.foryFactory = Objects.requireNonNull(foryFactory); + this.sharedRegistry = Objects.requireNonNull(sharedRegistry); + if (maxPoolSize < 0) { + throw new IllegalArgumentException( + String.format("ThreadSafeForyPool maxPoolSize must be >= 0, got %s", maxPoolSize)); } + this.maxPoolSize = maxPoolSize; + idleCapacity = new Semaphore(maxPoolSize); } @Override public R execute(Function action) { - ClassLoaderForyPooled pooledCache = null; - Fory fory = null; + used = true; + Fory fory = acquire(); try { - pooledCache = foryPooledObjectFactory.getPooledCache(); - fory = pooledCache.getFory(); return action.apply(fory); - } catch (Exception e) { - LOG.error(e.getMessage(), e); - throw new RuntimeException(e); } finally { - if (pooledCache != null) { - pooledCache.returnFory(fory); + release(fory); + } + } + + @Override + public void registerCallback(Consumer callback) { + synchronized (callbackLock) { + if (!used) { + factoryCallback = factoryCallback.andThen(callback); + return; } } + execute( + fory -> { + callback.accept(fory); + return null; + }); + synchronized (callbackLock) { + factoryCallback = factoryCallback.andThen(callback); + } } @Override @@ -135,6 +142,11 @@ public void serialize(OutputStream outputStream, Object obj, BufferCallback call }); } + @Override + public Object deserialize(ByteBuffer byteBuffer) { + return execute(fory -> fory.deserialize(MemoryUtils.wrap(byteBuffer))); + } + @Override public Object deserialize(byte[] bytes) { return execute(fory -> fory.deserialize(bytes)); @@ -175,11 +187,6 @@ public Object deserialize(MemoryBuffer buffer) { return execute(fory -> fory.deserialize(buffer)); } - @Override - public Object deserialize(ByteBuffer byteBuffer) { - return execute(fory -> fory.deserialize(MemoryUtils.wrap(byteBuffer))); - } - @Override public Object deserialize(MemoryBuffer buffer, Iterable outOfBandBuffers) { return execute(fory -> fory.deserialize(buffer, outOfBandBuffers)); @@ -210,23 +217,37 @@ public T copy(T obj) { return execute(fory -> fory.copy(obj)); } - @Override - public void setClassLoader(ClassLoader classLoader) { - setClassLoader(classLoader, LoaderBinding.StagingType.SOFT_STAGING); + public SharedRegistry getSharedRegistry() { + return sharedRegistry; } - @Override - public void setClassLoader(ClassLoader classLoader, LoaderBinding.StagingType stagingType) { - foryPooledObjectFactory.setClassLoader(classLoader, stagingType); + public int getMaxPoolSize() { + return maxPoolSize; } - @Override - public ClassLoader getClassLoader() { - return foryPooledObjectFactory.getClassLoader(); + private Fory acquire() { + Fory fory = pool.poll(); + if (fory != null) { + if (maxPoolSize > 0) { + idleCapacity.release(); + } + return fory; + } + ForyBuilder builder = new ForyBuilder().withSharedRegistry(sharedRegistry); + fory = foryFactory.apply(builder); + Consumer callback = factoryCallback; + if (callback != null) { + callback.accept(fory); + } + return fory; } - @Override - public void clearClassLoader(ClassLoader loader) { - foryPooledObjectFactory.clearClassLoader(loader); + private void release(Fory fory) { + if (fory == null || maxPoolSize == 0) { + return; + } + if (idleCapacity.tryAcquire()) { + pool.offer(fory); + } } } diff --git a/java/fory-core/src/main/java/org/apache/fory/builder/BaseObjectCodecBuilder.java b/java/fory-core/src/main/java/org/apache/fory/builder/BaseObjectCodecBuilder.java index 032f2ccd6a..cd17bca082 100644 --- a/java/fory-core/src/main/java/org/apache/fory/builder/BaseObjectCodecBuilder.java +++ b/java/fory-core/src/main/java/org/apache/fory/builder/BaseObjectCodecBuilder.java @@ -92,6 +92,8 @@ import java.util.function.Function; import java.util.function.Supplier; import org.apache.fory.Fory; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; import org.apache.fory.codegen.Code; import org.apache.fory.codegen.CodeGenerator; import org.apache.fory.codegen.CodegenContext; @@ -154,6 +156,9 @@ @SuppressWarnings("unchecked") public abstract class BaseObjectCodecBuilder extends CodecBuilder { public static final String BUFFER_NAME = "_f_buffer"; + public static final String WRITE_CONTEXT_NAME = "_f_writeContext"; + public static final String READ_CONTEXT_NAME = "_f_readContext"; + public static final String CONSTRUCTOR_TYPE_RESOLVER_NAME = "_f_ctorTypeResolver"; public static final String REF_RESOLVER_NAME = "_f_refResolver"; public static final String TYPE_RESOLVER_NAME = "_f_typeResolver"; public static final String POJO_CLASS_TYPE_NAME = "_f_classType"; @@ -191,6 +196,9 @@ public BaseObjectCodecBuilder(TypeRef beanType, Fory fory, Class parentSer addCommonImports(); ctx.reserveName(REF_RESOLVER_NAME); ctx.reserveName(TYPE_RESOLVER_NAME); + ctx.reserveName(WRITE_CONTEXT_NAME); + ctx.reserveName(READ_CONTEXT_NAME); + ctx.reserveName(CONSTRUCTOR_TYPE_RESOLVER_NAME); // use concrete type to avoid virtual methods call in generated code TypeRef refResolverTypeRef = TypeRef.of(fory.getRefResolver().getClass()); refResolverRef = fieldRef(REF_RESOLVER_NAME, refResolverTypeRef); @@ -291,9 +299,11 @@ public String genCode() { String constructorCode = StringUtils.format( "" - + "super(${fory}, ${cls});\n" - + "this.${fory} = ${fory};\n" - + "${fory}.getTypeResolver().setSerializerIfAbsent(${cls}, this);\n", + + "super(${typeResolver}, ${cls});\n" + + "this.${fory} = ${typeResolver}.getFory();\n" + + "${typeResolver}.setSerializerIfAbsent(${cls}, this);\n", + "typeResolver", + CONSTRUCTOR_TYPE_RESOLVER_NAME, "fory", FORY_NAME, "cls", @@ -302,20 +312,47 @@ public String genCode() { ctx.clearExprState(); String encodeCode = encodeExpr.genCode(ctx).code(); encodeCode = ctx.optimizeMethodCode(encodeCode); + encodeCode = + StringUtils.format( + "${bufferType} ${buffer} = ${writeContext}.getBuffer();\n${code}", + "bufferType", + ctx.type(MemoryBuffer.class), + "buffer", + BUFFER_NAME, + "writeContext", + WRITE_CONTEXT_NAME, + "code", + encodeCode); ctx.clearExprState(); String decodeCode = decodeExpr.genCode(ctx).code(); decodeCode = ctx.optimizeMethodCode(decodeCode); + decodeCode = + StringUtils.format( + "${bufferType} ${buffer} = ${readContext}.getBuffer();\n${code}", + "bufferType", + ctx.type(MemoryBuffer.class), + "buffer", + BUFFER_NAME, + "readContext", + READ_CONTEXT_NAME, + "code", + decodeCode); ctx.overrideMethod( writeMethodName, encodeCode, void.class, - MemoryBuffer.class, - BUFFER_NAME, + WriteContext.class, + WRITE_CONTEXT_NAME, Object.class, ROOT_OBJECT_NAME); - ctx.overrideMethod(readMethodName, decodeCode, Object.class, MemoryBuffer.class, BUFFER_NAME); + ctx.overrideMethod(readMethodName, decodeCode, Object.class, ReadContext.class, READ_CONTEXT_NAME); registerJITNotifyCallback(); - ctx.addConstructor(constructorCode, Fory.class, FORY_NAME, Class.class, POJO_CLASS_TYPE_NAME); + ctx.addConstructor( + constructorCode, + TypeResolver.class, + CONSTRUCTOR_TYPE_RESOLVER_NAME, + Class.class, + POJO_CLASS_TYPE_NAME); return ctx.genCode(); } @@ -373,7 +410,12 @@ protected void registerJITNotifyCallback() { */ protected void addCommonImports() { ctx.addImports( - Fory.class, MemoryBuffer.class, fory.getRefResolver().getClass(), Platform.class); + Fory.class, + MemoryBuffer.class, + WriteContext.class, + ReadContext.class, + fory.getRefResolver().getClass(), + Platform.class); ctx.addImports(TypeInfo.class, TypeInfoHolder.class, ClassResolver.class); ctx.addImport(Generated.class); ctx.addImports(LazyInitBeanSerializer.class, EnumSerializer.class); @@ -543,12 +585,12 @@ private Expression serializeForNotNullObjectForField( TypeRef typeRef = descriptor.getTypeRef(); Class clz = getRawType(typeRef); if (serializer != null) { - return new Invoke(serializer, writeMethodName, buffer, inputObject); + return new Invoke(serializer, writeMethodName, writeContextRef(), inputObject); } if (isMonomorphic(descriptor)) { // Use descriptor to get the appropriate serializer serializer = getSerializerForField(clz); - return new Invoke(serializer, writeMethodName, buffer, inputObject); + return new Invoke(serializer, writeMethodName, writeContextRef(), inputObject); } else { return writeForNotNullNonFinalObject(inputObject, buffer, typeRef); } @@ -702,11 +744,11 @@ protected Expression serializeForNotNullObject( Expression inputObject, Expression buffer, TypeRef typeRef, Expression serializer) { Class clz = getRawType(typeRef); if (serializer != null) { - return new Invoke(serializer, writeMethodName, buffer, inputObject); + return new Invoke(serializer, writeMethodName, writeContextRef(), inputObject); } if (isMonomorphic(clz)) { serializer = getOrCreateSerializer(clz); - return new Invoke(serializer, writeMethodName, buffer, inputObject); + return new Invoke(serializer, writeMethodName, writeContextRef(), inputObject); } else { return writeForNotNullNonFinalObject(inputObject, buffer, typeRef); } @@ -735,7 +777,7 @@ protected Expression writeForNotNullNonFinalObject( invokeInline(classInfo, "getSerializer", getSerializerType(clz)), writeMethodName, PRIMITIVE_VOID_TYPE, - buffer, + writeContextRef(), inputObject)); return invokeGenerated( ctx, ofHashSet(buffer, inputObject), writeClassAndObject, "writeClassAndObject", false); @@ -1051,7 +1093,7 @@ protected Expression serializeForCollection( new If( inlineInvoke(serializer, "supportCodegenHook", PRIMITIVE_BOOLEAN_TYPE), writeCollectionData(buffer, collection, serializer, elementType), - new Invoke(serializer, writeMethodName, buffer, collection)); + new Invoke(serializer, writeMethodName, writeContextRef(), collection)); // Wrap collection and ifExpr in a ListExpression to ensure collection is evaluated before the // If. This is necessary because 'collection' is used in both branches of the If expression. // Without this, the code generator would assign collection to a variable inside the @@ -1399,7 +1441,7 @@ protected Expression serializeForMap( new If( inlineInvoke(serializer, "supportCodegenHook", PRIMITIVE_BOOLEAN_TYPE), jitWriteMap(buffer, map, serializer, typeRef), - new Invoke(serializer, writeMethodName, buffer, map)); + new Invoke(serializer, writeMethodName, writeContextRef(), map)); // Wrap map and ifExpr in a ListExpression to ensure map is evaluated before the If. // This is necessary because 'map' is used in both branches of the If expression. // Without this, the code generator would assign map to a variable inside the then-branch, @@ -2132,10 +2174,11 @@ protected Expression read( Class type = returnType.getRawType(); Expression read; if (refMode == RefMode.NONE) { - read = new Invoke(serializer, readMethodName, returnType, buffer); + read = new Invoke(serializer, readMethodName, returnType, readContextRef()); } else { // janino take inherited generic method return type as return type of erased `T`. - read = new Invoke(serializer, readMethodName, OBJECT_TYPE, buffer, ofEnum(refMode)); + read = + new Invoke(serializer, readMethodName, OBJECT_TYPE, readContextRef(), ofEnum(refMode)); read = cast(inline(read), returnType); } if (ReflectionUtils.isMonomorphic(type) && !TypeUtils.hasExpandableLeafs(type)) { @@ -2697,4 +2740,12 @@ protected Expression beanClassExpr() { // Serializer has a `type` field. return new Reference("super.type", CLASS_TYPE); } + + private Expression writeContextRef() { + return new StaticInvoke(WriteContext.class, "current", TypeRef.of(WriteContext.class)); + } + + private Expression readContextRef() { + return new StaticInvoke(ReadContext.class, "current", TypeRef.of(ReadContext.class)); + } } diff --git a/java/fory-core/src/main/java/org/apache/fory/builder/Generated.java b/java/fory-core/src/main/java/org/apache/fory/builder/Generated.java index c86ed020fb..2bd2b2446e 100644 --- a/java/fory-core/src/main/java/org/apache/fory/builder/Generated.java +++ b/java/fory-core/src/main/java/org/apache/fory/builder/Generated.java @@ -27,6 +27,7 @@ import org.apache.fory.memory.MemoryBuffer; import org.apache.fory.meta.TypeDef; import org.apache.fory.reflect.ReflectionUtils; +import org.apache.fory.resolver.TypeResolver; import org.apache.fory.serializer.AbstractObjectSerializer; import org.apache.fory.serializer.Serializer; import org.apache.fory.util.Preconditions; @@ -40,8 +41,8 @@ public interface Generated { /** Base class for all generated serializers. */ abstract class GeneratedSerializer extends AbstractObjectSerializer implements Generated { - public GeneratedSerializer(Fory fory, Class cls) { - super(fory, cls); + public GeneratedSerializer(TypeResolver typeResolver, Class cls) { + super(typeResolver, cls); } /** @@ -95,8 +96,8 @@ public void onNotifyMissed() { /** Base class for all type consist serializers. */ abstract class GeneratedObjectSerializer extends GeneratedSerializer implements Generated { - public GeneratedObjectSerializer(Fory fory, Class cls) { - super(fory, cls); + public GeneratedObjectSerializer(TypeResolver typeResolver, Class cls) { + super(typeResolver, cls); } } @@ -107,13 +108,13 @@ abstract class GeneratedMetaSharedSerializer extends GeneratedSerializer impleme /** Will be set in generated constructor by {@link MetaSharedCodecBuilder}. */ public Serializer serializer; - public GeneratedMetaSharedSerializer(Fory fory, Class cls) { - super(fory, cls); + public GeneratedMetaSharedSerializer(TypeResolver typeResolver, Class cls) { + super(typeResolver, cls); } @Override - public void write(MemoryBuffer buffer, Object value) { - serializer.write(buffer, value); + public void write(org.apache.fory.context.WriteContext writeContext, Object value) { + serializer.write(writeContext, value); } } @@ -129,13 +130,13 @@ abstract class GeneratedMetaSharedLayerSerializer /** Will be set in generated constructor by MetaSharedLayerCodecBuilder. */ public org.apache.fory.serializer.MetaSharedLayerSerializerBase serializer; - public GeneratedMetaSharedLayerSerializer(Fory fory, Class cls) { - super(fory, cls); + public GeneratedMetaSharedLayerSerializer(TypeResolver typeResolver, Class cls) { + super(typeResolver, cls); } @Override - public void write(MemoryBuffer buffer, Object value) { - serializer.write(buffer, value); + public void write(org.apache.fory.context.WriteContext writeContext, Object value) { + serializer.write(writeContext, value); } @Override diff --git a/java/fory-core/src/main/java/org/apache/fory/builder/MetaSharedCodecBuilder.java b/java/fory-core/src/main/java/org/apache/fory/builder/MetaSharedCodecBuilder.java index 3e63c01761..7bb48a7676 100644 --- a/java/fory-core/src/main/java/org/apache/fory/builder/MetaSharedCodecBuilder.java +++ b/java/fory-core/src/main/java/org/apache/fory/builder/MetaSharedCodecBuilder.java @@ -30,6 +30,7 @@ import java.util.SortedMap; import java.util.concurrent.ConcurrentHashMap; import org.apache.fory.Fory; +import org.apache.fory.context.ReadContext; import org.apache.fory.builder.Generated.GeneratedMetaSharedSerializer; import org.apache.fory.codegen.CodeGenerator; import org.apache.fory.codegen.Expression; @@ -51,6 +52,7 @@ import org.apache.fory.type.Descriptor; import org.apache.fory.type.DescriptorBuilder; import org.apache.fory.type.DescriptorGrouper; +import org.apache.fory.resolver.TypeResolver; import org.apache.fory.util.DefaultValueUtils; import org.apache.fory.util.ExceptionUtils; import org.apache.fory.util.GraalvmSupport; @@ -162,9 +164,11 @@ public String genCode() { String constructorCode = StringUtils.format( "" - + "super(${fory}, ${cls});\n" - + "this.${fory} = ${fory};\n" - + "${serializer} = ${builderClass}.setCodegenSerializer(${fory}, ${cls}, this);\n", + + "super(${typeResolver}, ${cls});\n" + + "this.${fory} = ${typeResolver}.getFory();\n" + + "${serializer} = ${builderClass}.setCodegenSerializer(this.${fory}, ${cls}, this);\n", + "typeResolver", + CONSTRUCTOR_TYPE_RESOLVER_NAME, "fory", FORY_NAME, "cls", @@ -177,9 +181,25 @@ public String genCode() { Expression decodeExpr = buildDecodeExpression(); String decodeCode = decodeExpr.genCode(ctx).code(); decodeCode = ctx.optimizeMethodCode(decodeCode); - ctx.overrideMethod(readMethodName, decodeCode, Object.class, MemoryBuffer.class, BUFFER_NAME); + decodeCode = + StringUtils.format( + "${bufferType} ${buffer} = ${readContext}.getBuffer();\n${code}", + "bufferType", + ctx.type(MemoryBuffer.class), + "buffer", + BUFFER_NAME, + "readContext", + READ_CONTEXT_NAME, + "code", + decodeCode); + ctx.overrideMethod(readMethodName, decodeCode, Object.class, ReadContext.class, READ_CONTEXT_NAME); registerJITNotifyCallback(); - ctx.addConstructor(constructorCode, Fory.class, FORY_NAME, Class.class, POJO_CLASS_TYPE_NAME); + ctx.addConstructor( + constructorCode, + TypeResolver.class, + CONSTRUCTOR_TYPE_RESOLVER_NAME, + Class.class, + POJO_CLASS_TYPE_NAME); return ctx.genCode(); } diff --git a/java/fory-core/src/main/java/org/apache/fory/builder/MetaSharedLayerCodecBuilder.java b/java/fory-core/src/main/java/org/apache/fory/builder/MetaSharedLayerCodecBuilder.java index 6e6ae2077c..6112edd86f 100644 --- a/java/fory-core/src/main/java/org/apache/fory/builder/MetaSharedLayerCodecBuilder.java +++ b/java/fory-core/src/main/java/org/apache/fory/builder/MetaSharedLayerCodecBuilder.java @@ -24,6 +24,7 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.fory.Fory; +import org.apache.fory.context.ReadContext; import org.apache.fory.builder.Generated.GeneratedMetaSharedLayerSerializer; import org.apache.fory.codegen.CodeGenerator; import org.apache.fory.codegen.Expression; @@ -35,6 +36,7 @@ import org.apache.fory.serializer.MetaSharedLayerSerializer; import org.apache.fory.serializer.MetaSharedLayerSerializerBase; import org.apache.fory.serializer.Serializers; +import org.apache.fory.resolver.TypeResolver; import org.apache.fory.type.Descriptor; import org.apache.fory.type.DescriptorGrouper; import org.apache.fory.util.ExceptionUtils; @@ -100,9 +102,11 @@ public String genCode() { String constructorCode = StringUtils.format( "" - + "super(${fory}, ${cls});\n" - + "this.${fory} = ${fory};\n" - + "${serializer} = ${builderClass}.setCodegenSerializer(${fory}, ${cls}, this);\n", + + "super(${typeResolver}, ${cls});\n" + + "this.${fory} = ${typeResolver}.getFory();\n" + + "${serializer} = ${builderClass}.setCodegenSerializer(this.${fory}, ${cls}, this);\n", + "typeResolver", + CONSTRUCTOR_TYPE_RESOLVER_NAME, "fory", FORY_NAME, "cls", @@ -115,9 +119,25 @@ public String genCode() { Expression decodeExpr = buildDecodeExpression(); String decodeCode = decodeExpr.genCode(ctx).code(); decodeCode = ctx.optimizeMethodCode(decodeCode); - ctx.overrideMethod(readMethodName, decodeCode, Object.class, MemoryBuffer.class, BUFFER_NAME); + decodeCode = + StringUtils.format( + "${bufferType} ${buffer} = ${readContext}.getBuffer();\n${code}", + "bufferType", + ctx.type(MemoryBuffer.class), + "buffer", + BUFFER_NAME, + "readContext", + READ_CONTEXT_NAME, + "code", + decodeCode); + ctx.overrideMethod(readMethodName, decodeCode, Object.class, ReadContext.class, READ_CONTEXT_NAME); registerJITNotifyCallback(); - ctx.addConstructor(constructorCode, Fory.class, FORY_NAME, Class.class, POJO_CLASS_TYPE_NAME); + ctx.addConstructor( + constructorCode, + TypeResolver.class, + CONSTRUCTOR_TYPE_RESOLVER_NAME, + Class.class, + POJO_CLASS_TYPE_NAME); return ctx.genCode(); } diff --git a/java/fory-core/src/main/java/org/apache/fory/config/Config.java b/java/fory-core/src/main/java/org/apache/fory/config/Config.java index d7f92a2e29..66b14fb6be 100644 --- a/java/fory-core/src/main/java/org/apache/fory/config/Config.java +++ b/java/fory-core/src/main/java/org/apache/fory/config/Config.java @@ -162,7 +162,7 @@ public boolean serializeEnumByName() { * Fory#registerSerializer(Class, Serializer)}, ex: * *

-   *   fory.registerSerializer(Date.class, new DateSerializer(fory, true));
+   *   fory.registerSerializer(Date.class, new DateSerializer(fory.getConfig(), true));
    * 
* *

Note that enabling ref tracking should happen before serializer codegen of any types which diff --git a/java/fory-core/src/main/java/org/apache/fory/config/ForyBuilder.java b/java/fory-core/src/main/java/org/apache/fory/config/ForyBuilder.java index b554c15fd6..d8be23eaaf 100644 --- a/java/fory-core/src/main/java/org/apache/fory/config/ForyBuilder.java +++ b/java/fory-core/src/main/java/org/apache/fory/config/ForyBuilder.java @@ -22,18 +22,15 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; -import java.util.concurrent.TimeUnit; import java.util.function.Consumer; import org.apache.fory.Fory; -import org.apache.fory.ThreadLocalFory; import org.apache.fory.ThreadSafeFory; +import org.apache.fory.ThreadSafeForyPool; import org.apache.fory.logging.Logger; import org.apache.fory.logging.LoggerFactory; import org.apache.fory.memory.Platform; import org.apache.fory.meta.DeflaterMetaCompressor; import org.apache.fory.meta.MetaCompressor; -import org.apache.fory.pool.FastForyPool; -import org.apache.fory.pool.ThreadPoolFory; import org.apache.fory.reflect.ReflectionUtils; import org.apache.fory.resolver.SharedRegistry; import org.apache.fory.serializer.JavaSerializer; @@ -270,8 +267,9 @@ public ForyBuilder withBufferSizeLimitBytes(int bufferSizeLimitBytes) { * the class meta data, if classloader can be updated, there may be class meta collision if * different classloaders have classes with same name. * - *

If you want to change classloader, please use {@link org.apache.fory.util.LoaderBinding} or - * {@link ThreadSafeFory} to setup mapping between classloaders and fory instances. + *

If you need a different classloader, create a new {@link Fory} or {@link ThreadSafeFory} + * configured with that loader. For simple cases, you can rely on the thread context classloader + * instead of setting this explicitly. */ public ForyBuilder withClassLoader(ClassLoader classLoader) { this.classLoader = classLoader; @@ -595,47 +593,35 @@ private static Fory newFory( public Fory build() { finish(); ClassLoader loader = this.classLoader; - // clear classLoader to avoid `LoaderBinding#foryFactory` lambda capture classLoader by - // capturing `ForyBuilder`, which make `classLoader` not able to be gc. + // Clear the builder reference after build so the configured classloader is not retained longer + // than necessary. this.classLoader = null; return newFory(this, loader, sharedRegistry); } /** Build thread safe fory. */ public ThreadSafeFory buildThreadSafeFory() { - return buildThreadLocalFory(); - } - - public ThreadSafeFory buildFastForyPool() { - return buildVirtualThreadSafeFory(); - } - - public ThreadSafeFory buildFastForyPool(int maxPoolSize) { - return buildVirtualThreadSafeFory(maxPoolSize); + return buildThreadSafeForyPool(2048); } /** * Builds a thread-safe {@link Fory} optimized for JDK 21+ virtual-thread workloads. * - *

This variant uses {@link FastForyPool} with a shared {@link SharedRegistry} and retains at - * most 3000 idle pooled {@link Fory} instances for reuse. Active concurrent operations may still - * create more instances temporarily, but at most 3000 idle instances are kept after the burst. - * - *

The returned serializer uses the current thread context class loader for virtual-thread - * execution. Use {@link #buildVirtualThreadSafeFory(int)} to choose a different idle pool cap. + *

This variant reuses a shared registry and retains idle pooled serializers for reuse. Active + * concurrent operations may still create more instances temporarily, but idle retention is capped + * once the burst subsides. */ public ThreadSafeFory buildVirtualThreadSafeFory() { - // each fory instance use 30+ kb memory. - return buildVirtualThreadSafeFory(2048); + return buildThreadSafeFory(); } /** * Builds a thread-safe {@link Fory} optimized for JDK 21+ virtual-thread workloads. * - *

This variant uses {@link FastForyPool} with a shared {@link SharedRegistry}. The {@code - * maxPoolSize} parameter limits how many idle pooled {@link Fory} instances are retained for - * reuse after operations complete. It does not cap the number of concurrently active {@link Fory} - * instances during a burst. + *

This variant uses {@link ThreadSafeForyPool} with a shared {@link SharedRegistry}. The + * {@code maxPoolSize} parameter limits how many idle pooled {@link Fory} instances are retained + * for reuse after operations complete. It does not cap the number of concurrently active {@link + * Fory} instances during a burst. * * @param maxPoolSize maximum number of idle pooled {@link Fory} instances to retain, must be * non-negative @@ -643,73 +629,37 @@ public ThreadSafeFory buildVirtualThreadSafeFory() { public ThreadSafeFory buildVirtualThreadSafeFory(int maxPoolSize) { forVirtualThread = true; finish(); - ClassLoader loader = this.classLoader; + ClassLoader builderClassLoader = this.classLoader; + ClassLoader resolvedClassLoader = + builderClassLoader != null + ? builderClassLoader + : Thread.currentThread().getContextClassLoader(); + if (resolvedClassLoader == null) { + resolvedClassLoader = Fory.class.getClassLoader(); + } + final ClassLoader finalClassLoader = resolvedClassLoader; this.classLoader = null; SharedRegistry sharedRegistry = new SharedRegistry(); List> actions = new ArrayList<>(this.actions); - return new FastForyPool( + return new ThreadSafeForyPool( builder -> { builder.replayActions(actions); builder.forVirtualThread = true; builder.finish(); - return newFory(builder, loader, sharedRegistry); + return newFory(builder, finalClassLoader, sharedRegistry); }, sharedRegistry, - loader, maxPoolSize); } - /** Build thread safe fory backed by {@link ThreadLocalFory}. */ - public ThreadLocalFory buildThreadLocalFory() { - finish(); - ClassLoader loader = this.classLoader; - // clear classLoader to avoid `LoaderBinding#foryFactory` lambda capture classLoader by - // capturing `ForyBuilder`, which make `classLoader` not able to be gc. - this.classLoader = null; - ThreadLocalFory threadSafeFory = new ThreadLocalFory(classLoader -> newFory(this, classLoader)); - threadSafeFory.setClassLoader(loader); - return threadSafeFory; - } - /** * Build pooled ThreadSafeFory. * - * @param minPoolSize min pool size - * @param maxPoolSize max pool size + * @param maxPoolSize max number of idle pooled serializers retained for reuse * @return ThreadSafeForyPool */ - public ThreadSafeFory buildThreadSafeForyPool(int minPoolSize, int maxPoolSize) { - return buildThreadSafeForyPool(minPoolSize, maxPoolSize, 30L, TimeUnit.SECONDS); + public ThreadSafeFory buildThreadSafeForyPool(int maxPoolSize) { + return buildVirtualThreadSafeFory(maxPoolSize); } - /** - * Build pooled ThreadSafeFory. - * - * @param minPoolSize min pool size - * @param maxPoolSize max pool size - * @param expireTime cache expire time, default 5's - * @param timeUnit TimeUnit, default SECONDS - * @return ThreadSafeForyPool - */ - public ThreadSafeFory buildThreadSafeForyPool( - int minPoolSize, int maxPoolSize, long expireTime, TimeUnit timeUnit) { - if (minPoolSize < 0 || maxPoolSize < 0 || minPoolSize > maxPoolSize) { - throw new IllegalArgumentException( - String.format( - "thread safe fory pool's init pool size error, please check it, min:[%s], max:[%s]", - minPoolSize, maxPoolSize)); - } - finish(); - ClassLoader loader = this.classLoader; - this.classLoader = null; - ThreadSafeFory threadSafeFory = - new ThreadPoolFory( - classLoader -> newFory(this, classLoader), - minPoolSize, - maxPoolSize, - expireTime, - timeUnit); - threadSafeFory.setClassLoader(loader); - return threadSafeFory; - } } diff --git a/java/fory-core/src/main/java/org/apache/fory/context/CopyContext.java b/java/fory-core/src/main/java/org/apache/fory/context/CopyContext.java new file mode 100644 index 0000000000..bb7d82f43d --- /dev/null +++ b/java/fory-core/src/main/java/org/apache/fory/context/CopyContext.java @@ -0,0 +1,194 @@ +/* + * 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.context; + +import java.util.Arrays; +import java.util.function.Supplier; +import org.apache.fory.resolver.ClassResolver; +import org.apache.fory.resolver.TypeInfo; +import org.apache.fory.resolver.TypeResolver; +import org.apache.fory.serializer.Serializer; +import org.apache.fory.type.Types; +import org.apache.fory.collection.IdentityMap; + +@SuppressWarnings("unchecked") +public final class CopyContext { + private static final ThreadLocal CURRENT = new ThreadLocal<>(); + + private final TypeResolver typeResolver; + private final boolean copyRefTracking; + private final IdentityMap originToCopyMap; + private int depth; + + public CopyContext(TypeResolver typeResolver, boolean copyRefTracking) { + this.typeResolver = typeResolver; + this.copyRefTracking = copyRefTracking; + originToCopyMap = new IdentityMap<>(2); + } + + public static CopyContext current() { + CopyContext context = CURRENT.get(); + if (context == null) { + throw new IllegalStateException("CopyContext is only available during copy operations"); + } + return context; + } + + public T run(Supplier action) { + CopyContext previous = CURRENT.get(); + CURRENT.set(this); + try { + return action.get(); + } finally { + if (previous == null) { + CURRENT.remove(); + } else { + CURRENT.set(previous); + } + } + } + + public void reset() { + if (copyRefTracking) { + originToCopyMap.clear(); + } + depth = 0; + } + + public int getDepth() { + return depth; + } + + public TypeResolver getTypeResolver() { + return typeResolver; + } + + public void incDepth(int diff) { + depth += diff; + } + + public boolean copyTrackingRef() { + return copyRefTracking; + } + + public void reference(T origin, T copied) { + if (copyRefTracking && origin != null) { + originToCopyMap.put(origin, copied); + } + } + + public T getCopyObject(T origin) { + return (T) originToCopyMap.get(origin); + } + + public T copyObject(T obj) { + if (obj == null) { + return null; + } + TypeInfo typeInfo = typeResolver.getTypeInfo(obj.getClass(), true); + int typeId = typeInfo.getTypeId(); + switch (typeId) { + case Types.BOOL: + case Types.INT8: + case ClassResolver.CHAR_ID: + case Types.INT16: + case Types.INT32: + case Types.FLOAT32: + case Types.INT64: + case Types.FLOAT64: + return obj; + case Types.STRING: + if (typeInfo.getCls() == String.class) { + return obj; + } + return copyObject(obj, typeInfo.getSerializer()); + case ClassResolver.PRIMITIVE_BOOLEAN_ARRAY_ID: + return (T) Arrays.copyOf((boolean[]) obj, ((boolean[]) obj).length); + case ClassResolver.PRIMITIVE_BYTE_ARRAY_ID: + return (T) Arrays.copyOf((byte[]) obj, ((byte[]) obj).length); + case ClassResolver.PRIMITIVE_CHAR_ARRAY_ID: + return (T) Arrays.copyOf((char[]) obj, ((char[]) obj).length); + case ClassResolver.PRIMITIVE_SHORT_ARRAY_ID: + return (T) Arrays.copyOf((short[]) obj, ((short[]) obj).length); + case ClassResolver.PRIMITIVE_INT_ARRAY_ID: + return (T) Arrays.copyOf((int[]) obj, ((int[]) obj).length); + case ClassResolver.PRIMITIVE_FLOAT_ARRAY_ID: + return (T) Arrays.copyOf((float[]) obj, ((float[]) obj).length); + case ClassResolver.PRIMITIVE_LONG_ARRAY_ID: + return (T) Arrays.copyOf((long[]) obj, ((long[]) obj).length); + case ClassResolver.PRIMITIVE_DOUBLE_ARRAY_ID: + return (T) Arrays.copyOf((double[]) obj, ((double[]) obj).length); + case ClassResolver.STRING_ARRAY_ID: + return (T) Arrays.copyOf((String[]) obj, ((String[]) obj).length); + default: + return copyObject(obj, typeInfo.getSerializer()); + } + } + + public T copyObject(T obj, int classId) { + if (obj == null) { + return null; + } + switch (classId) { + case ClassResolver.PRIMITIVE_BOOL_ID: + case ClassResolver.PRIMITIVE_INT8_ID: + case ClassResolver.PRIMITIVE_CHAR_ID: + case ClassResolver.PRIMITIVE_INT16_ID: + case ClassResolver.PRIMITIVE_INT32_ID: + case ClassResolver.PRIMITIVE_FLOAT32_ID: + case ClassResolver.PRIMITIVE_INT64_ID: + case ClassResolver.PRIMITIVE_FLOAT64_ID: + case Types.BOOL: + case Types.INT8: + case ClassResolver.CHAR_ID: + case Types.INT16: + case Types.INT32: + case Types.FLOAT32: + case Types.INT64: + case Types.FLOAT64: + return obj; + case Types.STRING: + if (obj.getClass() == String.class) { + return obj; + } + return copyObject(obj, typeResolver.getTypeInfo(obj.getClass(), true).getSerializer()); + default: + return copyObject(obj, typeResolver.getTypeInfo(obj.getClass(), true).getSerializer()); + } + } + + public T copyObject(T obj, Serializer serializer) { + depth++; + try { + if (serializer.needToCopyRef()) { + T existing = getCopyObject(obj); + if (existing != null) { + return existing; + } + T copied = serializer.copy(obj); + originToCopyMap.put(obj, copied); + return copied; + } + return serializer.copy(obj); + } finally { + depth--; + } + } +} diff --git a/java/fory-core/src/main/java/org/apache/fory/context/ReadContext.java b/java/fory-core/src/main/java/org/apache/fory/context/ReadContext.java new file mode 100644 index 0000000000..b855667b65 --- /dev/null +++ b/java/fory-core/src/main/java/org/apache/fory/context/ReadContext.java @@ -0,0 +1,396 @@ +/* + * 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.context; + +import java.util.Iterator; +import java.util.function.Supplier; +import org.apache.fory.Fory; +import org.apache.fory.config.Config; +import org.apache.fory.config.LongEncoding; +import org.apache.fory.exception.InsecureException; +import org.apache.fory.memory.MemoryBuffer; +import org.apache.fory.resolver.ClassResolver; +import org.apache.fory.resolver.MetaStringResolver; +import org.apache.fory.resolver.RefResolver; +import org.apache.fory.resolver.SerializationContext; +import org.apache.fory.resolver.TypeInfo; +import org.apache.fory.resolver.TypeInfoHolder; +import org.apache.fory.resolver.TypeResolver; +import org.apache.fory.serializer.PrimitiveSerializers.LongSerializer; +import org.apache.fory.serializer.Serializer; +import org.apache.fory.serializer.StringSerializer; +import org.apache.fory.type.Generics; +import org.apache.fory.type.Types; +import org.apache.fory.util.Preconditions; + +@SuppressWarnings({"rawtypes", "unchecked"}) +public final class ReadContext { + private static final ThreadLocal CURRENT = new ThreadLocal<>(); + private final Config config; + private final Generics generics; + private final TypeResolver typeResolver; + private final RefResolver refResolver; + private final MetaStringResolver metaStringResolver; + private final SerializationContext serializationContext; + private final StringSerializer stringSerializer; + private final boolean crossLanguage; + private final boolean compressInt; + private final LongEncoding longEncoding; + private final int maxDepth; + private MemoryBuffer buffer; + private Iterator outOfBandBuffers; + private boolean peerOutOfBandEnabled; + private int depth; + private boolean active; + + public ReadContext( + Config config, + Generics generics, + TypeResolver typeResolver, + RefResolver refResolver, + MetaStringResolver metaStringResolver, + SerializationContext serializationContext, + StringSerializer stringSerializer) { + this.config = config; + this.generics = generics; + this.typeResolver = typeResolver; + this.refResolver = refResolver; + this.metaStringResolver = metaStringResolver; + this.serializationContext = serializationContext; + this.stringSerializer = stringSerializer; + crossLanguage = config.isXlang(); + compressInt = config.compressInt(); + longEncoding = config.longEncoding(); + maxDepth = config.maxDepth(); + } + + public void prepare( + MemoryBuffer buffer, Iterable outOfBandBuffers, boolean peerOutOfBandEnabled) { + this.buffer = buffer; + this.peerOutOfBandEnabled = peerOutOfBandEnabled; + this.outOfBandBuffers = outOfBandBuffers == null ? null : outOfBandBuffers.iterator(); + active = true; + } + + public T run( + MemoryBuffer buffer, + Iterable outOfBandBuffers, + boolean peerOutOfBandEnabled, + Supplier action) { + prepare(buffer, outOfBandBuffers, peerOutOfBandEnabled); + ReadContext previous = enter(this); + try { + return action.get(); + } finally { + restore(previous); + reset(); + } + } + + public static ReadContext current() { + ReadContext context = CURRENT.get(); + if (context == null) { + throw new IllegalStateException("ReadContext is only available during deserialization"); + } + return context; + } + + public static ReadContext enter(ReadContext context) { + ReadContext previous = CURRENT.get(); + CURRENT.set(context); + return previous; + } + + public static void restore(ReadContext previous) { + if (previous == null) { + CURRENT.remove(); + } else { + CURRENT.set(previous); + } + } + + public boolean isActive() { + return active; + } + + public MemoryBuffer getBuffer() { + return buffer; + } + + public void reset() { + refResolver.resetRead(); + typeResolver.resetRead(); + metaStringResolver.resetRead(); + serializationContext.resetRead(); + buffer = null; + outOfBandBuffers = null; + peerOutOfBandEnabled = false; + depth = 0; + active = false; + } + + public Config getConfig() { + return config; + } + + public Generics getGenerics() { + return generics; + } + + public TypeResolver getTypeResolver() { + return typeResolver; + } + + public RefResolver getRefResolver() { + return refResolver; + } + + public StringSerializer getStringSerializer() { + return stringSerializer; + } + + public boolean isPeerOutOfBandEnabled() { + return peerOutOfBandEnabled; + } + + public int getDepth() { + return depth; + } + + public void setDepth(int depth) { + this.depth = depth; + } + + public void incDepth(int diff) { + depth += diff; + } + + public void decDepth() { + depth -= 1; + } + + public void incReadDepth() { + if ((depth += 1) > maxDepth) { + throw new InsecureException( + String.format( + "Read depth exceed max depth %s, the deserialization data may be malicious. If " + + "it's not malicious, please increase max read depth by " + + "ForyBuilder#withMaxDepth(largerDepth)", + maxDepth)); + } + } + + public MemoryBuffer readBufferObject(MemoryBuffer buffer) { + boolean inBand = buffer.readBoolean(); + if (inBand) { + int size; + if (!crossLanguage) { + size = buffer.readAlignedVarUint32(); + } else { + size = buffer.readVarUint32(); + } + if (buffer.readerIndex() + size > buffer.size() && buffer.getStreamReader() != null) { + buffer.getStreamReader().fillBuffer(buffer.readerIndex() + size - buffer.size()); + } + MemoryBuffer slice = buffer.slice(buffer.readerIndex(), size); + buffer.readerIndex(buffer.readerIndex() + size); + return slice; + } + Preconditions.checkArgument(outOfBandBuffers.hasNext()); + return outOfBandBuffers.next(); + } + + public String readString(MemoryBuffer buffer) { + return stringSerializer.readString(buffer); + } + + public String readStringRef(MemoryBuffer buffer) { + if (stringSerializer.needToWriteRef()) { + int nextReadRefId = refResolver.tryPreserveRefId(buffer); + if (nextReadRefId >= Fory.NOT_NULL_VALUE_FLAG) { + String obj = stringSerializer.read(this); + refResolver.setReadObject(nextReadRefId, obj); + return obj; + } + return (String) refResolver.getReadObject(); + } + byte headFlag = buffer.readByte(); + if (headFlag == Fory.NULL_FLAG) { + return null; + } + return stringSerializer.read(this); + } + + public long readInt64(MemoryBuffer buffer) { + return LongSerializer.readInt64(buffer, longEncoding); + } + + /** Deserialize nullable referencable object from the current buffer. */ + public Object readRef() { + MemoryBuffer buffer = this.buffer; + int nextReadRefId = refResolver.tryPreserveRefId(buffer); + if (nextReadRefId >= Fory.NOT_NULL_VALUE_FLAG) { + TypeInfo typeInfo = typeResolver.readTypeInfo(buffer); + Object o = readNonRef(typeInfo); + refResolver.setReadObject(nextReadRefId, o); + return o; + } + return refResolver.getReadObject(); + } + + public Object readRef(TypeInfo typeInfo) { + int nextReadRefId = refResolver.tryPreserveRefId(buffer); + if (nextReadRefId >= Fory.NOT_NULL_VALUE_FLAG) { + Object o = readNonRef(typeInfo); + refResolver.setReadObject(nextReadRefId, o); + return o; + } + return refResolver.getReadObject(); + } + + public Object readRef(TypeInfoHolder classInfoHolder) { + int nextReadRefId = refResolver.tryPreserveRefId(buffer); + if (nextReadRefId >= Fory.NOT_NULL_VALUE_FLAG) { + TypeInfo typeInfo = typeResolver.readTypeInfo(buffer, classInfoHolder); + Object o = readNonRef(typeInfo); + refResolver.setReadObject(nextReadRefId, o); + return o; + } + return refResolver.getReadObject(); + } + + public T readRef(Serializer serializer) { + if (serializer.needToWriteRef()) { + int nextReadRefId = refResolver.tryPreserveRefId(buffer); + if (nextReadRefId >= Fory.NOT_NULL_VALUE_FLAG) { + Object o = readNonRef(serializer); + refResolver.setReadObject(nextReadRefId, o); + return (T) o; + } + return (T) refResolver.getReadObject(); + } + byte headFlag = buffer.readByte(); + if (headFlag == Fory.NULL_FLAG) { + return null; + } + return (T) readNonRef(serializer); + } + + /** Deserialize not-null and non-reference object from the current buffer. */ + public Object readNonRef() { + TypeInfo typeInfo = typeResolver.readTypeInfo(buffer); + return readNonRef(typeInfo); + } + + public Object readNonRef(TypeInfoHolder classInfoHolder) { + TypeInfo typeInfo = typeResolver.readTypeInfo(buffer, classInfoHolder); + return readNonRef(typeInfo); + } + + public Object readNonRef(TypeInfo typeInfo) { + return readDataInternal(typeInfo); + } + + public Object readNonRef(Serializer serializer) { + incReadDepth(); + Object o = serializer.read(this); + depth--; + return o; + } + + /** Read object class and data without tracking ref. */ + public Object readNullable() { + byte headFlag = buffer.readByte(); + if (headFlag == Fory.NULL_FLAG) { + return null; + } + return readNonRef(); + } + + public Object readNullable(Serializer serializer) { + byte headFlag = buffer.readByte(); + if (headFlag == Fory.NULL_FLAG) { + return null; + } + return serializer.read(this); + } + + public Object readNullable(TypeInfoHolder classInfoHolder) { + byte headFlag = buffer.readByte(); + if (headFlag == Fory.NULL_FLAG) { + return null; + } + return readNonRef(classInfoHolder); + } + + /** Class should be read already. */ + public Object readData(TypeInfo typeInfo) { + incReadDepth(); + Serializer serializer = typeInfo.getSerializer(); + Object read = serializer.read(this); + depth--; + return read; + } + + private Object readDataInternal(TypeInfo typeInfo) { + MemoryBuffer buffer = this.buffer; + int typeId = typeInfo.getTypeId(); + switch (typeId) { + case Types.BOOL: + return buffer.readBoolean(); + case Types.INT8: + return buffer.readByte(); + case ClassResolver.CHAR_ID: + return buffer.readChar(); + case Types.INT16: + return buffer.readInt16(); + case Types.INT32: + if (compressInt) { + return buffer.readVarInt32(); + } + return buffer.readInt32(); + case Types.VARINT32: + return buffer.readVarInt32(); + case Types.FLOAT32: + return buffer.readFloat32(); + case Types.INT64: + return LongSerializer.readInt64(buffer, longEncoding); + case Types.VARINT64: + return buffer.readVarInt64(); + case Types.TAGGED_INT64: + return buffer.readTaggedInt64(); + case Types.FLOAT64: + return buffer.readFloat64(); + case Types.STRING: + if (typeInfo.getCls() == String.class) { + return stringSerializer.readString(buffer); + } + incReadDepth(); + Object stringLike = typeInfo.getSerializer().read(this); + depth--; + return stringLike; + default: + incReadDepth(); + Object read = typeInfo.getSerializer().read(this); + depth--; + return read; + } + } +} diff --git a/java/fory-core/src/main/java/org/apache/fory/context/WriteContext.java b/java/fory-core/src/main/java/org/apache/fory/context/WriteContext.java new file mode 100644 index 0000000000..0a220342f3 --- /dev/null +++ b/java/fory-core/src/main/java/org/apache/fory/context/WriteContext.java @@ -0,0 +1,463 @@ +/* + * 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.context; + +import java.util.function.Supplier; +import org.apache.fory.Fory; +import org.apache.fory.config.Config; +import org.apache.fory.config.LongEncoding; +import org.apache.fory.memory.MemoryBuffer; +import org.apache.fory.resolver.ClassResolver; +import org.apache.fory.resolver.MetaStringResolver; +import org.apache.fory.resolver.RefResolver; +import org.apache.fory.resolver.SerializationContext; +import org.apache.fory.resolver.TypeInfo; +import org.apache.fory.resolver.TypeInfoHolder; +import org.apache.fory.resolver.TypeResolver; +import org.apache.fory.serializer.ArraySerializers; +import org.apache.fory.serializer.BufferCallback; +import org.apache.fory.serializer.BufferObject; +import org.apache.fory.serializer.PrimitiveSerializers.LongSerializer; +import org.apache.fory.serializer.Serializer; +import org.apache.fory.serializer.StringSerializer; +import org.apache.fory.serializer.UnknownClass.UnknownStruct; +import org.apache.fory.type.Generics; +import org.apache.fory.type.Types; +import org.apache.fory.util.Preconditions; + +@SuppressWarnings({"rawtypes", "unchecked"}) +public final class WriteContext { + private static final ThreadLocal CURRENT = new ThreadLocal<>(); + private final Config config; + private final Generics generics; + private final TypeResolver typeResolver; + private final RefResolver refResolver; + private final MetaStringResolver metaStringResolver; + private final SerializationContext serializationContext; + private final StringSerializer stringSerializer; + private final boolean crossLanguage; + private final boolean compressInt; + private final LongEncoding longEncoding; + private final boolean forVirtualThread; + private MemoryBuffer defaultBuffer; + private MemoryBuffer buffer; + private BufferCallback bufferCallback; + private int depth; + private boolean active; + + public WriteContext( + Config config, + Generics generics, + TypeResolver typeResolver, + RefResolver refResolver, + MetaStringResolver metaStringResolver, + SerializationContext serializationContext, + StringSerializer stringSerializer) { + this.config = config; + this.generics = generics; + this.typeResolver = typeResolver; + this.refResolver = refResolver; + this.metaStringResolver = metaStringResolver; + this.serializationContext = serializationContext; + this.stringSerializer = stringSerializer; + crossLanguage = config.isXlang(); + compressInt = config.compressInt(); + longEncoding = config.longEncoding(); + forVirtualThread = config.forVirtualThread(); + } + + public void prepare(MemoryBuffer buffer, BufferCallback callback) { + this.buffer = buffer; + bufferCallback = callback; + active = true; + } + + public T run(MemoryBuffer buffer, BufferCallback callback, Supplier action) { + prepare(buffer, callback); + WriteContext previous = enter(this); + try { + return action.get(); + } finally { + restore(previous); + reset(); + } + } + + public static WriteContext current() { + WriteContext context = CURRENT.get(); + if (context == null) { + throw new IllegalStateException("WriteContext is only available during serialization"); + } + return context; + } + + public static WriteContext enter(WriteContext context) { + WriteContext previous = CURRENT.get(); + CURRENT.set(context); + return previous; + } + + public static void restore(WriteContext previous) { + if (previous == null) { + CURRENT.remove(); + } else { + CURRENT.set(previous); + } + } + + public boolean isActive() { + return active; + } + + public MemoryBuffer getBuffer() { + if (active) { + return buffer; + } + MemoryBuffer buf = defaultBuffer; + if (buf == null) { + buf = defaultBuffer = MemoryBuffer.newHeapBuffer(64); + } + return buf; + } + + public void resetBuffer() { + MemoryBuffer buf = defaultBuffer; + if (buf != null && buf.size() > config.bufferSizeLimitBytes()) { + defaultBuffer = MemoryBuffer.newHeapBuffer(config.bufferSizeLimitBytes()); + } + } + + public void reset() { + refResolver.resetWrite(); + typeResolver.resetWrite(); + metaStringResolver.resetWrite(); + serializationContext.resetWrite(); + buffer = null; + bufferCallback = null; + depth = 0; + active = false; + if (forVirtualThread) { + stringSerializer.clearBuffer(config.bufferSizeLimitBytes()); + } + } + + public Config getConfig() { + return config; + } + + public Generics getGenerics() { + return generics; + } + + public TypeResolver getTypeResolver() { + return typeResolver; + } + + public RefResolver getRefResolver() { + return refResolver; + } + + public StringSerializer getStringSerializer() { + return stringSerializer; + } + + public boolean isCrossLanguage() { + return crossLanguage; + } + + public boolean compressInt() { + return compressInt; + } + + public LongEncoding longEncoding() { + return longEncoding; + } + + public int getDepth() { + return depth; + } + + public void setDepth(int depth) { + this.depth = depth; + } + + public void incDepth(int diff) { + depth += diff; + } + + public void incDepth() { + depth += 1; + } + + public void decDepth() { + depth -= 1; + } + + public BufferCallback getBufferCallback() { + return bufferCallback; + } + + /** Serialize a nullable referencable object to the current buffer. */ + public void writeRef(Object obj) { + MemoryBuffer buffer = this.buffer; + if (!refResolver.writeRefOrNull(buffer, obj)) { + TypeResolver resolver = typeResolver; + TypeInfo typeInfo = resolver.getTypeInfo(obj.getClass()); + if (crossLanguage && typeInfo.getCls() == UnknownStruct.class) { + depth++; + typeInfo.getSerializer().write(this, obj); + depth--; + return; + } + resolver.writeTypeInfo(buffer, typeInfo); + writeData(typeInfo, obj); + } + } + + public void writeRef(Object obj, TypeInfoHolder classInfoHolder) { + MemoryBuffer buffer = this.buffer; + if (!refResolver.writeRefOrNull(buffer, obj)) { + TypeResolver resolver = typeResolver; + TypeInfo typeInfo = resolver.getTypeInfo(obj.getClass(), classInfoHolder); + if (crossLanguage && typeInfo.getCls() == UnknownStruct.class) { + depth++; + typeInfo.getSerializer().write(this, obj); + depth--; + return; + } + resolver.writeTypeInfo(buffer, typeInfo); + writeData(typeInfo, obj); + } + } + + public void writeRef(Object obj, TypeInfo typeInfo) { + MemoryBuffer buffer = this.buffer; + if (crossLanguage && typeInfo.getCls() == UnknownStruct.class) { + if (!refResolver.writeRefOrNull(buffer, obj)) { + depth++; + typeInfo.getSerializer().write(this, obj); + depth--; + } + return; + } + TypeResolver resolver = typeResolver; + Serializer serializer = typeInfo.getSerializer(); + if (serializer.needToWriteRef()) { + if (!refResolver.writeRefOrNull(buffer, obj)) { + resolver.writeTypeInfo(buffer, typeInfo); + depth++; + serializer.write(this, obj); + depth--; + } + } else if (obj == null) { + buffer.writeByte(Fory.NULL_FLAG); + } else { + buffer.writeByte(Fory.NOT_NULL_VALUE_FLAG); + resolver.writeTypeInfo(buffer, typeInfo); + depth++; + serializer.write(this, obj); + depth--; + } + } + + public void writeRef(T obj, Serializer serializer) { + MemoryBuffer buffer = this.buffer; + if (serializer.needToWriteRef()) { + if (!refResolver.writeRefOrNull(buffer, obj)) { + depth++; + serializer.write(this, obj); + depth--; + } + } else if (obj == null) { + buffer.writeByte(Fory.NULL_FLAG); + } else { + buffer.writeByte(Fory.NOT_NULL_VALUE_FLAG); + depth++; + serializer.write(this, obj); + depth--; + } + } + + /** + * Serialize a not-null and non-reference object to the current buffer. + * + *

If reference is enabled, this method should be called only when the object is first seen in + * the object graph. + */ + public void writeNonRef(Object obj) { + MemoryBuffer buffer = this.buffer; + TypeResolver resolver = typeResolver; + TypeInfo typeInfo = resolver.getTypeInfo(obj.getClass()); + if (crossLanguage && typeInfo.getCls() == UnknownStruct.class) { + depth++; + typeInfo.getSerializer().write(this, obj); + depth--; + return; + } + resolver.writeTypeInfo(buffer, typeInfo); + writeData(typeInfo, obj); + } + + public void writeNonRef(Object obj, Serializer serializer) { + depth++; + serializer.write(this, obj); + depth--; + } + + public void writeNonRef(Object obj, TypeInfoHolder holder) { + MemoryBuffer buffer = this.buffer; + TypeResolver resolver = typeResolver; + TypeInfo typeInfo = resolver.getTypeInfo(obj.getClass(), holder); + if (crossLanguage && typeInfo.getCls() == UnknownStruct.class) { + depth++; + typeInfo.getSerializer().write(this, obj); + depth--; + return; + } + resolver.writeTypeInfo(buffer, typeInfo); + writeData(typeInfo, obj); + } + + public void writeNonRef(Object obj, TypeInfo typeInfo) { + MemoryBuffer buffer = this.buffer; + if (crossLanguage && typeInfo.getCls() == UnknownStruct.class) { + depth++; + typeInfo.getSerializer().write(this, obj); + depth--; + return; + } + typeResolver.writeTypeInfo(buffer, typeInfo); + writeData(typeInfo, obj); + } + + /** Class/type info should be written already. */ + public void writeData(TypeInfo typeInfo, Object obj) { + MemoryBuffer buffer = this.buffer; + int typeId = typeInfo.getTypeId(); + switch (typeId) { + case Types.BOOL: + buffer.writeBoolean((Boolean) obj); + break; + case Types.INT8: + buffer.writeByte((Byte) obj); + break; + case Types.INT16: + buffer.writeInt16((Short) obj); + break; + case ClassResolver.CHAR_ID: + buffer.writeChar((Character) obj); + break; + case Types.INT32: + case Types.VARINT32: + if (compressInt) { + buffer.writeVarInt32((Integer) obj); + } else { + buffer.writeInt32((Integer) obj); + } + break; + case Types.INT64: + LongSerializer.writeInt64(buffer, (Long) obj, longEncoding); + break; + case Types.VARINT64: + buffer.writeVarInt64((Long) obj); + break; + case Types.TAGGED_INT64: + buffer.writeTaggedInt64((Long) obj); + break; + case Types.FLOAT32: + buffer.writeFloat32((Float) obj); + break; + case Types.FLOAT64: + buffer.writeFloat64((Double) obj); + break; + case Types.STRING: + if (typeInfo.getCls() == String.class) { + stringSerializer.writeString(buffer, (String) obj); + break; + } + depth++; + typeInfo.getSerializer().write(this, obj); + depth--; + break; + default: + depth++; + typeInfo.getSerializer().write(this, obj); + depth--; + } + } + + public void writeBufferObject(BufferObject bufferObject) { + MemoryBuffer buffer = this.buffer; + if (bufferCallback == null || bufferCallback.apply(bufferObject)) { + buffer.writeBoolean(true); + int totalBytes = bufferObject.totalBytes(); + if (!crossLanguage) { + buffer.writeVarUint32Aligned(totalBytes); + } else { + buffer.writeVarUint32(totalBytes); + } + int writerIndex = buffer.writerIndex(); + buffer.ensure(writerIndex + bufferObject.totalBytes()); + bufferObject.writeTo(buffer); + int size = buffer.writerIndex() - writerIndex; + Preconditions.checkArgument(size == totalBytes); + } else { + buffer.writeBoolean(false); + } + } + + public void writeBufferObject(ArraySerializers.PrimitiveArrayBufferObject bufferObject) { + MemoryBuffer buffer = this.buffer; + if (bufferCallback == null || bufferCallback.apply(bufferObject)) { + buffer.writeBoolean(true); + int totalBytes = bufferObject.totalBytes(); + if (!crossLanguage) { + buffer.writeVarUint32Aligned(totalBytes); + } else { + buffer.writeVarUint32(totalBytes); + } + bufferObject.writeTo(buffer); + } else { + buffer.writeBoolean(false); + } + } + + public void writeString(String str) { + stringSerializer.writeString(buffer, str); + } + + public void writeStringRef(String str) { + MemoryBuffer buffer = this.buffer; + if (stringSerializer.needToWriteRef()) { + if (!refResolver.writeRefOrNull(buffer, str)) { + stringSerializer.writeString(buffer, str); + } + } else if (str == null) { + buffer.writeByte(Fory.NULL_FLAG); + } else { + buffer.writeByte(Fory.NOT_NULL_VALUE_FLAG); + stringSerializer.write(this, str); + } + } + + public void writeInt64(long value) { + LongSerializer.writeInt64(buffer, value, longEncoding); + } +} diff --git a/java/fory-core/src/main/java/org/apache/fory/pool/ClassLoaderForyPooled.java b/java/fory-core/src/main/java/org/apache/fory/pool/ClassLoaderForyPooled.java deleted file mode 100644 index 8c4b6a430a..0000000000 --- a/java/fory-core/src/main/java/org/apache/fory/pool/ClassLoaderForyPooled.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * 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.pool; - -import java.util.Objects; -import java.util.WeakHashMap; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; -import java.util.function.Consumer; -import java.util.function.Function; -import org.apache.fory.Fory; -import org.apache.fory.logging.Logger; -import org.apache.fory.logging.LoggerFactory; - -/** A thread-safe object pool of {@link Fory}. */ -public class ClassLoaderForyPooled { - - private static final Logger LOG = LoggerFactory.getLogger(ClassLoaderForyPooled.class); - - private final Function foryFactory; - private Consumer factoryCallback = f -> {}; - - private final ClassLoader classLoader; - - /** idle Fory cache change. by : 1. init() 2. getFory() 3.returnFory() */ - private final BlockingQueue idleCacheQueue; - - final WeakHashMap allFory = new WeakHashMap<>(); - - /** - * The number of active Fory objects in the cache.Make sure it does not exceed the maximum number - * of object pools. - */ - private final AtomicInteger activeCacheNumber = new AtomicInteger(0); - - /** - * Dynamic capacity expansion and contraction The user sets the maximum number of object pools. - * Math.max(maxPoolSize, CPU * 2) - */ - private final int maxPoolSize; - - private final Lock lock = new ReentrantLock(); - - public ClassLoaderForyPooled( - ClassLoader classLoader, - Function foryFactory, - int minPoolSize, - int maxPoolSize) { - Objects.requireNonNull(foryFactory); - this.maxPoolSize = maxPoolSize; - this.foryFactory = foryFactory; - this.classLoader = classLoader; - idleCacheQueue = new LinkedBlockingQueue<>(maxPoolSize); - while (idleCacheQueue.size() < minPoolSize) { - addFory(true); - } - } - - public Fory getFory() { - if (activeCacheNumber.get() < maxPoolSize) { - Fory fory = idleCacheQueue.poll(); - if (fory != null) { - return fory; - } else { - // new Fory return directly, no need to add to queue, it will be added by returnFory() - fory = addFory(false); - if (fory != null) { - return fory; - } - } - } - try { - return idleCacheQueue.take(); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - } - - public void returnFory(Fory fory) { - Objects.requireNonNull(fory); - idleCacheQueue.offer(fory); - } - - private Fory addFory(boolean addQueue) { - // only activeCacheNumber increment success, can lock and create new Fory, otherwise return - // null, and block in getFory(), wait for other thread to release idleCacheQueue. - int after = activeCacheNumber.incrementAndGet(); - if (after > maxPoolSize) { - activeCacheNumber.decrementAndGet(); - return null; - } - try { - lock.lock(); - Fory fory = foryFactory.apply(classLoader); - factoryCallback.accept(fory); - allFory.put(fory, null); - if (addQueue) { - idleCacheQueue.add(fory); - } - return fory; - } finally { - lock.unlock(); - } - } - - void setFactoryCallback(Consumer factoryCallback) { - try { - lock.lock(); - this.factoryCallback = this.factoryCallback.andThen(factoryCallback); - allFory.keySet().forEach(factoryCallback); - } finally { - lock.unlock(); - } - } -} diff --git a/java/fory-core/src/main/java/org/apache/fory/pool/FastForyPool.java b/java/fory-core/src/main/java/org/apache/fory/pool/FastForyPool.java deleted file mode 100644 index 846600d442..0000000000 --- a/java/fory-core/src/main/java/org/apache/fory/pool/FastForyPool.java +++ /dev/null @@ -1,336 +0,0 @@ -/* - * 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.pool; - -import java.io.OutputStream; -import java.nio.ByteBuffer; -import java.util.Objects; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.Semaphore; -import java.util.function.Consumer; -import java.util.function.Function; -import org.apache.fory.AbstractThreadSafeFory; -import org.apache.fory.Fory; -import org.apache.fory.config.ForyBuilder; -import org.apache.fory.io.ForyInputStream; -import org.apache.fory.io.ForyReadableChannel; -import org.apache.fory.memory.MemoryBuffer; -import org.apache.fory.memory.MemoryUtils; -import org.apache.fory.resolver.SharedRegistry; -import org.apache.fory.serializer.BufferCallback; -import org.apache.fory.util.LoaderBinding; - -/** - * A lightweight non-expiring {@link Fory} pool optimized for fast borrow and return. - * - *

The pool keeps at most {@code maxPoolSize} idle {@link Fory} instances for reuse after - * operations complete. Bursts may still create more instances temporarily, but any returned - * instance beyond the idle retention limit is dropped instead of retained. - * - *

Class loader control is based on the current thread context class loader (TCCL). {@link - * #setClassLoader(ClassLoader)} updates the current thread TCCL, and {@link #getClassLoader()} - * reads from TCCL with {@code defaultClassLoader} as a fallback when TCCL is null. - */ -public class FastForyPool extends AbstractThreadSafeFory { - private static final int DEFAULT_POOL_SIZE = 1024; - private final Function foryFactory; - private final SharedRegistry sharedRegistry; - private final ConcurrentLinkedQueue pool = new ConcurrentLinkedQueue<>(); - private final Object callbackLock = new Object(); - private final ClassLoader defaultClassLoader; - private final int maxPoolSize; - // Tracks remaining idle capacity, not the number of borrowed instances. - private final Semaphore idleCapacity; - private Consumer factoryCallback = f -> {}; - private volatile boolean callbackRegistrationClosed; - - /** Creates a pool with a private shared registry and the default idle retention limit. */ - public FastForyPool(Function foryFactory) { - this(foryFactory, new SharedRegistry(), null, DEFAULT_POOL_SIZE); - } - - /** - * Creates a pool that reuses the provided shared registry and the default idle retention limit. - */ - public FastForyPool(Function foryFactory, SharedRegistry sharedRegistry) { - this(foryFactory, sharedRegistry, null, DEFAULT_POOL_SIZE); - } - - /** - * Creates a pool with the provided shared registry and default class loader fallback. - * - *

The default class loader is used only when the current thread context class loader is null. - */ - public FastForyPool( - Function foryFactory, - SharedRegistry sharedRegistry, - ClassLoader defaultClassLoader) { - this(foryFactory, sharedRegistry, defaultClassLoader, DEFAULT_POOL_SIZE); - } - - /** - * Creates a pool with explicit shared state, class loader fallback, and idle retention limit. - * - *

{@code maxPoolSize} limits how many idle {@link Fory} instances are retained after - * operations complete. - */ - public FastForyPool( - Function foryFactory, - SharedRegistry sharedRegistry, - ClassLoader defaultClassLoader, - int maxPoolSize) { - this.foryFactory = Objects.requireNonNull(foryFactory); - this.sharedRegistry = Objects.requireNonNull(sharedRegistry); - this.defaultClassLoader = defaultClassLoader; - if (maxPoolSize < 0) { - throw new IllegalArgumentException( - String.format("FastForyPool maxPoolSize must be >= 0, got %s", maxPoolSize)); - } - this.maxPoolSize = maxPoolSize; - idleCapacity = new Semaphore(maxPoolSize); - } - - @Override - public R execute(Function action) { - Fory fory = acquire(); - try { - return action.apply(fory); - } finally { - release(fory); - } - } - - @Override - public void setClassLoader(ClassLoader classLoader) { - setClassLoader(classLoader, LoaderBinding.StagingType.NO_STAGING); - } - - @Override - public void setClassLoader(ClassLoader classLoader, LoaderBinding.StagingType stagingType) { - Thread.currentThread().setContextClassLoader(classLoader); - } - - @Override - public ClassLoader getClassLoader() { - ClassLoader loader = Thread.currentThread().getContextClassLoader(); - if (loader != null) { - return loader; - } - return defaultClassLoader == null ? Fory.class.getClassLoader() : defaultClassLoader; - } - - @Override - public void clearClassLoader(ClassLoader loader) {} - - @Override - public void registerCallback(Consumer callback) { - if (callbackRegistrationClosed) { - throw new IllegalStateException( - "registerCallback must be invoked before FastForyPool serialize/deserialize starts."); - } - synchronized (callbackLock) { - if (callbackRegistrationClosed) { - throw new IllegalStateException( - "registerCallback must be invoked before FastForyPool serialize/deserialize starts."); - } - factoryCallback = factoryCallback.andThen(callback); - } - } - - @Override - public byte[] serialize(Object obj) { - callbackRegistrationClosed = true; - return execute(fory -> fory.serialize(obj)); - } - - @Override - public byte[] serialize(Object obj, BufferCallback callback) { - callbackRegistrationClosed = true; - return execute(fory -> fory.serialize(obj, callback)); - } - - @Override - public MemoryBuffer serialize(Object obj, long address, int size) { - callbackRegistrationClosed = true; - return execute(fory -> fory.serialize(obj, address, size)); - } - - @Override - public MemoryBuffer serialize(MemoryBuffer buffer, Object obj) { - callbackRegistrationClosed = true; - return execute(fory -> fory.serialize(buffer, obj)); - } - - @Override - public MemoryBuffer serialize(MemoryBuffer buffer, Object obj, BufferCallback callback) { - callbackRegistrationClosed = true; - return execute(fory -> fory.serialize(buffer, obj, callback)); - } - - @Override - public void serialize(OutputStream outputStream, Object obj) { - callbackRegistrationClosed = true; - execute( - fory -> { - fory.serialize(outputStream, obj); - return null; - }); - } - - @Override - public void serialize(OutputStream outputStream, Object obj, BufferCallback callback) { - callbackRegistrationClosed = true; - execute( - fory -> { - fory.serialize(outputStream, obj, callback); - return null; - }); - } - - @Override - public Object deserialize(ByteBuffer byteBuffer) { - callbackRegistrationClosed = true; - return execute(fory -> fory.deserialize(MemoryUtils.wrap(byteBuffer))); - } - - @Override - public Object deserialize(byte[] bytes) { - callbackRegistrationClosed = true; - return execute(fory -> fory.deserialize(bytes)); - } - - @Override - public T deserialize(byte[] bytes, Class type) { - callbackRegistrationClosed = true; - return execute(fory -> fory.deserialize(bytes, type)); - } - - @Override - public T deserialize(MemoryBuffer buffer, Class type) { - callbackRegistrationClosed = true; - return execute(fory -> fory.deserialize(buffer, type)); - } - - @Override - public T deserialize(ForyInputStream inputStream, Class type) { - callbackRegistrationClosed = true; - return execute(fory -> fory.deserialize(inputStream, type)); - } - - @Override - public T deserialize(ForyReadableChannel channel, Class type) { - callbackRegistrationClosed = true; - return execute(fory -> fory.deserialize(channel, type)); - } - - @Override - public Object deserialize(byte[] bytes, Iterable outOfBandBuffers) { - callbackRegistrationClosed = true; - return execute(fory -> fory.deserialize(bytes, outOfBandBuffers)); - } - - @Override - public Object deserialize(long address, int size) { - callbackRegistrationClosed = true; - return execute(fory -> fory.deserialize(address, size)); - } - - @Override - public Object deserialize(MemoryBuffer buffer) { - callbackRegistrationClosed = true; - return execute(fory -> fory.deserialize(buffer)); - } - - @Override - public Object deserialize(MemoryBuffer buffer, Iterable outOfBandBuffers) { - callbackRegistrationClosed = true; - return execute(fory -> fory.deserialize(buffer, outOfBandBuffers)); - } - - @Override - public Object deserialize(ForyInputStream inputStream) { - callbackRegistrationClosed = true; - return execute(fory -> fory.deserialize(inputStream)); - } - - @Override - public Object deserialize(ForyInputStream inputStream, Iterable outOfBandBuffers) { - callbackRegistrationClosed = true; - return execute(fory -> fory.deserialize(inputStream, outOfBandBuffers)); - } - - @Override - public Object deserialize(ForyReadableChannel channel) { - callbackRegistrationClosed = true; - return execute(fory -> fory.deserialize(channel)); - } - - @Override - public Object deserialize(ForyReadableChannel channel, Iterable outOfBandBuffers) { - callbackRegistrationClosed = true; - return execute(fory -> fory.deserialize(channel, outOfBandBuffers)); - } - - @Override - public T copy(T obj) { - return execute(fory -> fory.copy(obj)); - } - - /** Borrows a pooled instance or creates a new one when the idle pool is empty. */ - private Fory acquire() { - Fory fory = pool.poll(); - if (fory != null) { - freeIdleCapacity(); - return fory; - } - synchronized (callbackLock) { - ForyBuilder builder = - new ForyBuilder().withSharedRegistry(sharedRegistry).withClassLoader(defaultClassLoader); - fory = foryFactory.apply(builder); - factoryCallback.accept(fory); - } - return fory; - } - - /** Returns an instance to the idle pool only when there is idle capacity left to occupy. */ - private void release(Fory fory) { - if (fory != null) { - if (!tryOccupyIdleCapacity()) { - return; - } - pool.add(fory); - } - } - - /** Visible for tests to validate idle retention accounting. */ - int pooledForyCount() { - return maxPoolSize - idleCapacity.availablePermits(); - } - - /** Attempts to consume one unit of idle capacity before retaining a returned instance. */ - private boolean tryOccupyIdleCapacity() { - return idleCapacity.tryAcquire(); - } - - /** Frees one unit of idle capacity after a pooled instance is borrowed. */ - private void freeIdleCapacity() { - idleCapacity.release(); - } -} diff --git a/java/fory-core/src/main/java/org/apache/fory/pool/ForyPooledObjectFactory.java b/java/fory-core/src/main/java/org/apache/fory/pool/ForyPooledObjectFactory.java deleted file mode 100644 index e3a87a9ea7..0000000000 --- a/java/fory-core/src/main/java/org/apache/fory/pool/ForyPooledObjectFactory.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * 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.pool; - -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; -import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; -import java.util.function.Function; -import org.apache.fory.Fory; -import org.apache.fory.logging.Logger; -import org.apache.fory.logging.LoggerFactory; -import org.apache.fory.util.LoaderBinding; - -/** - * fory pool factory The pool is used to initialize instances of fory related objects for soft. - * connections - */ -public class ForyPooledObjectFactory { - - private static final Logger LOG = LoggerFactory.getLogger(ForyPooledObjectFactory.class); - - private final Function foryFactory; - - /** - * ClassLoaderForyPooled cache, have all. ClassLoaderForyPooled caches key: - * WeakReference:ClassLoader value: SoftReference:ClassLoaderForyPooled - * - * @see Cache - * @see com.google.common.cache.CacheBuilder - */ - final Cache classLoaderForyPooledCache; - - private volatile ClassLoader classLoader = null; - - /** ThreadLocal: ClassLoader. */ - private final ThreadLocal classLoaderLocal = - ThreadLocal.withInitial( - () -> { - if (classLoader != null) { - return classLoader; - } - ClassLoader loader = Thread.currentThread().getContextClassLoader(); - if (loader == null) { - loader = Fory.class.getClassLoader(); - } - return loader; - }); - - /** - * Dynamic capacity expansion and contraction The user sets the minimum number of object pools. - */ - private final int minPoolSize; - - /** - * Dynamic capacity expansion and contraction The user sets the maximum number of object pools. - * Math.max(maxPoolSize, CPU * 2) - */ - private final int maxPoolSize; - - /** factoryCallback will be set in every new classLoaderForyPooled so that can deal every fory. */ - private final Consumer factoryCallback; - - public ForyPooledObjectFactory( - Function foryFactory, - int minPoolSize, - int maxPoolSize, - long expireTime, - TimeUnit timeUnit, - Consumer factoryCallback) { - this.minPoolSize = minPoolSize; - this.maxPoolSize = maxPoolSize; - this.foryFactory = foryFactory; - this.factoryCallback = factoryCallback; - classLoaderForyPooledCache = - CacheBuilder.newBuilder().expireAfterAccess(expireTime, timeUnit).build(); - } - - public ClassLoaderForyPooled getPooledCache() { - try { - ClassLoader classLoader = classLoaderLocal.get(); - assert classLoader != null; - ClassLoaderForyPooled classLoaderForyPooled = - classLoaderForyPooledCache.getIfPresent(classLoader); - if (classLoaderForyPooled == null) { - // double check cache - return getOrAddCache(classLoader); - } - return classLoaderForyPooled; - } catch (Exception e) { - LOG.error(e.getMessage(), e); - throw new RuntimeException(e); - } - } - - /** todo setClassLoader support LoaderBinding.StagingType */ - public void setClassLoader(ClassLoader classLoader, LoaderBinding.StagingType stagingType) { - if (classLoader == null) { - // may be used to clear some classloader - classLoader = Fory.class.getClassLoader(); - } - this.classLoader = classLoader; - classLoaderLocal.set(classLoader); - getOrAddCache(classLoader); - } - - public ClassLoader getClassLoader() { - return classLoaderLocal.get(); - } - - public void clearClassLoader(ClassLoader loader) { - classLoaderForyPooledCache.invalidate(loader); - classLoaderLocal.remove(); - } - - /** Get cache or put new added pooledFory. */ - private synchronized ClassLoaderForyPooled getOrAddCache(ClassLoader classLoader) { - ClassLoaderForyPooled classLoaderForyPooled = - classLoaderForyPooledCache.getIfPresent(classLoader); - if (classLoaderForyPooled == null) { - classLoaderForyPooled = - new ClassLoaderForyPooled(classLoader, foryFactory, minPoolSize, maxPoolSize); - classLoaderForyPooled.setFactoryCallback(factoryCallback); - classLoaderForyPooledCache.put(classLoader, classLoaderForyPooled); - } - return classLoaderForyPooled; - } -} diff --git a/java/fory-core/src/main/java/org/apache/fory/resolver/AllowListChecker.java b/java/fory-core/src/main/java/org/apache/fory/resolver/AllowListChecker.java index 116e21da9b..b81f0daac2 100644 --- a/java/fory-core/src/main/java/org/apache/fory/resolver/AllowListChecker.java +++ b/java/fory-core/src/main/java/org/apache/fory/resolver/AllowListChecker.java @@ -26,11 +26,10 @@ import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import javax.annotation.concurrent.ThreadSafe; -import org.apache.fory.Fory; +import org.apache.fory.config.Config; import org.apache.fory.exception.InsecureException; import org.apache.fory.logging.Logger; import org.apache.fory.logging.LoggerFactory; -import org.apache.fory.memory.MemoryBuffer; import org.apache.fory.serializer.Serializer; /** White/black list based class checker. */ @@ -246,17 +245,17 @@ public void addListener(ClassResolver classResolver) { @SuppressWarnings({"rawtypes", "unchecked"}) private static class DisallowSerializer extends Serializer { - public DisallowSerializer(Fory fory, Class type) { - super(fory, type); + public DisallowSerializer(Config config, Class type) { + super(config, type); } @Override - public void write(MemoryBuffer buffer, Object value) { + public void write(org.apache.fory.context.WriteContext writeContext, Object value) { throw new InsecureException(String.format("Class %s not allowed for serialization.", type)); } @Override - public Object read(MemoryBuffer buffer) { + public Object read(org.apache.fory.context.ReadContext readContext) { throw new InsecureException(String.format("Class %s not allowed for serialization.", type)); } } 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 a3034fc1e2..3bf04d007d 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 @@ -86,6 +86,7 @@ import org.apache.fory.collection.Uint32List; import org.apache.fory.collection.Uint64List; import org.apache.fory.collection.Uint8List; +import org.apache.fory.config.Config; import org.apache.fory.config.Language; import org.apache.fory.exception.InsecureException; import org.apache.fory.logging.Logger; @@ -307,30 +308,31 @@ public void initialize() { } private void addDefaultSerializers() { + Config config = fory.getConfig(); // primitive types will be boxed. addDefaultSerializer(void.class, NoneSerializer.class); addDefaultSerializer(String.class, fory.getStringSerializer()); PrimitiveSerializers.registerDefaultSerializers(fory); - UnsignedSerializers.registerDefaultSerializers(fory); + UnsignedSerializers.registerDefaultSerializers(this); Serializers.registerDefaultSerializers(fory); ArraySerializers.registerDefaultSerializers(fory); - PrimitiveListSerializers.registerDefaultSerializers(fory); - TimeSerializers.registerDefaultSerializers(fory); - OptionalSerializers.registerDefaultSerializers(fory); + PrimitiveListSerializers.registerDefaultSerializers(this); + TimeSerializers.registerDefaultSerializers(this); + OptionalSerializers.registerDefaultSerializers(this); CollectionSerializers.registerDefaultSerializers(fory); MapSerializers.registerDefaultSerializers(fory); - addDefaultSerializer(Locale.class, new LocaleSerializer(fory)); + addDefaultSerializer(Locale.class, new LocaleSerializer(config)); addDefaultSerializer( - SerializedLambda.class, new SerializedLambdaSerializer(fory, SerializedLambda.class)); + SerializedLambda.class, new SerializedLambdaSerializer(this, SerializedLambda.class)); addDefaultSerializer( LambdaSerializer.ReplaceStub.class, - new LambdaSerializer(fory, LambdaSerializer.ReplaceStub.class)); + new LambdaSerializer(this, LambdaSerializer.ReplaceStub.class)); addDefaultSerializer( JdkProxySerializer.ReplaceStub.class, - new JdkProxySerializer(fory, JdkProxySerializer.ReplaceStub.class)); + new JdkProxySerializer(this, JdkProxySerializer.ReplaceStub.class)); addDefaultSerializer( ReplaceResolveSerializer.ReplaceStub.class, - new ReplaceResolveSerializer(fory, ReplaceResolveSerializer.ReplaceStub.class)); + new ReplaceResolveSerializer(this, ReplaceResolveSerializer.ReplaceStub.class)); SynchronizedSerializers.registerSerializers(fory); UnmodifiableSerializers.registerSerializers(fory); ImmutableCollectionSerializers.registerSerializers(fory); @@ -342,7 +344,7 @@ private void addDefaultSerializers() { if (metaContextShareEnabled) { registerInternal(UnknownStruct.class, NONEXISTENT_META_SHARED_ID); registerInternalSerializer( - UnknownStruct.class, new UnknownClassSerializers.UnknownStructSerializer(fory, null)); + UnknownStruct.class, new UnknownClassSerializers.UnknownStructSerializer(this, null)); } else { registerInternal(UnknownEmptyStruct.class); } @@ -1197,7 +1199,7 @@ public Class getSerializerClass(Class cls, boolean code return typeInfo.serializer.getClass(); } else { if (getSerializerFactory() != null) { - Serializer serializer = getSerializerFactory().createSerializer(fory, cls); + Serializer serializer = getSerializerFactory().createSerializer(this, cls); if (serializer != null) { return serializer.getClass(); } @@ -1494,9 +1496,9 @@ private Serializer createSerializer(Class cls) { DisallowedList.checkNotInDisallowedList(cls.getName()); if (!isSecure(cls)) { if (getSerializerClass(cls, false) == ObjectSerializer.class) { - Serializer serializer = new CopyOnlyObjectSerializer<>(fory, cls); + Serializer serializer = new CopyOnlyObjectSerializer<>(this, cls); if (ForyCopyable.class.isAssignableFrom(cls)) { - serializer = new ForyCopyableSerializer<>(fory, cls, serializer); + serializer = new ForyCopyableSerializer<>(fory.getConfig(), cls, serializer); } return serializer; } @@ -1522,7 +1524,7 @@ private Serializer createSerializer(Class cls) { } if (extRegistry.serializerFactory != null) { - Serializer serializer = extRegistry.serializerFactory.createSerializer(fory, cls); + Serializer serializer = extRegistry.serializerFactory.createSerializer(this, cls); if (serializer != null) { return serializer; } @@ -1553,7 +1555,7 @@ private Serializer createSerializer(Class cls) { Class serializerClass = getSerializerClass(cls); Serializer serializer = Serializers.newSerializer(fory, cls, serializerClass); if (ForyCopyable.class.isAssignableFrom(cls)) { - serializer = new ForyCopyableSerializer<>(fory, cls, serializer); + serializer = new ForyCopyableSerializer<>(fory.getConfig(), cls, serializer); } return serializer; } 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 e3641eb655..0d2e0ec2b8 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 @@ -155,36 +155,6 @@ DescriptorGrouper getOrCreateTypeDefDescriptorGrouper( return typeDefDescriptorGrouperCache.computeIfAbsent(key, ignored -> factory.get()); } - public void clearClassLoader(ClassLoader loader) { - if (loader == null) { - return; - } - clearSharedRegistrationIfClassLoader(loader); - typeDefMap.removeIf((cls, typeDef) -> cls.getClassLoader() == loader); - currentLayerTypeDef.removeIf((cls, typeDef) -> cls.getClassLoader() == loader); - typeDefById - .entrySet() - .removeIf( - entry -> { - Class cls = entry.getValue().getClassSpec().type; - return cls != null && cls.getClassLoader() == loader; - }); - descriptorsCache.entrySet().removeIf(entry -> entry.getKey().f0.getClassLoader() == loader); - fieldDescriptorsCache - .entrySet() - .removeIf(entry -> entry.getKey().type.getClassLoader() == loader); - typeDefDescriptorsCache - .entrySet() - .removeIf(entry -> entry.getKey().referencesClassLoader(loader)); - fieldDescriptorGrouperCache - .entrySet() - .removeIf(entry -> entry.getKey().fieldDescriptorsKey.type.getClassLoader() == loader); - typeDefDescriptorGrouperCache - .entrySet() - .removeIf(entry -> entry.getKey().typeDefDescriptorsKey.referencesClassLoader(loader)); - codeGeneratorMap.entrySet().removeIf(entry -> entry.getKey().contains(loader)); - } - private synchronized void clearSharedRegistrationIfClassLoader(ClassLoader loader) { IdentityHashMap, Integer> sharedRegisteredClassIdMap = registeredClassIdMap; BiMap> sharedRegisteredClasses = registeredClasses; 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 8e172300b0..820efa8d1b 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 @@ -60,6 +60,7 @@ import org.apache.fory.collection.LongMap; import org.apache.fory.collection.Tuple2; import org.apache.fory.config.CompatibleMode; +import org.apache.fory.config.Config; import org.apache.fory.exception.ForyException; import org.apache.fory.exception.SerializerUnregisteredException; import org.apache.fory.logging.Logger; @@ -77,6 +78,7 @@ import org.apache.fory.serializer.Serializer; import org.apache.fory.serializer.SerializerFactory; import org.apache.fory.serializer.Serializers; +import org.apache.fory.serializer.StringSerializer; import org.apache.fory.serializer.UnknownClass; import org.apache.fory.serializer.UnknownClass.UnknownEmptyStruct; import org.apache.fory.serializer.UnknownClass.UnknownStruct; @@ -85,6 +87,7 @@ import org.apache.fory.type.DescriptorBuilder; import org.apache.fory.type.DescriptorGrouper; import org.apache.fory.type.GenericType; +import org.apache.fory.type.Generics; import org.apache.fory.type.ScalaTypes; import org.apache.fory.type.TypeUtils; import org.apache.fory.type.Types; @@ -140,6 +143,26 @@ protected TypeResolver(Fory fory) { typeIdToTypeInfo = new TypeInfo[length]; } + public final Fory getFory() { + return fory; + } + + public final Config getConfig() { + return fory.getConfig(); + } + + public final RefResolver getRefResolver() { + return fory.getRefResolver(); + } + + public final Generics getGenerics() { + return fory.getGenerics(); + } + + public final StringSerializer getStringSerializer() { + return fory.getStringSerializer(); + } + protected final void checkRegisterAllowed() { if (fory.isRegistrationFinished()) { throw new ForyException( @@ -898,7 +921,7 @@ private TypeInfo getMetaSharedTypeInfo(TypeDef typeDef, Class clz) { if (UnknownClass.class.isAssignableFrom(TypeUtils.getComponentIfArray(cls))) { if (cls == UnknownStruct.class) { typeInfo.setSerializer( - this, new UnknownClassSerializers.UnknownStructSerializer(fory, typeDef)); + this, new UnknownClassSerializers.UnknownStructSerializer(this, typeDef)); // Ensure UnknownStruct is registered so writeTypeInfo emits a placeholder typeId // that UnknownStructSerializer can rewrite to the original typeId. if (!fory.isCrossLanguage()) { @@ -932,7 +955,7 @@ private TypeInfo getMetaSharedTypeInfo(TypeDef typeDef, Class clz) { } } if (sc == MetaSharedSerializer.class) { - typeInfo.setSerializer(this, new MetaSharedSerializer(fory, cls, typeDef)); + typeInfo.setSerializer(this, new MetaSharedSerializer(this, cls, typeDef)); } else { typeInfo.setSerializer(this, Serializers.newSerializer(fory, cls, sc)); } @@ -1629,10 +1652,6 @@ private Class getMetaSharedDeserializerClassFromGraalvmReg return null; } - public final Fory getFory() { - return fory; - } - public final MetaStringResolver getMetaStringResolver() { return metaStringResolver; } 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 c475c320a2..3899209be5 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 @@ -158,7 +158,7 @@ public void initialize() { // Before resolver unification, these were registered through ClassResolver initialization. Serializers.registerDefaultSerializers(fory); if (shareMeta) { - Serializer serializer = new UnknownStructSerializer(fory, null); + Serializer serializer = new UnknownStructSerializer(this, null); register(UnknownStruct.class, serializer, "", "unknown_struct", Types.COMPATIBLE_STRUCT, -1); } } @@ -287,13 +287,13 @@ private void register( GraalvmSupport.registerClass(type, fory.getConfig().getConfigHash()); if (serializer == null) { if (type.isEnum()) { - typeInfo.serializer = new EnumSerializer(fory, (Class) type); + typeInfo.serializer = new EnumSerializer(this, (Class) type); } else { AtomicBoolean updated = new AtomicBoolean(false); AtomicReference ref = new AtomicReference(null); typeInfo.serializer = new DeferedLazySerializer.DeferredLazyObjectSerializer( - fory, + this, type, () -> { if (ref.get() == null) { @@ -700,7 +700,7 @@ private TypeInfo buildTypeInfo(Class cls) { if (isSet(cls)) { if (cls.isAssignableFrom(HashSet.class)) { cls = HashSet.class; - serializer = new HashSetSerializer(fory); + serializer = new HashSetSerializer(this); } else { serializer = getCollectionSerializer(cls); } @@ -708,18 +708,18 @@ private TypeInfo buildTypeInfo(Class cls) { } else if (isCollection(cls)) { if (cls.isAssignableFrom(ArrayList.class)) { cls = ArrayList.class; - serializer = new ArrayListSerializer(fory); + serializer = new ArrayListSerializer(this); } else { serializer = getCollectionSerializer(cls); } typeId = Types.LIST; } else if (cls.isArray() && !cls.getComponentType().isPrimitive()) { - serializer = new ArraySerializers.ObjectArraySerializer(fory, cls); + serializer = new ArraySerializers.ObjectArraySerializer(this, cls); typeId = Types.LIST; } else if (isMap(cls)) { if (cls.isAssignableFrom(HashMap.class)) { cls = HashMap.class; - serializer = new HashMapSerializer(fory); + serializer = new HashMapSerializer(this); } else { TypeInfo typeInfo = classInfoMap.get(cls); if (typeInfo != null @@ -728,7 +728,7 @@ private TypeInfo buildTypeInfo(Class cls) { && ((MapLikeSerializer) typeInfo.serializer).supportCodegenHook()) { serializer = typeInfo.serializer; } else { - serializer = new MapSerializer(fory, cls); + serializer = new MapSerializer(this, cls); } } typeId = Types.MAP; @@ -765,122 +765,161 @@ private Serializer getCollectionSerializer(Class cls) { && ((CollectionLikeSerializer) (typeInfo.serializer)).supportCodegenHook()) { return typeInfo.serializer; } - return new CollectionSerializer(fory, cls); + return new CollectionSerializer(this, cls); } private void registerDefaultTypes() { + Config config = fory.getConfig(); // Boolean types registerType( - Types.BOOL, Boolean.class, new PrimitiveSerializers.BooleanSerializer(fory, Boolean.class)); + Types.BOOL, + Boolean.class, + new PrimitiveSerializers.BooleanSerializer(config, Boolean.class)); registerType( - Types.BOOL, boolean.class, new PrimitiveSerializers.BooleanSerializer(fory, boolean.class)); - registerType(Types.BOOL, AtomicBoolean.class, new Serializers.AtomicBooleanSerializer(fory)); + Types.BOOL, + boolean.class, + new PrimitiveSerializers.BooleanSerializer(config, boolean.class)); + registerType( + Types.BOOL, AtomicBoolean.class, new Serializers.AtomicBooleanSerializer(config)); // Byte types registerType( - Types.UINT8, Byte.class, new PrimitiveSerializers.ByteSerializer(fory, Byte.class)); + Types.UINT8, Byte.class, new PrimitiveSerializers.ByteSerializer(config, Byte.class)); + registerType( + Types.UINT8, byte.class, new PrimitiveSerializers.ByteSerializer(config, byte.class)); registerType( - Types.UINT8, byte.class, new PrimitiveSerializers.ByteSerializer(fory, byte.class)); - registerType(Types.INT8, Byte.class, new PrimitiveSerializers.ByteSerializer(fory, Byte.class)); - registerType(Types.INT8, byte.class, new PrimitiveSerializers.ByteSerializer(fory, byte.class)); - registerType(Types.UINT8, Uint8.class, new UnsignedSerializers.Uint8Serializer(fory)); + Types.INT8, Byte.class, new PrimitiveSerializers.ByteSerializer(config, Byte.class)); + registerType( + Types.INT8, byte.class, new PrimitiveSerializers.ByteSerializer(config, byte.class)); + registerType(Types.UINT8, Uint8.class, new UnsignedSerializers.Uint8Serializer(config)); // Short types registerType( - Types.UINT16, Short.class, new PrimitiveSerializers.ShortSerializer(fory, Short.class)); + Types.UINT16, Short.class, new PrimitiveSerializers.ShortSerializer(config, Short.class)); registerType( - Types.UINT16, short.class, new PrimitiveSerializers.ShortSerializer(fory, short.class)); + Types.UINT16, short.class, new PrimitiveSerializers.ShortSerializer(config, short.class)); registerType( - Types.INT16, Short.class, new PrimitiveSerializers.ShortSerializer(fory, Short.class)); + Types.INT16, Short.class, new PrimitiveSerializers.ShortSerializer(config, Short.class)); registerType( - Types.INT16, short.class, new PrimitiveSerializers.ShortSerializer(fory, short.class)); - registerType(Types.UINT16, Uint16.class, new UnsignedSerializers.Uint16Serializer(fory)); + Types.INT16, short.class, new PrimitiveSerializers.ShortSerializer(config, short.class)); + registerType(Types.UINT16, Uint16.class, new UnsignedSerializers.Uint16Serializer(config)); // Integer types registerType( - Types.UINT32, Integer.class, new PrimitiveSerializers.IntSerializer(fory, Integer.class)); - registerType(Types.UINT32, int.class, new PrimitiveSerializers.IntSerializer(fory, int.class)); - registerType(Types.UINT32, AtomicInteger.class, new Serializers.AtomicIntegerSerializer(fory)); + Types.UINT32, + Integer.class, + new PrimitiveSerializers.IntSerializer(config, Integer.class)); + registerType( + Types.UINT32, int.class, new PrimitiveSerializers.IntSerializer(config, int.class)); + registerType( + Types.UINT32, AtomicInteger.class, new Serializers.AtomicIntegerSerializer(config)); + registerType( + Types.INT32, + Integer.class, + new PrimitiveSerializers.IntSerializer(config, Integer.class)); + registerType( + Types.INT32, int.class, new PrimitiveSerializers.IntSerializer(config, int.class)); registerType( - Types.INT32, Integer.class, new PrimitiveSerializers.IntSerializer(fory, Integer.class)); - registerType(Types.INT32, int.class, new PrimitiveSerializers.IntSerializer(fory, int.class)); - registerType(Types.INT32, AtomicInteger.class, new Serializers.AtomicIntegerSerializer(fory)); + Types.INT32, AtomicInteger.class, new Serializers.AtomicIntegerSerializer(config)); registerType( - Types.VAR_UINT32, Integer.class, new PrimitiveSerializers.VarUint32Serializer(fory)); - registerType(Types.VAR_UINT32, int.class, new PrimitiveSerializers.VarUint32Serializer(fory)); + Types.VAR_UINT32, Integer.class, new PrimitiveSerializers.VarUint32Serializer(config)); registerType( - Types.VARINT32, Integer.class, new PrimitiveSerializers.IntSerializer(fory, Integer.class)); + Types.VAR_UINT32, int.class, new PrimitiveSerializers.VarUint32Serializer(config)); registerType( - Types.VARINT32, int.class, new PrimitiveSerializers.IntSerializer(fory, int.class)); + Types.VARINT32, + Integer.class, + new PrimitiveSerializers.IntSerializer(config, Integer.class)); registerType( - Types.VARINT32, AtomicInteger.class, new Serializers.AtomicIntegerSerializer(fory)); - registerType(Types.UINT32, Uint32.class, new UnsignedSerializers.Uint32Serializer(fory)); - registerType(Types.UINT64, Uint64.class, new UnsignedSerializers.Uint64Serializer(fory)); + Types.VARINT32, int.class, new PrimitiveSerializers.IntSerializer(config, int.class)); + registerType( + Types.VARINT32, AtomicInteger.class, new Serializers.AtomicIntegerSerializer(config)); + registerType(Types.UINT32, Uint32.class, new UnsignedSerializers.Uint32Serializer(config)); + registerType(Types.UINT64, Uint64.class, new UnsignedSerializers.Uint64Serializer(config)); // Long types registerType( - Types.UINT64, Long.class, new PrimitiveSerializers.LongSerializer(fory, Long.class)); + Types.UINT64, Long.class, new PrimitiveSerializers.LongSerializer(config, Long.class)); + registerType( + Types.UINT64, long.class, new PrimitiveSerializers.LongSerializer(config, long.class)); + registerType(Types.UINT64, AtomicLong.class, new Serializers.AtomicLongSerializer(config)); registerType( - Types.UINT64, long.class, new PrimitiveSerializers.LongSerializer(fory, long.class)); - registerType(Types.UINT64, AtomicLong.class, new Serializers.AtomicLongSerializer(fory)); + Types.TAGGED_UINT64, + Long.class, + new PrimitiveSerializers.LongSerializer(config, Long.class)); registerType( - Types.TAGGED_UINT64, Long.class, new PrimitiveSerializers.LongSerializer(fory, Long.class)); + Types.TAGGED_UINT64, + long.class, + new PrimitiveSerializers.LongSerializer(config, long.class)); registerType( - Types.TAGGED_UINT64, long.class, new PrimitiveSerializers.LongSerializer(fory, long.class)); - registerType(Types.TAGGED_UINT64, AtomicLong.class, new Serializers.AtomicLongSerializer(fory)); + Types.TAGGED_UINT64, AtomicLong.class, new Serializers.AtomicLongSerializer(config)); registerType( - Types.INT64, Long.class, new PrimitiveSerializers.LongSerializer(fory, Long.class)); + Types.INT64, Long.class, new PrimitiveSerializers.LongSerializer(config, Long.class)); registerType( - Types.INT64, long.class, new PrimitiveSerializers.LongSerializer(fory, long.class)); - registerType(Types.INT64, AtomicLong.class, new Serializers.AtomicLongSerializer(fory)); + Types.INT64, long.class, new PrimitiveSerializers.LongSerializer(config, long.class)); + registerType(Types.INT64, AtomicLong.class, new Serializers.AtomicLongSerializer(config)); registerType( - Types.TAGGED_INT64, Long.class, new PrimitiveSerializers.LongSerializer(fory, Long.class)); + Types.TAGGED_INT64, + Long.class, + new PrimitiveSerializers.LongSerializer(config, Long.class)); registerType( - Types.TAGGED_INT64, long.class, new PrimitiveSerializers.LongSerializer(fory, long.class)); - registerType(Types.TAGGED_INT64, AtomicLong.class, new Serializers.AtomicLongSerializer(fory)); - registerType(Types.VAR_UINT64, Long.class, new PrimitiveSerializers.VarUint64Serializer(fory)); - registerType(Types.VAR_UINT64, long.class, new PrimitiveSerializers.VarUint64Serializer(fory)); + Types.TAGGED_INT64, + long.class, + new PrimitiveSerializers.LongSerializer(config, long.class)); registerType( - Types.VARINT64, Long.class, new PrimitiveSerializers.LongSerializer(fory, Long.class)); + Types.TAGGED_INT64, AtomicLong.class, new Serializers.AtomicLongSerializer(config)); + registerType(Types.VAR_UINT64, Long.class, new PrimitiveSerializers.VarUint64Serializer(config)); + registerType(Types.VAR_UINT64, long.class, new PrimitiveSerializers.VarUint64Serializer(config)); registerType( - Types.VARINT64, long.class, new PrimitiveSerializers.LongSerializer(fory, long.class)); - registerType(Types.VARINT64, AtomicLong.class, new Serializers.AtomicLongSerializer(fory)); + Types.VARINT64, Long.class, new PrimitiveSerializers.LongSerializer(config, Long.class)); + registerType( + Types.VARINT64, long.class, new PrimitiveSerializers.LongSerializer(config, long.class)); + registerType(Types.VARINT64, AtomicLong.class, new Serializers.AtomicLongSerializer(config)); // Float types registerType( - Types.FLOAT32, Float.class, new PrimitiveSerializers.FloatSerializer(fory, Float.class)); + Types.FLOAT32, Float.class, new PrimitiveSerializers.FloatSerializer(config, Float.class)); registerType( - Types.FLOAT32, float.class, new PrimitiveSerializers.FloatSerializer(fory, float.class)); + Types.FLOAT32, float.class, new PrimitiveSerializers.FloatSerializer(config, float.class)); registerType( Types.FLOAT16, Float16.class, - new PrimitiveSerializers.Float16Serializer(fory, Float16.class)); + new PrimitiveSerializers.Float16Serializer(config, Float16.class)); registerType( - Types.FLOAT64, Double.class, new PrimitiveSerializers.DoubleSerializer(fory, Double.class)); + Types.FLOAT64, + Double.class, + new PrimitiveSerializers.DoubleSerializer(config, Double.class)); registerType( - Types.FLOAT64, double.class, new PrimitiveSerializers.DoubleSerializer(fory, double.class)); + Types.FLOAT64, + double.class, + new PrimitiveSerializers.DoubleSerializer(config, double.class)); // String types registerType(Types.STRING, String.class, fory.getStringSerializer()); - registerType(Types.STRING, StringBuilder.class, new Serializers.StringBuilderSerializer(fory)); - registerType(Types.STRING, StringBuffer.class, new Serializers.StringBufferSerializer(fory)); + registerType( + Types.STRING, StringBuilder.class, new Serializers.StringBuilderSerializer(config)); + registerType( + Types.STRING, StringBuffer.class, new Serializers.StringBufferSerializer(config)); // Time types - registerType(Types.DURATION, Duration.class, new TimeSerializers.DurationSerializer(fory)); - registerType(Types.TIMESTAMP, Instant.class, new TimeSerializers.InstantSerializer(fory)); - registerType(Types.TIMESTAMP, Date.class, new TimeSerializers.DateSerializer(fory)); - registerType(Types.TIMESTAMP, java.sql.Date.class, new TimeSerializers.SqlDateSerializer(fory)); - registerType(Types.TIMESTAMP, Timestamp.class, new TimeSerializers.TimestampSerializer(fory)); + registerType(Types.DURATION, Duration.class, new TimeSerializers.DurationSerializer(config)); + registerType(Types.TIMESTAMP, Instant.class, new TimeSerializers.InstantSerializer(config)); + registerType(Types.TIMESTAMP, Date.class, new TimeSerializers.DateSerializer(config)); + registerType( + Types.TIMESTAMP, java.sql.Date.class, new TimeSerializers.SqlDateSerializer(config)); + registerType( + Types.TIMESTAMP, Timestamp.class, new TimeSerializers.TimestampSerializer(config)); registerType( - Types.TIMESTAMP, LocalDateTime.class, new TimeSerializers.LocalDateTimeSerializer(fory)); - registerType(Types.DATE, LocalDate.class, new TimeSerializers.LocalDateSerializer(fory)); + Types.TIMESTAMP, + LocalDateTime.class, + new TimeSerializers.LocalDateTimeSerializer(config)); + registerType(Types.DATE, LocalDate.class, new TimeSerializers.LocalDateSerializer(config)); // Decimal types - registerType(Types.DECIMAL, BigDecimal.class, new Serializers.BigDecimalSerializer(fory)); - registerType(Types.DECIMAL, BigInteger.class, new Serializers.BigIntegerSerializer(fory)); + registerType(Types.DECIMAL, BigDecimal.class, new Serializers.BigDecimalSerializer(config)); + registerType(Types.DECIMAL, BigInteger.class, new Serializers.BigIntegerSerializer(config)); // Binary types - registerType(Types.BINARY, byte[].class, new ArraySerializers.ByteArraySerializer(fory)); + registerType(Types.BINARY, byte[].class, new ArraySerializers.ByteArraySerializer(this)); @SuppressWarnings("unchecked") Class heapByteBufferClass = (Class) Platform.HEAP_BYTE_BUFFER_CLASS; @@ -888,7 +927,7 @@ private void registerDefaultTypes() { Types.BINARY, Platform.HEAP_BYTE_BUFFER_CLASS, new org.apache.fory.serializer.BufferSerializers.ByteBufferSerializer( - fory, heapByteBufferClass)); + this, heapByteBufferClass)); @SuppressWarnings("unchecked") Class directByteBufferClass = (Class) Platform.DIRECT_BYTE_BUFFER_CLASS; @@ -896,88 +935,96 @@ private void registerDefaultTypes() { Types.BINARY, Platform.DIRECT_BYTE_BUFFER_CLASS, new org.apache.fory.serializer.BufferSerializers.ByteBufferSerializer( - fory, directByteBufferClass)); + this, directByteBufferClass)); // Primitive arrays registerType( - Types.BOOL_ARRAY, boolean[].class, new ArraySerializers.BooleanArraySerializer(fory)); - registerType(Types.INT16_ARRAY, short[].class, new ArraySerializers.ShortArraySerializer(fory)); - registerType(Types.INT32_ARRAY, int[].class, new ArraySerializers.IntArraySerializer(fory)); - registerType(Types.INT64_ARRAY, long[].class, new ArraySerializers.LongArraySerializer(fory)); + Types.BOOL_ARRAY, boolean[].class, new ArraySerializers.BooleanArraySerializer(this)); + registerType(Types.INT16_ARRAY, short[].class, new ArraySerializers.ShortArraySerializer(this)); + registerType(Types.INT32_ARRAY, int[].class, new ArraySerializers.IntArraySerializer(this)); + registerType(Types.INT64_ARRAY, long[].class, new ArraySerializers.LongArraySerializer(this)); registerType( - Types.FLOAT32_ARRAY, float[].class, new ArraySerializers.FloatArraySerializer(fory)); + Types.FLOAT32_ARRAY, float[].class, new ArraySerializers.FloatArraySerializer(this)); registerType( - Types.FLOAT64_ARRAY, double[].class, new ArraySerializers.DoubleArraySerializer(fory)); + Types.FLOAT64_ARRAY, double[].class, new ArraySerializers.DoubleArraySerializer(this)); registerType( - Types.FLOAT16_ARRAY, Float16[].class, new ArraySerializers.Float16ArraySerializer(fory)); + Types.FLOAT16_ARRAY, Float16[].class, new ArraySerializers.Float16ArraySerializer(this)); // Primitive lists registerType( - Types.BOOL_ARRAY, BoolList.class, new PrimitiveListSerializers.BoolListSerializer(fory)); + Types.BOOL_ARRAY, BoolList.class, new PrimitiveListSerializers.BoolListSerializer(config)); registerType( - Types.INT8_ARRAY, Int8List.class, new PrimitiveListSerializers.Int8ListSerializer(fory)); + Types.INT8_ARRAY, Int8List.class, new PrimitiveListSerializers.Int8ListSerializer(config)); registerType( - Types.INT16_ARRAY, Int16List.class, new PrimitiveListSerializers.Int16ListSerializer(fory)); + Types.INT16_ARRAY, + Int16List.class, + new PrimitiveListSerializers.Int16ListSerializer(config)); registerType( - Types.INT32_ARRAY, Int32List.class, new PrimitiveListSerializers.Int32ListSerializer(fory)); + Types.INT32_ARRAY, + Int32List.class, + new PrimitiveListSerializers.Int32ListSerializer(config)); registerType( - Types.INT64_ARRAY, Int64List.class, new PrimitiveListSerializers.Int64ListSerializer(fory)); + Types.INT64_ARRAY, + Int64List.class, + new PrimitiveListSerializers.Int64ListSerializer(config)); registerType( - Types.UINT8_ARRAY, Uint8List.class, new PrimitiveListSerializers.Uint8ListSerializer(fory)); + Types.UINT8_ARRAY, + Uint8List.class, + new PrimitiveListSerializers.Uint8ListSerializer(config)); registerType( Types.UINT16_ARRAY, Uint16List.class, - new PrimitiveListSerializers.Uint16ListSerializer(fory)); + new PrimitiveListSerializers.Uint16ListSerializer(config)); registerType( Types.UINT32_ARRAY, Uint32List.class, - new PrimitiveListSerializers.Uint32ListSerializer(fory)); + new PrimitiveListSerializers.Uint32ListSerializer(config)); registerType( Types.UINT64_ARRAY, Uint64List.class, - new PrimitiveListSerializers.Uint64ListSerializer(fory)); + new PrimitiveListSerializers.Uint64ListSerializer(config)); registerType( Types.FLOAT32_ARRAY, Float32List.class, - new PrimitiveListSerializers.Float32ListSerializer(fory)); + new PrimitiveListSerializers.Float32ListSerializer(config)); registerType( Types.FLOAT64_ARRAY, Float64List.class, - new PrimitiveListSerializers.Float64ListSerializer(fory)); + new PrimitiveListSerializers.Float64ListSerializer(config)); registerType( Types.FLOAT16_ARRAY, Float16List.class, - new PrimitiveListSerializers.Float16ListSerializer(fory)); + new PrimitiveListSerializers.Float16ListSerializer(config)); // Collections - registerType(Types.LIST, ArrayList.class, new ArrayListSerializer(fory)); + registerType(Types.LIST, ArrayList.class, new ArrayListSerializer(this)); registerType( Types.LIST, Object[].class, - new ArraySerializers.ObjectArraySerializer(fory, Object[].class)); - registerType(Types.LIST, List.class, new XlangListDefaultSerializer(fory, List.class)); + new ArraySerializers.ObjectArraySerializer(this, Object[].class)); + registerType(Types.LIST, List.class, new XlangListDefaultSerializer(this, List.class)); registerType( - Types.LIST, Collection.class, new XlangListDefaultSerializer(fory, Collection.class)); + Types.LIST, Collection.class, new XlangListDefaultSerializer(this, Collection.class)); // Sets - registerType(Types.SET, HashSet.class, new HashSetSerializer(fory)); + registerType(Types.SET, HashSet.class, new HashSetSerializer(this)); registerType( Types.SET, LinkedHashSet.class, new org.apache.fory.serializer.collection.CollectionSerializers.LinkedHashSetSerializer( - fory)); - registerType(Types.SET, Set.class, new XlangSetDefaultSerializer(fory, Set.class)); + this)); + registerType(Types.SET, Set.class, new XlangSetDefaultSerializer(this, Set.class)); // Maps registerType( Types.MAP, HashMap.class, - new org.apache.fory.serializer.collection.MapSerializers.HashMapSerializer(fory)); + new org.apache.fory.serializer.collection.MapSerializers.HashMapSerializer(this)); registerType( Types.MAP, LinkedHashMap.class, - new org.apache.fory.serializer.collection.MapSerializers.LinkedHashMapSerializer(fory)); - registerType(Types.MAP, Map.class, new XlangMapSerializer(fory, Map.class)); + new org.apache.fory.serializer.collection.MapSerializers.LinkedHashMapSerializer(this)); + registerType(Types.MAP, Map.class, new XlangMapSerializer(this, Map.class)); registerUnionTypes(); } @@ -1004,7 +1051,7 @@ private void registerUnionTypes() { @SuppressWarnings("unchecked") Class unionCls = (Class) cls; - UnionSerializer serializer = new UnionSerializer(fory, unionCls); + UnionSerializer serializer = new UnionSerializer(this, unionCls); TypeInfo typeInfo = newTypeInfo(cls, serializer, Types.UNION, INVALID_USER_TYPE_ID); classInfoMap.put(cls, typeInfo); } diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/AbstractObjectSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/AbstractObjectSerializer.java index cd66bcd750..c4e81e2ae0 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/AbstractObjectSerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/AbstractObjectSerializer.java @@ -62,14 +62,15 @@ public abstract class AbstractObjectSerializer extends Serializer { private SerializationFieldInfo[] fieldInfos; private RecordInfo copyRecordInfo; - public AbstractObjectSerializer(Fory fory, Class type) { - this(fory, type, ObjectCreators.getObjectCreator(type)); + public AbstractObjectSerializer(TypeResolver typeResolver, Class type) { + this(typeResolver, type, ObjectCreators.getObjectCreator(type)); } - public AbstractObjectSerializer(Fory fory, Class type, ObjectCreator objectCreator) { - super(fory, type); - this.refResolver = fory.getRefResolver(); - this.typeResolver = fory.getTypeResolver(); + public AbstractObjectSerializer( + TypeResolver typeResolver, Class type, ObjectCreator objectCreator) { + super(typeResolver, type); + this.refResolver = typeResolver.getRefResolver(); + this.typeResolver = typeResolver; this.isRecord = RecordUtils.isRecord(type); this.objectCreator = objectCreator; } @@ -96,7 +97,7 @@ static void writeField( Serializer serializer = fieldInfo.typeInfo.getSerializer(); if (refMode == RefMode.TRACKING) { if (!refResolver.writeRefOrNull(buffer, fieldValue)) { - serializer.write(buffer, fieldValue); + serializer.write(org.apache.fory.context.WriteContext.current(), fieldValue); } } else if (refMode == RefMode.NULL_ONLY) { if (fieldValue == null) { @@ -104,9 +105,9 @@ static void writeField( return; } buffer.writeByte(Fory.NOT_NULL_VALUE_FLAG); - serializer.write(buffer, fieldValue); + serializer.write(org.apache.fory.context.WriteContext.current(), fieldValue); } else { - serializer.write(buffer, fieldValue); + serializer.write(org.apache.fory.context.WriteContext.current(), fieldValue); } return; } @@ -154,7 +155,10 @@ static Object readField( int nextReadRefId = refResolver.tryPreserveRefId(buffer); if (nextReadRefId >= Fory.NOT_NULL_VALUE_FLAG) { Object value = - typeResolver.readTypeInfo(buffer, fieldInfo.type).getSerializer().read(buffer); + typeResolver + .readTypeInfo(buffer, fieldInfo.type) + .getSerializer() + .read(org.apache.fory.context.ReadContext.current()); refResolver.setReadObject(nextReadRefId, value); return value; } @@ -162,7 +166,9 @@ static Object readField( } if (refMode != RefMode.NULL_ONLY || buffer.readByte() != Fory.NULL_FLAG) { TypeInfo typeInfo = typeResolver.readTypeInfo(buffer, fieldInfo.type); - return typeInfo.getSerializer().read(buffer, RefMode.NONE); + return typeInfo + .getSerializer() + .read(org.apache.fory.context.ReadContext.current(), RefMode.NONE); } return null; } @@ -1005,7 +1011,7 @@ private void copyFields(T originObj, T newObj) { if (fieldInfos == null) { fieldInfos = buildFieldsInfo(); } - copyFields(fory, fieldInfos, originObj, newObj); + copyFields(fory.getFory(), fieldInfos, originObj, newObj); } public static void copyFields( @@ -1187,8 +1193,8 @@ private SerializationFieldInfo[] buildFieldsInfo() { } } DescriptorGrouper descriptorGrouper = - FieldGroups.buildDescriptorGrouper(fory, descriptors, false, null); - FieldGroups fieldGroups = FieldGroups.buildFieldInfos(fory, descriptorGrouper); + FieldGroups.buildDescriptorGrouper(fory.getFory(), descriptors, false, null); + FieldGroups fieldGroups = FieldGroups.buildFieldInfos(fory.getFory(), descriptorGrouper); fieldInfos = fieldGroups.allFields; if (isRecord) { List fieldNames = 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 3f5bef901d..46de4f9c91 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 @@ -50,9 +50,9 @@ public static final class ObjectArraySerializer extends Serializer { private final int[] stubDims; private final GenericType componentGenericType; - public ObjectArraySerializer(Fory fory, Class cls) { - super(fory, cls); - TypeResolver resolver = fory.getTypeResolver(); + public ObjectArraySerializer(TypeResolver typeResolver, Class cls) { + super(typeResolver, cls); + TypeResolver resolver = typeResolver; if (resolver instanceof ClassResolver) { resolver.setSerializer(cls, this); } @@ -69,23 +69,24 @@ public ObjectArraySerializer(Fory fory, Class cls) { } this.innerType = (Class) innerType; Class componentType = cls.getComponentType(); - componentGenericType = fory.getTypeResolver().buildGenericType(componentType); - if (fory.getTypeResolver().isMonomorphic(componentType)) { + componentGenericType = typeResolver.buildGenericType(componentType); + if (typeResolver.isMonomorphic(componentType)) { if (fory.isCrossLanguage()) { this.componentTypeSerializer = null; } else { - this.componentTypeSerializer = fory.getTypeResolver().getSerializer(componentType); + this.componentTypeSerializer = typeResolver.getSerializer(componentType); } } else { // TODO add TypeInfo cache for non-final component type. this.componentTypeSerializer = null; } this.stubDims = new int[dimension]; - classInfoHolder = fory.getTypeResolver().nilTypeInfoHolder(); + classInfoHolder = typeResolver.nilTypeInfoHolder(); } @Override - public void write(MemoryBuffer buffer, T[] arr) { + public void write(org.apache.fory.context.WriteContext writeContext, T[] arr) { + MemoryBuffer buffer = writeContext.getBuffer(); if (!isJava) { int len = arr.length; buffer.writeVarUint32Small7(len); @@ -104,11 +105,11 @@ public void write(MemoryBuffer buffer, T[] arr) { if (componentSerializer != null) { for (T t : arr) { if (!refResolver.writeRefOrNull(buffer, t)) { - componentSerializer.write(buffer, t); + componentSerializer.write(org.apache.fory.context.WriteContext.current(), t); } } } else { - Fory fory = this.fory; + Fory fory = this.fory.getFory(); ClassResolver classResolver = (ClassResolver) fory.getTypeResolver(); TypeInfo typeInfo = null; Class elemClass = null; @@ -148,7 +149,8 @@ public T[] copy(T[] originArray) { } @Override - public T[] read(MemoryBuffer buffer) { + public T[] read(org.apache.fory.context.ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); if (!isJava) { int numElements = buffer.readVarUint32Small7(); Object[] value = newArray(numElements); @@ -172,7 +174,7 @@ public T[] read(MemoryBuffer buffer) { Object elem; int nextReadRefId = refResolver.tryPreserveRefId(buffer); if (nextReadRefId >= Fory.NOT_NULL_VALUE_FLAG) { - elem = componentTypeSerializer.read(buffer); + elem = componentTypeSerializer.read(org.apache.fory.context.ReadContext.current()); refResolver.setReadObject(nextReadRefId, elem); } else { elem = refResolver.getReadObject(); @@ -180,7 +182,7 @@ public T[] read(MemoryBuffer buffer) { value[i] = elem; } } else { - Fory fory = this.fory; + Fory fory = this.fory.getFory(); TypeInfoHolder classInfoHolder = this.classInfoHolder; for (int i = 0; i < numElements; i++) { int nextReadRefId = refResolver.tryPreserveRefId(buffer); @@ -251,19 +253,20 @@ public MemoryBuffer toBuffer() { public abstract static class PrimitiveArraySerializer extends Serializers.CrossLanguageCompatibleSerializer { - public PrimitiveArraySerializer(Fory fory, Class cls) { - super(fory, cls); + public PrimitiveArraySerializer(TypeResolver typeResolver, Class cls) { + super(typeResolver.getConfig(), cls); } } public static final class BooleanArraySerializer extends PrimitiveArraySerializer { - public BooleanArraySerializer(Fory fory) { - super(fory, boolean[].class); + public BooleanArraySerializer(TypeResolver typeResolver) { + super(typeResolver, boolean[].class); } @Override - public void write(MemoryBuffer buffer, boolean[] value) { + public void write(org.apache.fory.context.WriteContext writeContext, boolean[] value) { + MemoryBuffer buffer = writeContext.getBuffer(); if (fory.getBufferCallback() == null) { int size = Math.multiplyExact(value.length, 1); buffer.writePrimitiveArrayWithSize(value, Platform.BOOLEAN_ARRAY_OFFSET, size); @@ -280,7 +283,8 @@ public boolean[] copy(boolean[] originArray) { } @Override - public boolean[] read(MemoryBuffer buffer) { + public boolean[] read(org.apache.fory.context.ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); if (fory.isPeerOutOfBandEnabled()) { MemoryBuffer buf = fory.readBufferObject(buffer); int size = buf.remaining(); @@ -300,12 +304,13 @@ public boolean[] read(MemoryBuffer buffer) { public static final class ByteArraySerializer extends PrimitiveArraySerializer { - public ByteArraySerializer(Fory fory) { - super(fory, byte[].class); + public ByteArraySerializer(TypeResolver typeResolver) { + super(typeResolver, byte[].class); } @Override - public void write(MemoryBuffer buffer, byte[] value) { + public void write(org.apache.fory.context.WriteContext writeContext, byte[] value) { + MemoryBuffer buffer = writeContext.getBuffer(); if (fory.getBufferCallback() == null) { int size = Math.multiplyExact(value.length, 1); buffer.writePrimitiveArrayWithSize(value, Platform.BYTE_ARRAY_OFFSET, size); @@ -322,7 +327,8 @@ public byte[] copy(byte[] originArray) { } @Override - public byte[] read(MemoryBuffer buffer) { + public byte[] read(org.apache.fory.context.ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); if (fory.isPeerOutOfBandEnabled()) { MemoryBuffer buf = fory.readBufferObject(buffer); int size = buf.remaining(); @@ -340,12 +346,13 @@ public byte[] read(MemoryBuffer buffer) { public static final class CharArraySerializer extends PrimitiveArraySerializer { - public CharArraySerializer(Fory fory) { - super(fory, char[].class); + public CharArraySerializer(TypeResolver typeResolver) { + super(typeResolver, char[].class); } @Override - public void write(MemoryBuffer buffer, char[] value) { + public void write(org.apache.fory.context.WriteContext writeContext, char[] value) { + MemoryBuffer buffer = writeContext.getBuffer(); if (!isJava) { throw new UnsupportedOperationException(); } @@ -380,7 +387,8 @@ public char[] copy(char[] originArray) { } @Override - public char[] read(MemoryBuffer buffer) { + public char[] read(org.apache.fory.context.ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); if (!isJava) { throw new UnsupportedOperationException(); } @@ -421,12 +429,13 @@ private void readCharBySwapEndian(MemoryBuffer buffer, char[] values, int numEle public static final class ShortArraySerializer extends PrimitiveArraySerializer { - public ShortArraySerializer(Fory fory) { - super(fory, short[].class); + public ShortArraySerializer(TypeResolver typeResolver) { + super(typeResolver, short[].class); } @Override - public void write(MemoryBuffer buffer, short[] value) { + public void write(org.apache.fory.context.WriteContext writeContext, short[] value) { + MemoryBuffer buffer = writeContext.getBuffer(); if (fory.getBufferCallback() == null) { int size = Math.multiplyExact(value.length, 2); if (Platform.IS_LITTLE_ENDIAN) { @@ -458,7 +467,8 @@ public short[] copy(short[] originArray) { } @Override - public short[] read(MemoryBuffer buffer) { + public short[] read(org.apache.fory.context.ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); if (fory.isPeerOutOfBandEnabled()) { MemoryBuffer buf = fory.readBufferObject(buffer); int size = buf.remaining(); @@ -496,12 +506,13 @@ private void readInt16BySwapEndian(MemoryBuffer buffer, short[] values, int numE public static final class IntArraySerializer extends PrimitiveArraySerializer { - public IntArraySerializer(Fory fory) { - super(fory, int[].class); + public IntArraySerializer(TypeResolver typeResolver) { + super(typeResolver, int[].class); } @Override - public void write(MemoryBuffer buffer, int[] value) { + public void write(org.apache.fory.context.WriteContext writeContext, int[] value) { + MemoryBuffer buffer = writeContext.getBuffer(); if (fory.getBufferCallback() == null) { if (fory.getConfig().compressIntArray()) { writeInt32Compressed(buffer, value); @@ -537,7 +548,8 @@ public int[] copy(int[] originArray) { } @Override - public int[] read(MemoryBuffer buffer) { + public int[] read(org.apache.fory.context.ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); if (fory.isPeerOutOfBandEnabled()) { MemoryBuffer buf = fory.readBufferObject(buffer); int size = buf.remaining(); @@ -599,15 +611,16 @@ private int[] readInt32Compressed(MemoryBuffer buffer) { public static final class LongArraySerializer extends PrimitiveArraySerializer { private final boolean compressLongArray; - public LongArraySerializer(Fory fory) { - super(fory, long[].class); + public LongArraySerializer(TypeResolver typeResolver) { + super(typeResolver, long[].class); compressLongArray = - fory.getConfig().compressLongArray() - && fory.getConfig().longEncoding() != LongEncoding.FIXED; + typeResolver.getConfig().compressLongArray() + && typeResolver.getConfig().longEncoding() != LongEncoding.FIXED; } @Override - public void write(MemoryBuffer buffer, long[] value) { + public void write(org.apache.fory.context.WriteContext writeContext, long[] value) { + MemoryBuffer buffer = writeContext.getBuffer(); if (fory.getBufferCallback() == null) { if (compressLongArray) { writeInt64Compressed(buffer, value, fory.getConfig().longEncoding()); @@ -643,7 +656,8 @@ public long[] copy(long[] originArray) { } @Override - public long[] read(MemoryBuffer buffer) { + public long[] read(org.apache.fory.context.ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); if (fory.isPeerOutOfBandEnabled()) { MemoryBuffer buf = fory.readBufferObject(buffer); int size = buf.remaining(); @@ -719,12 +733,13 @@ private long[] readInt64Compressed(MemoryBuffer buffer, LongEncoding longEncodin public static final class FloatArraySerializer extends PrimitiveArraySerializer { - public FloatArraySerializer(Fory fory) { - super(fory, float[].class); + public FloatArraySerializer(TypeResolver typeResolver) { + super(typeResolver, float[].class); } @Override - public void write(MemoryBuffer buffer, float[] value) { + public void write(org.apache.fory.context.WriteContext writeContext, float[] value) { + MemoryBuffer buffer = writeContext.getBuffer(); if (fory.getBufferCallback() == null) { int size = Math.multiplyExact(value.length, 4); if (Platform.IS_LITTLE_ENDIAN) { @@ -756,7 +771,8 @@ public float[] copy(float[] originArray) { } @Override - public float[] read(MemoryBuffer buffer) { + public float[] read(org.apache.fory.context.ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); if (fory.isPeerOutOfBandEnabled()) { MemoryBuffer buf = fory.readBufferObject(buffer); int size = buf.remaining(); @@ -794,12 +810,13 @@ private void readFloat32BySwapEndian(MemoryBuffer buffer, float[] values, int nu public static final class DoubleArraySerializer extends PrimitiveArraySerializer { - public DoubleArraySerializer(Fory fory) { - super(fory, double[].class); + public DoubleArraySerializer(TypeResolver typeResolver) { + super(typeResolver, double[].class); } @Override - public void write(MemoryBuffer buffer, double[] value) { + public void write(org.apache.fory.context.WriteContext writeContext, double[] value) { + MemoryBuffer buffer = writeContext.getBuffer(); if (fory.getBufferCallback() == null) { int size = Math.multiplyExact(value.length, 8); if (Platform.IS_LITTLE_ENDIAN) { @@ -831,7 +848,8 @@ public double[] copy(double[] originArray) { } @Override - public double[] read(MemoryBuffer buffer) { + public double[] read(org.apache.fory.context.ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); if (fory.isPeerOutOfBandEnabled()) { MemoryBuffer buf = fory.readBufferObject(buffer); int size = buf.remaining(); @@ -868,12 +886,13 @@ private void readFloat64BySwapEndian(MemoryBuffer buffer, double[] values, int n } public static final class Float16ArraySerializer extends PrimitiveArraySerializer { - public Float16ArraySerializer(Fory fory) { - super(fory, Float16[].class); + public Float16ArraySerializer(TypeResolver typeResolver) { + super(typeResolver, Float16[].class); } @Override - public void write(MemoryBuffer buffer, Float16[] value) { + public void write(org.apache.fory.context.WriteContext writeContext, Float16[] value) { + MemoryBuffer buffer = writeContext.getBuffer(); int length = value.length; for (int i = 0; i < length; i++) { if (value[i] == null) { @@ -908,7 +927,8 @@ public Float16[] copy(Float16[] originArray) { } @Override - public Float16[] read(MemoryBuffer buffer) { + public Float16[] read(org.apache.fory.context.ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); int size = buffer.readVarUint32Small7(); int numElements = size / 2; Float16[] values = new Float16[numElements]; @@ -933,15 +953,16 @@ public static final class StringArraySerializer extends Serializer { private final ForyArrayAsListSerializer collectionSerializer; private final ForyArrayAsListSerializer.ArrayAsList list; - public StringArraySerializer(Fory fory) { - super(fory, String[].class); - stringSerializer = fory.getStringSerializer(); - collectionSerializer = new ForyArrayAsListSerializer(fory); + public StringArraySerializer(TypeResolver typeResolver) { + super(typeResolver, String[].class); + stringSerializer = typeResolver.getStringSerializer(); + collectionSerializer = new ForyArrayAsListSerializer(typeResolver); list = new ForyArrayAsListSerializer.ArrayAsList(0); } @Override - public void write(MemoryBuffer buffer, String[] value) { + public void write(org.apache.fory.context.WriteContext writeContext, String[] value) { + MemoryBuffer buffer = writeContext.getBuffer(); if (!isJava) { int len = value.length; buffer.writeVarUint32Small7(len); @@ -968,7 +989,7 @@ public void write(MemoryBuffer buffer, String[] value) { StringSerializer stringSerializer = this.stringSerializer; if ((flags & CollectionFlags.HAS_NULL) != CollectionFlags.HAS_NULL) { for (String elem : value) { - stringSerializer.write(buffer, elem); + stringSerializer.write(org.apache.fory.context.WriteContext.current(), elem); } } else { for (String elem : value) { @@ -976,7 +997,7 @@ public void write(MemoryBuffer buffer, String[] value) { buffer.writeByte(Fory.NULL_FLAG); } else { buffer.writeByte(Fory.NOT_NULL_VALUE_FLAG); - stringSerializer.write(buffer, elem); + stringSerializer.write(org.apache.fory.context.WriteContext.current(), elem); } } } @@ -990,7 +1011,8 @@ public String[] copy(String[] originArray) { } @Override - public String[] read(MemoryBuffer buffer) { + public String[] read(org.apache.fory.context.ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); if (!isJava) { int numElements = buffer.readVarUint32Small7(); String[] value = new String[numElements]; @@ -1028,35 +1050,35 @@ public String[] read(MemoryBuffer buffer) { public static void registerDefaultSerializers(Fory fory) { TypeResolver resolver = fory.getTypeResolver(); resolver.registerInternalSerializer( - Object[].class, new ObjectArraySerializer<>(fory, Object[].class)); + Object[].class, new ObjectArraySerializer<>(resolver, Object[].class)); resolver.registerInternalSerializer( - Class[].class, new ObjectArraySerializer<>(fory, Class[].class)); - resolver.registerInternalSerializer(byte[].class, new ByteArraySerializer(fory)); + Class[].class, new ObjectArraySerializer<>(resolver, Class[].class)); + resolver.registerInternalSerializer(byte[].class, new ByteArraySerializer(resolver)); resolver.registerInternalSerializer( - Byte[].class, new ObjectArraySerializer<>(fory, Byte[].class)); - resolver.registerInternalSerializer(char[].class, new CharArraySerializer(fory)); + Byte[].class, new ObjectArraySerializer<>(resolver, Byte[].class)); + resolver.registerInternalSerializer(char[].class, new CharArraySerializer(resolver)); resolver.registerInternalSerializer( - Character[].class, new ObjectArraySerializer<>(fory, Character[].class)); - resolver.registerInternalSerializer(short[].class, new ShortArraySerializer(fory)); + Character[].class, new ObjectArraySerializer<>(resolver, Character[].class)); + resolver.registerInternalSerializer(short[].class, new ShortArraySerializer(resolver)); resolver.registerInternalSerializer( - Short[].class, new ObjectArraySerializer<>(fory, Short[].class)); - resolver.registerInternalSerializer(int[].class, new IntArraySerializer(fory)); + Short[].class, new ObjectArraySerializer<>(resolver, Short[].class)); + resolver.registerInternalSerializer(int[].class, new IntArraySerializer(resolver)); resolver.registerInternalSerializer( - Integer[].class, new ObjectArraySerializer<>(fory, Integer[].class)); - resolver.registerInternalSerializer(long[].class, new LongArraySerializer(fory)); + Integer[].class, new ObjectArraySerializer<>(resolver, Integer[].class)); + resolver.registerInternalSerializer(long[].class, new LongArraySerializer(resolver)); resolver.registerInternalSerializer( - Long[].class, new ObjectArraySerializer<>(fory, Long[].class)); - resolver.registerInternalSerializer(float[].class, new FloatArraySerializer(fory)); + Long[].class, new ObjectArraySerializer<>(resolver, Long[].class)); + resolver.registerInternalSerializer(float[].class, new FloatArraySerializer(resolver)); resolver.registerInternalSerializer( - Float[].class, new ObjectArraySerializer<>(fory, Float[].class)); - resolver.registerInternalSerializer(double[].class, new DoubleArraySerializer(fory)); + Float[].class, new ObjectArraySerializer<>(resolver, Float[].class)); + resolver.registerInternalSerializer(double[].class, new DoubleArraySerializer(resolver)); resolver.registerInternalSerializer( - Double[].class, new ObjectArraySerializer<>(fory, Double[].class)); - resolver.registerInternalSerializer(Float16[].class, new Float16ArraySerializer(fory)); - resolver.registerInternalSerializer(boolean[].class, new BooleanArraySerializer(fory)); + Double[].class, new ObjectArraySerializer<>(resolver, Double[].class)); + resolver.registerInternalSerializer(Float16[].class, new Float16ArraySerializer(resolver)); + resolver.registerInternalSerializer(boolean[].class, new BooleanArraySerializer(resolver)); resolver.registerInternalSerializer( - Boolean[].class, new ObjectArraySerializer<>(fory, Boolean[].class)); - resolver.registerInternalSerializer(String[].class, new StringArraySerializer(fory)); + Boolean[].class, new ObjectArraySerializer<>(resolver, Boolean[].class)); + resolver.registerInternalSerializer(String[].class, new StringArraySerializer(resolver)); } // ########################## utils ########################## @@ -1080,19 +1102,22 @@ public abstract static class AbstractUnknownArraySerializer extends Serializer { protected final String className; private final int dims; - public AbstractUnknownArraySerializer(Fory fory, String className, Class stubClass) { - super(fory, stubClass); + public AbstractUnknownArraySerializer( + TypeResolver typeResolver, String className, Class stubClass) { + super(typeResolver, stubClass); this.className = className; this.dims = TypeUtils.getArrayDimensions(stubClass); } @Override - public void write(MemoryBuffer buffer, Object value) { + public void write(org.apache.fory.context.WriteContext writeContext, Object value) { + MemoryBuffer buffer = writeContext.getBuffer(); throw new UnsupportedOperationException(); } @Override - public Object[] read(MemoryBuffer buffer) { + public Object[] read(org.apache.fory.context.ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); switch (dims) { case 1: return read1DArray(buffer); @@ -1195,17 +1220,18 @@ private Object[] read3DArray(MemoryBuffer buffer) { public static final class UnknownArraySerializer extends AbstractUnknownArraySerializer { private final Serializer componentSerializer; - public UnknownArraySerializer(Fory fory, Class cls) { - this(fory, "Unknown", cls); + public UnknownArraySerializer(TypeResolver typeResolver, Class cls) { + this(typeResolver, "Unknown", cls); } - public UnknownArraySerializer(Fory fory, String className, Class cls) { - super(fory, className, cls); + public UnknownArraySerializer(TypeResolver typeResolver, String className, Class cls) { + super(typeResolver, className, cls); if (TypeUtils.getArrayComponent(cls).isEnum()) { - componentSerializer = new UnknownClassSerializers.UnknownEnumSerializer(fory); + componentSerializer = new UnknownClassSerializers.UnknownEnumSerializer(typeResolver); } else { - if (fory.getConfig().getCompatibleMode() == CompatibleMode.COMPATIBLE) { - componentSerializer = new ObjectSerializer<>(fory, UnknownClass.UnknownEmptyStruct.class); + if (typeResolver.getConfig().getCompatibleMode() == CompatibleMode.COMPATIBLE) { + componentSerializer = + new ObjectSerializer<>(typeResolver, UnknownClass.UnknownEmptyStruct.class); } else { componentSerializer = null; } @@ -1218,7 +1244,7 @@ protected Object readInnerElement(MemoryBuffer buffer) { throw new IllegalStateException( String.format("Class %s should serialize elements as non-morphic", className)); } - return componentSerializer.read(buffer); + return componentSerializer.read(org.apache.fory.context.ReadContext.current()); } } } 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 85219fa313..f3a8ea4ea1 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 @@ -21,9 +21,9 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; -import org.apache.fory.Fory; import org.apache.fory.memory.ByteBufferUtil; import org.apache.fory.memory.MemoryBuffer; +import org.apache.fory.resolver.TypeResolver; /** Serializers for buffer related classes. */ public class BufferSerializers { @@ -35,12 +35,13 @@ public class BufferSerializers { public static final class ByteBufferSerializer extends Serializers.CrossLanguageCompatibleSerializer { - public ByteBufferSerializer(Fory fory, Class cls) { - super(fory, cls); + public ByteBufferSerializer(TypeResolver typeResolver, Class cls) { + super(typeResolver.getConfig(), cls); } @Override - public void write(MemoryBuffer buffer, ByteBuffer value) { + public void write(org.apache.fory.context.WriteContext writeContext, ByteBuffer value) { + MemoryBuffer buffer = writeContext.getBuffer(); fory.writeBufferObject(buffer, new BufferObject.ByteBufferBufferObject(value)); } @@ -53,7 +54,8 @@ public ByteBuffer copy(ByteBuffer value) { } @Override - public ByteBuffer read(MemoryBuffer buffer) { + public ByteBuffer read(org.apache.fory.context.ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); MemoryBuffer newBuffer = fory.readBufferObject(buffer); int readerIndex = newBuffer.readerIndex(); int size = newBuffer.remaining(); diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/CodegenSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/CodegenSerializer.java index af8126786d..6501920cb8 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/CodegenSerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/CodegenSerializer.java @@ -26,6 +26,7 @@ import org.apache.fory.builder.CodecUtils; import org.apache.fory.builder.Generated; import org.apache.fory.memory.MemoryBuffer; +import org.apache.fory.resolver.TypeResolver; /** Util for JIT Serialization. */ @SuppressWarnings("UnstableApiUsage") @@ -69,18 +70,18 @@ public static final class LazyInitBeanSerializer extends AbstractObjectSerial private Serializer serializer; private Serializer interpreterSerializer; - public LazyInitBeanSerializer(Fory fory, Class cls) { - super(fory, cls); + public LazyInitBeanSerializer(TypeResolver typeResolver, Class cls) { + super(typeResolver, cls); } @Override - public void write(MemoryBuffer buffer, T value) { - getOrCreateGeneratedSerializer().write(buffer, value); + public void write(org.apache.fory.context.WriteContext writeContext, T value) { + getOrCreateGeneratedSerializer().write(writeContext, value); } @Override - public T read(MemoryBuffer buffer) { - return getOrCreateGeneratedSerializer().read(buffer); + public T read(org.apache.fory.context.ReadContext readContext) { + return getOrCreateGeneratedSerializer().read(readContext); } @SuppressWarnings({"rawtypes"}) @@ -99,7 +100,7 @@ private Serializer getOrCreateGeneratedSerializer() { // jit not finished, avoid recursive call current serializer. Class sc = fory.getTypeResolver().getSerializerClass(type, false); fory.getTypeResolver().getTypeInfo(type).setSerializer(this); - return interpreterSerializer = Serializers.newSerializer(fory, type, sc); + return interpreterSerializer = Serializers.newSerializer(fory.getFory(), type, sc); } else { Class sc = fory.getTypeResolver().getSerializerClass(type); fory.getTypeResolver().getTypeInfo(type).setSerializer(this); @@ -108,7 +109,7 @@ private Serializer getOrCreateGeneratedSerializer() { "Expect jit serializer but got %s for class %s", sc, type); - serializer = Serializers.newSerializer(fory, type, sc); + serializer = Serializers.newSerializer(fory.getFory(), type, sc); fory.getTypeResolver().setSerializer(type, serializer); return serializer; } diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/CopyOnlyObjectSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/CopyOnlyObjectSerializer.java index b90f911475..ba80c376f7 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/CopyOnlyObjectSerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/CopyOnlyObjectSerializer.java @@ -20,6 +20,7 @@ package org.apache.fory.serializer; import org.apache.fory.Fory; +import org.apache.fory.resolver.TypeResolver; import org.apache.fory.exception.InsecureException; import org.apache.fory.memory.MemoryBuffer; @@ -30,17 +31,19 @@ * reuses {@link AbstractObjectSerializer}'s field-copy implementation. */ public final class CopyOnlyObjectSerializer extends AbstractObjectSerializer { - public CopyOnlyObjectSerializer(Fory fory, Class type) { - super(fory, type); + public CopyOnlyObjectSerializer(TypeResolver typeResolver, Class type) { + super(typeResolver, type); } @Override - public void write(MemoryBuffer buffer, T value) { + public void write(org.apache.fory.context.WriteContext writeContext, T value) { + MemoryBuffer buffer = writeContext.getBuffer(); throw insecureException(); } @Override - public T read(MemoryBuffer buffer) { + public T read(org.apache.fory.context.ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); throw insecureException(); } diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/DeferedLazySerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/DeferedLazySerializer.java index 2e3296ca2d..912b4dfbab 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/DeferedLazySerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/DeferedLazySerializer.java @@ -20,9 +20,9 @@ package org.apache.fory.serializer; import java.util.function.Supplier; -import org.apache.fory.Fory; import org.apache.fory.collection.Tuple2; import org.apache.fory.memory.MemoryBuffer; +import org.apache.fory.resolver.TypeResolver; @SuppressWarnings({"rawtypes", "unchecked"}) public class DeferedLazySerializer extends Serializer { @@ -30,19 +30,21 @@ public class DeferedLazySerializer extends Serializer { private Serializer serializer; public DeferedLazySerializer( - Fory fory, Class type, Supplier> serializerSupplier) { - super(fory, type); + TypeResolver typeResolver, Class type, Supplier> serializerSupplier) { + super(typeResolver, type); this.serializerSupplier = serializerSupplier; } @Override - public void write(MemoryBuffer buffer, Object value) { - getSerializer().write(buffer, value); + public void write(org.apache.fory.context.WriteContext writeContext, Object value) { + MemoryBuffer buffer = writeContext.getBuffer(); + getSerializer().write(writeContext, value); } @Override - public Object read(MemoryBuffer buffer) { - return getSerializer().read(buffer); + public Object read(org.apache.fory.context.ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); + return getSerializer().read(readContext); } private Serializer getSerializer() { @@ -75,8 +77,10 @@ public Object copy(Object value) { public static class DeferredLazyObjectSerializer extends DeferedLazySerializer { public DeferredLazyObjectSerializer( - Fory fory, Class type, Supplier> serializerSupplier) { - super(fory, type, serializerSupplier); + TypeResolver typeResolver, + Class type, + Supplier> serializerSupplier) { + super(typeResolver, type, serializerSupplier); } } } 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 5556ad7b8f..63290f24c8 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 @@ -20,12 +20,12 @@ package org.apache.fory.serializer; import java.util.Arrays; -import org.apache.fory.Fory; import org.apache.fory.collection.ForyObjectMap; import org.apache.fory.memory.MemoryBuffer; import org.apache.fory.meta.Encoders; import org.apache.fory.resolver.MetaStringRef; import org.apache.fory.resolver.MetaStringResolver; +import org.apache.fory.resolver.TypeResolver; import org.apache.fory.util.Preconditions; @SuppressWarnings("rawtypes") @@ -35,9 +35,9 @@ public class EnumSerializer extends ImmutableSerializer { private final ForyObjectMap metaStringtoEnumRepresentation; private final MetaStringRef[] metaStringStateArrByEnumOrdinal; - public EnumSerializer(Fory fory, Class cls) { - super(fory, cls, false); - metaStringResolver = fory.getMetaStringResolver(); + public EnumSerializer(TypeResolver typeResolver, Class cls) { + super(typeResolver, cls, false); + metaStringResolver = this.fory.getFory().getMetaStringResolver(); if (cls.isEnum()) { enumConstants = cls.getEnumConstants(); } else { @@ -72,7 +72,8 @@ public EnumSerializer(Fory fory, Class cls) { } @Override - public void write(MemoryBuffer buffer, Enum value) { + public void write(org.apache.fory.context.WriteContext writeContext, Enum value) { + MemoryBuffer buffer = writeContext.getBuffer(); if (isJava && fory.getConfig().serializeEnumByName()) { MetaStringRef metaStringState = metaStringStateArrByEnumOrdinal[value.ordinal()]; metaStringResolver.writeMetaStringBytes(buffer, metaStringState); @@ -82,7 +83,8 @@ public void write(MemoryBuffer buffer, Enum value) { } @Override - public Enum read(MemoryBuffer buffer) { + public Enum read(org.apache.fory.context.ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); if (isJava && fory.getConfig().serializeEnumByName()) { MetaStringRef metaStringState = metaStringResolver.readMetaStringBytes(buffer); Enum e = metaStringtoEnumRepresentation.get(metaStringState); diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/ExternalizableSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/ExternalizableSerializer.java index a0c3b501c3..59f98295d3 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/ExternalizableSerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/ExternalizableSerializer.java @@ -22,6 +22,7 @@ import java.io.Externalizable; import java.io.IOException; import org.apache.fory.Fory; +import org.apache.fory.resolver.TypeResolver; import org.apache.fory.io.MemoryBufferObjectInput; import org.apache.fory.io.MemoryBufferObjectOutput; import org.apache.fory.memory.MemoryBuffer; @@ -33,14 +34,15 @@ public class ExternalizableSerializer private final MemoryBufferObjectInput objectInput; private final MemoryBufferObjectOutput objectOutput; - public ExternalizableSerializer(Fory fory, Class cls) { - super(fory, cls); - objectInput = new MemoryBufferObjectInput(fory, null); - objectOutput = new MemoryBufferObjectOutput(fory, null); + public ExternalizableSerializer(TypeResolver typeResolver, Class cls) { + super(typeResolver, cls); + objectInput = new MemoryBufferObjectInput(fory.getFory(), null); + objectOutput = new MemoryBufferObjectOutput(fory.getFory(), null); } @Override - public void write(MemoryBuffer buffer, T value) { + public void write(org.apache.fory.context.WriteContext writeContext, T value) { + MemoryBuffer buffer = writeContext.getBuffer(); if (!isJava) { throw new UnsupportedOperationException("Externalizable can only be used in java"); } @@ -53,7 +55,8 @@ public void write(MemoryBuffer buffer, T value) { } @Override - public T read(MemoryBuffer buffer) { + public T read(org.apache.fory.context.ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); T t = objectCreator.newInstance(); refResolver.reference(t); objectInput.setBuffer(buffer); diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/FieldGroups.java b/java/fory-core/src/main/java/org/apache/fory/serializer/FieldGroups.java index a60a45ac2f..2fb02f0489 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/FieldGroups.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/FieldGroups.java @@ -181,7 +181,8 @@ public static final class SerializationFieldInfo { && !fory.isCompatible() && typeInfo.getSerializer() instanceof ReplaceResolveSerializer) { // overwrite replace resolve serializer for final field - typeInfo.setSerializer(new FinalFieldReplaceResolveSerializer(fory, typeInfo.getCls())); + typeInfo.setSerializer( + new FinalFieldReplaceResolveSerializer(resolver, typeInfo.getCls())); } } else { typeInfo = null; diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/FinalFieldReplaceResolveSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/FinalFieldReplaceResolveSerializer.java index 2a3a99fc7f..b65565787d 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/FinalFieldReplaceResolveSerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/FinalFieldReplaceResolveSerializer.java @@ -19,9 +19,9 @@ package org.apache.fory.serializer; -import org.apache.fory.Fory; import org.apache.fory.config.CompatibleMode; import org.apache.fory.memory.MemoryBuffer; +import org.apache.fory.resolver.TypeResolver; /** * Serializer for class which: - has jdk `writeReplace`/`readResolve` method defined, - is a final @@ -31,23 +31,23 @@ @SuppressWarnings({"unchecked", "rawtypes"}) public class FinalFieldReplaceResolveSerializer extends ReplaceResolveSerializer { - public FinalFieldReplaceResolveSerializer(Fory fory, Class type) { + public FinalFieldReplaceResolveSerializer(TypeResolver typeResolver, Class type) { // the serializer does not write class info // and does not set itself for the provided class // see checks in ReplaceResolveSerializer constructor - super(fory, type, true, false); + super(typeResolver, type, true, false); } @Override protected void writeObject( MemoryBuffer buffer, Object value, MethodInfoCache jdkMethodInfoCache) { - jdkMethodInfoCache.objectSerializer.write(buffer, value); + jdkMethodInfoCache.objectSerializer.write(org.apache.fory.context.WriteContext.current(), value); } @Override protected Object readObject(MemoryBuffer buffer) { MethodInfoCache jdkMethodInfoCache = getMethodInfoCache(type); - Object o = jdkMethodInfoCache.objectSerializer.read(buffer); + Object o = jdkMethodInfoCache.objectSerializer.read(org.apache.fory.context.ReadContext.current()); ReplaceResolveInfo replaceResolveInfo = jdkMethodInfoCache.info; if (replaceResolveInfo.readResolveMethod == null) { return o; 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 e92aef336d..baa9b58b93 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 @@ -19,23 +19,25 @@ package org.apache.fory.serializer; -import org.apache.fory.Fory; +import org.apache.fory.config.Config; import org.apache.fory.memory.MemoryBuffer; import org.apache.fory.type.Float16; public final class Float16Serializer extends ImmutableSerializer { - public Float16Serializer(Fory fory) { - super(fory, Float16.class); + public Float16Serializer(Config config) { + super(config, Float16.class); } @Override - public void write(MemoryBuffer buffer, Float16 value) { + public void write(org.apache.fory.context.WriteContext writeContext, Float16 value) { + MemoryBuffer buffer = writeContext.getBuffer(); buffer.writeInt16(value.toBits()); } @Override - public Float16 read(MemoryBuffer buffer) { + public Float16 read(org.apache.fory.context.ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); return Float16.fromBits(buffer.readInt16()); } } diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/ForwardSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/ForwardSerializer.java deleted file mode 100644 index 11e3615d8d..0000000000 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/ForwardSerializer.java +++ /dev/null @@ -1,280 +0,0 @@ -/* - * 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 java.nio.ByteBuffer; -import java.util.Collections; -import java.util.IdentityHashMap; -import java.util.Set; -import java.util.function.Consumer; -import javax.annotation.concurrent.ThreadSafe; -import org.apache.fory.Fory; -import org.apache.fory.config.Language; -import org.apache.fory.memory.MemoryBuffer; -import org.apache.fory.memory.MemoryUtils; -import org.apache.fory.memory.Platform; -import org.apache.fory.util.LoaderBinding; -import org.apache.fory.util.LoaderBinding.StagingType; - -/** - * A thread-safe serializer used to forward serialization to different serializer implementation. - */ -@ThreadSafe -@SuppressWarnings("unchecked") -public class ForwardSerializer { - - public abstract static class SerializerProxy { - /** Register custom serializers should be done in this method. */ - protected abstract T newSerializer(); - - protected void setClassLoader(T serializer, ClassLoader classLoader) { - throw new UnsupportedOperationException(); - } - - public void setClassLoader(T serializer, ClassLoader classLoader, StagingType stagingType) { - setClassLoader(serializer, classLoader); - } - - protected ClassLoader getClassLoader(T serializer) { - throw new UnsupportedOperationException(); - } - - public void clearClassLoader(T serializer, ClassLoader loader) {} - - protected void register(T serializer, Class clz) { - throw new UnsupportedOperationException(); - } - - protected void register(T serializer, Class clz, long id) { - throw new UnsupportedOperationException(); - } - - protected abstract byte[] serialize(T serializer, Object obj); - - protected MemoryBuffer serialize(T serializer, MemoryBuffer buffer, Object obj) { - byte[] bytes = serialize(serializer, obj); - buffer.writeBytes(bytes); - return buffer; - } - - protected ByteBuffer serialize(T serializer, ByteBuffer buffer, Object obj) { - byte[] bytes = serialize(serializer, obj); - buffer.put(bytes); - return buffer; - } - - protected abstract Object deserialize(T serializer, byte[] bytes); - - protected Object deserialize(T serializer, long address, int size) { - byte[] bytes = new byte[size]; - Platform.copyMemory(null, address, bytes, Platform.BYTE_ARRAY_OFFSET, size); - return deserialize(serializer, bytes); - } - - protected Object deserialize(T serializer, ByteBuffer byteBuffer) { - return deserialize(serializer, MemoryUtils.wrap(byteBuffer)); - } - - protected Object deserialize(T serializer, MemoryBuffer buffer) { - byte[] bytes = buffer.getRemainingBytes(); - return deserialize(serializer, bytes); - } - - protected Object copy(T serializer, Object obj) { - throw new UnsupportedOperationException(); - } - } - - public static class DefaultForyProxy extends SerializerProxy { - - private final ThreadLocal bufferLocal = - ThreadLocal.withInitial(() -> MemoryUtils.buffer(32)); - - /** Override this method to register custom serializers. */ - @Override - protected LoaderBinding newSerializer() { - LoaderBinding loaderBinding = new LoaderBinding(this::newForySerializer); - loaderBinding.setClassLoader(Thread.currentThread().getContextClassLoader()); - return loaderBinding; - } - - protected Fory newForySerializer(ClassLoader loader) { - return Fory.builder() - .withLanguage(Language.JAVA) - .withRefTracking(true) - .withClassLoader(loader) - .requireClassRegistration(false) - .build(); - } - - @Override - protected void setClassLoader(LoaderBinding binding, ClassLoader classLoader) { - binding.setClassLoader(classLoader); - } - - @Override - public void setClassLoader( - LoaderBinding binding, ClassLoader classLoader, StagingType stagingType) { - binding.setClassLoader(classLoader, stagingType); - } - - @Override - protected ClassLoader getClassLoader(LoaderBinding binding) { - return binding.getClassLoader(); - } - - @Override - public void clearClassLoader(LoaderBinding loaderBinding, ClassLoader loader) { - loaderBinding.clearClassLoader(loader); - } - - @Override - protected void register(LoaderBinding binding, Class clz) { - binding.register(clz); - } - - @Override - protected void register(LoaderBinding binding, Class clz, long id) { - binding.register(clz, id); - } - - @Override - protected byte[] serialize(LoaderBinding binding, Object obj) { - MemoryBuffer buffer = bufferLocal.get(); - buffer.writerIndex(0); - binding.get().serialize(buffer, obj); - return buffer.getBytes(0, buffer.writerIndex()); - } - - @Override - protected MemoryBuffer serialize(LoaderBinding binding, MemoryBuffer buffer, Object obj) { - binding.get().serialize(buffer, obj); - return buffer; - } - - @Override - protected Object deserialize(LoaderBinding serializer, byte[] bytes) { - return serializer.get().deserialize(bytes); - } - - @Override - protected Object deserialize(LoaderBinding serializer, MemoryBuffer buffer) { - return serializer.get().deserialize(buffer); - } - - @Override - protected Object copy(LoaderBinding serializer, Object obj) { - return serializer.get().copy(obj); - } - } - - private final SerializerProxy proxy; - private final ThreadLocal serializerLocal; - private Set serializerSet = Collections.newSetFromMap(new IdentityHashMap<>()); - private Consumer serializerCallback = obj -> {}; - - public ForwardSerializer(SerializerProxy proxy) { - this.proxy = proxy; - serializerLocal = - ThreadLocal.withInitial( - () -> { - Object serializer = proxy.newSerializer(); - synchronized (ForwardSerializer.this) { - serializerCallback.accept(serializer); - } - serializerSet.add(serializer); - return serializer; - }); - } - - /** Set classLoader of serializer for current thread only. */ - public void setClassLoader(ClassLoader classLoader) { - proxy.setClassLoader(serializerLocal.get(), classLoader); - } - - public void setClassLoader(ClassLoader classLoader, StagingType stagingType) { - proxy.setClassLoader(serializerLocal.get(), classLoader, stagingType); - } - - /** Returns classLoader of serializer for current thread. */ - public ClassLoader getClassLoader() { - return proxy.getClassLoader(serializerLocal.get()); - } - - /** - * Clean up classloader set by {@link #setClassLoader(ClassLoader, StagingType)}, - * classLoader - * won't be referenced by {@link Fory} after this call and can be gc if it's not - * referenced by other objects. - */ - public void clearClassLoader(ClassLoader loader) { - proxy.clearClassLoader(serializerLocal.get(), loader); - } - - public synchronized void register(Class clz) { - serializerSet.forEach(serializer -> proxy.register(serializer, clz)); - serializerCallback = - serializerCallback.andThen( - serializer -> { - proxy.register(serializer, clz); - }); - } - - public synchronized void register(Class clz, long id) { - serializerSet.forEach(serializer -> proxy.register(serializer, clz, id)); - serializerCallback = - serializerCallback.andThen( - serializer -> { - proxy.register(serializer, clz, id); - }); - } - - public byte[] serialize(Object obj) { - return proxy.serialize(serializerLocal.get(), obj); - } - - public MemoryBuffer serialize(MemoryBuffer buffer, Object obj) { - return proxy.serialize(serializerLocal.get(), buffer, obj); - } - - public ByteBuffer serialize(ByteBuffer buffer, Object obj) { - return proxy.serialize(serializerLocal.get(), buffer, obj); - } - - public T deserialize(byte[] bytes) { - return (T) proxy.deserialize(serializerLocal.get(), bytes); - } - - public T deserialize(long address, int size) { - return (T) proxy.deserialize(serializerLocal.get(), address, size); - } - - public T deserialize(ByteBuffer byteBuffer) { - return (T) proxy.deserialize(serializerLocal.get(), byteBuffer); - } - - public T deserialize(MemoryBuffer buffer) { - return (T) proxy.deserialize(serializerLocal.get(), buffer); - } - - public T copy(T obj) { - return (T) proxy.copy(serializerLocal.get(), obj); - } -} diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/ForyCopyableSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/ForyCopyableSerializer.java index d372a86889..ab5d6de31a 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/ForyCopyableSerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/ForyCopyableSerializer.java @@ -19,6 +19,7 @@ package org.apache.fory.serializer; +import org.apache.fory.config.Config; import org.apache.fory.Fory; import org.apache.fory.ForyCopyable; import org.apache.fory.memory.MemoryBuffer; @@ -28,23 +29,25 @@ public class ForyCopyableSerializer extends Serializer { private final Serializer serializer; - public ForyCopyableSerializer(Fory fory, Class type, Serializer serializer) { - super(fory, type); + public ForyCopyableSerializer(Config config, Class type, Serializer serializer) { + super(config, type); this.serializer = serializer; } @Override - public void write(MemoryBuffer buffer, T value) { - serializer.write(buffer, value); + public void write(org.apache.fory.context.WriteContext writeContext, T value) { + MemoryBuffer buffer = writeContext.getBuffer(); + serializer.write(org.apache.fory.context.WriteContext.current(), value); } @Override public T copy(T obj) { - return ((ForyCopyable) obj).copy(fory); + return ((ForyCopyable) obj).copy(fory.getFory()); } @Override - public T read(MemoryBuffer buffer) { - return serializer.read(buffer); + public T read(org.apache.fory.context.ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); + return serializer.read(org.apache.fory.context.ReadContext.current()); } } diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/ImmutableSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/ImmutableSerializer.java index fd9a42c346..ebe48e7404 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/ImmutableSerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/ImmutableSerializer.java @@ -19,7 +19,8 @@ package org.apache.fory.serializer; -import org.apache.fory.Fory; +import org.apache.fory.config.Config; +import org.apache.fory.resolver.TypeResolver; /** * Serializer for immutable objects. @@ -28,15 +29,34 @@ */ public abstract class ImmutableSerializer extends Serializer { - public ImmutableSerializer(Fory fory, Class type) { - super(fory, type, true); + public ImmutableSerializer(Config config, Class type) { + super(config, type, true); } - public ImmutableSerializer(Fory fory, Class type, boolean needToWriteRef) { - super(fory, type, needToWriteRef, true); + public ImmutableSerializer(TypeResolver typeResolver, Class type) { + super(typeResolver, type, true); } - public ImmutableSerializer(Fory fory, Class type, boolean needToWriteRef, boolean immutable) { - super(fory, type, needToWriteRef, immutable); + public ImmutableSerializer(Config config, Class type, boolean needToWriteRef) { + super(config, type, needToWriteRef, true); + } + + public ImmutableSerializer(TypeResolver typeResolver, Class type, boolean needToWriteRef) { + super(typeResolver, type, needToWriteRef, true); + } + + public ImmutableSerializer( + Config config, Class type, boolean needToWriteRef, boolean immutable) { + super(config, type, needToWriteRef, immutable); + } + + public ImmutableSerializer( + TypeResolver typeResolver, Class type, boolean needToWriteRef, boolean immutable) { + super(typeResolver, type, needToWriteRef, immutable); + } + + @Override + public boolean threadSafe() { + return true; } } diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/JavaSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/JavaSerializer.java index d6096f47d1..d4fc997a75 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/JavaSerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/JavaSerializer.java @@ -29,6 +29,7 @@ import java.lang.reflect.Modifier; import java.nio.ByteBuffer; import org.apache.fory.Fory; +import org.apache.fory.resolver.TypeResolver; import org.apache.fory.io.ClassLoaderObjectInputStream; import org.apache.fory.io.MemoryBufferObjectInput; import org.apache.fory.io.MemoryBufferObjectOutput; @@ -53,8 +54,8 @@ public class JavaSerializer extends AbstractObjectSerializer { private final MemoryBufferObjectInput objectInput; private final MemoryBufferObjectOutput objectOutput; - public JavaSerializer(Fory fory, Class cls) { - super(fory, cls); + public JavaSerializer(TypeResolver typeResolver, Class cls) { + super(typeResolver, cls); // TODO(chgaokunyang) enable this check when ObjectSerializer is implemented. // Preconditions.checkArgument(ClassResolver.requireJavaSerialization(cls)); if (cls != SerializedLambda.class) { @@ -65,12 +66,13 @@ public JavaSerializer(Fory fory, Class cls) { Serializer.class.getName(), Externalizable.class.getName()); } - objectInput = new MemoryBufferObjectInput(fory, null); - objectOutput = new MemoryBufferObjectOutput(fory, null); + objectInput = new MemoryBufferObjectInput(fory.getFory(), null); + objectOutput = new MemoryBufferObjectOutput(fory.getFory(), null); } @Override - public void write(MemoryBuffer buffer, Object value) { + public void write(org.apache.fory.context.WriteContext writeContext, Object value) { + MemoryBuffer buffer = writeContext.getBuffer(); try { objectOutput.setBuffer(buffer); ObjectOutputStream objectOutputStream = @@ -87,7 +89,8 @@ public void write(MemoryBuffer buffer, Object value) { } @Override - public Object read(MemoryBuffer buffer) { + public Object read(org.apache.fory.context.ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); try { objectInput.setBuffer(buffer); ObjectInputStream objectInputStream = diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/JdkProxySerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/JdkProxySerializer.java index 273a860ef2..4b2bd3ec26 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/JdkProxySerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/JdkProxySerializer.java @@ -23,11 +23,11 @@ import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; -import org.apache.fory.Fory; import org.apache.fory.memory.MemoryBuffer; import org.apache.fory.memory.Platform; import org.apache.fory.reflect.ReflectionUtils; import org.apache.fory.resolver.RefResolver; +import org.apache.fory.resolver.TypeResolver; import org.apache.fory.util.GraalvmSupport; import org.apache.fory.util.Preconditions; @@ -60,8 +60,8 @@ private interface StubInterface { Proxy.newProxyInstance( Serializer.class.getClassLoader(), new Class[] {StubInterface.class}, STUB_HANDLER); - public JdkProxySerializer(Fory fory, Class cls) { - super(fory, cls); + public JdkProxySerializer(TypeResolver typeResolver, Class cls) { + super(typeResolver, cls); if (cls != ReplaceStub.class) { // Skip proxy class validation in GraalVM native image runtime to avoid issues with proxy // detection @@ -72,7 +72,8 @@ public JdkProxySerializer(Fory fory, Class cls) { } @Override - public void write(MemoryBuffer buffer, Object value) { + public void write(org.apache.fory.context.WriteContext writeContext, Object value) { + MemoryBuffer buffer = writeContext.getBuffer(); fory.writeRef(buffer, value.getClass().getInterfaces()); fory.writeRef(buffer, Proxy.getInvocationHandler(value)); } @@ -90,7 +91,8 @@ public Object copy(Object value) { } @Override - public Object read(MemoryBuffer buffer) { + public Object read(org.apache.fory.context.ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); final RefResolver resolver = fory.getRefResolver(); final int refId = resolver.lastPreservedRefId(); final Class[] interfaces = (Class[]) fory.readRef(buffer); diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/LambdaSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/LambdaSerializer.java index 6860742fe5..7019a0d077 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/LambdaSerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/LambdaSerializer.java @@ -23,9 +23,9 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.SerializedLambda; import java.lang.reflect.Method; -import org.apache.fory.Fory; import org.apache.fory.collection.ClassValueCache; import org.apache.fory.memory.MemoryBuffer; +import org.apache.fory.resolver.TypeResolver; import org.apache.fory.util.Preconditions; import org.apache.fory.util.function.SerializableFunction; import org.apache.fory.util.unsafe._JDKAccess; @@ -49,11 +49,11 @@ private static Class stubLambdaClass() { return function.getClass(); } - public LambdaSerializer(Fory fory, Class cls) { - super(fory, cls); + public LambdaSerializer(TypeResolver typeResolver, Class cls) { + super(typeResolver, cls); serializedLambdaSerializer = (SerializedLambdaSerializer) - fory.getTypeResolver().getSerializer(SerializedLambdaSerializer.SERIALIZED_LAMBDA); + typeResolver.getSerializer(SerializedLambdaSerializer.SERIALIZED_LAMBDA); if (cls == ReplaceStub.class) { writeReplaceHandle = null; return; @@ -74,8 +74,9 @@ public LambdaSerializer(Fory fory, Class cls) { } @Override - public void write(MemoryBuffer buffer, Object value) { - serializedLambdaSerializer.write(buffer, extractSerializedLambda(value, "serialize")); + public void write(org.apache.fory.context.WriteContext writeContext, Object value) { + MemoryBuffer buffer = writeContext.getBuffer(); + serializedLambdaSerializer.write(org.apache.fory.context.WriteContext.current(), extractSerializedLambda(value, "serialize")); } @Override @@ -86,7 +87,8 @@ public Object copy(Object value) { } @Override - public Object read(MemoryBuffer buffer) { + public Object read(org.apache.fory.context.ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); try { return SerializedLambdaSerializer.readResolve( serializedLambdaSerializer.readUnresolved(buffer)); diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/LazySerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/LazySerializer.java index 60626a9836..d6729d1d58 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/LazySerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/LazySerializer.java @@ -20,21 +20,22 @@ package org.apache.fory.serializer; import java.util.function.Supplier; -import org.apache.fory.Fory; import org.apache.fory.memory.MemoryBuffer; +import org.apache.fory.resolver.TypeResolver; @SuppressWarnings({"rawtypes", "unchecked"}) public class LazySerializer extends Serializer { private final Supplier serializerSupplier; private Serializer serializer; - public LazySerializer(Fory fory, Class type, Supplier serializerSupplier) { - super(fory, type); + public LazySerializer(TypeResolver typeResolver, Class type, Supplier serializerSupplier) { + super(typeResolver, type); this.serializerSupplier = serializerSupplier; } @Override - public void write(MemoryBuffer buffer, Object value) { + public void write(org.apache.fory.context.WriteContext writeContext, Object value) { + MemoryBuffer buffer = writeContext.getBuffer(); if (serializer == null) { serializer = serializerSupplier.get(); fory.getTypeResolver().setSerializer(value.getClass(), serializer); @@ -42,16 +43,17 @@ public void write(MemoryBuffer buffer, Object value) { fory.getTypeResolver().getTypeInfo(value.getClass()).setSerializer(serializer); } } - serializer.write(buffer, value); + serializer.write(org.apache.fory.context.WriteContext.current(), value); } @Override - public Object read(MemoryBuffer buffer) { + public Object read(org.apache.fory.context.ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); boolean unInit = serializer == null; if (unInit) { serializer = serializerSupplier.get(); } - Object value = serializer.read(buffer); + Object value = serializer.read(org.apache.fory.context.ReadContext.current()); if (unInit) { fory.getTypeResolver().setSerializer(value.getClass(), serializer); if (!isJava) { @@ -71,8 +73,9 @@ public Object copy(Object value) { } public static class LazyObjectSerializer extends LazySerializer { - public LazyObjectSerializer(Fory fory, Class type, Supplier serializerSupplier) { - super(fory, type, serializerSupplier); + public LazyObjectSerializer( + TypeResolver typeResolver, Class type, Supplier serializerSupplier) { + super(typeResolver, type, serializerSupplier); } } } 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 46e8a5bc34..dc717e1751 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 @@ -22,7 +22,7 @@ import java.util.HashMap; import java.util.Locale; import java.util.Map; -import org.apache.fory.Fory; +import org.apache.fory.config.Config; import org.apache.fory.collection.Tuple3; import org.apache.fory.memory.MemoryBuffer; @@ -65,17 +65,19 @@ private static void populateMap(Map, Locale> map, map.put(Tuple3.of(locale.getCountry(), locale.getLanguage(), locale.getVariant()), locale); } - public LocaleSerializer(Fory fory) { - super(fory, Locale.class); + public LocaleSerializer(Config config) { + super(config, Locale.class); } - public void write(MemoryBuffer buffer, Locale l) { + public void write(org.apache.fory.context.WriteContext writeContext, Locale l) { + MemoryBuffer buffer = writeContext.getBuffer(); fory.writeString(buffer, l.getLanguage()); fory.writeString(buffer, l.getCountry()); fory.writeString(buffer, l.getVariant()); } - public Locale read(MemoryBuffer buffer) { + public Locale read(org.apache.fory.context.ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); String language = fory.readString(buffer); String country = fory.readString(buffer); String variant = fory.readString(buffer); diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/MetaSharedLayerSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/MetaSharedLayerSerializer.java index 8c47e51269..32d37e5eac 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/MetaSharedLayerSerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/MetaSharedLayerSerializer.java @@ -55,17 +55,17 @@ public class MetaSharedLayerSerializer extends MetaSharedLayerSerializerBase< /** * Creates a new MetaSharedLayerSerializer. * - * @param fory the Fory instance + * @param typeResolver the type resolver for this serializer * @param type the target class for this layer * @param layerTypeDef the TypeDef for this layer only (resolveParent=false) * @param layerMarkerClass the generated marker class used as key in metaContext.classMap */ public MetaSharedLayerSerializer( - Fory fory, Class type, TypeDef layerTypeDef, Class layerMarkerClass) { - super(fory, type); + TypeResolver typeResolver, Class type, TypeDef layerTypeDef, Class layerMarkerClass) { + super(typeResolver, type); this.layerTypeDef = layerTypeDef; this.layerMarkerClass = layerMarkerClass; - TypeResolver typeResolver = fory.getTypeResolver(); + Fory fory = this.fory.getFory(); // Build field infos from layerTypeDef DescriptorGrouper descriptorGrouper = typeResolver.createDescriptorGrouper(layerTypeDef, type); @@ -76,7 +76,8 @@ public MetaSharedLayerSerializer( } @Override - public void write(MemoryBuffer buffer, T value) { + public void write(org.apache.fory.context.WriteContext writeContext, T value) { + MemoryBuffer buffer = writeContext.getBuffer(); // Write layer class meta using marker class as key (only if meta share is enabled) if (fory.getConfig().isMetaShareEnabled()) { writeLayerClassMeta(buffer); @@ -113,6 +114,7 @@ public void writeFieldsOnly(MemoryBuffer buffer, T value) { } private void writeBuildInFields(MemoryBuffer buffer, T value) { + Fory fory = this.fory.getFory(); for (SerializationFieldInfo fieldInfo : buildInFields) { AbstractObjectSerializer.writeBuildInField( fory, typeResolver, refResolver, fieldInfo, buffer, value); @@ -120,6 +122,7 @@ private void writeBuildInFields(MemoryBuffer buffer, T value) { } private void writeContainerFields(MemoryBuffer buffer, T value) { + Fory fory = this.fory.getFory(); Generics generics = fory.getGenerics(); for (SerializationFieldInfo fieldInfo : containerFields) { FieldAccessor fieldAccessor = fieldInfo.fieldAccessor; @@ -130,6 +133,7 @@ private void writeContainerFields(MemoryBuffer buffer, T value) { } private void writeOtherFields(MemoryBuffer buffer, T value) { + Fory fory = this.fory.getFory(); for (SerializationFieldInfo fieldInfo : otherFields) { FieldAccessor fieldAccessor = fieldInfo.fieldAccessor; Object fieldValue = fieldAccessor.getObject(value); @@ -140,6 +144,7 @@ private void writeOtherFields(MemoryBuffer buffer, T value) { @Override public void writeFieldValues(MemoryBuffer buffer, Object[] vals) { + Fory fory = this.fory.getFory(); // Write fields from array in order: buildIn, container, other int index = 0; // Write buildIn fields @@ -162,6 +167,7 @@ public void writeFieldValues(MemoryBuffer buffer, Object[] vals) { @Override public Object[] readFieldValues(MemoryBuffer buffer) { + Fory fory = this.fory.getFory(); Object[] vals = new Object[getNumFields()]; int index = 0; // Read buildIn fields @@ -220,7 +226,8 @@ private void populateSingleFieldInfo( } @Override - public T read(MemoryBuffer buffer) { + public T read(org.apache.fory.context.ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); // Note: Layer class meta is read by ObjectStreamSerializer before calling this method. // This serializer is designed for use with ObjectStreamSerializer only. T obj = newBean(); @@ -263,6 +270,7 @@ public T readFieldsOnly(MemoryBuffer buffer, Object obj) { } private void readFinalFields(MemoryBuffer buffer, T targetObject) { + Fory fory = this.fory.getFory(); for (SerializationFieldInfo fieldInfo : buildInFields) { FieldAccessor fieldAccessor = fieldInfo.fieldAccessor; if (fieldAccessor != null) { @@ -276,6 +284,7 @@ private void readFinalFields(MemoryBuffer buffer, T targetObject) { } private void readContainerFields(MemoryBuffer buffer, T obj) { + Fory fory = this.fory.getFory(); Generics generics = fory.getGenerics(); for (SerializationFieldInfo fieldInfo : containerFields) { Object fieldValue = @@ -289,6 +298,7 @@ private void readContainerFields(MemoryBuffer buffer, T obj) { } private void readUserTypeFields(MemoryBuffer buffer, T obj) { + Fory fory = this.fory.getFory(); for (SerializationFieldInfo fieldInfo : otherFields) { Object fieldValue = AbstractObjectSerializer.readField(fory, typeResolver, refResolver, fieldInfo, buffer); @@ -391,6 +401,7 @@ public void skipFields(MemoryBuffer buffer) { } private void skipBuildInFields(MemoryBuffer buffer) { + Fory fory = this.fory.getFory(); for (SerializationFieldInfo fieldInfo : buildInFields) { // Read the field value (discarding the result) to advance buffer position FieldSkipper.skipField(fory, typeResolver, refResolver, fieldInfo, buffer); @@ -398,6 +409,7 @@ private void skipBuildInFields(MemoryBuffer buffer) { } private void skipContainerFields(MemoryBuffer buffer) { + Fory fory = this.fory.getFory(); Generics generics = fory.getGenerics(); for (SerializationFieldInfo fieldInfo : containerFields) { // Read container field value to advance buffer position @@ -407,6 +419,7 @@ private void skipContainerFields(MemoryBuffer buffer) { } private void skipOtherFields(MemoryBuffer buffer) { + Fory fory = this.fory.getFory(); for (SerializationFieldInfo fieldInfo : otherFields) { // Read field value to advance buffer position AbstractObjectSerializer.readField(fory, typeResolver, refResolver, fieldInfo, buffer); diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/MetaSharedLayerSerializerBase.java b/java/fory-core/src/main/java/org/apache/fory/serializer/MetaSharedLayerSerializerBase.java index 456e93f222..3e150ebeee 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/MetaSharedLayerSerializerBase.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/MetaSharedLayerSerializerBase.java @@ -22,6 +22,7 @@ import org.apache.fory.Fory; import org.apache.fory.collection.ObjectIntMap; import org.apache.fory.memory.MemoryBuffer; +import org.apache.fory.resolver.TypeResolver; /** * Base class for meta-shared layer serializers. This provides the common interface for both @@ -32,8 +33,8 @@ */ public abstract class MetaSharedLayerSerializerBase extends AbstractObjectSerializer { - public MetaSharedLayerSerializerBase(Fory fory, Class type) { - super(fory, type); + public MetaSharedLayerSerializerBase(TypeResolver typeResolver, Class type) { + super(typeResolver, type); } /** diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/MetaSharedSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/MetaSharedSerializer.java index fa54ff2428..bcbea2352a 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/MetaSharedSerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/MetaSharedSerializer.java @@ -23,6 +23,7 @@ import java.util.List; import java.util.stream.Collectors; import org.apache.fory.Fory; +import org.apache.fory.resolver.TypeResolver; import org.apache.fory.builder.MetaSharedCodecBuilder; import org.apache.fory.config.CompatibleMode; import org.apache.fory.config.ForyBuilder; @@ -75,8 +76,8 @@ public class MetaSharedSerializer extends AbstractObjectSerializer { private final boolean hasDefaultValues; private final DefaultValueUtils.DefaultValueField[] defaultValueFields; - public MetaSharedSerializer(Fory fory, Class type, TypeDef typeDef) { - super(fory, type); + public MetaSharedSerializer(TypeResolver typeResolver, Class type, TypeDef typeDef) { + super(typeResolver, type); Preconditions.checkArgument( !fory.getConfig().checkClassVersion(), "Class version check should be disabled when compatible mode is enabled."); @@ -101,11 +102,11 @@ public MetaSharedSerializer(Fory fory, Class type, TypeDef typeDef) { d.getTypeName(), d.isTrackingRef(), d.isNullable(), - Types.getDescriptorTypeId(fory, d)); + Types.getDescriptorTypeId(fory.getFory(), d)); } } // d.getField() may be null if not exists in this class when meta share enabled. - FieldGroups fieldGroups = FieldGroups.buildFieldInfos(fory, descriptorGrouper); + FieldGroups fieldGroups = FieldGroups.buildFieldInfos(fory.getFory(), descriptorGrouper); buildInFields = fieldGroups.buildInFields; containerFields = fieldGroups.containerFields; otherFields = fieldGroups.userTypeFields; @@ -127,7 +128,7 @@ public MetaSharedSerializer(Fory fory, Class type, TypeDef typeDef) { hasDefaultValues = defaultValueSupport.hasDefaultValues(type); defaultValueFields = defaultValueSupport.buildDefaultValueFields( - fory, type, descriptorGrouper.getSortedDescriptors()); + fory.getFory(), type, descriptorGrouper.getSortedDescriptors()); } if (!hasDefaultValues) { DefaultValueUtils.DefaultValueSupport kotlinDefaultValueSupport = @@ -136,7 +137,7 @@ public MetaSharedSerializer(Fory fory, Class type, TypeDef typeDef) { hasDefaultValues = kotlinDefaultValueSupport.hasDefaultValues(type); defaultValueFields = kotlinDefaultValueSupport.buildDefaultValueFields( - fory, type, descriptorGrouper.getSortedDescriptors()); + fory.getFory(), type, descriptorGrouper.getSortedDescriptors()); } } this.hasDefaultValues = hasDefaultValues; @@ -144,14 +145,15 @@ public MetaSharedSerializer(Fory fory, Class type, TypeDef typeDef) { } @Override - public void write(MemoryBuffer buffer, T value) { + public void write(org.apache.fory.context.WriteContext writeContext, T value) { + MemoryBuffer buffer = writeContext.getBuffer(); if (serializer == null) { // xlang mode will register class and create serializer in advance, it won't go to here. serializer = ((ClassResolver) typeResolver) - .createSerializerSafe(type, () -> new ObjectSerializer<>(fory, type)); + .createSerializerSafe(type, () -> new ObjectSerializer<>(typeResolver, type)); } - serializer.write(buffer, value); + serializer.write(org.apache.fory.context.WriteContext.current(), value); } private T newInstance() { @@ -165,7 +167,8 @@ private T newInstance() { } @Override - public T read(MemoryBuffer buffer) { + public T read(org.apache.fory.context.ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); if (isRecord) { Object[] fieldValues = new Object[buildInFields.length + otherFields.length + containerFields.length]; @@ -176,7 +179,7 @@ public T read(MemoryBuffer buffer) { return t; } T targetObject = newInstance(); - Fory fory = this.fory; + Fory fory = this.fory.getFory(); RefResolver refResolver = this.refResolver; if (refResolver instanceof MapRefResolver) { MapRefResolver mapRefResolver = (MapRefResolver) refResolver; @@ -232,6 +235,7 @@ public T read(MemoryBuffer buffer) { } private void compatibleRead(MemoryBuffer buffer, SerializationFieldInfo fieldInfo, Object obj) { + Fory fory = this.fory.getFory(); Object fieldValue = AbstractObjectSerializer.readBuildInFieldValue( fory, typeResolver, refResolver, fieldInfo, buffer); @@ -240,7 +244,7 @@ private void compatibleRead(MemoryBuffer buffer, SerializationFieldInfo fieldInf private void readFields(MemoryBuffer buffer, Object[] fields) { int counter = 0; - Fory fory = this.fory; + Fory fory = this.fory.getFory(); RefResolver refResolver = this.refResolver; // read order: primitive,boxed,final,other,collection,map for (SerializationFieldInfo fieldInfo : this.buildInFields) { diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/NoneSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/NoneSerializer.java index 9c4beb7223..d0345b6202 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/NoneSerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/NoneSerializer.java @@ -19,20 +19,22 @@ package org.apache.fory.serializer; -import org.apache.fory.Fory; +import org.apache.fory.config.Config; import org.apache.fory.memory.MemoryBuffer; @SuppressWarnings("rawtypes") public class NoneSerializer extends Serializer { - public NoneSerializer(Fory fory, Class type) { - super(fory, type); + public NoneSerializer(Config config, Class type) { + super(config, type); } @Override - public void write(MemoryBuffer buffer, Object value) {} + public void write(org.apache.fory.context.WriteContext writeContext, Object value) { + MemoryBuffer buffer = writeContext.getBuffer();} @Override - public Object read(MemoryBuffer buffer) { + public Object read(org.apache.fory.context.ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); return null; } } diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/ObjectSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/ObjectSerializer.java index b756054119..7490b39902 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/ObjectSerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/ObjectSerializer.java @@ -27,6 +27,7 @@ import java.util.List; import java.util.stream.Collectors; import org.apache.fory.Fory; +import org.apache.fory.resolver.TypeResolver; import org.apache.fory.exception.ForyException; import org.apache.fory.logging.Logger; import org.apache.fory.logging.LoggerFactory; @@ -68,12 +69,12 @@ public final class ObjectSerializer extends AbstractObjectSerializer { private final SerializationFieldInfo[] containerFields; private final int classVersionHash; - public ObjectSerializer(Fory fory, Class cls) { - this(fory, cls, true); + public ObjectSerializer(TypeResolver typeResolver, Class cls) { + this(typeResolver, cls, true); } - public ObjectSerializer(Fory fory, Class cls, boolean resolveParent) { - super(fory, cls); + public ObjectSerializer(TypeResolver typeResolver, Class cls, boolean resolveParent) { + super(typeResolver, cls); // avoid recursive building serializers. // Use `setSerializerIfAbsent` to avoid overwriting existing serializer for class when used // as data serializer. @@ -123,19 +124,20 @@ public ObjectSerializer(Fory fory, Class cls, boolean resolveParent) { recordInfo = null; } if (fory.checkClassVersion()) { - classVersionHash = computeStructHash(fory, grouper); + classVersionHash = computeStructHash(fory.getFory(), grouper); } else { classVersionHash = 0; } - FieldGroups fieldGroups = FieldGroups.buildFieldInfos(fory, grouper); + FieldGroups fieldGroups = FieldGroups.buildFieldInfos(fory.getFory(), grouper); buildInFields = fieldGroups.buildInFields; otherFields = fieldGroups.userTypeFields; containerFields = fieldGroups.containerFields; } @Override - public void write(MemoryBuffer buffer, T value) { - Fory fory = this.fory; + public void write(org.apache.fory.context.WriteContext writeContext, T value) { + MemoryBuffer buffer = writeContext.getBuffer(); + Fory fory = this.fory.getFory(); RefResolver refResolver = this.refResolver; if (fory.checkClassVersion()) { buffer.writeInt32(classVersionHash); @@ -163,6 +165,7 @@ private void printReadFieldDebugInfo(SerializationFieldInfo fieldInfo, MemoryBuf } private void writeOtherFields(MemoryBuffer buffer, T value) { + Fory fory = this.fory.getFory(); for (SerializationFieldInfo fieldInfo : otherFields) { if (Utils.DEBUG_OUTPUT_VERBOSE) { printWriteFieldDebugInfo(fieldInfo, buffer); @@ -199,7 +202,8 @@ private void writeContainerFields( } @Override - public T read(MemoryBuffer buffer) { + public T read(org.apache.fory.context.ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); if (isRecord) { Object[] fields = readFields(buffer); fields = RecordUtils.remapping(recordInfo, fields); @@ -213,7 +217,7 @@ public T read(MemoryBuffer buffer) { } public Object[] readFields(MemoryBuffer buffer) { - Fory fory = this.fory; + Fory fory = this.fory.getFory(); if (fory.checkClassVersion()) { int hash = buffer.readInt32(); checkClassVersion(type, hash, classVersionHash); @@ -249,7 +253,7 @@ public Object[] readFields(MemoryBuffer buffer) { } public T readAndSetFields(MemoryBuffer buffer, T obj) { - Fory fory = this.fory; + Fory fory = this.fory.getFory(); if (fory.checkClassVersion()) { int hash = buffer.readInt32(); checkClassVersion(type, hash, classVersionHash); diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/ObjectStreamSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/ObjectStreamSerializer.java index 500e8913d1..84510b2582 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/ObjectStreamSerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/ObjectStreamSerializer.java @@ -43,6 +43,7 @@ import java.util.function.BiConsumer; import java.util.function.Consumer; import org.apache.fory.Fory; +import org.apache.fory.resolver.TypeResolver; import org.apache.fory.builder.CodecUtils; import org.apache.fory.builder.LayerMarkerClassGenerator; import org.apache.fory.collection.LongMap; @@ -162,8 +163,8 @@ private static ObjectStreamClass safeObjectStreamClassLookup(Class type) { } } - public ObjectStreamSerializer(Fory fory, Class type) { - super(fory, type, createObjectCreatorForGraalVM(type)); + public ObjectStreamSerializer(TypeResolver typeResolver, Class type) { + super(typeResolver, type, createObjectCreatorForGraalVM(type)); if (!Serializable.class.isAssignableFrom(type)) { throw new IllegalArgumentException( String.format("Class %s should implement %s.", type, Serializable.class)); @@ -185,7 +186,7 @@ public ObjectStreamSerializer(Fory fory, Class type) { end = end.getSuperclass(); } while (type != end) { - slotsInfoList.add(new SlotsInfo(fory, type)); + slotsInfoList.add(new SlotsInfo(fory.getFory(), type)); type = type.getSuperclass(); } Collections.reverse(slotsInfoList); @@ -207,7 +208,8 @@ private static ObjectCreator createObjectCreatorForGraalVM(Class type) } @Override - public void write(MemoryBuffer buffer, Object value) { + public void write(org.apache.fory.context.WriteContext writeContext, Object value) { + MemoryBuffer buffer = writeContext.getBuffer(); buffer.writeInt16((short) slotsInfos.length); try { ClassResolver classResolver = (ClassResolver) typeResolver; @@ -255,7 +257,8 @@ public void write(MemoryBuffer buffer, Object value) { } @Override - public Object read(MemoryBuffer buffer) { + public Object read(org.apache.fory.context.ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); Object obj = objectCreator.newInstance(); fory.getRefResolver().reference(obj); int numClasses = buffer.readInt16(); @@ -304,7 +307,7 @@ public Object read(MemoryBuffer buffer) { // Read data for the matched layer - getReadSerializer reads TypeDef from buffer // This must be called exactly once per layer to read the TypeDef - matchedSlot.getReadSerializer(fory, buffer); + matchedSlot.getReadSerializer(fory.getFory(), buffer); StreamTypeInfo streamTypeInfo = matchedSlot.getStreamTypeInfo(); Method readObjectMethod = streamTypeInfo.readObjectMethod; @@ -412,7 +415,8 @@ private void skipUnknownLayerData(MemoryBuffer buffer, Class senderClass) { } else { // Not cached - read full TypeDef and create TypeInfo TypeDef typeDef = - fory.getTypeResolver().cacheTypeDef(TypeDef.readTypeDef(fory, buffer, typeDefId)); + fory.getTypeResolver() + .cacheTypeDef(TypeDef.readTypeDef(fory.getFory(), buffer, typeDefId)); typeInfo = new TypeInfo(senderClass, typeDef); typeDefIdToTypeInfo.put(typeDefId, typeInfo); } @@ -423,9 +427,11 @@ private void skipUnknownLayerData(MemoryBuffer buffer, Class senderClass) { MetaSharedLayerSerializerBase skipSerializer = (MetaSharedLayerSerializerBase) typeInfo.getSerializer(); if (skipSerializer == null) { - Class layerMarkerClass = LayerMarkerClassGenerator.getOrCreate(fory, senderClass, 0); + Class layerMarkerClass = + LayerMarkerClassGenerator.getOrCreate(fory.getFory(), senderClass, 0); MetaSharedLayerSerializer newSerializer = - new MetaSharedLayerSerializer(fory, senderClass, typeInfo.getTypeDef(), layerMarkerClass); + new MetaSharedLayerSerializer( + fory.getTypeResolver(), senderClass, typeInfo.getTypeDef(), layerMarkerClass); typeInfo.setSerializer(newSerializer); skipSerializer = newSerializer; } @@ -436,7 +442,7 @@ private static void throwUnsupportedEncodingException(Class cls) throws UnsupportedEncodingException { throw new UnsupportedEncodingException( String.format( - "Use %s instead by `fory.registerSerializer(%s, new JavaSerializer(fory, %s))` or " + "Use %s instead by `fory.registerSerializer(%s, new JavaSerializer(fory.getTypeResolver(), %s))` or " + "implement a custom %s.", JavaSerializer.class, cls, cls, Serializer.class)); } @@ -445,7 +451,7 @@ private static void throwSerializationException(Class type, Exception e) { throw new RuntimeException( String.format( "Serialize object of type %s failed, " - + "Try to use %s instead by `fory.registerSerializer(%s, new JavaSerializer(fory, %s))` or " + + "Try to use %s instead by `fory.registerSerializer(%s, new JavaSerializer(fory.getTypeResolver(), %s))` or " + "implement a custom %s.", type, JavaSerializer.class, type, type, Serializer.class), e); @@ -648,7 +654,8 @@ public SlotsInfo(Fory fory, Class type) { // Create interpreter-mode serializer first this.slotsSerializer = - new MetaSharedLayerSerializer(fory, type, layerTypeDef, layerMarkerClass); + new MetaSharedLayerSerializer( + fory.getTypeResolver(), type, layerTypeDef, layerMarkerClass); // Register JIT callback to replace with JIT serializer when ready if (fory.getConfig().isCodeGenEnabled()) { @@ -775,7 +782,8 @@ public MetaSharedLayerSerializerBase getReadSerializer(Fory fory, MemoryBuffer b // Create a new serializer based on the TypeDef from stream Class layerMarkerClass = LayerMarkerClassGenerator.getOrCreate(fory, cls, 0); MetaSharedLayerSerializer newSerializer = - new MetaSharedLayerSerializer(fory, cls, typeInfo.getTypeDef(), layerMarkerClass); + new MetaSharedLayerSerializer( + fory.getTypeResolver(), cls, typeInfo.getTypeDef(), layerMarkerClass); typeInfo.setSerializer(newSerializer); result = newSerializer; } @@ -845,7 +853,7 @@ private static class ForyObjectOutputStream extends ObjectOutputStream { protected ForyObjectOutputStream(SlotInfo slotsInfo) throws IOException { super(); this.slotsInfo = slotsInfo; - this.fory = slotsInfo.getSlotsSerializer().fory; + this.fory = slotsInfo.getSlotsSerializer().fory.getFory(); this.compressInt = fory.compressInt(); } @@ -930,7 +938,6 @@ public void put(String name, Object val) { putValue(name, val); } - @Deprecated @Override public void write(ObjectOutput out) throws IOException { Class cls = slotsInfo.getSlotsSerializer().getType(); @@ -1158,7 +1165,7 @@ private static class ForyObjectInputStream extends ObjectInputStream { private TreeMap callbacks; protected ForyObjectInputStream(SlotInfo slotsInfo) throws IOException { - this.fory = slotsInfo.getSlotsSerializer().fory; + this.fory = slotsInfo.getSlotsSerializer().fory.getFory(); this.compressInt = fory.compressInt(); this.slotsInfo = slotsInfo; } 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 eb91cf21dd..8728dc5d09 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 @@ -23,7 +23,7 @@ import java.util.OptionalDouble; import java.util.OptionalInt; import java.util.OptionalLong; -import org.apache.fory.Fory; +import org.apache.fory.config.Config; import org.apache.fory.memory.MemoryBuffer; import org.apache.fory.resolver.TypeResolver; @@ -34,12 +34,13 @@ public final class OptionalSerializers { public static final class OptionalSerializer extends Serializer { - public OptionalSerializer(Fory fory) { - super(fory, Optional.class); + public OptionalSerializer(Config config) { + super(config, Optional.class); } @Override - public void write(MemoryBuffer buffer, Optional value) { + public void write(org.apache.fory.context.WriteContext writeContext, Optional value) { + MemoryBuffer buffer = writeContext.getBuffer(); Object nullable = value.isPresent() ? value.get() : null; fory.writeRef(buffer, nullable); } @@ -53,18 +54,20 @@ public Optional copy(Optional originOptional) { } @Override - public Optional read(MemoryBuffer buffer) { + public Optional read(org.apache.fory.context.ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); return Optional.ofNullable(fory.readRef(buffer)); } } public static final class OptionalIntSerializer extends ImmutableSerializer { - public OptionalIntSerializer(Fory fory) { - super(fory, OptionalInt.class); + public OptionalIntSerializer(Config config) { + super(config, OptionalInt.class); } @Override - public void write(MemoryBuffer buffer, OptionalInt value) { + public void write(org.apache.fory.context.WriteContext writeContext, OptionalInt value) { + MemoryBuffer buffer = writeContext.getBuffer(); boolean present = value.isPresent(); buffer.writeBoolean(present); if (present) { @@ -73,7 +76,8 @@ public void write(MemoryBuffer buffer, OptionalInt value) { } @Override - public OptionalInt read(MemoryBuffer buffer) { + public OptionalInt read(org.apache.fory.context.ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); if (buffer.readBoolean()) { return OptionalInt.of(buffer.readInt32()); } else { @@ -83,12 +87,13 @@ public OptionalInt read(MemoryBuffer buffer) { } public static final class OptionalLongSerializer extends ImmutableSerializer { - public OptionalLongSerializer(Fory fory) { - super(fory, OptionalLong.class); + public OptionalLongSerializer(Config config) { + super(config, OptionalLong.class); } @Override - public void write(MemoryBuffer buffer, OptionalLong value) { + public void write(org.apache.fory.context.WriteContext writeContext, OptionalLong value) { + MemoryBuffer buffer = writeContext.getBuffer(); boolean present = value.isPresent(); buffer.writeBoolean(present); if (present) { @@ -97,7 +102,8 @@ public void write(MemoryBuffer buffer, OptionalLong value) { } @Override - public OptionalLong read(MemoryBuffer buffer) { + public OptionalLong read(org.apache.fory.context.ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); if (buffer.readBoolean()) { return OptionalLong.of(buffer.readInt64()); } else { @@ -107,12 +113,13 @@ public OptionalLong read(MemoryBuffer buffer) { } public static final class OptionalDoubleSerializer extends ImmutableSerializer { - public OptionalDoubleSerializer(Fory fory) { - super(fory, OptionalDouble.class); + public OptionalDoubleSerializer(Config config) { + super(config, OptionalDouble.class); } @Override - public void write(MemoryBuffer buffer, OptionalDouble value) { + public void write(org.apache.fory.context.WriteContext writeContext, OptionalDouble value) { + MemoryBuffer buffer = writeContext.getBuffer(); boolean present = value.isPresent(); buffer.writeBoolean(present); if (present) { @@ -121,7 +128,8 @@ public void write(MemoryBuffer buffer, OptionalDouble value) { } @Override - public OptionalDouble read(MemoryBuffer buffer) { + public OptionalDouble read(org.apache.fory.context.ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); if (buffer.readBoolean()) { return OptionalDouble.of(buffer.readFloat64()); } else { @@ -130,11 +138,11 @@ public OptionalDouble read(MemoryBuffer buffer) { } } - public static void registerDefaultSerializers(Fory fory) { - TypeResolver resolver = fory.getTypeResolver(); - resolver.registerInternalSerializer(Optional.class, new OptionalSerializer(fory)); - resolver.registerInternalSerializer(OptionalInt.class, new OptionalIntSerializer(fory)); - resolver.registerInternalSerializer(OptionalLong.class, new OptionalLongSerializer(fory)); - resolver.registerInternalSerializer(OptionalDouble.class, new OptionalDoubleSerializer(fory)); + public static void registerDefaultSerializers(TypeResolver resolver) { + Config config = resolver.getConfig(); + resolver.registerInternalSerializer(Optional.class, new OptionalSerializer(config)); + resolver.registerInternalSerializer(OptionalInt.class, new OptionalIntSerializer(config)); + resolver.registerInternalSerializer(OptionalLong.class, new OptionalLongSerializer(config)); + resolver.registerInternalSerializer(OptionalDouble.class, new OptionalDoubleSerializer(config)); } } 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 8b32b626be..3d49608035 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 @@ -22,8 +22,11 @@ import static org.apache.fory.type.TypeUtils.PRIMITIVE_LONG_TYPE; import org.apache.fory.Fory; +import org.apache.fory.config.Config; import org.apache.fory.codegen.Expression; import org.apache.fory.codegen.Expression.Invoke; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; import org.apache.fory.config.LongEncoding; import org.apache.fory.memory.MemoryBuffer; import org.apache.fory.memory.Platform; @@ -36,101 +39,113 @@ @SuppressWarnings({"rawtypes", "unchecked"}) public class PrimitiveSerializers { public static final class BooleanSerializer extends CrossLanguageCompatibleSerializer { - public BooleanSerializer(Fory fory, Class cls) { - super(fory, (Class) cls, false, true); + public BooleanSerializer(Config config, Class cls) { + super(config, (Class) cls, false, true); } @Override - public void write(MemoryBuffer buffer, Boolean value) { + public void write(WriteContext writeContext, Boolean value) { + MemoryBuffer buffer = writeContext.getBuffer(); buffer.writeBoolean(value); } @Override - public Boolean read(MemoryBuffer buffer) { + public Boolean read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); return buffer.readBoolean(); } } public static final class ByteSerializer extends CrossLanguageCompatibleSerializer { - public ByteSerializer(Fory fory, Class cls) { - super(fory, (Class) cls, false, true); + public ByteSerializer(Config config, Class cls) { + super(config, (Class) cls, false, true); } @Override - public void write(MemoryBuffer buffer, Byte value) { + public void write(WriteContext writeContext, Byte value) { + MemoryBuffer buffer = writeContext.getBuffer(); buffer.writeByte(value); } @Override - public Byte read(MemoryBuffer buffer) { + public Byte read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); return buffer.readByte(); } } public static final class Uint8Serializer extends CrossLanguageCompatibleSerializer { - public Uint8Serializer(Fory fory) { - super(fory, Integer.class); + public Uint8Serializer(Config config) { + super(config, Integer.class); } @Override - public void write(MemoryBuffer buffer, Integer value) { + public void write(WriteContext writeContext, Integer value) { + MemoryBuffer buffer = writeContext.getBuffer(); Preconditions.checkArgument(value >= 0 && value <= 255); buffer.writeByte(value.byteValue()); } @Override - public Integer read(MemoryBuffer buffer) { + public Integer read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); int b = buffer.readByte(); return b >>> 24; } } public static final class Uint16Serializer extends CrossLanguageCompatibleSerializer { - public Uint16Serializer(Fory fory) { - super(fory, Integer.class); + public Uint16Serializer(Config config) { + super(config, Integer.class); } @Override - public void write(MemoryBuffer buffer, Integer value) { + public void write(WriteContext writeContext, Integer value) { + MemoryBuffer buffer = writeContext.getBuffer(); Preconditions.checkArgument(value >= 0 && value <= 65535); buffer.writeByte(value.byteValue()); } @Override - public Integer read(MemoryBuffer buffer) { + public Integer read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); int b = buffer.readByte(); return b >>> 16; } } public static final class CharSerializer extends ImmutableSerializer { - public CharSerializer(Fory fory, Class cls) { - super(fory, (Class) cls, false); + public CharSerializer(Config config, Class cls) { + super(config, (Class) cls, false); } @Override - public void write(MemoryBuffer buffer, Character value) { + public void write(WriteContext writeContext, Character value) { + MemoryBuffer buffer = writeContext.getBuffer(); buffer.writeChar(value); } @Override - public Character read(MemoryBuffer buffer) { + public Character read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); return buffer.readChar(); } } public static final class ShortSerializer extends CrossLanguageCompatibleSerializer { - public ShortSerializer(Fory fory, Class cls) { - super(fory, (Class) cls, false, true); + public ShortSerializer(Config config, Class cls) { + super(config, (Class) cls, false, true); } @Override - public void write(MemoryBuffer buffer, Short value) { + public void write(WriteContext writeContext, Short value) { + MemoryBuffer buffer = writeContext.getBuffer(); buffer.writeInt16(value); } @Override - public Short read(MemoryBuffer buffer) { + public Short read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); return buffer.readInt16(); } } @@ -138,14 +153,15 @@ public Short read(MemoryBuffer buffer) { public static final class IntSerializer extends CrossLanguageCompatibleSerializer { private final boolean compressNumber; - public IntSerializer(Fory fory, Class cls) { - super(fory, (Class) cls, false, true); + public IntSerializer(Config config, Class cls) { + super(config, (Class) cls, false, true); // Cross-language encoding always uses varint; Java mode follows compressInt config. - compressNumber = !isJava || fory.compressInt(); + compressNumber = !isJava || config.compressInt(); } @Override - public void write(MemoryBuffer buffer, Integer value) { + public void write(WriteContext writeContext, Integer value) { + MemoryBuffer buffer = writeContext.getBuffer(); if (compressNumber) { buffer.writeVarInt32(value); } else { @@ -154,7 +170,8 @@ public void write(MemoryBuffer buffer, Integer value) { } @Override - public Integer read(MemoryBuffer buffer) { + public Integer read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); if (compressNumber) { return buffer.readVarInt32(); } else { @@ -164,18 +181,20 @@ public Integer read(MemoryBuffer buffer) { } public static final class VarUint32Serializer extends Serializer { - public VarUint32Serializer(Fory fory) { - super(fory, Integer.class); + public VarUint32Serializer(Config config) { + super(config, Integer.class); } @Override - public void write(MemoryBuffer buffer, Integer value) { + public void write(WriteContext writeContext, Integer value) { + MemoryBuffer buffer = writeContext.getBuffer(); Preconditions.checkArgument(value >= 0); buffer.writeVarUint32(value); } @Override - public Integer read(MemoryBuffer buffer) { + public Integer read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); return buffer.readVarUint32(); } } @@ -183,18 +202,20 @@ public Integer read(MemoryBuffer buffer) { public static final class LongSerializer extends CrossLanguageCompatibleSerializer { private final LongEncoding longEncoding; - public LongSerializer(Fory fory, Class cls) { - super(fory, (Class) cls, false, true); - longEncoding = isJava ? fory.longEncoding() : LongEncoding.VARINT; + public LongSerializer(Config config, Class cls) { + super(config, (Class) cls, false, true); + longEncoding = isJava ? config.longEncoding() : LongEncoding.VARINT; } @Override - public void write(MemoryBuffer buffer, Long value) { + public void write(WriteContext writeContext, Long value) { + MemoryBuffer buffer = writeContext.getBuffer(); writeInt64(buffer, value, longEncoding); } @Override - public Long read(MemoryBuffer buffer) { + public Long read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); return readInt64(buffer, longEncoding); } @@ -252,66 +273,74 @@ public static String readLongFunc(LongEncoding longEncoding) { } public static final class VarUint64Serializer extends Serializer { - public VarUint64Serializer(Fory fory) { - super(fory, Long.class); + public VarUint64Serializer(Config config) { + super(config, Long.class); } @Override - public void write(MemoryBuffer buffer, Long value) { + public void write(WriteContext writeContext, Long value) { + MemoryBuffer buffer = writeContext.getBuffer(); Preconditions.checkArgument(value >= 0); buffer.writeVarUint64(value); } @Override - public Long read(MemoryBuffer buffer) { + public Long read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); return buffer.readVarUint64(); } } public static final class FloatSerializer extends CrossLanguageCompatibleSerializer { - public FloatSerializer(Fory fory, Class cls) { - super(fory, (Class) cls, false, true); + public FloatSerializer(Config config, Class cls) { + super(config, (Class) cls, false, true); } @Override - public void write(MemoryBuffer buffer, Float value) { + public void write(WriteContext writeContext, Float value) { + MemoryBuffer buffer = writeContext.getBuffer(); buffer.writeFloat32(value); } @Override - public Float read(MemoryBuffer buffer) { + public Float read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); return buffer.readFloat32(); } } public static final class DoubleSerializer extends CrossLanguageCompatibleSerializer { - public DoubleSerializer(Fory fory, Class cls) { - super(fory, (Class) cls, false, true); + public DoubleSerializer(Config config, Class cls) { + super(config, (Class) cls, false, true); } @Override - public void write(MemoryBuffer buffer, Double value) { + public void write(WriteContext writeContext, Double value) { + MemoryBuffer buffer = writeContext.getBuffer(); buffer.writeFloat64(value); } @Override - public Double read(MemoryBuffer buffer) { + public Double read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); return buffer.readFloat64(); } } public static final class Float16Serializer extends CrossLanguageCompatibleSerializer { - public Float16Serializer(Fory fory, Class cls) { - super(fory, (Class) cls, false, true); + public Float16Serializer(Config config, Class cls) { + super(config, (Class) cls, false, true); } @Override - public void write(MemoryBuffer buffer, Float16 value) { + public void write(WriteContext writeContext, Float16 value) { + MemoryBuffer buffer = writeContext.getBuffer(); buffer.writeInt16(value.toBits()); } @Override - public Float16 read(MemoryBuffer buffer) { + public Float16 read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); return Float16.fromBits(buffer.readInt16()); } } @@ -319,22 +348,25 @@ public Float16 read(MemoryBuffer buffer) { public static void registerDefaultSerializers(Fory fory) { // primitive types will be boxed. TypeResolver resolver = fory.getTypeResolver(); - resolver.registerInternalSerializer(boolean.class, new BooleanSerializer(fory, boolean.class)); - resolver.registerInternalSerializer(byte.class, new ByteSerializer(fory, byte.class)); - resolver.registerInternalSerializer(short.class, new ShortSerializer(fory, short.class)); - resolver.registerInternalSerializer(char.class, new CharSerializer(fory, char.class)); - resolver.registerInternalSerializer(int.class, new IntSerializer(fory, int.class)); - resolver.registerInternalSerializer(long.class, new LongSerializer(fory, long.class)); - resolver.registerInternalSerializer(float.class, new FloatSerializer(fory, float.class)); - resolver.registerInternalSerializer(double.class, new DoubleSerializer(fory, double.class)); - resolver.registerInternalSerializer(Boolean.class, new BooleanSerializer(fory, Boolean.class)); - resolver.registerInternalSerializer(Byte.class, new ByteSerializer(fory, Byte.class)); - resolver.registerInternalSerializer(Short.class, new ShortSerializer(fory, Short.class)); - resolver.registerInternalSerializer(Character.class, new CharSerializer(fory, Character.class)); - resolver.registerInternalSerializer(Integer.class, new IntSerializer(fory, Integer.class)); - resolver.registerInternalSerializer(Long.class, new LongSerializer(fory, Long.class)); - resolver.registerInternalSerializer(Float.class, new FloatSerializer(fory, Float.class)); - resolver.registerInternalSerializer(Double.class, new DoubleSerializer(fory, Double.class)); - resolver.registerInternalSerializer(Float16.class, new Float16Serializer(fory, Float16.class)); + Config config = fory.getConfig(); + resolver.registerInternalSerializer(boolean.class, new BooleanSerializer(config, boolean.class)); + resolver.registerInternalSerializer(byte.class, new ByteSerializer(config, byte.class)); + resolver.registerInternalSerializer(short.class, new ShortSerializer(config, short.class)); + resolver.registerInternalSerializer(char.class, new CharSerializer(config, char.class)); + resolver.registerInternalSerializer(int.class, new IntSerializer(config, int.class)); + resolver.registerInternalSerializer(long.class, new LongSerializer(config, long.class)); + resolver.registerInternalSerializer(float.class, new FloatSerializer(config, float.class)); + resolver.registerInternalSerializer(double.class, new DoubleSerializer(config, double.class)); + resolver.registerInternalSerializer(Boolean.class, new BooleanSerializer(config, Boolean.class)); + resolver.registerInternalSerializer(Byte.class, new ByteSerializer(config, Byte.class)); + resolver.registerInternalSerializer(Short.class, new ShortSerializer(config, Short.class)); + resolver.registerInternalSerializer( + Character.class, new CharSerializer(config, Character.class)); + resolver.registerInternalSerializer(Integer.class, new IntSerializer(config, Integer.class)); + resolver.registerInternalSerializer(Long.class, new LongSerializer(config, Long.class)); + resolver.registerInternalSerializer(Float.class, new FloatSerializer(config, Float.class)); + resolver.registerInternalSerializer(Double.class, new DoubleSerializer(config, Double.class)); + resolver.registerInternalSerializer( + Float16.class, new Float16Serializer(config, Float16.class)); } } diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/ReplaceResolveSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/ReplaceResolveSerializer.java index 228cb40baa..ffa257f17b 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/ReplaceResolveSerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/ReplaceResolveSerializer.java @@ -36,6 +36,7 @@ import org.apache.fory.resolver.ClassResolver; import org.apache.fory.resolver.RefResolver; import org.apache.fory.resolver.TypeInfo; +import org.apache.fory.resolver.TypeResolver; import org.apache.fory.util.Preconditions; import org.apache.fory.util.unsafe._JDKAccess; @@ -217,15 +218,16 @@ private static Serializer createDataSerializer( protected final TypeInfo writeTypeInfo; protected final Map, MethodInfoCache> classTypeInfoHolderMap = new HashMap<>(); - public ReplaceResolveSerializer(Fory fory, Class type) { - this(fory, type, false, true); + public ReplaceResolveSerializer(TypeResolver typeResolver, Class type) { + this(typeResolver, type, false, true); } public ReplaceResolveSerializer( - Fory fory, Class type, boolean isFinalField, boolean setSerializer) { - super(fory, type); - refResolver = fory.getRefResolver(); - classResolver = (ClassResolver) fory.getTypeResolver(); + TypeResolver typeResolver, Class type, boolean isFinalField, boolean setSerializer) { + super(typeResolver, type); + Fory fory = this.fory.getFory(); + refResolver = typeResolver.getRefResolver(); + classResolver = (ClassResolver) typeResolver; if (setSerializer) { // `setSerializer` before `newJDKMethodInfoCache` since it query classinfo from // `classResolver`, @@ -251,7 +253,8 @@ public ReplaceResolveSerializer( } @Override - public void write(MemoryBuffer buffer, Object value) { + public void write(org.apache.fory.context.WriteContext writeContext, Object value) { + MemoryBuffer buffer = writeContext.getBuffer(); MethodInfoCache jdkMethodInfoCache = this.jdkMethodInfoWriteCache; ReplaceResolveInfo replaceResolveInfo = jdkMethodInfoCache.info; Method writeReplaceMethod = replaceResolveInfo.writeReplaceMethod; @@ -297,11 +300,12 @@ public void write(MemoryBuffer buffer, Object value) { protected void writeObject( MemoryBuffer buffer, Object value, MethodInfoCache jdkMethodInfoCache) { classResolver.writeClassInternal(buffer, writeTypeInfo); - jdkMethodInfoCache.objectSerializer.write(buffer, value); + jdkMethodInfoCache.objectSerializer.write(org.apache.fory.context.WriteContext.current(), value); } @Override - public Object read(MemoryBuffer buffer) { + public Object read(org.apache.fory.context.ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); byte flag = buffer.readByte(); RefResolver refResolver = this.refResolver; if (flag == REPLACED_NEW_TYPE) { @@ -337,7 +341,7 @@ public Object read(MemoryBuffer buffer) { protected Object readObject(MemoryBuffer buffer) { Class cls = classResolver.readClassInternal(buffer); MethodInfoCache jdkMethodInfoCache = getMethodInfoCache(cls); - Object o = jdkMethodInfoCache.objectSerializer.read(buffer); + Object o = jdkMethodInfoCache.objectSerializer.read(org.apache.fory.context.ReadContext.current()); ReplaceResolveInfo replaceResolveInfo = jdkMethodInfoCache.info; if (replaceResolveInfo.readResolveMethod == null) { return o; @@ -368,7 +372,7 @@ public Object copy(Object originObj) { protected MethodInfoCache getMethodInfoCache(Class cls) { MethodInfoCache jdkMethodInfoCache = classTypeInfoHolderMap.get(cls); if (jdkMethodInfoCache == null) { - jdkMethodInfoCache = newJDKMethodInfoCache(cls, fory); + jdkMethodInfoCache = newJDKMethodInfoCache(cls, fory.getFory()); classTypeInfoHolderMap.put(cls, jdkMethodInfoCache); } return jdkMethodInfoCache; diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/SerializedLambdaSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/SerializedLambdaSerializer.java index af5772a184..54a38e3aa4 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/SerializedLambdaSerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/SerializedLambdaSerializer.java @@ -22,9 +22,9 @@ import java.lang.invoke.MethodHandle; import java.lang.invoke.SerializedLambda; import java.lang.reflect.Method; -import org.apache.fory.Fory; import org.apache.fory.exception.ForyException; import org.apache.fory.memory.MemoryBuffer; +import org.apache.fory.resolver.TypeResolver; import org.apache.fory.util.Preconditions; import org.apache.fory.util.unsafe._JDKAccess; @@ -49,13 +49,14 @@ public class SerializedLambdaSerializer extends Serializer { } } - public SerializedLambdaSerializer(Fory fory, Class cls) { - super(fory, cls); + public SerializedLambdaSerializer(TypeResolver typeResolver, Class cls) { + super(typeResolver, cls); Preconditions.checkArgument(cls == SERIALIZED_LAMBDA); } @Override - public void write(MemoryBuffer buffer, Object value) { + public void write(org.apache.fory.context.WriteContext writeContext, Object value) { + MemoryBuffer buffer = writeContext.getBuffer(); SerializedLambda serializedLambda = (SerializedLambda) value; fory.writeStringRef(buffer, serializedLambda.getCapturingClass()); fory.writeStringRef(buffer, serializedLambda.getFunctionalInterfaceClass()); @@ -95,7 +96,8 @@ public Object copy(Object value) { } @Override - public Object read(MemoryBuffer buffer) { + public Object read(org.apache.fory.context.ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); return readResolve(readUnresolved(buffer)); } 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 436e95ccbb..1b639c4f9f 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 @@ -21,9 +21,14 @@ import javax.annotation.concurrent.NotThreadSafe; import org.apache.fory.Fory; +import org.apache.fory.config.Config; +import org.apache.fory.context.CopyContext; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; import org.apache.fory.memory.MemoryBuffer; import org.apache.fory.resolver.RefMode; import org.apache.fory.resolver.RefResolver; +import org.apache.fory.resolver.TypeResolver; import org.apache.fory.type.TypeUtils; /** @@ -35,8 +40,12 @@ @NotThreadSafe @SuppressWarnings("unchecked") public abstract class Serializer { - protected final Fory fory; - protected final RefResolver refResolver; + protected final Config config; + /** + * Config-scoped runtime facade for serializer implementations. This is not a retained + * {@link Fory} instance. + */ + protected final SerializerRuntime fory; protected final Class type; protected final boolean isJava; protected final boolean needToWriteRef; @@ -49,25 +58,68 @@ public abstract class Serializer { protected final boolean immutable; - public Serializer(Fory fory, Class type) { + public Serializer(Config config, Class type) { + this( + config, + new SerializerRuntime(config), + type, + config.trackingRef() && !TypeUtils.isBoxed(TypeUtils.wrap(type)), + TypeUtils.isPrimitive(type) || TypeUtils.isBoxed(type)); + } + + public Serializer(TypeResolver typeResolver, Class type) { this( - fory, + typeResolver.getConfig(), + new SerializerRuntime(typeResolver), type, - fory.trackingRef() && !TypeUtils.isBoxed(TypeUtils.wrap(type)), + typeResolver.getConfig().trackingRef() && !TypeUtils.isBoxed(TypeUtils.wrap(type)), TypeUtils.isPrimitive(type) || TypeUtils.isBoxed(type)); } - public Serializer(Fory fory, Class type, boolean immutable) { - this(fory, type, fory.trackingRef() && !TypeUtils.isBoxed(TypeUtils.wrap(type)), immutable); + public Serializer(Config config, Class type, boolean immutable) { + this( + config, + new SerializerRuntime(config), + type, + config.trackingRef() && !TypeUtils.isBoxed(TypeUtils.wrap(type)), + immutable); } - public Serializer(Fory fory, Class type, boolean needToWriteRef, boolean immutable) { - this.fory = fory; - this.refResolver = fory.getRefResolver(); + public Serializer(TypeResolver typeResolver, Class type, boolean immutable) { + this( + typeResolver.getConfig(), + new SerializerRuntime(typeResolver), + type, + typeResolver.getConfig().trackingRef() && !TypeUtils.isBoxed(TypeUtils.wrap(type)), + immutable); + } + + public Serializer(Config config, Class type, boolean needToWriteRef, boolean immutable) { + this(config, new SerializerRuntime(config), type, needToWriteRef, immutable); + } + + public Serializer( + TypeResolver typeResolver, Class type, boolean needToWriteRef, boolean immutable) { + this( + typeResolver.getConfig(), + new SerializerRuntime(typeResolver), + type, + needToWriteRef, + immutable); + } + + private Serializer( + Config config, + SerializerRuntime runtime, + Class type, + boolean needToWriteRef, + boolean immutable) { + this.config = config; + this.fory = runtime; this.type = type; - this.isJava = !fory.isCrossLanguage(); + this.isJava = !config.isXlang(); this.needToWriteRef = needToWriteRef; - this.needToCopyRef = fory.copyTrackingRef() && !immutable; + this.needToCopyRef = config.copyRef() && !immutable; this.immutable = immutable; } @@ -76,7 +128,9 @@ public Serializer(Fory fory, Class type, boolean needToWriteRef, boolean immu * the passed value can be null. Note that this method don't write type info, this method is * mostly be used in cases the context has already knows the value type when deserialization. */ - public void write(MemoryBuffer buffer, RefMode refMode, T value) { + public void write(WriteContext writeContext, RefMode refMode, T value) { + MemoryBuffer buffer = writeContext.getBuffer(); + RefResolver refResolver = writeContext.getRefResolver(); // noinspection Duplicates if (refMode == RefMode.TRACKING) { if (refResolver.writeRefOrNull(buffer, value)) { @@ -90,26 +144,28 @@ public void write(MemoryBuffer buffer, RefMode refMode, T value) { buffer.writeByte(Fory.NOT_NULL_VALUE_FLAG); } } - write(buffer, value); + write(writeContext, value); } /** * Write value to buffer, this method do not write ref/null flags and the passed value must not be * null. */ - public abstract void write(MemoryBuffer buffer, T value); + public abstract void write(WriteContext writeContext, T value); /** * Read value from buffer, this method may read ref/null flags based passed {@code refMode}, and * the read value can be null. Note that this method don't read type info, this method is mostly * be used in cases the context has already knows the value type for deserialization. */ - public T read(MemoryBuffer buffer, RefMode refMode) { + public T read(ReadContext readContext, RefMode refMode) { + MemoryBuffer buffer = readContext.getBuffer(); + RefResolver refResolver = readContext.getRefResolver(); if (refMode == RefMode.TRACKING) { T obj; int nextReadRefId = refResolver.tryPreserveRefId(buffer); if (nextReadRefId >= Fory.NOT_NULL_VALUE_FLAG) { - obj = read(buffer); + obj = read(readContext); refResolver.setReadObject(nextReadRefId, obj); return obj; } else { @@ -121,7 +177,7 @@ public T read(MemoryBuffer buffer, RefMode refMode) { // support circular reference, so we still need this `-1` refResolver.preserveRefId(-1); } - return read(buffer); + return read(readContext); } return null; } @@ -129,7 +185,7 @@ public T read(MemoryBuffer buffer, RefMode refMode) { /** * Read value from buffer, this method wont read ref/null flags and the read value won't be null. */ - public abstract T read(MemoryBuffer buffer); + public abstract T read(ReadContext readContext); public T copy(T value) { if (isImmutable()) { @@ -154,4 +210,12 @@ public Class getType() { public boolean isImmutable() { return immutable; } + + public boolean threadSafe() { + return false; + } + + protected final CopyContext copyContext() { + return CopyContext.current(); + } } diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/SerializerFactory.java b/java/fory-core/src/main/java/org/apache/fory/serializer/SerializerFactory.java index a5b2ad66e9..afc9701c78 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/SerializerFactory.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/SerializerFactory.java @@ -19,10 +19,10 @@ package org.apache.fory.serializer; -import org.apache.fory.Fory; +import org.apache.fory.resolver.TypeResolver; /** Serializer factory for customizing serializer creation. */ public interface SerializerFactory { - Serializer createSerializer(Fory fory, Class cls); + Serializer createSerializer(TypeResolver typeResolver, Class cls); } diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/SerializerRuntime.java b/java/fory-core/src/main/java/org/apache/fory/serializer/SerializerRuntime.java new file mode 100644 index 0000000000..a657c8674d --- /dev/null +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/SerializerRuntime.java @@ -0,0 +1,341 @@ +/* + * 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.Fory; +import org.apache.fory.builder.JITContext; +import org.apache.fory.config.Config; +import org.apache.fory.context.CopyContext; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; +import org.apache.fory.memory.MemoryBuffer; +import org.apache.fory.resolver.RefResolver; +import org.apache.fory.resolver.SerializationContext; +import org.apache.fory.resolver.TypeInfo; +import org.apache.fory.resolver.TypeInfoHolder; +import org.apache.fory.resolver.TypeResolver; +import org.apache.fory.type.Generics; + +/** + * Internal adapter that exposes the old runtime helper surface without retaining a {@code Fory} + * instance inside serializers. + */ +@SuppressWarnings("unchecked") +public final class SerializerRuntime { + private final Config config; + private final TypeResolver fallbackTypeResolver; + + SerializerRuntime(Config config) { + this(config, null); + } + + SerializerRuntime(TypeResolver typeResolver) { + this(typeResolver.getConfig(), typeResolver); + } + + private SerializerRuntime(Config config, TypeResolver fallbackTypeResolver) { + this.config = config; + this.fallbackTypeResolver = fallbackTypeResolver; + } + + public Config getConfig() { + return config; + } + + public TypeResolver getTypeResolver() { + return currentTypeResolver(); + } + + public Fory getFory() { + return currentFory(); + } + + public RefResolver getRefResolver() { + return currentRefResolver(); + } + + public Generics getGenerics() { + return currentGenerics(); + } + + public StringSerializer getStringSerializer() { + return currentStringSerializer(); + } + + public boolean trackingRef() { + return config.trackingRef(); + } + + public boolean copyTrackingRef() { + return config.copyRef(); + } + + public boolean isCrossLanguage() { + return config.isXlang(); + } + + public boolean compressInt() { + return config.compressInt(); + } + + public boolean isCompatible() { + return currentFory().isCompatible(); + } + + public boolean isShareMeta() { + return config.isMetaShareEnabled(); + } + + public boolean checkClassVersion() { + return config.checkClassVersion(); + } + + public ClassLoader getClassLoader() { + return currentFory().getClassLoader(); + } + + public SerializationContext getSerializationContext() { + return currentFory().getSerializationContext(); + } + + public JITContext getJITContext() { + return currentFory().getJITContext(); + } + + public Class getDefaultJDKStreamSerializerType() { + return config.getDefaultJDKStreamSerializerType(); + } + + public BufferCallback getBufferCallback() { + return WriteContext.current().getBufferCallback(); + } + + public boolean isPeerOutOfBandEnabled() { + return ReadContext.current().isPeerOutOfBandEnabled(); + } + + public void writeRef(MemoryBuffer buffer, Object obj) { + WriteContext.current().writeRef(obj); + } + + public void writeRef(MemoryBuffer buffer, Object obj, TypeInfoHolder typeInfoHolder) { + WriteContext.current().writeRef(obj, typeInfoHolder); + } + + public void writeRef(MemoryBuffer buffer, Object obj, TypeInfo typeInfo) { + WriteContext.current().writeRef(obj, typeInfo); + } + + public void writeRef(MemoryBuffer buffer, T obj, Serializer serializer) { + WriteContext.current().writeRef(obj, serializer); + } + + public void writeNonRef(MemoryBuffer buffer, Object obj) { + WriteContext.current().writeNonRef(obj); + } + + public void writeNonRef(MemoryBuffer buffer, Object obj, TypeInfoHolder holder) { + WriteContext.current().writeNonRef(obj, holder); + } + + public void writeNonRef(MemoryBuffer buffer, Object obj, Serializer serializer) { + WriteContext.current().writeNonRef(obj, serializer); + } + + public Object readRef(MemoryBuffer buffer) { + return ReadContext.current().readRef(); + } + + public Object readRef(MemoryBuffer buffer, TypeInfo typeInfo) { + return ReadContext.current().readRef(typeInfo); + } + + public Object readRef(MemoryBuffer buffer, TypeInfoHolder typeInfoHolder) { + return ReadContext.current().readRef(typeInfoHolder); + } + + public T readRef(MemoryBuffer buffer, Serializer serializer) { + return ReadContext.current().readRef(serializer); + } + + public Object readNonRef(MemoryBuffer buffer) { + return ReadContext.current().readNonRef(); + } + + public Object readNonRef(MemoryBuffer buffer, TypeInfoHolder typeInfoHolder) { + return ReadContext.current().readNonRef(typeInfoHolder); + } + + public Object readNonRef(MemoryBuffer buffer, TypeInfo typeInfo) { + return ReadContext.current().readNonRef(typeInfo); + } + + public Object readData(MemoryBuffer buffer, TypeInfo typeInfo) { + return ReadContext.current().readData(typeInfo); + } + + public Object readNullable(MemoryBuffer buffer) { + return ReadContext.current().readNullable(); + } + + public Object readNullable(MemoryBuffer buffer, Serializer serializer) { + return ReadContext.current().readNullable(serializer); + } + + public void writeString(MemoryBuffer buffer, String value) { + currentStringSerializer().writeString(buffer, value); + } + + public String readString(MemoryBuffer buffer) { + return currentStringSerializer().readString(buffer); + } + + public void writeStringRef(MemoryBuffer buffer, String value) { + WriteContext.current().writeStringRef(value); + } + + public String readStringRef(MemoryBuffer buffer) { + return ReadContext.current().readStringRef(buffer); + } + + public void writeBufferObject(MemoryBuffer buffer, BufferObject bufferObject) { + WriteContext.current().writeBufferObject(bufferObject); + } + + public void writeBufferObject( + MemoryBuffer buffer, ArraySerializers.PrimitiveArrayBufferObject bufferObject) { + WriteContext.current().writeBufferObject(bufferObject); + } + + public MemoryBuffer readBufferObject(MemoryBuffer buffer) { + return ReadContext.current().readBufferObject(buffer); + } + + public void reference(Object origin, Object copied) { + CopyContext.current().reference(origin, copied); + } + + public T copyObject(T value) { + return CopyContext.current().copyObject(value); + } + + public T copyObject(T value, int classId) { + return CopyContext.current().copyObject(value, classId); + } + + public T copyObject(T value, Serializer serializer) { + return CopyContext.current().copyObject(value, serializer); + } + + public int getDepth() { + try { + return ReadContext.current().getDepth(); + } catch (IllegalStateException ignored) { + return WriteContext.current().getDepth(); + } + } + + public void setDepth(int depth) { + try { + ReadContext.current().setDepth(depth); + } catch (IllegalStateException ignored) { + WriteContext.current().setDepth(depth); + } + } + + public void incDepth(int diff) { + try { + ReadContext.current().incDepth(diff); + } catch (IllegalStateException ignored) { + WriteContext.current().incDepth(diff); + } + } + + public void decDepth() { + try { + ReadContext.current().decDepth(); + } catch (IllegalStateException ignored) { + WriteContext.current().decDepth(); + } + } + + public void incReadDepth() { + ReadContext.current().incReadDepth(); + } + + private TypeResolver currentTypeResolver() { + try { + return WriteContext.current().getTypeResolver(); + } catch (IllegalStateException ignored) { + try { + return ReadContext.current().getTypeResolver(); + } catch (IllegalStateException ignored2) { + try { + return CopyContext.current().getTypeResolver(); + } catch (IllegalStateException ignored3) { + if (fallbackTypeResolver != null) { + return fallbackTypeResolver; + } + throw ignored3; + } + } + } + } + + private RefResolver currentRefResolver() { + try { + return WriteContext.current().getRefResolver(); + } catch (IllegalStateException ignored) { + try { + return ReadContext.current().getRefResolver(); + } catch (IllegalStateException ignored2) { + return currentTypeResolver().getRefResolver(); + } + } + } + + private Generics currentGenerics() { + try { + return WriteContext.current().getGenerics(); + } catch (IllegalStateException ignored) { + try { + return ReadContext.current().getGenerics(); + } catch (IllegalStateException ignored2) { + return currentTypeResolver().getGenerics(); + } + } + } + + private StringSerializer currentStringSerializer() { + try { + return WriteContext.current().getStringSerializer(); + } catch (IllegalStateException ignored) { + try { + return ReadContext.current().getStringSerializer(); + } catch (IllegalStateException ignored2) { + return currentTypeResolver().getStringSerializer(); + } + } + } + + private Fory currentFory() { + return currentTypeResolver().getFory(); + } +} 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 821f3c1e86..f73bcdf975 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 @@ -45,6 +45,9 @@ import org.apache.fory.Fory; import org.apache.fory.builder.Generated; import org.apache.fory.collection.Tuple2; +import org.apache.fory.config.Config; +import org.apache.fory.context.ReadContext; +import org.apache.fory.context.WriteContext; import org.apache.fory.memory.MemoryBuffer; import org.apache.fory.memory.Platform; import org.apache.fory.meta.TypeDef; @@ -72,14 +75,19 @@ public class Serializers { } } - private static final MethodType SIG1 = MethodType.methodType(void.class, Fory.class, Class.class); - private static final MethodType SIG2 = MethodType.methodType(void.class, Fory.class); - private static final MethodType SIG3 = MethodType.methodType(void.class, Class.class); - private static final MethodType SIG4 = MethodType.methodType(void.class); + private static final MethodType SIG1 = + MethodType.methodType(void.class, TypeResolver.class, Class.class); + private static final MethodType SIG2 = MethodType.methodType(void.class, TypeResolver.class); + private static final MethodType SIG3 = + MethodType.methodType(void.class, Config.class, Class.class); + private static final MethodType SIG4 = MethodType.methodType(void.class, Config.class); + private static final MethodType SIG5 = MethodType.methodType(void.class, Class.class); + private static final MethodType SIG6 = MethodType.methodType(void.class); /** - * Serializer subclass must have a constructor which take parameters of type {@link Fory} and - * {@link Class}, or {@link Fory} or {@link Class} or no-arg constructor. + * Serializer subclass must have a constructor which take parameters of type {@link TypeResolver} + * and {@link Class}, or {@link TypeResolver}, or {@link Config} and {@link Class}, or {@link + * Config}, or {@link Class}, or no-arg constructor. */ public static Serializer newSerializer( Fory fory, Class type, Class serializerClass) { @@ -88,26 +96,30 @@ public static Serializer newSerializer( Serializer serializer = typeInfo == null ? null : typeInfo.getSerializer(); try { if (serializerClass == ObjectSerializer.class) { - return new ObjectSerializer(fory, type); + return new ObjectSerializer(typeResolver, type); } if (serializerClass == MetaSharedSerializer.class) { TypeDef typeDef = typeResolver.getTypeDef(type, true); - return new MetaSharedSerializer(fory, type, typeDef); + return new MetaSharedSerializer(typeResolver, type, typeDef); } Tuple2 ctrInfo = CTR_MAP.getIfPresent(serializerClass); if (ctrInfo != null) { if (GraalvmSupport.isGraalBuildtime()) { if (Generated.class.isAssignableFrom(serializerClass)) { - return new GraalvmSerializerHolder(fory, type, serializerClass); + return new GraalvmSerializerHolder(fory.getConfig(), type, serializerClass); } } MethodType sig = ctrInfo.f0; MethodHandle handle = ctrInfo.f1; if (sig.equals(SIG1)) { - return (Serializer) handle.invoke(fory, type); + return (Serializer) handle.invoke(typeResolver, type); } else if (sig.equals(SIG2)) { - return (Serializer) handle.invoke(fory); + return (Serializer) handle.invoke(typeResolver); } else if (sig.equals(SIG3)) { + return (Serializer) handle.invoke(fory.getConfig(), type); + } else if (sig.equals(SIG4)) { + return (Serializer) handle.invoke(fory.getConfig()); + } else if (sig.equals(SIG5)) { return (Serializer) handle.invoke(type); } else { return (Serializer) handle.invoke(); @@ -135,15 +147,17 @@ public static Serializer newSerializer( private static Serializer createSerializer( Fory fory, Class type, Class serializerClass) throws Throwable { MethodHandles.Lookup lookup = _JDKAccess._trustedLookup(serializerClass); + TypeResolver typeResolver = fory.getTypeResolver(); + Config config = fory.getConfig(); try { MethodHandle ctr = lookup.findConstructor(serializerClass, SIG1); CTR_MAP.put(serializerClass, Tuple2.of(SIG1, ctr)); if (GraalvmSupport.isGraalBuildtime()) { if (Generated.class.isAssignableFrom(serializerClass)) { - return new GraalvmSerializerHolder(fory, type, serializerClass); + return new GraalvmSerializerHolder(fory.getConfig(), type, serializerClass); } } - return (Serializer) ctr.invoke(fory, type); + return (Serializer) ctr.invoke(typeResolver, type); } catch (NoSuchMethodException e) { ExceptionUtils.ignore(e); } @@ -152,10 +166,10 @@ private static Serializer createSerializer( CTR_MAP.put(serializerClass, Tuple2.of(SIG2, ctr)); if (GraalvmSupport.isGraalBuildtime()) { if (Generated.class.isAssignableFrom(serializerClass)) { - return new GraalvmSerializerHolder(fory, type, serializerClass); + return new GraalvmSerializerHolder(fory.getConfig(), type, serializerClass); } } - return (Serializer) ctr.invoke(fory); + return (Serializer) ctr.invoke(typeResolver); } catch (NoSuchMethodException e) { ExceptionUtils.ignore(e); } @@ -164,39 +178,69 @@ private static Serializer createSerializer( CTR_MAP.put(serializerClass, Tuple2.of(SIG3, ctr)); if (GraalvmSupport.isGraalBuildtime()) { if (Generated.class.isAssignableFrom(serializerClass)) { - return new GraalvmSerializerHolder(fory, type, serializerClass); + return new GraalvmSerializerHolder(fory.getConfig(), type, serializerClass); } } - return (Serializer) ctr.invoke(type); + return (Serializer) ctr.invoke(config, type); } catch (NoSuchMethodException e) { - MethodHandle ctr = ReflectionUtils.getCtrHandle(serializerClass); + ExceptionUtils.ignore(e); + } + try { + MethodHandle ctr = lookup.findConstructor(serializerClass, SIG4); CTR_MAP.put(serializerClass, Tuple2.of(SIG4, ctr)); if (GraalvmSupport.isGraalBuildtime()) { if (Generated.class.isAssignableFrom(serializerClass)) { - return new GraalvmSerializerHolder(fory, type, serializerClass); + return new GraalvmSerializerHolder(fory.getConfig(), type, serializerClass); } } - return (Serializer) ctr.invoke(); + return (Serializer) ctr.invoke(config); + } catch (NoSuchMethodException e) { + ExceptionUtils.ignore(e); } + try { + MethodHandle ctr = lookup.findConstructor(serializerClass, SIG5); + CTR_MAP.put(serializerClass, Tuple2.of(SIG5, ctr)); + if (GraalvmSupport.isGraalBuildtime()) { + if (Generated.class.isAssignableFrom(serializerClass)) { + return new GraalvmSerializerHolder(fory.getConfig(), type, serializerClass); + } + } + return (Serializer) ctr.invoke(type); + } catch (NoSuchMethodException e) { + ExceptionUtils.ignore(e); + } + MethodHandle ctr = ReflectionUtils.getCtrHandle(serializerClass); + CTR_MAP.put(serializerClass, Tuple2.of(SIG6, ctr)); + if (GraalvmSupport.isGraalBuildtime()) { + if (Generated.class.isAssignableFrom(serializerClass)) { + return new GraalvmSerializerHolder(fory.getConfig(), type, serializerClass); + } + } + return (Serializer) ctr.invoke(); } - public static void write(MemoryBuffer buffer, Serializer serializer, T obj) { - serializer.write(buffer, obj); + public static void write(WriteContext writeContext, Serializer serializer, T obj) { + serializer.write(writeContext, obj); } - public static T read(MemoryBuffer buffer, Serializer serializer) { - return serializer.read(buffer); + public static T read(ReadContext readContext, Serializer serializer) { + return serializer.read(readContext); } public abstract static class CrossLanguageCompatibleSerializer extends Serializer { - public CrossLanguageCompatibleSerializer(Fory fory, Class cls) { - super(fory, cls); + public CrossLanguageCompatibleSerializer(Config config, Class cls) { + super(config, cls); } public CrossLanguageCompatibleSerializer( - Fory fory, Class cls, boolean needToWriteRef, boolean immutable) { - super(fory, cls, needToWriteRef, immutable); + Config config, Class cls, boolean needToWriteRef, boolean immutable) { + super(config, cls, needToWriteRef, immutable); + } + + @Override + public boolean threadSafe() { + return true; } } @@ -217,15 +261,14 @@ public CrossLanguageCompatibleSerializer( public abstract static class AbstractStringBuilderSerializer extends Serializer { - protected final StringSerializer stringSerializer; - - public AbstractStringBuilderSerializer(Fory fory, Class type) { - super(fory, type); - stringSerializer = fory.getStringSerializer(); + public AbstractStringBuilderSerializer(Config config, Class type) { + super(config, type); } @Override - public void write(MemoryBuffer buffer, T value) { + public void write(WriteContext writeContext, T value) { + MemoryBuffer buffer = writeContext.getBuffer(); + StringSerializer stringSerializer = writeContext.getStringSerializer(); if (isJava) { if (GET_CODER != null) { int coder = GET_CODER.applyAsInt(value); @@ -257,8 +300,8 @@ public void write(MemoryBuffer buffer, T value) { public static final class StringBuilderSerializer extends AbstractStringBuilderSerializer { - public StringBuilderSerializer(Fory fory) { - super(fory, StringBuilder.class); + public StringBuilderSerializer(Config config) { + super(config, StringBuilder.class); } @Override @@ -267,20 +310,17 @@ public StringBuilder copy(StringBuilder origin) { } @Override - public StringBuilder read(MemoryBuffer buffer) { - if (isJava) { - return new StringBuilder(stringSerializer.readString(buffer)); - } else { - return new StringBuilder(stringSerializer.readString(buffer)); - } + public StringBuilder read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); + return new StringBuilder(readContext.getStringSerializer().readString(buffer)); } } public static final class StringBufferSerializer extends AbstractStringBuilderSerializer { - public StringBufferSerializer(Fory fory) { - super(fory, StringBuffer.class); + public StringBufferSerializer(Config config) { + super(config, StringBuffer.class); } @Override @@ -289,22 +329,20 @@ public StringBuffer copy(StringBuffer origin) { } @Override - public StringBuffer read(MemoryBuffer buffer) { - if (isJava) { - return new StringBuffer(stringSerializer.readString(buffer)); - } else { - return new StringBuffer(stringSerializer.readString(buffer)); - } + public StringBuffer read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); + return new StringBuffer(readContext.getStringSerializer().readString(buffer)); } } public static final class BigDecimalSerializer extends ImmutableSerializer { - public BigDecimalSerializer(Fory fory) { - super(fory, BigDecimal.class); + public BigDecimalSerializer(Config config) { + super(config, BigDecimal.class); } @Override - public void write(MemoryBuffer buffer, BigDecimal value) { + public void write(WriteContext writeContext, BigDecimal value) { + MemoryBuffer buffer = writeContext.getBuffer(); final byte[] bytes = value.unscaledValue().toByteArray(); buffer.writeVarUint32Small7(value.scale()); buffer.writeVarUint32Small7(value.precision()); @@ -313,7 +351,8 @@ public void write(MemoryBuffer buffer, BigDecimal value) { } @Override - public BigDecimal read(MemoryBuffer buffer) { + public BigDecimal read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); int scale = buffer.readVarUint32Small7(); int precision = buffer.readVarUint32Small7(); int len = buffer.readVarUint32Small7(); @@ -324,19 +363,21 @@ public BigDecimal read(MemoryBuffer buffer) { } public static final class BigIntegerSerializer extends ImmutableSerializer { - public BigIntegerSerializer(Fory fory) { - super(fory, BigInteger.class); + public BigIntegerSerializer(Config config) { + super(config, BigInteger.class); } @Override - public void write(MemoryBuffer buffer, BigInteger value) { + public void write(WriteContext writeContext, BigInteger value) { + MemoryBuffer buffer = writeContext.getBuffer(); final byte[] bytes = value.toByteArray(); buffer.writeVarUint32Small7(bytes.length); buffer.writeBytes(bytes); } @Override - public BigInteger read(MemoryBuffer buffer) { + public BigInteger read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); int len = buffer.readVarUint32Small7(); byte[] bytes = buffer.readBytes(len); return new BigInteger(bytes); @@ -345,12 +386,13 @@ public BigInteger read(MemoryBuffer buffer) { public static final class AtomicBooleanSerializer extends Serializer { - public AtomicBooleanSerializer(Fory fory) { - super(fory, AtomicBoolean.class); + public AtomicBooleanSerializer(Config config) { + super(config, AtomicBoolean.class); } @Override - public void write(MemoryBuffer buffer, AtomicBoolean value) { + public void write(WriteContext writeContext, AtomicBoolean value) { + MemoryBuffer buffer = writeContext.getBuffer(); buffer.writeBoolean(value.get()); } @@ -360,19 +402,26 @@ public AtomicBoolean copy(AtomicBoolean origin) { } @Override - public AtomicBoolean read(MemoryBuffer buffer) { + public AtomicBoolean read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); return new AtomicBoolean(buffer.readBoolean()); } + + @Override + public boolean threadSafe() { + return true; + } } public static final class AtomicIntegerSerializer extends Serializer { - public AtomicIntegerSerializer(Fory fory) { - super(fory, AtomicInteger.class); + public AtomicIntegerSerializer(Config config) { + super(config, AtomicInteger.class); } @Override - public void write(MemoryBuffer buffer, AtomicInteger value) { + public void write(WriteContext writeContext, AtomicInteger value) { + MemoryBuffer buffer = writeContext.getBuffer(); buffer.writeInt32(value.get()); } @@ -382,19 +431,26 @@ public AtomicInteger copy(AtomicInteger origin) { } @Override - public AtomicInteger read(MemoryBuffer buffer) { + public AtomicInteger read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); return new AtomicInteger(buffer.readInt32()); } + + @Override + public boolean threadSafe() { + return true; + } } public static final class AtomicLongSerializer extends Serializer { - public AtomicLongSerializer(Fory fory) { - super(fory, AtomicLong.class); + public AtomicLongSerializer(Config config) { + super(config, AtomicLong.class); } @Override - public void write(MemoryBuffer buffer, AtomicLong value) { + public void write(WriteContext writeContext, AtomicLong value) { + MemoryBuffer buffer = writeContext.getBuffer(); buffer.writeInt64(value.get()); } @@ -404,96 +460,112 @@ public AtomicLong copy(AtomicLong origin) { } @Override - public AtomicLong read(MemoryBuffer buffer) { + public AtomicLong read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); return new AtomicLong(buffer.readInt64()); } + + @Override + public boolean threadSafe() { + return true; + } } public static final class AtomicReferenceSerializer extends Serializer { - public AtomicReferenceSerializer(Fory fory) { - super(fory, AtomicReference.class); + public AtomicReferenceSerializer(Config config) { + super(config, AtomicReference.class); } @Override - public void write(MemoryBuffer buffer, AtomicReference value) { - fory.writeRef(buffer, value.get()); + public void write(WriteContext writeContext, AtomicReference value) { + writeContext.writeRef(value.get()); } @Override public AtomicReference copy(AtomicReference origin) { - return new AtomicReference(fory.copyObject(origin.get())); + return new AtomicReference(copyContext().copyObject(origin.get())); + } + + @Override + public AtomicReference read(ReadContext readContext) { + return new AtomicReference(readContext.readRef()); } @Override - public AtomicReference read(MemoryBuffer buffer) { - return new AtomicReference(fory.readRef(buffer)); + public boolean threadSafe() { + return true; } } public static final class CurrencySerializer extends ImmutableSerializer { - public CurrencySerializer(Fory fory) { - super(fory, Currency.class); + public CurrencySerializer(Config config) { + super(config, Currency.class); } @Override - public void write(MemoryBuffer buffer, Currency object) { - fory.writeString(buffer, object.getCurrencyCode()); + public void write(WriteContext writeContext, Currency object) { + writeContext.writeString(object.getCurrencyCode()); } @Override - public Currency read(MemoryBuffer buffer) { - String currencyCode = fory.readString(buffer); + public Currency read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); + String currencyCode = readContext.readString(buffer); return Currency.getInstance(currencyCode); } } /** Serializer for {@link Charset}. */ public static final class CharsetSerializer extends ImmutableSerializer { - public CharsetSerializer(Fory fory, Class type) { - super(fory, type); + public CharsetSerializer(Config config, Class type) { + super(config, type); } - public void write(MemoryBuffer buffer, T object) { - fory.writeString(buffer, object.name()); + public void write(WriteContext writeContext, T object) { + writeContext.writeString(object.name()); } - public T read(MemoryBuffer buffer) { - return (T) Charset.forName(fory.readString(buffer)); + public T read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); + return (T) Charset.forName(readContext.readString(buffer)); } } public static final class URISerializer extends ImmutableSerializer { - public URISerializer(Fory fory) { - super(fory, URI.class); + public URISerializer(Config config) { + super(config, URI.class); } @Override - public void write(MemoryBuffer buffer, final URI uri) { - fory.writeString(buffer, uri.toString()); + public void write(WriteContext writeContext, final URI uri) { + writeContext.writeString(uri.toString()); } @Override - public URI read(MemoryBuffer buffer) { - return URI.create(fory.readString(buffer)); + public URI read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); + return URI.create(readContext.readString(buffer)); } } public static final class RegexSerializer extends ImmutableSerializer { - public RegexSerializer(Fory fory) { - super(fory, Pattern.class); + public RegexSerializer(Config config) { + super(config, Pattern.class); } @Override - public void write(MemoryBuffer buffer, Pattern pattern) { - fory.writeString(buffer, pattern.pattern()); + public void write(WriteContext writeContext, Pattern pattern) { + MemoryBuffer buffer = writeContext.getBuffer(); + writeContext.writeString(pattern.pattern()); buffer.writeInt32(pattern.flags()); } @Override - public Pattern read(MemoryBuffer buffer) { - String regex = fory.readString(buffer); + public Pattern read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); + String regex = readContext.readString(buffer); int flags = buffer.readInt32(); return Pattern.compile(regex, flags); } @@ -501,35 +573,39 @@ public Pattern read(MemoryBuffer buffer) { public static final class UUIDSerializer extends ImmutableSerializer { - public UUIDSerializer(Fory fory) { - super(fory, UUID.class); + public UUIDSerializer(Config config) { + super(config, UUID.class); } @Override - public void write(MemoryBuffer buffer, final UUID uuid) { + public void write(WriteContext writeContext, final UUID uuid) { + MemoryBuffer buffer = writeContext.getBuffer(); buffer.writeInt64(uuid.getMostSignificantBits()); buffer.writeInt64(uuid.getLeastSignificantBits()); } @Override - public UUID read(MemoryBuffer buffer) { + public UUID read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); return new UUID(buffer.readInt64(), buffer.readInt64()); } } public static final class ClassSerializer extends ImmutableSerializer { - public ClassSerializer(Fory fory) { - super(fory, Class.class); + public ClassSerializer(Config config) { + super(config, Class.class); } @Override - public void write(MemoryBuffer buffer, Class value) { - ((ClassResolver) fory.getTypeResolver()).writeClassInternal(buffer, value); + public void write(WriteContext writeContext, Class value) { + MemoryBuffer buffer = writeContext.getBuffer(); + ((ClassResolver) writeContext.getTypeResolver()).writeClassInternal(buffer, value); } @Override - public Class read(MemoryBuffer buffer) { - return ((ClassResolver) fory.getTypeResolver()).readClassInternal(buffer); + public Class read(ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); + return ((ClassResolver) readContext.getTypeResolver()).readClassInternal(buffer); } } @@ -542,34 +618,36 @@ public Class read(MemoryBuffer buffer) { // Use a separate serializer to avoid codegen for empty object. public static final class EmptyObjectSerializer extends ImmutableSerializer { - public EmptyObjectSerializer(Fory fory) { - super(fory, Object.class); + public EmptyObjectSerializer(Config config) { + super(config, Object.class); } @Override - public void write(MemoryBuffer buffer, Object value) {} + public void write(WriteContext writeContext, Object value) {} @Override - public Object read(MemoryBuffer buffer) { + public Object read(ReadContext readContext) { return new Object(); } } public static void registerDefaultSerializers(Fory fory) { + Config config = fory.getConfig(); TypeResolver resolver = fory.getTypeResolver(); - resolver.registerInternalSerializer(Class.class, new ClassSerializer(fory)); - resolver.registerInternalSerializer(StringBuilder.class, new StringBuilderSerializer(fory)); - resolver.registerInternalSerializer(StringBuffer.class, new StringBufferSerializer(fory)); - resolver.registerInternalSerializer(BigInteger.class, new BigIntegerSerializer(fory)); - resolver.registerInternalSerializer(BigDecimal.class, new BigDecimalSerializer(fory)); - resolver.registerInternalSerializer(AtomicBoolean.class, new AtomicBooleanSerializer(fory)); - resolver.registerInternalSerializer(AtomicInteger.class, new AtomicIntegerSerializer(fory)); - resolver.registerInternalSerializer(AtomicLong.class, new AtomicLongSerializer(fory)); - resolver.registerInternalSerializer(AtomicReference.class, new AtomicReferenceSerializer(fory)); - resolver.registerInternalSerializer(Currency.class, new CurrencySerializer(fory)); - resolver.registerInternalSerializer(URI.class, new URISerializer(fory)); - resolver.registerInternalSerializer(Pattern.class, new RegexSerializer(fory)); - resolver.registerInternalSerializer(UUID.class, new UUIDSerializer(fory)); - resolver.registerInternalSerializer(Object.class, new EmptyObjectSerializer(fory)); + resolver.registerInternalSerializer(Class.class, new ClassSerializer(config)); + resolver.registerInternalSerializer(StringBuilder.class, new StringBuilderSerializer(config)); + resolver.registerInternalSerializer(StringBuffer.class, new StringBufferSerializer(config)); + resolver.registerInternalSerializer(BigInteger.class, new BigIntegerSerializer(config)); + resolver.registerInternalSerializer(BigDecimal.class, new BigDecimalSerializer(config)); + resolver.registerInternalSerializer(AtomicBoolean.class, new AtomicBooleanSerializer(config)); + resolver.registerInternalSerializer(AtomicInteger.class, new AtomicIntegerSerializer(config)); + resolver.registerInternalSerializer(AtomicLong.class, new AtomicLongSerializer(config)); + resolver.registerInternalSerializer( + AtomicReference.class, new AtomicReferenceSerializer(config)); + resolver.registerInternalSerializer(Currency.class, new CurrencySerializer(config)); + resolver.registerInternalSerializer(URI.class, new URISerializer(config)); + resolver.registerInternalSerializer(Pattern.class, new RegexSerializer(config)); + resolver.registerInternalSerializer(UUID.class, new UUIDSerializer(config)); + resolver.registerInternalSerializer(Object.class, new EmptyObjectSerializer(config)); } } diff --git a/java/fory-core/src/main/java/org/apache/fory/serializer/StringSerializer.java b/java/fory-core/src/main/java/org/apache/fory/serializer/StringSerializer.java index 331d4d349a..5817200a56 100644 --- a/java/fory-core/src/main/java/org/apache/fory/serializer/StringSerializer.java +++ b/java/fory-core/src/main/java/org/apache/fory/serializer/StringSerializer.java @@ -33,8 +33,8 @@ import java.util.Arrays; import java.util.function.BiFunction; import java.util.function.Function; -import org.apache.fory.Fory; import org.apache.fory.annotation.CodegenInvoke; +import org.apache.fory.config.Config; import org.apache.fory.codegen.Expression; import org.apache.fory.codegen.Expression.Invoke; import org.apache.fory.codegen.Expression.StaticInvoke; @@ -131,23 +131,25 @@ private static class Offset { private int smoothCharArrayLength = DEFAULT_BUFFER_SIZE; private byte[] byteArray2 = EMPTY_BYTES_STUB; - public StringSerializer(Fory fory) { - super(fory, String.class, fory.trackingRef() && !fory.isStringRefIgnored()); - compressString = fory.compressString(); - xlang = fory.isCrossLanguage(); + public StringSerializer(Config config) { + super(config, String.class, config.trackingRef() && !config.isStringRefIgnored()); + compressString = config.compressString(); + xlang = config.isXlang(); if (xlang) { Preconditions.checkArgument(compressString, "compress string muse be enabled for xlang mode"); } - writeNumUtf16BytesForUtf8Encoding = fory.getConfig().writeNumUtf16BytesForUtf8Encoding(); + writeNumUtf16BytesForUtf8Encoding = config.writeNumUtf16BytesForUtf8Encoding(); } @Override - public void write(MemoryBuffer buffer, String value) { + public void write(org.apache.fory.context.WriteContext writeContext, String value) { + MemoryBuffer buffer = writeContext.getBuffer(); writeString(buffer, value); } @Override - public String read(MemoryBuffer buffer) { + public String read(org.apache.fory.context.ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); return readString(buffer); } 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 bbed8517b6..fb2599d180 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 @@ -39,7 +39,7 @@ import java.util.Date; import java.util.GregorianCalendar; import java.util.TimeZone; -import org.apache.fory.Fory; +import org.apache.fory.config.Config; import org.apache.fory.memory.MemoryBuffer; import org.apache.fory.resolver.TypeResolver; import org.apache.fory.util.DateTimeUtils; @@ -48,42 +48,44 @@ public class TimeSerializers { public abstract static class TimeSerializer extends Serializer { - public TimeSerializer(Fory fory, Class type) { - super(fory, type, !fory.getConfig().isTimeRefIgnored(), false); + public TimeSerializer(Config config, Class type) { + super(config, type, !config.isTimeRefIgnored(), false); } - public TimeSerializer(Fory fory, Class type, boolean needToWriteRef) { - super(fory, type, needToWriteRef, false); + public TimeSerializer(Config config, Class type, boolean needToWriteRef) { + super(config, type, needToWriteRef, false); } } public abstract static class ImmutableTimeSerializer extends ImmutableSerializer { - public ImmutableTimeSerializer(Fory fory, Class type) { - super(fory, type, !fory.getConfig().isTimeRefIgnored()); + public ImmutableTimeSerializer(Config config, Class type) { + super(config, type, !config.isTimeRefIgnored()); } - public ImmutableTimeSerializer(Fory fory, Class type, boolean needToWriteRef) { - super(fory, type, needToWriteRef); + public ImmutableTimeSerializer(Config config, Class type, boolean needToWriteRef) { + super(config, type, needToWriteRef); } } public abstract static class BaseDateSerializer extends TimeSerializer { - public BaseDateSerializer(Fory fory, Class type) { - super(fory, type); + public BaseDateSerializer(Config config, Class type) { + super(config, type); } - public BaseDateSerializer(Fory fory, Class type, boolean needToWriteRef) { - super(fory, type, needToWriteRef); + public BaseDateSerializer(Config config, Class type, boolean needToWriteRef) { + super(config, type, needToWriteRef); } @Override - public void write(MemoryBuffer buffer, T value) { + public void write(org.apache.fory.context.WriteContext writeContext, T value) { + MemoryBuffer buffer = writeContext.getBuffer(); buffer.writeInt64(value.getTime()); } @Override - public T read(MemoryBuffer buffer) { + public T read(org.apache.fory.context.ReadContext readContext) { + MemoryBuffer buffer = readContext.getBuffer(); return newInstance(buffer.readInt64()); } @@ -91,12 +93,12 @@ public T read(MemoryBuffer buffer) { } public static final class DateSerializer extends BaseDateSerializer { - public DateSerializer(Fory fory) { - super(fory, Date.class); + public DateSerializer(Config config) { + super(config, Date.class); } - public DateSerializer(Fory fory, boolean needToWriteRef) { - super(fory, Date.class, needToWriteRef); + public DateSerializer(Config config, boolean needToWriteRef) { + super(config, Date.class, needToWriteRef); } @Override @@ -111,12 +113,12 @@ public Date copy(Date value) { } public static final class SqlDateSerializer extends BaseDateSerializer { - public SqlDateSerializer(Fory fory) { - super(fory, java.sql.Date.class); + public SqlDateSerializer(Config config) { + super(config, java.sql.Date.class); } - public SqlDateSerializer(Fory fory, boolean needToWriteRef) { - super(fory, java.sql.Date.class, needToWriteRef); + public SqlDateSerializer(Config config, boolean needToWriteRef) { + super(config, java.sql.Date.class, needToWriteRef); } @Override @@ -132,12 +134,12 @@ public java.sql.Date copy(java.sql.Date value) { public static final class SqlTimeSerializer extends BaseDateSerializer