diff --git a/.agents/docs-and-formatting.md b/.agents/docs-and-formatting.md index 7d29e77b8f..86b3c7b697 100644 --- a/.agents/docs-and-formatting.md +++ b/.agents/docs-and-formatting.md @@ -18,6 +18,9 @@ Load this file when changing documentation, public APIs, protocol specs, benchma - Update the relevant docs under `docs/` when important public APIs change. - Update `docs/specification/**` when protocol behavior changes. - Keep examples working and aligned with the current API and protocol behavior. +- Do not use emoji in documentation, including headings, feature lists, status + tables, callouts, or READMEs. Use plain words such as "Supported" or + "Unsupported" instead. - Provide or update working examples when adding new features or materially changing workflows. - Add migration guidance when a change is breaking or materially changes workflow. - `docs/DEVELOPMENT.md` plus updates under `docs/guide/` and `docs/benchmarks/` are synced to `apache/fory-site`; other website content should be changed there instead of this repo. diff --git a/.agents/languages/csharp.md b/.agents/languages/csharp.md index 9d32167507..4ba1e6a361 100644 --- a/.agents/languages/csharp.md +++ b/.agents/languages/csharp.md @@ -1,4 +1,4 @@ -# C# +# C\# Load this file when changing `csharp/` or C# xlang behavior. diff --git a/.agents/skills/fory-code-review/references/validation-command-matrix.md b/.agents/skills/fory-code-review/references/validation-command-matrix.md index d4c67cf8a8..372c847bcb 100644 --- a/.agents/skills/fory-code-review/references/validation-command-matrix.md +++ b/.agents/skills/fory-code-review/references/validation-command-matrix.md @@ -18,7 +18,7 @@ Canonical runtime-specific rules now live under `../../../languages/*.md` and `. - `mvn -T16 test` - targeted: `mvn -T16 test -Dtest=#` -## C# +## C\# - Build/test from `csharp/` - Typical checks: diff --git a/.agents/skills/fory-performance-optimization/references/language-command-matrix.md b/.agents/skills/fory-performance-optimization/references/language-command-matrix.md index 59082d72e9..6975811861 100644 --- a/.agents/skills/fory-performance-optimization/references/language-command-matrix.md +++ b/.agents/skills/fory-performance-optimization/references/language-command-matrix.md @@ -49,7 +49,7 @@ Canonical runtime-specific rules now live under `../../../languages/*.md` and `. - Format: `go fmt ./...` - Profile: `pprof` (`go test -bench` + cpu/mem profiles) -## C# +## C\# - Build: `dotnet build Fory.sln -c Release --no-restore` - Tests: `dotnet test Fory.sln -c Release` diff --git a/AGENTS.md b/AGENTS.md index e0a8d35f9c..d6d7d31afe 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -61,6 +61,9 @@ This is the entry point for AI guidance in Apache Fory. Read this file first, th - Do not replace existing C, C++, Cython, unsafe, or other low-level optimized paths with simpler high-level implementations just to make a refactor easier. - If a refactor accidentally changes logic or implementation strategy, revert that part and re-implement the refactor around the existing logic. - Use English only in code, comments, and documentation. +- Do not use emoji in documentation, including headings, feature lists, status + tables, callouts, or READMEs. Use plain words such as "Supported" or + "Unsupported" instead. - After editing Markdown files outside `tasks/`, run `prettier --write ` on each changed Markdown file before finishing. Do not format Markdown under `tasks/`. - User guide docs must explain user-visible behavior, commands, and examples. Do not add implementation details, internal ownership rationale, build flags, diff --git a/README.md b/README.md index 2c4bf7accc..4b93788f81 100644 --- a/README.md +++ b/README.md @@ -17,12 +17,6 @@ idiomatic domain objects, schema IDL, and cross-language data exchange. -> [!IMPORTANT] -> Apache Fory™ was previously named Apache Fury. For versions before 0.11, use -> `fury` instead of `fory` in package names, imports, and dependencies. See the -> [Fury docs](https://fory.apache.org/docs/0.10/docs/introduction/) for older -> releases. - ## Why Fory Fory is built for fast, compact serialization across languages and runtimes. It @@ -285,16 +279,23 @@ Snapshots for Java, Scala, and Kotlin are available from ## Choose Serialization Mode -| Mode | Use it when | Start here | -| -------------------- | ------------------------------------------------------------- | -------------------------------------------------------- | -| Xlang serialization | Data crosses language boundaries | [Cross-language guide](docs/guide/xlang) | -| Native serialization | Producer and consumer are in the same language | Language guide for your runtime | -| Row format | You need random field access or analytics-style partial reads | [Row format spec](docs/specification/row_format_spec.md) | +| Mode | Use it when | Start here | +| ----------- | ------------------------------------------------------------- | -------------------------------------------------------- | +| Xlang mode | Data crosses language boundaries | [Cross-language guide](docs/guide/xlang) | +| Native mode | Producer and consumer are in the same language | Language guide for your runtime | +| Row format | You need random field access or analytics-style partial reads | [Row format spec](docs/specification/row_format_spec.md) | + +For Java, Scala, Kotlin, Python, C++, Go, and Rust, use native mode for +same-language traffic. It avoids xlang's cross-language type mapping and +metadata constraints, stays closer to each runtime's native type system, and +supports broader language-specific object graphs. Use it when both producer and +consumer are in the same runtime family and you want the native object model +rather than a portable cross-language schema. -Use native mode for same-language traffic. It avoids xlang's cross-language -type mapping and metadata constraints, so it can serialize broader -language-specific object graphs and is the fastest path for same-language -payloads. +For Java/JVM-only systems, native mode is the replacement path for JDK +serialization, Kryo, FST, Hessian, and Java-only Protocol Buffers payloads. For +Python-only systems, native mode is the replacement path for pickle and +cloudpickle. Compatible mode is Fory's schema-evolution mode. It writes the metadata readers and writers need to tolerate schema differences. Xlang mode enables compatible @@ -327,7 +328,7 @@ public class Example { } public static void main(String[] args) { - Fory fory = Fory.builder().withXlang(true).withCompatible(true).build(); + Fory fory = Fory.builder().withXlang(true).build(); fory.register(Person.class, "example.Person"); Person person = new Person(); @@ -353,7 +354,7 @@ class Person: name: str age: pyfory.Int32 -fory = pyfory.Fory(xlang=True, compatible=True) +fory = pyfory.Fory(xlang=True) fory.register_type(Person, typename="example.Person") data = fory.serialize(Person("Alice", 30)) @@ -378,7 +379,7 @@ type Person struct { } func main() { - f := fory.New(fory.WithXlang(true), fory.WithCompatible(true)) + f := fory.New(fory.WithXlang(true)) if err := f.RegisterStructByName(Person{}, "example.Person"); err != nil { panic(err) } @@ -404,7 +405,7 @@ struct Person { } fn main() -> Result<(), Error> { - let mut fory = Fory::builder().xlang(true).compatible(true).build(); + let mut fory = Fory::builder().xlang(true).build(); fory.register_by_name::("example", "Person")?; let bytes = fory.serialize(&Person { @@ -434,8 +435,8 @@ struct Person { FORY_STRUCT(Person, name, age); int main() { - auto fory = Fory::builder().xlang(true).compatible(true).build(); - fory.register_struct("example.Person"); + auto fory = Fory::builder().xlang(true).build(); + fory.register_struct("example", "Person"); auto bytes = fory.serialize(Person{"Alice", 30}).value(); Person person = fory.deserialize(bytes).value(); @@ -456,7 +457,7 @@ const personType = Type.struct( }, ); -const fory = new Fory({ compatible: true }); +const fory = new Fory(); const { serialize, deserialize } = fory.register(personType); const bytes = serialize({ name: "Alice", age: 30 }); @@ -476,9 +477,7 @@ public sealed class Person public int Age { get; set; } } -Fory fory = Fory.Builder() - .Compatible(true) - .Build(); +Fory fory = Fory.Builder().Build(); fory.Register("example", "Person"); byte[] bytes = fory.Serialize(new Person { Name = "Alice", Age = 30 }); @@ -507,7 +506,7 @@ class Person { } void main() { - final fory = Fory(compatible: true); + final fory = Fory(); PersonFory.register( fory, Person, @@ -541,7 +540,7 @@ struct Person { var age: Int32 = 0 } -let fory = Fory(xlang: true, compatible: true) +let fory = Fory() try fory.register(Person.self, namespace: "example", name: "Person") let bytes = try fory.serialize(Person(name: "Alice", age: 30)) @@ -556,10 +555,7 @@ import org.apache.fory.scala.ForyScala case class Person(name: String, age: Int) -val fory = ForyScala.builder() - .withXlang(true) - .withCompatible(true) - .build() +val fory = ForyScala.builder().withXlang(true).build() fory.register(classOf[Person], "example.Person") val bytes = fory.serialize(Person("Alice", 30)) @@ -575,10 +571,7 @@ import org.apache.fory.kotlin.ForyKotlin data class Person(val name: String, val age: Int) fun main() { - val fory = ForyKotlin.builder() - .withXlang(true) - .withCompatible(true) - .build() + val fory = ForyKotlin.builder().withXlang(true).build() fory.register(Person::class.java, "example.Person") val bytes = fory.serialize(Person("Alice", 30)) @@ -593,10 +586,16 @@ type-mapping rules, see the [cross-language guide](docs/guide/xlang) and ## Native Serialization -Use native mode when the writer and reader are in the same language. Java and -Python can serialize broader language-specific object graphs this way. The -languages below expose an explicit `xlang=false` or native-mode setting; runtimes -without that switch stay on their documented default path. +Use native mode when the writer and reader are in the same language. It is +optimized for each runtime's native type system and can cover language-specific +types, object graphs, and framework-replacement cases that xlang mode keeps out +of the portable wire format. The languages below expose an explicit +`xlang=false` or native-mode setting; runtimes without that switch stay on their +documented default path. + +Choose Java native mode for Java/JVM-only replacements of JDK serialization, +Kryo, FST, Hessian, or Java-only Protocol Buffers payloads. Choose Python native +mode when replacing pickle or cloudpickle for Python-only payloads. Keep class/type registration enabled for untrusted input. See the language guides for runtime-specific security and compatibility settings. @@ -769,12 +768,12 @@ deserialization, see the **Specifications** -| Specification | Source | Website | -| ---------------------- | ----------------------------------------------------------------------------- | -------------------------------------------------------------------------------- | -| Xlang serialization | [xlang_serialization_spec.md](docs/specification/xlang_serialization_spec.md) | [View](https://fory.apache.org/docs/specification/fory_xlang_serialization_spec) | -| Java serialization | [java_serialization_spec.md](docs/specification/java_serialization_spec.md) | [View](https://fory.apache.org/docs/specification/fory_java_serialization_spec) | -| Row format | [row_format_spec.md](docs/specification/row_format_spec.md) | [View](https://fory.apache.org/docs/specification/fory_row_format_spec) | -| Cross-language mapping | [xlang_type_mapping.md](docs/specification/xlang_type_mapping.md) | [View](https://fory.apache.org/docs/specification/fory_xlang_serialization_spec) | +| Specification | Source | Website | +| ---------------------- | ----------------------------------------------------------------------------- | --------------------------------------------------------------------------- | +| Xlang serialization | [xlang_serialization_spec.md](docs/specification/xlang_serialization_spec.md) | [View](https://fory.apache.org/docs/specification/xlang_serialization_spec) | +| Java serialization | [java_serialization_spec.md](docs/specification/java_serialization_spec.md) | [View](https://fory.apache.org/docs/specification/java_serialization_spec) | +| Row format | [row_format_spec.md](docs/specification/row_format_spec.md) | [View](https://fory.apache.org/docs/specification/row_format_spec) | +| Cross-language mapping | [xlang_type_mapping.md](docs/specification/xlang_type_mapping.md) | [View](https://fory.apache.org/docs/specification/xlang_type_mapping) | ## Community diff --git a/benchmarks/java/src/test/java/org/apache/fory/benchmark/state/ProtobufSerializerTest.java b/benchmarks/java/src/test/java/org/apache/fory/benchmark/state/ProtobufSerializerTest.java index b9120d0e41..604c84dd62 100644 --- a/benchmarks/java/src/test/java/org/apache/fory/benchmark/state/ProtobufSerializerTest.java +++ b/benchmarks/java/src/test/java/org/apache/fory/benchmark/state/ProtobufSerializerTest.java @@ -31,7 +31,7 @@ public class ProtobufSerializerTest { public void testSample() { Sample object = new Sample().populate(false); ProtoMessage.Sample samplePb = ProtoBuffersState.buildSample(object); - Fory fory = Fory.builder().requireClassRegistration(false).build(); + Fory fory = Fory.builder().withXlang(false).requireClassRegistration(false).build(); fory.register(ProtoMessage.Sample.class); byte[] bytes = fory.serialize(samplePb); Object newObj = fory.deserialize(bytes); @@ -40,7 +40,7 @@ public void testSample() { @Test public void testByteString() { - Fory fory = Fory.builder().requireClassRegistration(false).build(); + Fory fory = Fory.builder().withXlang(false).requireClassRegistration(false).build(); Assert.assertEquals(fory.deserialize(fory.serialize(ByteString.empty())), ByteString.empty()); ByteString bytes = ByteString.copyFrom(new byte[] {1, 2, 3}); Assert.assertEquals(fory.deserialize(fory.serialize(bytes)), bytes); diff --git a/compiler/README.md b/compiler/README.md index 77293f345d..aece751cd6 100644 --- a/compiler/README.md +++ b/compiler/README.md @@ -16,10 +16,10 @@ The FDL compiler generates cross-language serialization code from schema definit For comprehensive documentation, see the [FDL Schema Guide](../docs/compiler/index.md): - [FDL Syntax Reference](../docs/compiler/schema-idl.md) - Complete language syntax and grammar -- [Type System](../docs/compiler/type-system.md) - Primitive types, collections, and language mappings +- [Type System](../docs/compiler/schema-idl.md#type-system) - Primitive types, collections, and language mappings - [Compiler Guide](../docs/compiler/compiler-guide.md) - CLI options and build integration - [Generated Code](../docs/compiler/generated-code.md) - Output format for each target language -- [Protocol Buffers vs FDL](../docs/compiler/protobuf-idl.md) - Feature comparison and migration guide +- [Protocol Buffers vs FDL](../docs/compiler/protobuf-idl.md) - Feature comparison and porting guide ## Installation @@ -83,7 +83,6 @@ import org.apache.fory.Fory; Fory fory = Fory.builder() .withXlang(true) - .withCompatible(true) .withRefTracking(true) .withModule(DemoForyModule.INSTANCE) .build(); @@ -100,7 +99,7 @@ byte[] bytes = fory.serialize(cat); import pyfory from demo import Cat, register_demo_types -fory = pyfory.Fory() +fory = pyfory.Fory(xlang=True) register_demo_types(fory) cat = Cat(name="Whiskers", lives=9) diff --git a/compiler/fory_compiler/generators/swift.py b/compiler/fory_compiler/generators/swift.py index 30d30812ff..ca630cfb97 100644 --- a/compiler/fory_compiler/generators/swift.py +++ b/compiler/fory_compiler/generators/swift.py @@ -1230,7 +1230,7 @@ def generate_registration_type(self, indent: int = 0) -> List[str]: f"{ind}{self.indent_str * 2}nonisolated(unsafe) static let fory: Fory = {{" ) lines.append( - f"{ind}{self.indent_str * 3}let fory = Fory(config: .init(xlang: true, trackRef: true, compatible: true))" + f"{ind}{self.indent_str * 3}let fory = Fory(config: .init(trackRef: true, compatible: true))" ) lines.append(f"{ind}{self.indent_str * 3}do {{") lines.append(f"{ind}{self.indent_str * 4}try register(fory)") diff --git a/cpp/README.md b/cpp/README.md index 5e8ccd3fc8..4e7ab89e54 100644 --- a/cpp/README.md +++ b/cpp/README.md @@ -4,7 +4,7 @@ **Apache Fory™** is a blazing fast multi-language serialization framework powered by **JIT compilation** and **zero-copy** techniques, providing up to **ultra-fast performance** while maintaining ease of use and type safety. -The C++ implementation provides high-performance serialization with compile-time type safety through template metaprogramming and zero-copy row format for analytics workloads. +The C++ implementation provides high-performance serialization with compile-time type safety through template metaprogramming and zero-copy row format for analytics workloads. It defaults to xlang mode for cross-language payloads; use native mode with `.xlang(false)` for C++-only traffic when you want the C++ runtime type system without portable xlang type-mapping constraints. ## Why Apache Fory™ C++? @@ -45,20 +45,17 @@ struct Person { }; int main() { - // Create Fory instance - auto fory = apache::fory::ForyBuilder() - .xlang(true).compatible(true) - .track_ref(true) - .build(); + // Create an xlang Fory instance. + auto fory = fory::serialization::Fory::builder().xlang(true).build(); // Register type - fory->register_struct(1); + fory.register_struct(1); // Create object Person person{"Alice", 30, "alice@example.com"}; // Serialize - auto result = fory->serialize(person); + auto result = fory.serialize(person); if (!result.ok()) { std::cerr << "Serialization failed: " << result.error().message() << std::endl; return 1; @@ -66,7 +63,7 @@ int main() { std::vector bytes = std::move(result.value()); // Deserialize - auto decoded = fory->deserialize(bytes); + auto decoded = fory.deserialize(bytes); if (!decoded.ok()) { std::cerr << "Deserialization failed: " << decoded.error().message() << std::endl; return 1; @@ -115,9 +112,9 @@ public: FORY_STRUCT(Person, name, age, address, hobbies, metadata); }; -auto fory = apache::fory::ForyBuilder().build(); -fory->register_struct
(100); -fory->register_struct(200); +auto fory = fory::serialization::Fory::builder().xlang(true).build(); +fory.register_struct
(100); +fory.register_struct(200); Person person{ "John Doe", @@ -127,8 +124,8 @@ Person person{ {{"role", "developer"}} }; -auto bytes = fory->serialize(person).value(); -auto decoded = fory->deserialize(bytes).value(); +auto bytes = fory.serialize(person).value(); +auto decoded = fory.deserialize(bytes).value(); ``` ### 1.1 External/Third-Party Types @@ -146,8 +143,8 @@ struct Foo { FORY_STRUCT(Foo, id, name); } // namespace thirdparty -auto fory = apache::fory::ForyBuilder().build(); -fory->register_struct(1); +auto fory = fory::serialization::Fory::builder().xlang(true).build(); +fory.register_struct(1); ``` ### 1.2 Inherited Fields @@ -173,7 +170,8 @@ struct Derived : Base { Apache Fory™ automatically tracks and preserves reference identity for shared objects using `std::shared_ptr`. When the same object is referenced multiple times, Fory serializes it only once and uses reference IDs for subsequent occurrences. ```cpp -auto fory = apache::fory::ForyBuilder() +auto fory = fory::serialization::Fory::builder() + .xlang(true) .track_ref(true) .build(); @@ -184,8 +182,8 @@ auto shared = std::make_shared("shared_value"); std::vector> data = {shared, shared, shared}; // The shared value is serialized only once -auto bytes = fory->serialize(data).value(); -auto decoded = fory->deserialize>>(bytes).value(); +auto bytes = fory.serialize(data).value(); +auto decoded = fory.deserialize>>(bytes).value(); // Verify reference identity is preserved assert(decoded[0].get() == decoded[1].get()); @@ -194,7 +192,7 @@ assert(decoded[1].get() == decoded[2].get()); ### 3. Schema Evolution -Apache Fory™ supports schema evolution in **Compatible mode**, allowing serialization and deserialization peers to have different type definitions. +Apache Fory™ supports schema evolution in **Compatible mode**, allowing serialization and deserialization peers to have different type definitions. Xlang mode uses compatible schema evolution by default; in native mode, add `.xlang(false).compatible(true)` when C++-only payloads need schema evolution. ```cpp // Version 1 @@ -213,21 +211,17 @@ struct PersonV2 { FORY_STRUCT(PersonV2, name, age, phone); }; -auto fory1 = apache::fory::ForyBuilder() - .compatible(true) - .build(); -fory1->register_struct(1); +auto fory1 = fory::serialization::Fory::builder().xlang(true).build(); +fory1.register_struct(1); -auto fory2 = apache::fory::ForyBuilder() - .compatible(true) - .build(); -fory2->register_struct(1); +auto fory2 = fory::serialization::Fory::builder().xlang(true).build(); +fory2.register_struct(1); PersonV1 v1{"Alice", 30, "123 Main St"}; -auto bytes = fory1->serialize(v1).value(); +auto bytes = fory1.serialize(v1).value(); // Deserialize with V2 - missing fields get default values -auto v2 = fory2->deserialize(bytes).value(); +auto v2 = fory2.deserialize(bytes).value(); assert(v2.name == "Alice"); assert(v2.phone == std::nullopt); ``` @@ -241,14 +235,14 @@ Apache Fory™ supports enum serialization with automatic ordinal mapping: enum class Color { Red, Green, Blue }; // Non-continuous enum - needs FORY_ENUM -enum class LegacyStatus { Active = 1, Inactive = 5, Pending = 10 }; -FORY_ENUM(LegacyStatus, Active, Inactive, Pending); +enum class SparseStatus { Active = 1, Inactive = 5, Pending = 10 }; +FORY_ENUM(SparseStatus, Active, Inactive, Pending); // FORY_ENUM must be defined at namespace scope. struct Item { std::string name; Color color; - LegacyStatus status; + SparseStatus status; FORY_STRUCT(Item, name, color, status); }; ``` @@ -267,7 +261,7 @@ struct Config { }; Config config{"timeout", 30}; -auto bytes = fory->serialize(config).value(); +auto bytes = fory.serialize(config).value(); ``` ### 6. Row-Based Serialization @@ -286,7 +280,7 @@ struct UserProfile { FORY_STRUCT(UserProfile, id, username, email, scores, is_active); }; -apache::fory::RowEncoder encoder; +fory::row::encoder::RowEncoder encoder; UserProfile profile{12345, "alice", "alice@example.com", {95, 87, 92}, true}; @@ -306,14 +300,10 @@ assert(row.get_bool(4) == true); // is_active Apache Fory™ supports seamless data exchange across multiple languages: ```cpp -// Enable cross-language mode -auto fory = apache::fory::ForyBuilder() - .xlang(true) - .compatible(true) - .build(); +auto fory = fory::serialization::Fory::builder().xlang(true).build(); // Register types with consistent IDs across languages -fory->register_struct(1); +fory.register_struct(1); ``` See [xlang_type_mapping.md](https://fory.apache.org/docs/specification/xlang_type_mapping) for type mapping across languages. @@ -322,10 +312,10 @@ See [xlang_type_mapping.md](https://fory.apache.org/docs/specification/xlang_typ ```cpp // Single-threaded (fastest performance) -auto fory = apache::fory::ForyBuilder().build(); +auto fory = fory::serialization::Fory::builder().xlang(true).build(); // Thread-safe with internal Fory pool -auto fory = apache::fory::ForyBuilder().build_thread_safe(); +auto fory = fory::serialization::Fory::builder().xlang(true).build_thread_safe(); ``` ## Architecture @@ -406,7 +396,7 @@ bash ci/format.sh --cpp ## Documentation - **[User Guide](https://fory.apache.org/docs/guide/cpp)** - Comprehensive user documentation -- **[Protocol Specification](https://fory.apache.org/docs/specification/fory_xlang_serialization_spec)** - Serialization protocol details +- **[Protocol Specification](https://fory.apache.org/docs/specification/xlang_serialization_spec)** - Serialization protocol details - **[Type Mapping](https://fory.apache.org/docs/specification/xlang_type_mapping)** - Cross-language type mappings - **[Source](https://github.com/apache/fory/tree/main/docs/guide/cpp)** - Documentation source diff --git a/cpp/fory/serialization/fory.h b/cpp/fory/serialization/fory.h index 5deb726105..869271ade4 100644 --- a/cpp/fory/serialization/fory.h +++ b/cpp/fory/serialization/fory.h @@ -61,17 +61,11 @@ class ThreadSafeFory; /// /// Example: /// ```cpp -/// // Single-threaded Fory (fastest, not thread-safe) -/// auto fory = Fory::builder() -/// .xlang(true) -/// .compatible(true) -/// .build(); +/// // Single-threaded xlang Fory (not thread-safe) +/// auto fory = Fory::builder().xlang(true).build(); /// /// // Thread-safe Fory (uses context pools) -/// auto fory = Fory::builder() -/// .xlang(true) -/// .compatible(true) -/// .build_thread_safe(); +/// auto fory = Fory::builder().xlang(true).build_thread_safe(); /// ``` class ForyBuilder { public: @@ -502,7 +496,7 @@ class BaseFory { /// /// Example: /// ```cpp -/// auto fory = Fory::builder().xlang(true).compatible(true).build(); +/// auto fory = Fory::builder().xlang(true).build(); /// fory.register_struct(1); /// /// MyStruct obj{...}; @@ -893,10 +887,7 @@ class Fory : public BaseFory { /// /// Example: /// ```cpp -/// auto fory = Fory::builder() -/// .xlang(true) -/// .compatible(true) -/// .build_thread_safe(); +/// auto fory = Fory::builder().xlang(true).build_thread_safe(); /// fory.register_struct(1); /// /// // Can be used from multiple threads safely diff --git a/cpp/fory/serialization/serialization_test.cc b/cpp/fory/serialization/serialization_test.cc index 27ab634fa9..0a988a9d2c 100644 --- a/cpp/fory/serialization/serialization_test.cc +++ b/cpp/fory/serialization/serialization_test.cc @@ -708,7 +708,7 @@ TEST(SerializationTest, NestedStructRoundtrip) { // ============================================================================ TEST(SerializationTest, DeserializeInvalidData) { - auto fory = Fory::builder().build(); + auto fory = Fory::builder().xlang(true).build(); uint8_t invalid_data[] = {0xFF, 0xFF, 0xFF}; auto result = fory.deserialize(invalid_data, 3); @@ -716,13 +716,13 @@ TEST(SerializationTest, DeserializeInvalidData) { } TEST(SerializationTest, DeserializeNullPointer) { - auto fory = Fory::builder().build(); + auto fory = Fory::builder().xlang(true).build(); auto result = fory.deserialize(nullptr, 0); EXPECT_FALSE(result.ok()); } TEST(SerializationTest, DeserializeZeroSize) { - auto fory = Fory::builder().build(); + auto fory = Fory::builder().xlang(true).build(); uint8_t data[] = {0x01}; auto result = fory.deserialize(data, 0); EXPECT_FALSE(result.ok()); @@ -1043,8 +1043,11 @@ TEST(SerializationTest, ConfigurationBuilder) { Fory::builder().compatible(false).xlang(true).build(); auto explicit_schema_consistent_reverse_order = Fory::builder().xlang(true).compatible(false).build(); - auto compatible_with_version_check = - Fory::builder().compatible(true).check_struct_version(true).build(); + auto compatible_with_version_check = Fory::builder() + .xlang(true) + .compatible(true) + .check_struct_version(true) + .build(); EXPECT_TRUE(default_xlang.config().compatible); EXPECT_FALSE(default_xlang.config().check_struct_version); diff --git a/cpp/fory/serialization/smart_ptr_serializer_test.cc b/cpp/fory/serialization/smart_ptr_serializer_test.cc index ea5c7385e1..bc23179ca4 100644 --- a/cpp/fory/serialization/smart_ptr_serializer_test.cc +++ b/cpp/fory/serialization/smart_ptr_serializer_test.cc @@ -51,7 +51,7 @@ struct UniqueHolder { namespace { Fory create_serializer(bool track_ref) { - return Fory::builder().track_ref(track_ref).build(); + return Fory::builder().xlang(true).track_ref(track_ref).build(); } // Helper to register all test struct types @@ -464,7 +464,7 @@ TEST(SmartPtrSerializerTest, NonDynamicFieldConfig) { original.ptr->value = 42; original.ptr->data = "test data"; - auto fory = Fory::builder().track_ref(false).build(); + auto fory = Fory::builder().xlang(true).track_ref(false).build(); fory.register_struct(400); fory.register_struct(401); @@ -487,7 +487,7 @@ TEST(SmartPtrSerializerTest, NonDynamicFieldNullValue) { NonDynamicFieldHolder original; original.ptr = nullptr; - auto fory = Fory::builder().track_ref(false).build(); + auto fory = Fory::builder().xlang(true).track_ref(false).build(); fory.register_struct(404); fory.register_struct(405); diff --git a/cpp/fory/serialization/tuple_serializer.h b/cpp/fory/serialization/tuple_serializer.h index c817c13ec1..14b36f6b8f 100644 --- a/cpp/fory/serialization/tuple_serializer.h +++ b/cpp/fory/serialization/tuple_serializer.h @@ -65,7 +65,7 @@ using tuple_first_type_t = typename tuple_first_type::type; // Tuple Serialization Helpers // ============================================================================ -/// write tuple elements directly (non-xlang mode) +/// write tuple elements directly (native mode) template inline void write_tuple_elements_direct(const Tuple &tuple, WriteContext &ctx, std::index_sequence) { @@ -121,7 +121,7 @@ inline void write_tuple_elements_homogeneous(const Tuple &tuple, ...); } -/// Read tuple elements directly (non-xlang mode) +/// Read tuple elements directly (native mode) template inline Tuple read_tuple_elements_direct(ReadContext &ctx, std::index_sequence) { diff --git a/cpp/fory/serialization/unsigned_serializer.h b/cpp/fory/serialization/unsigned_serializer.h index 5705b5ceb2..29d99b330d 100644 --- a/cpp/fory/serialization/unsigned_serializer.h +++ b/cpp/fory/serialization/unsigned_serializer.h @@ -32,7 +32,7 @@ namespace fory { namespace serialization { // ============================================================================ -// Unsigned Integer Type Serializers (xlang=false mode only) +// Unsigned Integer Type Serializers (native mode only) // These serializers use distinct TypeIds for unsigned types. // Unsigned types are NOT supported in xlang mode per the xlang spec. // ============================================================================ diff --git a/cpp/fory/serialization/weak_ptr_serializer_test.cc b/cpp/fory/serialization/weak_ptr_serializer_test.cc index 850b19fb8d..25aa6ac2da 100644 --- a/cpp/fory/serialization/weak_ptr_serializer_test.cc +++ b/cpp/fory/serialization/weak_ptr_serializer_test.cc @@ -33,7 +33,7 @@ namespace { // ============================================================================ Fory create_serializer(bool track_ref = true) { - return Fory::builder().track_ref(track_ref).build(); + return Fory::builder().xlang(true).track_ref(track_ref).build(); } // ============================================================================ @@ -425,14 +425,17 @@ TEST(WeakPtrSerializerTest, RequiresTrackRef) { original.weak_ref = SharedWeak::from(target); // Create serializer WITHOUT track_ref - auto fory = Fory::builder().track_ref(false).build(); + auto fory = Fory::builder().xlang(true).track_ref(false).build(); fory.register_struct(100); fory.register_struct(101); auto bytes_result = fory.serialize(original); EXPECT_FALSE(bytes_result.ok()) << "Should fail when track_ref is disabled"; - EXPECT_TRUE(bytes_result.error().to_string().find("track_ref") != - std::string::npos); + auto error = bytes_result.error().to_string(); + EXPECT_EQ(error, + "Invalid ref: SharedWeak requires track_ref to be enabled. Use " + "Fory::builder().track_ref(true).build()"); + EXPECT_EQ(error.find("xlang("), std::string::npos); } // ============================================================================ diff --git a/cpp/fory/util/error.h b/cpp/fory/util/error.h index 13928adf5f..f1a08ad4a6 100644 --- a/cpp/fory/util/error.h +++ b/cpp/fory/util/error.h @@ -60,7 +60,7 @@ enum class ErrorCode : char { /// ## Pattern 1: Static Factory Functions (General Use) /// /// ```cpp -/// // ✅ CORRECT: Use static factory functions +/// // CORRECT: Use static factory functions /// auto err = Error::type_error("Expected string type"); /// auto err = Error::invalid_data("Invalid value: " + std::to_string(42)); /// auto err = Error::type_mismatch(1, 2); diff --git a/cpp/fory/util/result.h b/cpp/fory/util/result.h index 3d96737633..0fa5c91bad 100644 --- a/cpp/fory/util/result.h +++ b/cpp/fory/util/result.h @@ -699,17 +699,17 @@ template class Result { /// Declare and assign value from Result or return error /// -/// ⚠️ IMPORTANT: This macro expands to multiple statements. +/// IMPORTANT: This macro expands to multiple statements. /// Always use braces with control flow statements! /// -/// ✅ CORRECT: +/// CORRECT: /// ```cpp /// if (condition) { /// FORY_TRY(data, load_data()); /// } /// ``` /// -/// ❌ WRONG: +/// WRONG: /// ```cpp /// if (condition) /// FORY_TRY(data, load_data()); // BREAKS! diff --git a/dart/packages/fory/README.md b/dart/packages/fory/README.md index 78ea3ea30e..174261a983 100644 --- a/dart/packages/fory/README.md +++ b/dart/packages/fory/README.md @@ -123,7 +123,6 @@ Keep the same registration identity on all runtimes that exchange the type. ```dart final fory = Fory( - compatible: true, maxDepth: 256, maxCollectionSize: 1 << 20, maxBinarySize: 64 * 1024 * 1024, @@ -132,8 +131,8 @@ final fory = Fory( | Option | Default | Description | | -------------------- | ---------- | ------------------------------------------------------- | -| `compatible` | `false` | Enables compatible struct encoding for schema evolution | -| `checkStructVersion` | `true` | Validates struct version in schema-consistent mode | +| `compatible` | `true` | Enables compatible struct encoding for schema evolution | +| `checkStructVersion` | `false` | Validates struct version in schema-consistent mode | | `maxDepth` | `256` | Maximum nesting depth per operation | | `maxCollectionSize` | `1 << 20` | Maximum collection and map payload size | | `maxBinarySize` | `64 << 20` | Maximum binary payload size | diff --git a/docs/README.md b/docs/README.md index 54bfd25d7f..fc36d7c864 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,9 +1,9 @@ # User Guide -- For Cross Language Object Graph Guide, see [xlang serialization guide](guide/xlang_serialization_guide.md) doc. -- For Java Object Graph Guide, see [java serialization guide](guide/java_serialization_guide.md) doc. -- For Row Format Guide, see [row format guide](guide/row_format_guide.md) doc. -- For Scala Guide, see [scala guide](guide/scala_guide.md) doc. +- For xlang serialization, see the [xlang guide](guide/xlang/index.md). +- For Java serialization, see the [Java guide](guide/java/index.md). +- For row format, see the [row format spec](specification/row_format_spec.md). +- For Scala serialization, see the [Scala guide](guide/scala/index.md). - For using Apache Fory™ with GraalVM native image, see [graalvm support](guide/java/graalvm-support.md) doc. ## Fory IDL Schema @@ -15,7 +15,7 @@ Define cross-language data structures with Fory IDL and generate native code for - [Type System](compiler/schema-idl.md#type-system) - Primitive types, collections, and mappings - [Compiler Guide](compiler/compiler-guide.md) - CLI usage and build integration - [Generated Code](compiler/generated-code.md) - Output format for each language -- [Protocol Buffers vs Fory IDL](compiler/protobuf-idl.md) - Feature comparison and migration +- [Protocol Buffers vs Fory IDL](compiler/protobuf-idl.md) - Feature comparison and porting ## Serialization Format diff --git a/docs/compiler/flatbuffers-idl.md b/docs/compiler/flatbuffers-idl.md index 7362a1028c..c8e7945701 100644 --- a/docs/compiler/flatbuffers-idl.md +++ b/docs/compiler/flatbuffers-idl.md @@ -27,7 +27,7 @@ translates them into Fory IR for code generation. - When to use FlatBuffers input with Fory - Exact FlatBuffers to Fory mapping behavior - Supported Fory-specific attributes in `.fbs` -- Migration notes and generated-code differences +- Adoption notes and generated-code differences ## Why Use Apache Fory @@ -36,10 +36,10 @@ translates them into Fory IR for code generation. - Java performance: In Java object-serialization workloads, Fory is faster than FlatBuffers in Fory benchmarks. - Other languages: serialization performance is generally in a similar range. -- Deserialization in practice: FlatBuffers does not perform native-object deserialization and is - faster by default, but if your application needs native objects, it requires - conversion and that conversion step can dominate read cost. In those cases, - Fory deserialization is often faster end-to-end. +- Deserialization in practice: FlatBuffers can be faster when callers read + directly from its buffer, but applications that need native objects still + require conversion, and that conversion step can dominate read cost. In those + cases, Fory deserialization is often faster end-to-end. - Easier APIs: Fory uses direct native objects, so you do not need to reverse-build tables or manually manage offsets. - Better graph modeling: Shared and circular references are first-class features @@ -183,7 +183,7 @@ Inspect translated schema syntax for debugging: foryc schema.fbs --emit-fdl --emit-fdl-path ./translated ``` -## Migration Notes +## Adoption Notes 1. Keep existing `namespace` values stable to keep type registration stable. 2. Review fields that relied on FlatBuffers default literals and set explicit @@ -195,5 +195,5 @@ foryc schema.fbs --emit-fdl --emit-fdl-path ./translated ## Summary FlatBuffers input lets you reuse existing `.fbs` schemas while moving to Fory's -runtime and code generation model. This is useful for incremental migration, -while preserving schema investment and adopting Fory-native object APIs. +runtime and code generation model. This is useful for incremental adoption while +preserving schema investment and using Fory-native object APIs. diff --git a/docs/compiler/generated-code.md b/docs/compiler/generated-code.md index 60d7ce7893..520dda8d3b 100644 --- a/docs/compiler/generated-code.md +++ b/docs/compiler/generated-code.md @@ -1151,7 +1151,7 @@ including nested positions. Generated schema modules register schema types and resolve KSP-generated serializers from the target class name. The package-owned helper runtime uses -`ForyKotlin.builder()` with the schema module installed, so message +`ForyKotlin.builder().withXlang(true)` with the schema module installed, so message `toBytes`/`fromBytes` helpers work without caller-managed runtime setup. For `addressbook.fdl`: @@ -1160,7 +1160,6 @@ public object AddressbookForyModule : ForyModule { private val fory: ThreadSafeFory by lazy { ForyKotlin.builder() .withXlang(true) - .withCompatible(true) .withRefTracking(true) .withModule(this) .buildThreadSafeFory() @@ -1285,16 +1284,15 @@ use type-use annotations such as `List[Node @Ref]`. ### Schema Module Generated schema modules register schema serializers, enums, structs, and -unions. The package-owned helper runtime uses `ForyScala.builder()` with the -schema module installed, so message `toBytes`/`fromBytes` helpers work without -caller-managed runtime setup: +unions. The package-owned helper runtime uses +`ForyScala.builder().withXlang(true)` with the schema module installed, so +message `toBytes`/`fromBytes` helpers work without caller-managed runtime setup: ```scala object AddressbookForyModule extends org.apache.fory.ForyModule { private lazy val fory: ThreadSafeFory = ForyScala.builder() .withXlang(true) - .withCompatible(true) .withRefTracking(true) .withModule(this) .buildThreadSafeFory() diff --git a/docs/compiler/index.md b/docs/compiler/index.md index d0c9f2e5e7..8f516ec7ab 100644 --- a/docs/compiler/index.md +++ b/docs/compiler/index.md @@ -176,7 +176,7 @@ data = bytes(person) # or `person.to_bytes()` | [Type System](schema-idl.md#type-system) | Primitive types, collections, and type rules | | [Compiler Guide](compiler-guide.md) | CLI options and build integration | | [Generated Code](generated-code.md) | Output format for each target language | -| [Protocol Buffers IDL Support](protobuf-idl.md) | Comparison with protobuf and migration guide | +| [Protocol Buffers IDL Support](protobuf-idl.md) | Protobuf mapping rules and adoption guidance | | [FlatBuffers IDL Support](flatbuffers-idl.md) | FlatBuffers mapping rules and codegen differences | ## Key Concepts diff --git a/docs/compiler/protobuf-idl.md b/docs/compiler/protobuf-idl.md index 60dabe49f0..58bb92bf6e 100644 --- a/docs/compiler/protobuf-idl.md +++ b/docs/compiler/protobuf-idl.md @@ -25,9 +25,9 @@ how protobuf concepts map to Fory, and how to use protobuf-only Fory extension o ## What This Page Covers - Choosing protobuf vs Fory for your use case -- Syntax and semantic differences that matter during migration +- Syntax and semantic differences that matter during adoption - Supported Fory extension options in protobuf files -- Practical migration patterns from protobuf to Fory +- Practical transition patterns from protobuf to Fory ## Quick Decision Guide @@ -280,7 +280,7 @@ message TreeNode { } ``` -## Migration Guide: Protobuf to Fory +## Porting Protobuf Schemas To Fory ### Step 1: Translate Schema Syntax @@ -305,12 +305,12 @@ languages. ### Step 5: Run Compatibility Checks -For staged migrations, keep both formats in parallel and verify payload-level +For staged transitions, keep both formats in parallel and verify payload-level parity with integration tests. ## Coexistence Strategy -You can run protobuf and Fory in parallel during migration: +You can run protobuf and Fory in parallel during a staged transition: ```java public byte[] serialize(Object obj, Format format) { diff --git a/docs/guide/cpp/basic-serialization.md b/docs/guide/cpp/basic-serialization.md index 630b68388c..d8b3ebcb57 100644 --- a/docs/guide/cpp/basic-serialization.md +++ b/docs/guide/cpp/basic-serialization.md @@ -70,7 +70,7 @@ struct Person { FORY_STRUCT(Person, name, age, address, hobbies, metadata); int main() { - auto fory = Fory::builder().xlang(true).compatible(true).build(); + auto fory = Fory::builder().xlang(true).build(); fory.register_struct
(100); fory.register_struct(200); @@ -93,7 +93,7 @@ int main() { ### Serialize to New Vector ```cpp -auto fory = Fory::builder().xlang(true).compatible(true).build(); +auto fory = Fory::builder().xlang(true).build(); fory.register_struct(1); MyStruct obj{/* ... */}; diff --git a/docs/guide/cpp/configuration.md b/docs/guide/cpp/configuration.md index 0c73995a7c..d58bfaaa31 100644 --- a/docs/guide/cpp/configuration.md +++ b/docs/guide/cpp/configuration.md @@ -19,57 +19,40 @@ license: | limitations under the License. --- -This page covers Fory configuration options and serialization modes. - -## Serialization Modes - -Apache Fory™ supports two serialization modes: - -### SchemaConsistent Mode (Default) - -Type declarations must match exactly between peers: - -```cpp -auto fory = Fory::builder().build(); // SchemaConsistent by default -``` - -### Compatible Mode - -Allows independent schema evolution: - -```cpp -auto fory = Fory::builder().compatible(true).build(); -``` +This page covers C++ runtime configuration. `Fory::builder()` creates xlang +payloads by default, and omitted compatible mode resolves to compatible mode in +xlang. Native mode is selected explicitly with `.xlang(false)` and defaults to +schema-consistent payloads. ## Builder Pattern -Use `ForyBuilder` to construct Fory instances with custom configuration: +Use `Fory::builder()` to construct Fory instances with custom configuration: ```cpp #include "fory/serialization/fory.h" using namespace fory::serialization; -// Default configuration -auto fory = Fory::builder().build(); +// Xlang mode with compatible schema evolution. +auto fory = Fory::builder().xlang(true).build(); -// Compatible mode for schema evolution +// Schema-consistent xlang payloads. auto fory = Fory::builder() - .compatible(true) + .xlang(true) + .compatible(false) .build(); -// Cross-language mode +// Native mode for C++-only traffic. auto fory = Fory::builder() - .xlang(true).compatible(true) + .xlang(false) .build(); -// Full configuration +// Native mode with compatible schema evolution. auto fory = Fory::builder() - .compatible(true) - .xlang(true) + .xlang(false) .track_ref(true) .max_dyn_depth(10) - .check_struct_version(true) + .compatible(true) .build(); ``` @@ -77,31 +60,36 @@ auto fory = Fory::builder() ### xlang(bool) -Enable/disable cross-language (xlang) serialization mode. +Select the wire mode. ```cpp auto fory = Fory::builder() - .xlang(true).compatible(true) // Enable cross-language compatibility + .xlang(false) .build(); ``` -When enabled, includes metadata for cross-language compatibility with Java, Python, Go, Rust, and JavaScript. +When `true`, C++ writes the xlang wire format used by Java, Python, Go, Rust, +JavaScript, C#, Swift, and Dart. When `false`, C++ writes native-mode payloads +for C++-only traffic. **Default:** `true` ### compatible(bool) -Enable/disable compatible mode for schema evolution. +Enable compatible schema evolution. ```cpp auto fory = Fory::builder() - .compatible(true) // Enable schema evolution + .xlang(true) + .compatible(true) .build(); ``` When enabled, supports reading data serialized with different schema versions. +When omitted, xlang mode defaults to compatible mode. Native mode defaults to +schema-consistent mode and uses compatible mode only when this option is set. -**Default:** `false` +**Default:** `true` in xlang mode; `false` in native mode ### track_ref(bool) @@ -109,6 +97,7 @@ Enable/disable reference tracking for shared and circular references. ```cpp auto fory = Fory::builder() + .xlang(true) .track_ref(true) // Enable reference tracking .build(); ``` @@ -123,6 +112,7 @@ Set maximum allowed nesting depth for dynamically-typed objects. ```cpp auto fory = Fory::builder() + .xlang(true) .max_dyn_depth(10) // Allow up to 10 levels .build(); ``` @@ -142,6 +132,8 @@ Enable/disable struct version checking. ```cpp auto fory = Fory::builder() + .xlang(true) + .compatible(false) .check_struct_version(true) // Enable version checking .build(); ``` @@ -155,9 +147,7 @@ When enabled, validates type hashes to detect schema mismatches. ### Single-Threaded (Fastest) ```cpp -auto fory = Fory::builder() - .xlang(true).compatible(true) - .build(); // Returns Fory +auto fory = Fory::builder().xlang(true).build(); // Returns Fory ``` Single-threaded `Fory` is the fastest option, but NOT thread-safe. Use one instance per thread. @@ -165,25 +155,23 @@ Single-threaded `Fory` is the fastest option, but NOT thread-safe. Use one insta ### Thread-Safe ```cpp -auto fory = Fory::builder() - .xlang(true).compatible(true) - .build_thread_safe(); // Returns ThreadSafeFory +auto fory = Fory::builder().xlang(true).build_thread_safe(); // Returns ThreadSafeFory ``` `ThreadSafeFory` uses a pool of Fory instances to provide thread-safe serialization. Slightly slower due to pool overhead, but safe to use from multiple threads concurrently. ## Configuration Summary -| Option | Description | Default | -| ---------------------------- | --------------------------------------- | ------- | -| `xlang(bool)` | Enable cross-language mode | `true` | -| `compatible(bool)` | Enable schema evolution | `false` | -| `track_ref(bool)` | Enable reference tracking | `true` | -| `max_dyn_depth(uint32_t)` | Maximum nesting depth for dynamic types | `5` | -| `check_struct_version(bool)` | Enable struct version checking | `false` | +| Option | Description | Default | +| ---------------------------- | --------------------------------------- | ------------------------------ | +| `xlang(bool)` | Use xlang mode | `true` | +| `compatible(bool)` | Enable schema evolution | xlang: `true`; native: `false` | +| `track_ref(bool)` | Enable reference tracking | `true` | +| `max_dyn_depth(uint32_t)` | Maximum nesting depth for dynamic types | `5` | +| `check_struct_version(bool)` | Enable struct version checking | `false` | ## Related Topics - [Basic Serialization](basic-serialization.md) - Using configured Fory -- [Cross-Language](cross-language.md) - XLANG mode details +- [Cross-Language](cross-language.md) - xlang mode details - [Type Registration](type-registration.md) - Registering types diff --git a/docs/guide/cpp/cross-language.md b/docs/guide/cpp/cross-language.md index c54b197e28..eb73b84b61 100644 --- a/docs/guide/cpp/cross-language.md +++ b/docs/guide/cpp/cross-language.md @@ -1,6 +1,6 @@ --- title: Cross-Language Serialization -sidebar_position: 10 +sidebar_position: 3 id: cross_language license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -25,16 +25,16 @@ This page explains how to use Fory for cross-language serialization between C++ Apache Fory™ enables seamless data exchange between C++, Java, Python, Go, Rust, and JavaScript. The xlang (cross-language) mode ensures binary compatibility across all supported languages. -## Enabling Cross-Language Mode +## Create an Xlang Runtime + +C++ defaults to xlang mode. Compatible schema evolution is also the xlang default. Set the mode explicitly in xlang examples: ```cpp #include "fory/serialization/fory.h" using namespace fory::serialization; -auto fory = Fory::builder() - .xlang(true).compatible(true) // Enable cross-language mode - .build(); +auto fory = Fory::builder().xlang(true).build(); ``` ## Cross-Language Example @@ -61,7 +61,7 @@ struct Message { FORY_STRUCT(Message, topic, timestamp, headers, payload); int main() { - auto fory = Fory::builder().xlang(true).compatible(true).build(); + auto fory = Fory::builder().xlang(true).build(); fory.register_struct(100); Message msg{ @@ -97,7 +97,7 @@ public class Message { public class Consumer { public static void main(String[] args) throws Exception { Fory fory = Fory.builder() - .withXlang(true).withCompatible(true) + .withXlang(true) .build(); fory.register(Message.class, 100); // Same ID as C++ @@ -121,7 +121,7 @@ class Message: headers: dict[str, str] payload: bytes -fory = pyfory.Fory(xlang=True, compatible=True) +fory = pyfory.Fory(xlang=True) fory.register(Message, type_id=100) # Same ID as C++ with open("message.bin", "rb") as f: @@ -255,14 +255,11 @@ fory.register(Order, type_id=102) ## Compatible Mode -For schema evolution across language boundaries: +Xlang mode already uses compatible schema evolution by default. Keep that default for schemas that +may evolve independently: ```cpp -// C++ with compatible mode -auto fory = Fory::builder() - .xlang(true) - .compatible(true) // Enable schema evolution - .build(); +auto fory = Fory::builder().xlang(true).build(); ``` Compatible mode allows: diff --git a/docs/guide/cpp/custom-serializers.md b/docs/guide/cpp/custom-serializers.md index 9e013ac556..d59b75b497 100644 --- a/docs/guide/cpp/custom-serializers.md +++ b/docs/guide/cpp/custom-serializers.md @@ -1,6 +1,6 @@ --- title: Custom Serializers -sidebar_position: 6 +sidebar_position: 10 id: custom_serializers license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -25,7 +25,7 @@ For types that don't support `FORY_STRUCT`, implement a `Serializer` template sp - External types from third-party libraries - Types with special serialization requirements -- Legacy data format compatibility +- Existing data format compatibility - Performance-critical custom encoding - Cross-language interoperability with custom protocols @@ -146,7 +146,7 @@ The `type_id` constant should be set to `TypeId::EXT` for custom extension types Register your custom serializer with Fory before use: ```cpp -auto fory = Fory::builder().xlang(true).compatible(true).build(); +auto fory = Fory::builder().xlang(true).build(); // Register with numeric type ID (must match across languages) auto result = fory.register_extension_type(103); @@ -255,7 +255,7 @@ struct Serializer { } // namespace fory int main() { - auto fory = Fory::builder().xlang(true).compatible(true).build(); + auto fory = Fory::builder().xlang(true).build(); fory.register_extension_type(100); CustomType original{42, "test"}; diff --git a/docs/guide/cpp/index.md b/docs/guide/cpp/index.md index a87f0a5d82..70acae8f6d 100644 --- a/docs/guide/cpp/index.md +++ b/docs/guide/cpp/index.md @@ -21,17 +21,17 @@ license: | **Apache Fory™** is a blazing fast multi-language serialization framework powered by **JIT compilation** and **zero-copy** techniques, providing up to **ultra-fast performance** while maintaining ease of use and safety. -The C++ implementation provides high-performance serialization with compile-time type safety using modern C++17 features and template metaprogramming. +The C++ implementation provides high-performance serialization with compile-time type safety using modern C++17 features and template metaprogramming. It supports both xlang mode for cross-language payloads and native mode for C++-only payloads. ## Why Apache Fory™ C++? -- **🔥 Blazingly Fast**: Fast serialization and optimized binary protocols -- **🌍 Cross-Language**: Seamlessly serialize/deserialize data across Java, Python, C++, Go, JavaScript, and Rust -- **🎯 Type-Safe**: Compile-time type checking with macro-based struct registration -- **🔄 Reference Tracking**: Automatic tracking of shared and circular references -- **📦 Schema Evolution**: Compatible mode for independent schema changes -- **⚡ Two Formats**: Object graph serialization and zero-copy row-based format -- **🧵 Thread Safety**: Both single-threaded (fastest) and thread-safe variants +- **Fast binary encoding**: Fast serialization and optimized binary protocols +- **Cross-language**: Seamlessly serialize/deserialize data across Java, Python, C++, Go, JavaScript, and Rust +- **Type-safe**: Compile-time type checking with macro-based struct registration +- **Reference tracking**: Automatic tracking of shared and circular references +- **Schema evolution**: Compatible mode for independent schema changes +- **Two formats**: Object graph serialization and zero-copy row-based format +- **Thread safety**: Both single-threaded and thread-safe variants ## Installation @@ -155,9 +155,9 @@ struct Person { FORY_STRUCT(Person, name, age, hobbies); int main() { - // Create a Fory instance + // Create an xlang Fory instance auto fory = Fory::builder() - .xlang(true).compatible(true) // Enable cross-language mode + .xlang(true) .track_ref(false) // Disable reference tracking for simple types .build(); @@ -206,6 +206,14 @@ struct Derived : Base { }; ``` +## Xlang Mode And Native Mode + +Use xlang mode for cross-language payloads and schemas shared with other Fory runtimes. Xlang mode is the default C++ wire mode, and C++ examples that use it set `.xlang(true)` explicitly so the mode choice is visible. + +Use native mode for C++-only traffic. Native mode is selected with `.xlang(false)`, uses schema-consistent payloads unless compatible mode is enabled, and keeps C++ object serialization on the C++ runtime path. It is optimized for C++ types and avoids portable xlang type-mapping constraints when the payload never leaves C++. + +See [Cross-Language Serialization](cross-language.md) for C++ xlang registration and interoperability rules, and [Configuration](configuration.md) for native-mode builder options. + ## Thread Safety Apache Fory™ C++ provides two variants for different threading needs: @@ -214,18 +222,14 @@ Apache Fory™ C++ provides two variants for different threading needs: ```cpp // Single-threaded Fory - fastest, NOT thread-safe -auto fory = Fory::builder() - .xlang(true).compatible(true) - .build(); +auto fory = Fory::builder().xlang(true).build(); ``` ### Thread-Safe ```cpp // Thread-safe Fory - uses context pools -auto fory = Fory::builder() - .xlang(true).compatible(true) - .build_thread_safe(); +auto fory = Fory::builder().xlang(true).build_thread_safe(); // Can be used from multiple threads safely std::thread t1([&]() { @@ -258,9 +262,9 @@ std::thread t2([&]() { - [Configuration](configuration.md) - Builder options and modes - [Basic Serialization](basic-serialization.md) - Object graph serialization +- [Cross-Language](cross-language.md) - xlang mode and interoperability +- [Schema Metadata](schema-metadata.md) - Field-level metadata (nullable, ref tracking) - [Schema Evolution](schema-evolution.md) - Compatible mode and schema changes - [Type Registration](type-registration.md) - Registering types -- [Field Configuration](field-configuration.md) - Field-level metadata (nullable, ref tracking) - [Supported Types](supported-types.md) - All supported types -- [Cross-Language](cross-language.md) - XLANG mode - [Row Format](row-format.md) - Zero-copy row-based format diff --git a/docs/guide/cpp/polymorphism.md b/docs/guide/cpp/polymorphism.md index f72ebab1ec..74a7dc38a0 100644 --- a/docs/guide/cpp/polymorphism.md +++ b/docs/guide/cpp/polymorphism.md @@ -1,6 +1,6 @@ --- title: Polymorphic Serialization -sidebar_position: 5 +sidebar_position: 7 id: polymorphism license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -63,7 +63,7 @@ struct Zoo { FORY_STRUCT(Zoo, star_animal); int main() { - auto fory = Fory::builder().track_ref(true).build(); + auto fory = Fory::builder().xlang(true).track_ref(true).build(); // Register all types with unique type IDs fory.register_struct(100); @@ -167,7 +167,7 @@ FORY_STRUCT(Pet, animal1, (animal2, fory::F().dynamic(true)), - Performance is critical and you don't need subtype support - Working with monomorphic data despite having a polymorphic base class -### Field Configuration +### Schema Metadata Configure field metadata directly in `FORY_STRUCT`: @@ -182,7 +182,7 @@ FORY_STRUCT(Zoo, (star, fory::F(0)), (mascot, fory::F(2).dynamic(false))); ``` -See [Field Configuration](field-configuration.md) for complete details on +See [Schema Metadata](schema-metadata.md) for complete details on `nullable()`, `ref()`, and other field-level options. ## std::unique_ptr Polymorphism @@ -195,7 +195,7 @@ struct Container { }; FORY_STRUCT(Container, pet); -auto fory = Fory::builder().track_ref(true).build(); +auto fory = Fory::builder().xlang(true).track_ref(true).build(); fory.register_struct(200); fory.register_struct(201); @@ -224,7 +224,7 @@ struct AnimalShelter { }; FORY_STRUCT(AnimalShelter, animals, registry); -auto fory = Fory::builder().track_ref(true).build(); +auto fory = Fory::builder().xlang(true).track_ref(true).build(); fory.register_struct(100); fory.register_struct(101); fory.register_struct(102); @@ -261,11 +261,11 @@ struct Container { FORY_STRUCT(Container, value, nested); // Default max_dyn_depth is 5 -auto fory1 = Fory::builder().build(); +auto fory1 = Fory::builder().xlang(true).build(); assert(fory1.config().max_dyn_depth == 5); // Increase limit for deeper nesting -auto fory2 = Fory::builder().max_dyn_depth(10).build(); +auto fory2 = Fory::builder().xlang(true).max_dyn_depth(10).build(); fory2.register_struct(1); // Create deeply nested structure @@ -290,7 +290,7 @@ auto decoded = fory2.deserialize>(bytes).value(); **Depth exceeded error:** ```cpp -auto fory_shallow = Fory::builder().max_dyn_depth(2).build(); +auto fory_shallow = Fory::builder().xlang(true).max_dyn_depth(2).build(); fory_shallow.register_struct(1); // 3 levels exceeds max_dyn_depth=2 @@ -320,7 +320,7 @@ struct Pet { FORY_STRUCT(Pet, primary, (optional, fory::F().nullable())); ``` -See [Field Configuration](field-configuration.md) for more details. +See [Schema Metadata](schema-metadata.md) for more details. ## Combining Polymorphism with Other Features @@ -340,7 +340,7 @@ struct WeightedNode : GraphNode { FORY_STRUCT(WeightedNode, id, neighbors, weight); // Enable ref tracking to handle shared references and cycles -auto fory = Fory::builder().track_ref(true).build(); +auto fory = Fory::builder().xlang(true).track_ref(true).build(); fory.register_struct(100); fory.register_struct(101); @@ -365,6 +365,7 @@ Use compatible mode for schema evolution with polymorphic types: ```cpp auto fory = Fory::builder() + .xlang(true) .compatible(true) // Enable schema evolution .track_ref(true) .build(); @@ -381,7 +382,7 @@ auto fory = Fory::builder() 2. **Enable reference tracking** for polymorphic types: ```cpp - auto fory = Fory::builder().track_ref(true).build(); + auto fory = Fory::builder().xlang(true).track_ref(true).build(); ``` 3. **Virtual destructors required**: Ensure base classes have virtual destructors: @@ -411,7 +412,7 @@ auto fory = Fory::builder() 6. **Adjust `max_dyn_depth`** based on your data structure depth: ```cpp - auto fory = Fory::builder().max_dyn_depth(10).build(); + auto fory = Fory::builder().xlang(true).max_dyn_depth(10).build(); ``` 7. **Use `nullable()`** for optional polymorphic fields: @@ -473,7 +474,7 @@ if (!decoded_result.ok()) { ## Related Topics - [Type Registration](type-registration.md) - Registering types for serialization -- [Field Configuration](field-configuration.md) - Field-level metadata and options +- [Schema Metadata](schema-metadata.md) - Field-level metadata and options - [Supported Types](supported-types.md) - Smart pointers and collections - [Configuration](configuration.md) - `max_dyn_depth` and other settings - [Basic Serialization](basic-serialization.md) - Core serialization concepts diff --git a/docs/guide/cpp/row-format.md b/docs/guide/cpp/row-format.md index d1551ab5f5..f12d265e57 100644 --- a/docs/guide/cpp/row-format.md +++ b/docs/guide/cpp/row-format.md @@ -33,14 +33,14 @@ Apache Fory™ Row Format is a binary format optimized for: ## When to Use Row Format -| Use Case | Row Format | Object Graph | -| ----------------------------- | ---------- | ------------ | -| Analytics/OLAP | ✅ | ❌ | -| Random field access | ✅ | ❌ | -| Full object serialization | ❌ | ✅ | -| Complex object graphs | ❌ | ✅ | -| Reference tracking | ❌ | ✅ | -| Cross-language (simple types) | ✅ | ✅ | +| Use Case | Row Format | Object Graph | +| ----------------------------- | ------------- | ------------- | +| Analytics/OLAP | Supported | Not supported | +| Random field access | Supported | Not supported | +| Full object serialization | Not supported | Supported | +| Complex object graphs | Not supported | Supported | +| Reference tracking | Not supported | Supported | +| Cross-language (simple types) | Supported | Supported | ## Quick Start diff --git a/docs/guide/cpp/schema-evolution.md b/docs/guide/cpp/schema-evolution.md index 9369c1d6c0..119ba0293a 100644 --- a/docs/guide/cpp/schema-evolution.md +++ b/docs/guide/cpp/schema-evolution.md @@ -1,6 +1,6 @@ --- title: Schema Evolution -sidebar_position: 3 +sidebar_position: 5 id: schema_evolution license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -48,13 +48,13 @@ FORY_STRUCT(PersonV2, name, age, email); int main() { // Create separate Fory instances for each schema version auto fory_v1 = Fory::builder() - .compatible(true) // Enable schema evolution .xlang(true) + .compatible(true) // Enable schema evolution .build(); auto fory_v2 = Fory::builder() - .compatible(true) .xlang(true) + .compatible(true) .build(); // Register with the SAME type ID for schema evolution @@ -92,14 +92,14 @@ FORY_STRUCT_EVOLVING(StableMessage, false); Compatible mode supports the following schema changes: -| Change Type | Support | Behavior | -| ------------------ | ------- | --------------------------------------- | -| Add new fields | ✅ | Missing fields use default values | -| Remove fields | ✅ | Extra fields are skipped | -| Reorder fields | ✅ | Fields matched by name, not position | -| Change nullability | ✅ | `T` ↔ `std::optional` | -| Change field types | ❌ | Types must be compatible | -| Rename fields | ❌ | Field names must match (case-sensitive) | +| Change Type | Support | Behavior | +| ------------------ | ------------- | --------------------------------------- | +| Add new fields | Supported | Missing fields use default values | +| Remove fields | Supported | Extra fields are skipped | +| Reorder fields | Supported | Fields matched by name, not position | +| Change nullability | Supported | `T` ↔ `std::optional` | +| Change field types | Not supported | Types must be compatible | +| Rename fields | Not supported | Field names must match (case-sensitive) | ## Adding Fields (Backward Compatibility) @@ -394,16 +394,16 @@ Schema evolution works across languages when using xlang mode: ```cpp // C++ with compatible mode auto fory = Fory::builder() - .compatible(true) .xlang(true) + .compatible(true) .build(); ``` ```java // Java with compatible mode Fory fory = Fory.builder() - .withCompatible(true) .withXlang(true) + .withCompatible(true) .build(); ``` diff --git a/docs/guide/cpp/field-configuration.md b/docs/guide/cpp/schema-metadata.md similarity index 98% rename from docs/guide/cpp/field-configuration.md rename to docs/guide/cpp/schema-metadata.md index 070e9f2d96..e0bb38dc57 100644 --- a/docs/guide/cpp/field-configuration.md +++ b/docs/guide/cpp/schema-metadata.md @@ -1,7 +1,7 @@ --- -title: Field Configuration -sidebar_position: 7 -id: field_configuration +title: Schema Metadata +sidebar_position: 4 +id: schema_metadata license: | Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with diff --git a/docs/guide/cpp/type-registration.md b/docs/guide/cpp/type-registration.md index 9e9a34350e..16c0f45b27 100644 --- a/docs/guide/cpp/type-registration.md +++ b/docs/guide/cpp/type-registration.md @@ -1,6 +1,6 @@ --- title: Type Registration -sidebar_position: 4 +sidebar_position: 6 id: type_registration license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -45,7 +45,7 @@ struct Person { FORY_STRUCT(Person, name, age); int main() { - auto fory = Fory::builder().xlang(true).compatible(true).build(); + auto fory = Fory::builder().xlang(true).build(); // Register with a unique type ID fory.register_struct(100); @@ -93,12 +93,12 @@ FORY_ENUM(Priority, LOW, MEDIUM, HIGH); // FORY_ENUM must be defined at namespace scope. // Global namespace enum (prefix with ::) -enum LegacyStatus { UNKNOWN = -1, OK = 0, ERROR = 1 }; -FORY_ENUM(::LegacyStatus, UNKNOWN, OK, ERROR); +enum SparseStatus { UNKNOWN = -1, OK = 0, ERROR = 1 }; +FORY_ENUM(::SparseStatus, UNKNOWN, OK, ERROR); // Register after FORY_ENUM fory.register_enum(1); -fory.register_enum(2); +fory.register_enum(2); ``` **When to use `FORY_ENUM`:** @@ -112,7 +112,7 @@ fory.register_enum(2); For `ThreadSafeFory`, register types before spawning threads: ```cpp -auto fory = Fory::builder().xlang(true).compatible(true).build_thread_safe(); +auto fory = Fory::builder().xlang(true).build_thread_safe(); // Register all types first fory.register_struct(100); @@ -137,7 +137,7 @@ For cross-language compatibility, ensure: ### Java ```java -Fory fory = Fory.builder().withXlang(true).withCompatible(true).build(); +Fory fory = Fory.builder().withXlang(true).build(); fory.register(Person.class, 100); fory.register(Address.class, 101); ``` @@ -147,7 +147,7 @@ fory.register(Address.class, 101); ```python import pyfory -fory = pyfory.Fory(xlang=True, compatible=True) +fory = pyfory.Fory(xlang=True) fory.register(Person, type_id=100) fory.register(Address, type_id=101) ``` @@ -155,7 +155,7 @@ fory.register(Address, type_id=101) ### C++ ```cpp -auto fory = Fory::builder().xlang(true).compatible(true).build(); +auto fory = Fory::builder().xlang(true).build(); fory.register_struct(100); fory.register_struct
(101); ``` @@ -164,40 +164,68 @@ fory.register_struct
(101); Built-in types have pre-assigned type IDs and don't need registration: -| Type ID | Type | -| ------- | -------------------- | -| 0 | NONE | -| 1 | BOOL | -| 2 | INT8 | -| 3 | INT16 | -| 4 | INT32 | -| 5 | VAR_INT32 | -| 6 | INT64 | -| 7 | VAR_INT64 | -| 8 | SLI_INT64 | -| 9 | FLOAT16 | -| 10 | FLOAT32 | -| 11 | FLOAT64 | -| 12 | STRING | -| 13 | LIST | -| 14 | MAP | -| 15 | SET | -| 16 | TIMESTAMP | -| 17 | DURATION | -| 18 | DATE | -| 19 | DECIMAL | -| 20 | BINARY | -| 21 | ARRAY | -| 22 | BOOL_ARRAY | -| 23-28 | INT_ARRAY variants | -| 29-31 | FLOAT_ARRAY variants | -| 32 | STRUCT | -| 33 | COMPATIBLE_STRUCT | -| 34 | NAMED_STRUCT | -| 35 | NAMED*COMPATIBLE*... | -| 36 | EXT | -| 37 | NAMED_EXT | -| 63 | UNKNOWN | +| Type ID | Type | +| ------- | ----------------------- | +| 0 | UNKNOWN | +| 1 | BOOL | +| 2 | INT8 | +| 3 | INT16 | +| 4 | INT32 | +| 5 | VARINT32 | +| 6 | INT64 | +| 7 | VARINT64 | +| 8 | TAGGED_INT64 | +| 9 | UINT8 | +| 10 | UINT16 | +| 11 | UINT32 | +| 12 | VAR_UINT32 | +| 13 | UINT64 | +| 14 | VAR_UINT64 | +| 15 | TAGGED_UINT64 | +| 16 | FLOAT8 | +| 17 | FLOAT16 | +| 18 | BFLOAT16 | +| 19 | FLOAT32 | +| 20 | FLOAT64 | +| 21 | STRING | +| 22 | LIST | +| 23 | SET | +| 24 | MAP | +| 25 | ENUM | +| 26 | NAMED_ENUM | +| 27 | STRUCT | +| 28 | COMPATIBLE_STRUCT | +| 29 | NAMED_STRUCT | +| 30 | NAMED_COMPATIBLE_STRUCT | +| 31 | EXT | +| 32 | NAMED_EXT | +| 33 | UNION | +| 34 | TYPED_UNION | +| 35 | NAMED_UNION | +| 36 | NONE | +| 37 | DURATION | +| 38 | TIMESTAMP | +| 39 | DATE | +| 40 | DECIMAL | +| 41 | BINARY | +| 42 | ARRAY | +| 43 | BOOL_ARRAY | +| 44 | INT8_ARRAY | +| 45 | INT16_ARRAY | +| 46 | INT32_ARRAY | +| 47 | INT64_ARRAY | +| 48 | UINT8_ARRAY | +| 49 | UINT16_ARRAY | +| 50 | UINT32_ARRAY | +| 51 | UINT64_ARRAY | +| 52 | FLOAT8_ARRAY | +| 53 | FLOAT16_ARRAY | +| 54 | BFLOAT16_ARRAY | +| 55 | FLOAT32_ARRAY | +| 56 | FLOAT64_ARRAY | +| 64 | CHAR | +| 65 | CHAR16 | +| 66 | CHAR32 | ## Error Handling diff --git a/docs/guide/csharp/configuration.md b/docs/guide/csharp/configuration.md index 9d35ae0e5e..0a548e1a2f 100644 --- a/docs/guide/csharp/configuration.md +++ b/docs/guide/csharp/configuration.md @@ -35,17 +35,16 @@ ThreadSafeFory threadSafe = Fory.Builder().BuildThreadSafe(); `Fory.Builder().Build()` uses: -| Option | Default | Description | -| -------------------- | ------- | ---------------------------------------------- | -| `TrackRef` | `false` | Reference tracking disabled | -| `Compatible` | `false` | Schema-consistent mode (no evolution metadata) | -| `CheckStructVersion` | `false` | Struct schema hash checks disabled | -| `MaxDepth` | `20` | Max dynamic nesting depth | +| Option | Default | Description | +| -------------------- | ------- | -------------------------------------------- | +| `TrackRef` | `false` | Reference tracking disabled | +| `Compatible` | `true` | Compatible schema-evolution metadata enabled | +| `CheckStructVersion` | `false` | Struct schema hash checks disabled | +| `MaxDepth` | `20` | Max dynamic nesting depth | ## Builder Options -C# always uses xlang-compatible framing, so `ForyBuilder` does not expose a separate `Xlang(...)` -toggle. +C# always uses xlang-compatible framing, so `ForyBuilder` does not expose a mode toggle. ### `TrackRef(bool enabled = false)` @@ -59,7 +58,9 @@ Fory fory = Fory.Builder() ### `Compatible(bool enabled = false)` -Enables schema evolution mode. +Enables schema evolution mode. C# uses the xlang wire format only, so compatible mode is enabled by +default for independently deployed peers. Passing `false` opts into schema-consistent payloads when +every writer and reader uses the same schema. ```csharp Fory fory = Fory.Builder() @@ -91,7 +92,7 @@ Fory fory = Fory.Builder() ## Common Configurations -### Fast schema-consistent service +### Schema-consistent service ```csharp Fory fory = Fory.Builder() @@ -104,7 +105,6 @@ Fory fory = Fory.Builder() ```csharp Fory fory = Fory.Builder() - .Compatible(true) .TrackRef(true) .Build(); ``` diff --git a/docs/guide/csharp/cross-language.md b/docs/guide/csharp/cross-language.md index 4598bd23fb..4551f72d15 100644 --- a/docs/guide/csharp/cross-language.md +++ b/docs/guide/csharp/cross-language.md @@ -1,6 +1,6 @@ --- title: Cross-Language Serialization -sidebar_position: 8 +sidebar_position: 3 id: cross_language license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -23,9 +23,9 @@ Apache Fory™ C# supports cross-language serialization with other Fory runtimes ## Cross-Language Runtime -C# always writes and reads the xlang frame header. There is no separate `Xlang(...)` builder -option, so interoperability code only needs to configure the remaining runtime behavior such as -compatibility mode and reference tracking. +C# always writes and reads the xlang frame header. There is no mode switch, so interoperability code +only needs to configure the remaining runtime behavior such as compatibility mode and reference +tracking. ```csharp Fory fory = Fory.Builder() @@ -71,8 +71,8 @@ byte[] payload = fory.Serialize(person); ```java Fory fory = Fory.builder() - .withXlang(true).withCompatible(true) - .withRefTracking(true) + .withXlang(true) + .withRefTracking(true) .build(); fory.register(Person.class, 100); @@ -84,7 +84,7 @@ Person value = (Person) fory.deserialize(payloadFromCSharp); ```python import pyfory -fory = pyfory.Fory(xlang=True, compatible=True, ref=True) +fory = pyfory.Fory(xlang=True, ref=True) fory.register_type(Person, type_id=100) value = fory.deserialize(payload_from_csharp) ``` diff --git a/docs/guide/csharp/custom-serializers.md b/docs/guide/csharp/custom-serializers.md index 363e4ad82e..164a32a48c 100644 --- a/docs/guide/csharp/custom-serializers.md +++ b/docs/guide/csharp/custom-serializers.md @@ -1,6 +1,6 @@ --- title: Custom Serializers -sidebar_position: 4 +sidebar_position: 11 id: custom_serializers license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -80,5 +80,5 @@ Point decoded = fory.Deserialize(payload); ## Related Topics - [Type Registration](type-registration.md) -- [Field Configuration](field-configuration.md) +- [Schema Metadata](schema-metadata.md) - [Troubleshooting](troubleshooting.md) diff --git a/docs/guide/csharp/index.md b/docs/guide/csharp/index.md index f0a899c855..04f19b3b78 100644 --- a/docs/guide/csharp/index.md +++ b/docs/guide/csharp/index.md @@ -87,12 +87,12 @@ User decoded = fory.Deserialize(payload); | --------------------------------------------- | --------------------------------------------- | | [Configuration](configuration.md) | Builder options and runtime modes | | [Basic Serialization](basic-serialization.md) | Typed and dynamic serialization APIs | +| [Cross-Language](cross-language.md) | Interoperability guidance | +| [Schema Metadata](schema-metadata.md) | `[ForyField]` ids and schema type descriptors | | [Type Registration](type-registration.md) | Registering user types and custom serializers | | [Custom Serializers](custom-serializers.md) | Implementing `Serializer` | -| [Field Configuration](field-configuration.md) | `[ForyField]` ids and schema type descriptors | | [References](references.md) | Shared/circular reference handling | | [Schema Evolution](schema-evolution.md) | Compatible mode behavior | -| [Cross-Language](cross-language.md) | Interoperability guidance | | [Supported Types](supported-types.md) | Built-in and generated type support | | [Thread Safety](thread-safety.md) | `Fory` vs `ThreadSafeFory` usage | | [Troubleshooting](troubleshooting.md) | Common errors and debugging steps | diff --git a/docs/guide/csharp/references.md b/docs/guide/csharp/references.md index 001f8a378c..1420be04fa 100644 --- a/docs/guide/csharp/references.md +++ b/docs/guide/csharp/references.md @@ -1,6 +1,6 @@ --- title: References -sidebar_position: 6 +sidebar_position: 7 id: references license: | Licensed to the Apache Software Foundation (ASF) under one or more diff --git a/docs/guide/csharp/schema-evolution.md b/docs/guide/csharp/schema-evolution.md index 429abd2081..10efda0272 100644 --- a/docs/guide/csharp/schema-evolution.md +++ b/docs/guide/csharp/schema-evolution.md @@ -1,6 +1,6 @@ --- title: Schema Evolution -sidebar_position: 7 +sidebar_position: 8 id: schema_evolution license: | Licensed to the Apache Software Foundation (ASF) under one or more diff --git a/docs/guide/csharp/field-configuration.md b/docs/guide/csharp/schema-metadata.md similarity index 97% rename from docs/guide/csharp/field-configuration.md rename to docs/guide/csharp/schema-metadata.md index 778054c80e..3941a3824d 100644 --- a/docs/guide/csharp/field-configuration.md +++ b/docs/guide/csharp/schema-metadata.md @@ -1,7 +1,7 @@ --- -title: Field Configuration -sidebar_position: 5 -id: field_configuration +title: Schema Metadata +sidebar_position: 4 +id: schema_metadata license: | Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with diff --git a/docs/guide/csharp/troubleshooting.md b/docs/guide/csharp/troubleshooting.md index 6f2ec28e07..4f89a86d88 100644 --- a/docs/guide/csharp/troubleshooting.md +++ b/docs/guide/csharp/troubleshooting.md @@ -1,6 +1,6 @@ --- title: Troubleshooting -sidebar_position: 11 +sidebar_position: 12 id: troubleshooting license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -41,12 +41,17 @@ Ensure the same type-ID/name mapping exists on both write and read sides. **Cause**: The payload is not an xlang Fory frame, or it came from a peer/runtime mode that does not emit the xlang header C# requires. -**Fix**: Ensure the payload was produced by an xlang-compatible Fory runtime. C# always expects the -xlang header and does not expose a separate `Xlang(...)` builder option. +**Fix**: Ensure the payload was produced by an xlang-compatible peer runtime. C# always expects the +xlang header and does not expose a mode switch, so configure the writer instead: -```csharp -Fory writer = Fory.Builder().Compatible(true).Build(); -Fory reader = Fory.Builder().Compatible(true).Build(); +```java +Fory fory = Fory.builder() + .withXlang(true) + .build(); +``` + +```python +fory = pyfory.Fory(xlang=True) ``` ## Schema Version Mismatch in Strict Mode diff --git a/docs/guide/csharp/type-registration.md b/docs/guide/csharp/type-registration.md index 9640c91ac8..1f8d8e2286 100644 --- a/docs/guide/csharp/type-registration.md +++ b/docs/guide/csharp/type-registration.md @@ -1,6 +1,6 @@ --- title: Type Registration -sidebar_position: 3 +sidebar_position: 5 id: type_registration license: | Licensed to the Apache Software Foundation (ASF) under one or more diff --git a/docs/guide/dart/basic-serialization.md b/docs/guide/dart/basic-serialization.md index 87ea752df2..17f78bccef 100644 --- a/docs/guide/dart/basic-serialization.md +++ b/docs/guide/dart/basic-serialization.md @@ -1,7 +1,7 @@ --- title: Basic Serialization -sidebar_position: 2 -id: dart_basic_serialization +sidebar_position: 1 +id: basic_serialization license: | Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -143,4 +143,4 @@ If you skip registration, you will get a `Type ... is not registered` error at r - [Configuration](configuration.md) - [Type Registration](type-registration.md) -- [Field Configuration](field-configuration.md) +- [Schema Metadata](schema-metadata.md) diff --git a/docs/guide/dart/code-generation.md b/docs/guide/dart/code-generation.md index d24d8c229b..5f5d999612 100644 --- a/docs/guide/dart/code-generation.md +++ b/docs/guide/dart/code-generation.md @@ -1,7 +1,7 @@ --- title: Code Generation sidebar_position: 3 -id: dart_code_generation +id: code_generation license: | Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -111,5 +111,5 @@ If you cannot annotate a type (e.g., it comes from a package you do not own), wr ## Related Topics - [Type Registration](type-registration.md) -- [Field Configuration](field-configuration.md) +- [Schema Metadata](schema-metadata.md) - [Schema Evolution](schema-evolution.md) diff --git a/docs/guide/dart/configuration.md b/docs/guide/dart/configuration.md index 46d87556d3..09fdadda44 100644 --- a/docs/guide/dart/configuration.md +++ b/docs/guide/dart/configuration.md @@ -1,7 +1,7 @@ --- title: Configuration -sidebar_position: 1 -id: dart_configuration +sidebar_position: 2 +id: configuration license: | Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -28,12 +28,11 @@ Pass options directly to the constructor: ```dart import 'package:fory/fory.dart'; -// defaults — good for most single-service scenarios +// defaults: xlang wire format with compatible schema evolution final fory = Fory(); -// cross-language service with schema evolution +// customize limits while keeping default compatible mode final fory = Fory( - compatible: true, maxDepth: 512, ); ``` @@ -44,10 +43,12 @@ Create one instance per application and reuse it; there is no benefit to creatin ### `compatible` -Set to `true` when your service needs to handle payloads from code that may have a different version of the same model — for example, when you deploy services independently and cannot guarantee that both sides update at the same time. +Compatible mode is enabled by default. Keep it enabled when your service needs to handle payloads +from code that may have a different version of the same model, for example when you deploy services +independently and cannot guarantee that both sides update at the same time. ```dart -final fory = Fory(compatible: true); +final fory = Fory(); ``` When `compatible: true`: @@ -100,8 +101,8 @@ final fory = Fory(maxBinarySize: 8 * 1024 * 1024); | Option | Default | | -------------------- | --------- | -| `compatible` | `false` | -| `checkStructVersion` | `true` | +| `compatible` | `true` | +| `checkStructVersion` | `false` | | `maxDepth` | 256 | | `maxCollectionSize` | 1 048 576 | | `maxBinarySize` | 64 MiB | @@ -110,7 +111,7 @@ final fory = Fory(maxBinarySize: 8 * 1024 * 1024); When Fory is used to communicate between services written in different languages: -- Set `compatible: true` on **all** sides if any side needs schema evolution. +- Keep compatible mode enabled on all sides if any side needs schema evolution. - Use the same numeric IDs or `namespace + typeName` pairs on every side. - Match the `compatible` setting on both the writing and reading side — mismatching modes will fail. diff --git a/docs/guide/dart/cross-language.md b/docs/guide/dart/cross-language.md index 1918d9468c..aabe9d9d5e 100644 --- a/docs/guide/dart/cross-language.md +++ b/docs/guide/dart/cross-language.md @@ -1,7 +1,7 @@ --- title: Cross-Language Serialization -sidebar_position: 9 -id: dart_cross_language +sidebar_position: 4 +id: cross_language license: | Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -23,10 +23,10 @@ Apache Fory™ Dart serializes to the same binary format as the Java, Go, C#, Py ## Setup -Create a `Fory` instance as normal. There is no separate "cross-language mode" to enable in Dart: +Create a `Fory` instance as normal. There is no separate xlang option to enable in Dart: ```dart -final fory = Fory(); // or Fory(compatible: true) for schema evolution +final fory = Fory(); // xlang payloads with compatible schema evolution ``` The key requirement is that both sides register the same type using the same identity. @@ -90,8 +90,8 @@ final bytes = fory.serialize(Person() ```java Fory fory = Fory.builder() - .withXlang(true).withCompatible(true) - .build(); + .withXlang(true) + .build(); fory.register(Person.class, 100); Person value = (Person) fory.deserialize(bytesFromDart); @@ -102,7 +102,7 @@ Person value = (Person) fory.deserialize(bytesFromDart); ### Dart ```dart -final fory = Fory(compatible: true); +final fory = Fory(); PersonFory.register(fory, Person, id: 100); final bytes = fory.serialize(Person() ..name = 'Alice' @@ -147,7 +147,7 @@ type Person struct { Age int32 } -f := fory.New(fory.WithXlang(true), fory.WithCompatible(true)) +f := fory.New(fory.WithXlang(true)) _ = f.RegisterStruct(Person{}, 100) var person Person diff --git a/docs/guide/dart/custom-serializers.md b/docs/guide/dart/custom-serializers.md index 8c8c1ee449..096438ecb2 100644 --- a/docs/guide/dart/custom-serializers.md +++ b/docs/guide/dart/custom-serializers.md @@ -1,7 +1,7 @@ --- title: Custom Serializers -sidebar_position: 5 -id: dart_custom_serializers +sidebar_position: 9 +id: custom_serializers license: | Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with diff --git a/docs/guide/dart/index.md b/docs/guide/dart/index.md index c7458389ca..b3d912395b 100644 --- a/docs/guide/dart/index.md +++ b/docs/guide/dart/index.md @@ -1,7 +1,7 @@ --- title: Dart Serialization Guide sidebar_position: 0 -id: dart_serialization_index +id: serialization_index license: | Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -130,12 +130,12 @@ dart run build_runner build --delete-conflicting-outputs | [Configuration](configuration.md) | Runtime options, compatible mode, and safety limits | | [Basic Serialization](basic-serialization.md) | `serialize`, `deserialize`, generated registration, root graphs | | [Code Generation](code-generation.md) | `@ForyStruct`, build runner, and generated namespaces | +| [Cross-Language](cross-language.md) | Interoperability rules and field alignment | +| [Schema Metadata](schema-metadata.md) | `@ForyField`, field IDs, nullability, references, polymorphism | | [Type Registration](type-registration.md) | ID-based vs name-based registration and registration rules | | [Custom Serializers](custom-serializers.md) | Manual `Serializer` implementations and unions | -| [Field Configuration](field-configuration.md) | `@ForyField`, field IDs, nullability, references, polymorphism | | [Supported Types](supported-types.md) | Built-in xlang values, wrappers, collections, and structs | | [Schema Evolution](schema-evolution.md) | Compatible structs and evolving schemas | -| [Cross-Language](cross-language.md) | Interoperability rules and field alignment | | [Web Platform Support](web-platform-support.md) | Dart VM/AOT, Flutter, and web support, limits, and validation | | [Troubleshooting](troubleshooting.md) | Common errors, diagnostics, and validation steps | diff --git a/docs/guide/dart/schema-evolution.md b/docs/guide/dart/schema-evolution.md index 81b4a8a36c..c165f1f629 100644 --- a/docs/guide/dart/schema-evolution.md +++ b/docs/guide/dart/schema-evolution.md @@ -1,7 +1,7 @@ --- title: Schema Evolution sidebar_position: 8 -id: dart_schema_evolution +id: schema_evolution license: | Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -21,14 +21,15 @@ license: | Schema evolution lets different versions of your app exchange messages safely — a v2 writer can produce a message that a v1 reader can still decode, and vice versa. -## Two Modes +## Compatible And Schema-Consistent Evolution -### Compatible Mode (recommended for evolving services) +### Compatible Mode -Enable this when services may run different versions at the same time — for example, during a rolling deployment or when clients are not updated immediately. +Compatible mode is the Dart default. Keep this default when services may run different versions at +the same time, for example during a rolling deployment or when clients are not updated immediately. ```dart -final fory = Fory(compatible: true); +final fory = Fory(); ``` In compatible mode, Fory includes enough field metadata in each message so that the reader can skip unknown fields and use defaults for missing ones. Use stable field IDs (see below) to anchor the schema across changes. @@ -88,5 +89,5 @@ Test rolling-upgrade scenarios with real round trips before deploying. ## Related Topics - [Configuration](configuration.md) -- [Field Configuration](field-configuration.md) +- [Schema Metadata](schema-metadata.md) - [Cross-Language](cross-language.md) diff --git a/docs/guide/dart/field-configuration.md b/docs/guide/dart/schema-metadata.md similarity index 97% rename from docs/guide/dart/field-configuration.md rename to docs/guide/dart/schema-metadata.md index a7d02904b2..978b7a1ffb 100644 --- a/docs/guide/dart/field-configuration.md +++ b/docs/guide/dart/schema-metadata.md @@ -1,7 +1,7 @@ --- -title: Field Configuration -sidebar_position: 6 -id: dart_field_configuration +title: Schema Metadata +sidebar_position: 5 +id: schema_metadata license: | Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -25,7 +25,7 @@ Add `@ForyField(...)` to a field inside a `@ForyStruct()` class to change how th ```dart @ForyField( - skip: false, // exclude the field from serialization + skip: false, // include the field; set true to exclude it id: 10, // stable field ID for schema evolution nullable: true, // override nullability detection ref: true, // enable reference tracking for this field diff --git a/docs/guide/dart/supported-types.md b/docs/guide/dart/supported-types.md index 12f2e30980..879dce949c 100644 --- a/docs/guide/dart/supported-types.md +++ b/docs/guide/dart/supported-types.md @@ -1,7 +1,7 @@ --- title: Supported Types sidebar_position: 7 -id: dart_supported_types +id: supported_types license: | Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -154,6 +154,6 @@ width is one of the most common cross-language bugs. ## Related Topics -- [Field Configuration](field-configuration.md) +- [Schema Metadata](schema-metadata.md) - [Cross-Language](cross-language.md) - [Schema Evolution](schema-evolution.md) diff --git a/docs/guide/dart/troubleshooting.md b/docs/guide/dart/troubleshooting.md index cc5b295b8a..8c8065a3cc 100644 --- a/docs/guide/dart/troubleshooting.md +++ b/docs/guide/dart/troubleshooting.md @@ -1,7 +1,7 @@ --- title: Troubleshooting sidebar_position: 11 -id: dart_troubleshooting +id: troubleshooting license: | Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -23,11 +23,11 @@ This page covers common Dart runtime issues and fixes. ## `Only xlang payloads are supported by the Dart runtime.` -The writer is sending a native-mode (non-xlang) payload. Make sure every service uses the xlang-compatible path: +The writer is sending a native-mode payload. Make sure every peer writes the xlang wire format: -- **Java**: add `.withXlang(true).withCompatible(true)` to the Fory builder. -- **Go**: use `WithXlang(true), WithCompatible(true)` in the Fory options. -- **Other runtimes**: check their respective guides for enabling cross-language mode. +- **Java**: configure the peer runtime for xlang mode instead of native mode. +- **Go**: configure the peer runtime for xlang mode. +- **Other runtimes**: check their respective guides for xlang mode. ## `Type ... is not registered.` @@ -76,7 +76,7 @@ Checklist: 2. Stable `@ForyField(id: ...)` assigned before the first payload was produced. 3. Compatible numeric widths — use `@ForyField(type: Int32Type())` in Dart when the peer field is `int` (Java), `int32` (Go), or `int` (C#). 4. `Timestamp` / `LocalDate` instead of raw `DateTime` for date/time fields. -5. `compatible: true` on **both** sides if using schema evolution. +5. Compatible schema evolution on both sides. Dart enables it by default; make sure peers have not explicitly selected schema-consistent mode. ## Int64 or Uint64 values fail on web @@ -118,7 +118,7 @@ class FileBlock { field, but it does not remove the web integer precision limit. Use `Int64` for full-range signed values and `Uint64` for full-range unsigned values. See [Web Platform Support](web-platform-support.md) for the full browser support -matrix and migration guidance. +matrix and runtime guidance. ## Running Tests Locally diff --git a/docs/guide/dart/type-registration.md b/docs/guide/dart/type-registration.md index a5f93e1c0e..96ab9a029a 100644 --- a/docs/guide/dart/type-registration.md +++ b/docs/guide/dart/type-registration.md @@ -1,7 +1,7 @@ --- title: Type Registration -sidebar_position: 4 -id: dart_type_registration +sidebar_position: 6 +id: type_registration license: | Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with diff --git a/docs/guide/dart/web-platform-support.md b/docs/guide/dart/web-platform-support.md index b1105e9810..481520770a 100644 --- a/docs/guide/dart/web-platform-support.md +++ b/docs/guide/dart/web-platform-support.md @@ -1,7 +1,7 @@ --- title: Web Platform Support sidebar_position: 10 -id: dart_web_platform_support +id: web_platform_support license: | Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -214,6 +214,6 @@ JS-safe non-negative range. ## Related Topics - [Supported Types](supported-types.md) -- [Field Configuration](field-configuration.md) +- [Schema Metadata](schema-metadata.md) - [Code Generation](code-generation.md) - [Troubleshooting](troubleshooting.md) diff --git a/docs/guide/go/basic-serialization.md b/docs/guide/go/basic-serialization.md index 8fc7779337..137ccf82bc 100644 --- a/docs/guide/go/basic-serialization.md +++ b/docs/guide/go/basic-serialization.md @@ -28,7 +28,7 @@ Create a Fory instance and register your types before serialization: ```go import "github.com/apache/fory/go/fory" -f := fory.New() +f := fory.New(fory.WithXlang(true)) // Register struct with a type ID f.RegisterStruct(User{}, 1) @@ -41,6 +41,11 @@ f.RegisterStructByName(User{}, "example.User") f.RegisterEnum(Color(0), 3) ``` +`fory.New()` uses xlang mode with compatible schema evolution. The example sets +`fory.WithXlang(true)` explicitly so the mode choice is visible. For Go-only +payloads that need native mode, configure `fory.WithXlang(false)` explicitly in +the native-mode examples. + **Important**: The Fory instance should be reused across serialization calls. Creating a new instance involves allocating internal buffers, type caches, and resolvers, which is expensive. The default Fory instance is not thread-safe; for concurrent usage, use the thread-safe wrapper (see [Thread Safety](thread-safety.md)). See [Type Registration](type-registration.md) for more details. @@ -201,7 +206,7 @@ type Node struct { } // Use WithTrackRef for pointer fields -f := fory.New(fory.WithTrackRef(true)) +f := fory.New(fory.WithXlang(true), fory.WithTrackRef(true)) f.RegisterStruct(Node{}, 1) root := &Node{ @@ -362,7 +367,7 @@ type Item struct { } func main() { - f := fory.New() + f := fory.New(fory.WithXlang(true)) f.RegisterStruct(Order{}, 1) f.RegisterStruct(Item{}, 2) diff --git a/docs/guide/go/codegen.md b/docs/guide/go/codegen.md index d78bbfb839..81756ed6cf 100644 --- a/docs/guide/go/codegen.md +++ b/docs/guide/go/codegen.md @@ -1,6 +1,6 @@ --- title: Code Generation -sidebar_position: 90 +sidebar_position: 100 id: codegen license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -181,7 +181,7 @@ Generate for a package: fory -pkg ./models ``` -### Explicit Types (Legacy) +### Explicit Types Specify types explicitly: @@ -284,7 +284,7 @@ steps: Generated code integrates transparently: ```go -f := fory.New() +f := fory.New(fory.WithXlang(true)) // Fory automatically uses generated serializer if available user := &User{ID: 1, Name: "Alice"} diff --git a/docs/guide/go/configuration.md b/docs/guide/go/configuration.md index 5b428008da..376558c6f8 100644 --- a/docs/guide/go/configuration.md +++ b/docs/guide/go/configuration.md @@ -28,24 +28,24 @@ Fory Go uses a functional options pattern for configuration. This allows you to ```go import "github.com/apache/fory/go/fory" -f := fory.New() +f := fory.New(fory.WithXlang(true)) ``` Default settings: -| Option | Default | Description | -| ---------- | ------- | ------------------------------ | -| TrackRef | false | Reference tracking disabled | -| MaxDepth | 20 | Maximum nesting depth | -| IsXlang | false | Cross-language mode disabled | -| Compatible | false | Schema evolution mode disabled | +| Option | Default | Description | +| ---------- | ------- | -------------------------------------------- | +| TrackRef | false | Reference tracking disabled | +| MaxDepth | 20 | Maximum nesting depth | +| IsXlang | true | Xlang mode enabled | +| Compatible | true | Compatible schema-evolution metadata enabled | ### With Options ```go f := fory.New( + fory.WithXlang(true), fory.WithTrackRef(true), - fory.WithCompatible(true), fory.WithMaxDepth(10), ) ``` @@ -57,7 +57,7 @@ f := fory.New( Enable reference tracking to handle circular references and shared objects: ```go -f := fory.New(fory.WithTrackRef(true)) +f := fory.New(fory.WithXlang(true), fory.WithTrackRef(true)) ``` **When enabled:** @@ -84,10 +84,11 @@ See [References](references.md) for details. ### WithCompatible -Enable compatible mode for schema evolution: +Enable compatible mode explicitly. Xlang mode enables it by default; use this option when +native-mode Go-only payloads need schema evolution: ```go -f := fory.New(fory.WithCompatible(true)) +f := fory.New(fory.WithXlang(false), fory.WithCompatible(true)) ``` **When enabled:** @@ -97,7 +98,7 @@ f := fory.New(fory.WithCompatible(true)) - Field names or ids are used for matching (order-independent) - Larger serialized output due to metadata -**When disabled (default):** +**When disabled:** - Compact serialization without field metadata - Faster serialization and smaller output @@ -111,7 +112,7 @@ See [Schema Evolution](schema-evolution.md) for details. Set the maximum nesting depth to prevent stack overflow: ```go -f := fory.New(fory.WithMaxDepth(30)) +f := fory.New(fory.WithXlang(true), fory.WithMaxDepth(30)) ``` - Default: 20 @@ -120,10 +121,11 @@ f := fory.New(fory.WithMaxDepth(30)) ### WithXlang -Enable cross-language serialization mode: +Select the wire mode: ```go -f := fory.New(fory.WithXlang(true), fory.WithCompatible(true)) +native := fory.New(fory.WithXlang(false)) +xlang := fory.New(fory.WithXlang(true)) ``` **When enabled:** @@ -132,11 +134,12 @@ f := fory.New(fory.WithXlang(true), fory.WithCompatible(true)) - Compatible with Java, Python, C++, Rust, JavaScript - Type IDs follow xlang specification -**When disabled (default):** +**When disabled:** - Go-native serialization mode -- Support more Go-native types +- Supports more Go-native type behavior - Not compatible with other language implementations +- Defaults to schema-consistent mode unless `WithCompatible(true)` is set ## Thread Safety @@ -147,8 +150,8 @@ import "github.com/apache/fory/go/fory/threadsafe" // Create thread-safe Fory with same options f := threadsafe.New( + fory.WithXlang(true), fory.WithTrackRef(true), - fory.WithCompatible(true), ) // Safe for concurrent use from multiple goroutines @@ -188,7 +191,7 @@ See [Thread Safety](thread-safety.md) for details. The default `Fory` instance reuses its internal buffer: ```go -f := fory.New() +f := fory.New(fory.WithXlang(true)) data1, _ := f.Serialize(value1) // WARNING: data1 becomes invalid after next Serialize call! @@ -203,7 +206,7 @@ copy(safeCopy, data1) The thread-safe wrapper automatically copies data, so this is not a concern: ```go -f := threadsafe.New() +f := threadsafe.New(fory.WithXlang(true)) data1, _ := f.Serialize(value1) data2, _ := f.Serialize(value2) // Both data1 and data2 are valid @@ -214,7 +217,7 @@ data2, _ := f.Serialize(value2) For high-throughput scenarios, you can manage buffers manually: ```go -f := fory.New() +f := fory.New(fory.WithXlang(true)) buf := fory.NewByteBuffer(nil) // Serialize to existing buffer @@ -231,12 +234,12 @@ buf.Reset() ## Configuration Examples -### Simple Data (Default) +### Simple Xlang Data For simple structs without circular references: ```go -f := fory.New() +f := fory.New(fory.WithXlang(true)) type Config struct { Host string @@ -252,7 +255,7 @@ data, _ := f.Serialize(&Config{Host: "localhost", Port: 8080}) For data with circular references: ```go -f := fory.New(fory.WithTrackRef(true)) +f := fory.New(fory.WithXlang(true), fory.WithTrackRef(true)) type Node struct { Value int32 @@ -286,13 +289,13 @@ type UserV2 struct { Email string // New field } -// Serialize with V1 -f1 := fory.New(fory.WithCompatible(true)) +// Serialize with V1 in native mode plus compatible schema evolution. +f1 := fory.New(fory.WithXlang(false), fory.WithCompatible(true)) f1.RegisterStruct(UserV1{}, 1) data, _ := f1.Serialize(&UserV1{ID: 1, Name: "Alice"}) // Deserialize into V2 - Email will have zero value -f2 := fory.New(fory.WithCompatible(true)) +f2 := fory.New(fory.WithXlang(false), fory.WithCompatible(true)) f2.RegisterStruct(UserV2{}, 1) var user UserV2 f2.Deserialize(data, &user) @@ -309,6 +312,7 @@ type Request struct { } f := threadsafe.New( + fory.WithXlang(true), fory.WithMaxDepth(30), ) f.RegisterStruct(Request{}, 1) diff --git a/docs/guide/go/cross-language.md b/docs/guide/go/cross-language.md index 7a4403f4b7..2b1fe36bc3 100644 --- a/docs/guide/go/cross-language.md +++ b/docs/guide/go/cross-language.md @@ -1,6 +1,6 @@ --- title: Cross-Language Serialization -sidebar_position: 80 +sidebar_position: 20 id: cross_language license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -21,12 +21,12 @@ license: | Fory Go enables seamless data exchange with Java, Python, C++, Rust, and JavaScript. This guide covers cross-language compatibility and type mapping. -## Enabling Cross-Language Mode +## Create an Xlang Runtime -Cross-language (xlang) mode must be explicitly enabled: +Go defaults to xlang mode with compatible schema evolution. Set the mode explicitly in xlang examples: ```go -f := fory.New(fory.WithXlang(true), fory.WithCompatible(true)) +f := fory.New(fory.WithXlang(true)) ``` ## Type Registration for Cross-Language @@ -41,7 +41,7 @@ type User struct { Name string } -f := fory.New(fory.WithXlang(true), fory.WithCompatible(true)) +f := fory.New(fory.WithXlang(true)) f.RegisterStruct(User{}, 1) data, _ := f.Serialize(&User{ID: 1, Name: "Alice"}) ``` @@ -53,7 +53,7 @@ public class User { public long id; public String name; } -Fory fory = Fory.builder().withXlang(true).withCompatible(true).build(); +Fory fory = Fory.builder().withXlang(true).build(); fory.register(User.class, 1); User user = fory.deserialize(data, User.class); ``` @@ -69,7 +69,7 @@ class User: id: pyfory.Int64 name: str -fory = pyfory.Fory(xlang=True, compatible=True) +fory = pyfory.Fory(xlang=True) fory.register(User, type_id=1) user = fory.deserialize(data) ``` @@ -118,7 +118,7 @@ type Order struct { Items []string } -f := fory.New(fory.WithXlang(true), fory.WithCompatible(true)) +f := fory.New(fory.WithXlang(true)) f.RegisterStruct(Order{}, 1) order := &Order{ @@ -141,7 +141,7 @@ public class Order { public List items; } -Fory fory = Fory.builder().withXlang(true).withCompatible(true).build(); +Fory fory = Fory.builder().withXlang(true).build(); fory.register(Order.class, 1); Order order = fory.deserialize(data, Order.class); @@ -161,7 +161,7 @@ class Message: content: str timestamp: pyfory.Int64 -fory = pyfory.Fory(xlang=True, compatible=True) +fory = pyfory.Fory(xlang=True) fory.register(Message, type_id=1) msg = Message(id=1, content="Hello from Python", timestamp=1234567890) @@ -177,7 +177,7 @@ type Message struct { Timestamp int64 } -f := fory.New(fory.WithXlang(true), fory.WithCompatible(true)) +f := fory.New(fory.WithXlang(true)) f.RegisterStruct(Message{}, 1) var msg Message @@ -226,7 +226,7 @@ type Company struct { Address Address } -f := fory.New(fory.WithXlang(true), fory.WithCompatible(true)) +f := fory.New(fory.WithXlang(true)) f.RegisterStruct(Address{}, 1) f.RegisterStruct(Company{}, 2) ``` diff --git a/docs/guide/go/custom-serializers.md b/docs/guide/go/custom-serializers.md index 96849d55d7..e44f465863 100644 --- a/docs/guide/go/custom-serializers.md +++ b/docs/guide/go/custom-serializers.md @@ -1,6 +1,6 @@ --- title: Custom Serializers -sidebar_position: 35 +sidebar_position: 90 id: custom_serializers license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -75,7 +75,7 @@ func (s *MyExtSerializer) ReadData(ctx *fory.ReadContext, value reflect.Value) { } // Register the custom serializer -f := fory.New() +f := fory.New(fory.WithXlang(true)) err := f.RegisterExtension(MyExt{}, 100, &MyExtSerializer{}) ``` @@ -168,7 +168,7 @@ func (s *Point3DSerializer) ReadData(ctx *fory.ReadContext, value reflect.Value) })) } -f := fory.New() +f := fory.New(fory.WithXlang(true)) f.RegisterExtension(Point3D{}, 101, &Point3DSerializer{}) ``` @@ -260,7 +260,7 @@ f.RegisterExtensionByName(MyType{}, "myapp.MyType", &MySerializer{}) ```go func TestMySerializer(t *testing.T) { - f := fory.New() + f := fory.New(fory.WithXlang(true)) f.RegisterExtension(MyType{}, 100, &MySerializer{}) original := MyType{Field: "test"} diff --git a/docs/guide/go/index.md b/docs/guide/go/index.md index 68ef08c549..260a22e1db 100644 --- a/docs/guide/go/index.md +++ b/docs/guide/go/index.md @@ -19,7 +19,7 @@ license: | limitations under the License. --- -Apache Fory Go is a high-performance, cross-language serialization library for Go. It provides automatic object graph serialization with support for circular references, polymorphism, and cross-language compatibility. +Apache Fory Go is a high-performance serialization library for Go. It supports xlang mode for cross-language payloads and native mode for Go-only payloads, with automatic object graph serialization, circular references, polymorphism, and schema-aware serializers. ## Why Fory Go? @@ -58,8 +58,8 @@ type User struct { } func main() { - // Create a Fory instance - f := fory.New() + // Create an xlang Fory instance. + f := fory.New(fory.WithXlang(true)) // Register struct with a type ID if err := f.RegisterStruct(User{}, 1); err != nil { @@ -84,16 +84,13 @@ func main() { } ``` -## Default Serialization Path +## Xlang Mode And Native Mode -Fory Go works out-of-the-box with ordinary Go structs. The standard runtime path caches type -metadata and keeps hot serialization paths optimized, so most applications can use it directly -without any extra build step: +Use xlang mode for cross-language payloads and schemas shared with other Fory runtimes. Xlang mode is the default Go wire mode, and Go examples that use it set `fory.WithXlang(true)` explicitly so the mode choice is visible. -```go -f := fory.New() -data, _ := f.Serialize(myStruct) -``` +Use native mode for Go-only traffic. Native mode is selected with `fory.WithXlang(false)`, uses schema-consistent payloads unless compatible mode is enabled, and keeps Go object serialization on the Go runtime path. It is optimized for Go structs, pointers, interfaces, and Go-specific type behavior that does not need a portable xlang mapping. + +See [Cross-Language Serialization](cross-language.md) for Go xlang registration and interoperability rules, and [Configuration](configuration.md) for native-mode options. ## Configuration @@ -101,8 +98,8 @@ Fory Go uses a functional options pattern for configuration: ```go f := fory.New( + fory.WithXlang(true), fory.WithTrackRef(true), // Enable reference tracking - fory.WithCompatible(true), // Enable schema evolution fory.WithMaxDepth(20), // Set max nesting depth ) ``` @@ -127,7 +124,7 @@ Fory Go is fully compatible with other Fory implementations. Data serialized in ```go // Go serialization -f := fory.New() +f := fory.New(fory.WithXlang(true)) f.RegisterStruct(User{}, 1) data, _ := f.Serialize(&User{ID: 1, Name: "Alice"}) // 'data' can be deserialized by Java, Python, etc. @@ -139,14 +136,14 @@ See [Cross-Language Serialization](cross-language.md) for type mapping and compa | Topic | Description | | --------------------------------------------- | -------------------------------------- | -| [Configuration](configuration.md) | Options and settings | | [Basic Serialization](basic-serialization.md) | Core APIs and usage patterns | +| [Configuration](configuration.md) | Options and settings | +| [Cross-Language](cross-language.md) | Multi-language serialization | +| [Schema Metadata](schema-metadata.md) | Field-level configuration | | [Type Registration](type-registration.md) | Registering types for serialization | | [Supported Types](supported-types.md) | Complete type support reference | | [References](references.md) | Circular references and shared objects | -| [Struct Tags](struct-tags.md) | Field-level configuration | | [Schema Evolution](schema-evolution.md) | Forward/backward compatibility | -| [Cross-Language](cross-language.md) | Multi-language serialization | | [Thread Safety](thread-safety.md) | Concurrent usage patterns | | [Troubleshooting](troubleshooting.md) | Common issues and solutions | diff --git a/docs/guide/go/references.md b/docs/guide/go/references.md index 3219dc9eb9..27d42ca02c 100644 --- a/docs/guide/go/references.md +++ b/docs/guide/go/references.md @@ -1,6 +1,6 @@ --- title: References -sidebar_position: 50 +sidebar_position: 60 id: references license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -26,7 +26,7 @@ Fory Go supports reference tracking to handle circular references and shared obj Reference tracking is **disabled by default**. Enable it when creating a Fory instance: ```go -f := fory.New(fory.WithTrackRef(true)) +f := fory.New(fory.WithXlang(true), fory.WithTrackRef(true)) ``` **Important**: Global reference tracking must be enabled for any reference tracking to occur. When `WithTrackRef(false)` (the default), all per-field reference tags are ignored. @@ -38,7 +38,7 @@ f := fory.New(fory.WithTrackRef(true)) When disabled, each object is serialized independently: ```go -f := fory.New() // TrackRef disabled by default +f := fory.New(fory.WithXlang(true)) // TrackRef disabled by default shared := &Data{Value: 42} container := &Container{A: shared, B: shared} @@ -52,7 +52,7 @@ data, _ := f.Serialize(container) When enabled, objects are tracked by identity: ```go -f := fory.New(fory.WithTrackRef(true)) +f := fory.New(fory.WithXlang(true), fory.WithTrackRef(true)) shared := &Data{Value: 42} container := &Container{A: shared, B: shared} @@ -109,7 +109,7 @@ type Container struct { - Pointer to primitive types (e.g., `*int`, `*string`) cannot use this tag - Default is `ref=false` (no reference tracking per field) -See [Struct Tags](struct-tags.md) for more details. +See [Struct Tags](schema-metadata.md) for more details. ## Circular References @@ -123,7 +123,7 @@ type Node struct { Next *Node `fory:"ref"` } -f := fory.New(fory.WithTrackRef(true)) +f := fory.New(fory.WithXlang(true), fory.WithTrackRef(true)) f.RegisterStruct(Node{}, 1) // Create circular list @@ -151,7 +151,7 @@ type TreeNode struct { Children []*TreeNode `fory:"ref"` } -f := fory.New(fory.WithTrackRef(true)) +f := fory.New(fory.WithXlang(true), fory.WithTrackRef(true)) f.RegisterStruct(TreeNode{}, 1) root := &TreeNode{Value: "root"} @@ -174,7 +174,7 @@ type GraphNode struct { Neighbors []*GraphNode `fory:"ref"` } -f := fory.New(fory.WithTrackRef(true)) +f := fory.New(fory.WithXlang(true), fory.WithTrackRef(true)) f.RegisterStruct(GraphNode{}, 1) // Create a graph @@ -208,7 +208,7 @@ type Application struct { FallbackConfig *Config `fory:"ref"` } -f := fory.New(fory.WithTrackRef(true)) +f := fory.New(fory.WithXlang(true), fory.WithTrackRef(true)) f.RegisterStruct(Config{}, 1) f.RegisterStruct(Application{}, 2) @@ -277,7 +277,7 @@ For large object graphs, this may increase memory usage. Circular references without tracking cause stack overflow or max depth errors: ```go -f := fory.New() // No reference tracking +f := fory.New(fory.WithXlang(true)) // No reference tracking n1 := &Node{Value: 1} n1.Next = n1 // Self-reference @@ -313,7 +313,7 @@ type Person struct { } func main() { - f := fory.New(fory.WithTrackRef(true)) + f := fory.New(fory.WithXlang(true), fory.WithTrackRef(true)) f.RegisterStruct(Person{}, 1) // Create people with mutual friendships @@ -352,5 +352,5 @@ func main() { ## Related Topics - [Configuration](configuration.md) -- [Struct Tags](struct-tags.md) +- [Struct Tags](schema-metadata.md) - [Cross-Language Serialization](cross-language.md) diff --git a/docs/guide/go/schema-evolution.md b/docs/guide/go/schema-evolution.md index 708290dc5e..f2d4b31c30 100644 --- a/docs/guide/go/schema-evolution.md +++ b/docs/guide/go/schema-evolution.md @@ -19,19 +19,26 @@ license: | limitations under the License. --- -Schema evolution allows your data structures to change over time while maintaining compatibility with previously serialized data. Fory Go supports this through compatible mode. +Schema evolution allows your data structures to change over time while maintaining compatibility with previously serialized data. Fory Go supports this through compatible mode. Xlang mode uses compatible schema evolution by default; native mode uses schema-consistent payloads by default and enables compatible mode explicitly. -## Enabling Compatible Mode +## Compatible Mode Defaults -Enable compatible mode when creating a Fory instance: +For cross-language and default Go payloads, use the default runtime: ```go -f := fory.New(fory.WithCompatible(true)) +f := fory.New(fory.WithXlang(true)) +``` + +For Go-only native-mode payloads that need schema evolution, enable compatible +mode explicitly: + +```go +f := fory.New(fory.WithXlang(false), fory.WithCompatible(true)) ``` ## How It Works -### Without Compatible Mode (Default) +### Schema-Consistent Native Mode - Compact serialization without metadata - Struct hash is checked during deserialization @@ -77,7 +84,7 @@ type UserV2 struct { Email string // New field } -f := fory.New(fory.WithCompatible(true)) +f := fory.New(fory.WithXlang(true)) f.RegisterStruct(UserV1{}, 1) // Serialize with V1 @@ -85,7 +92,7 @@ userV1 := &UserV1{ID: 1, Name: "Alice"} data, _ := f.Serialize(userV1) // Deserialize with V2 -f2 := fory.New(fory.WithCompatible(true)) +f2 := fory.New(fory.WithXlang(true)) f2.RegisterStruct(UserV2{}, 1) var userV2 UserV2 @@ -114,7 +121,7 @@ type ConfigV2 struct { // Debug field removed } -f := fory.New(fory.WithCompatible(true)) +f := fory.New(fory.WithXlang(true)) f.RegisterStruct(ConfigV1{}, 1) // Serialize with V1 @@ -122,7 +129,7 @@ config := &ConfigV1{Host: "localhost", Port: 8080, Timeout: 30, Debug: true} data, _ := f.Serialize(config) // Deserialize with V2 -f2 := fory.New(fory.WithCompatible(true)) +f2 := fory.New(fory.WithXlang(true)) f2.RegisterStruct(ConfigV2{}, 1) var configV2 ConfigV2 @@ -189,8 +196,14 @@ This is treated as removing `UserName` and adding `Username`, resulting in data ### 1. Use Compatible Mode for Persistent Data ```go -// For data stored in databases, files, or caches -f := fory.New(fory.WithCompatible(true)) +// Default xlang payloads already use compatible mode. +f := fory.New(fory.WithXlang(true)) +``` + +For Go-only native-mode data stored in databases, files, or caches, enable compatible mode: + +```go +f := fory.New(fory.WithXlang(false), fory.WithCompatible(true)) ``` ### 2. Provide Default Values @@ -227,7 +240,7 @@ type MessageV1 struct { Content string } -f := fory.New(fory.WithCompatible(true)) +f := fory.New(fory.WithXlang(true)) f.RegisterStruct(MessageV1{}, 1) data, _ := f.Serialize(&MessageV1{ID: 1, Content: "Hello"}) ``` @@ -241,10 +254,7 @@ public class Message { String author; // New field in Java } -Fory fory = Fory.builder() - .withXlang(true) - .withCompatible(true) - .build(); +Fory fory = Fory.builder().withXlang(true).build(); fory.register(Message.class, 1); Message msg = fory.deserialize(data, Message.class); // msg.author will be null @@ -268,7 +278,7 @@ Compatible mode mainly affects serialized size: - Cross-service communication - Long-lived caches -Use schema consistent mode for: +Use native schema-consistent mode for: - In-memory operations - Same-version communication @@ -276,10 +286,10 @@ Use schema consistent mode for: ## Error Handling -### Hash Mismatch (Schema Consistent Mode) +### Hash Mismatch (Native Schema-Consistent Mode) ```go -f := fory.New() // Compatible mode disabled +f := fory.New(fory.WithXlang(false)) // Compatible mode disabled // Schema changed without compatible mode err := f.Deserialize(oldData, &newStruct) @@ -323,7 +333,7 @@ type ProductV2 struct { func main() { // Serialize with V1 - f1 := fory.New(fory.WithCompatible(true)) + f1 := fory.New(fory.WithXlang(true)) f1.RegisterStruct(ProductV1{}, 1) product := &ProductV1{ID: 1, Name: "Widget", Price: 9.99} @@ -331,7 +341,7 @@ func main() { fmt.Printf("V1 serialized: %d bytes\n", len(data)) // Deserialize with V2 - f2 := fory.New(fory.WithCompatible(true)) + f2 := fory.New(fory.WithXlang(true)) f2.RegisterStruct(ProductV2{}, 1) var productV2 ProductV2 diff --git a/docs/guide/go/struct-tags.md b/docs/guide/go/schema-metadata.md similarity index 87% rename from docs/guide/go/struct-tags.md rename to docs/guide/go/schema-metadata.md index c383a85f86..e248cee95a 100644 --- a/docs/guide/go/struct-tags.md +++ b/docs/guide/go/schema-metadata.md @@ -1,7 +1,7 @@ --- -title: Field Configuration -sidebar_position: 60 -id: struct_tags +title: Schema Metadata +sidebar_position: 30 +id: schema_metadata license: | Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -75,7 +75,7 @@ The `Password` field will not be included in serialized output and will remain a ### Nullable -Use `nullable` to control whether null flags are written for pointer fields: +Use `nullable` to control whether null flags are written for pointer, slice, map, or interface fields: ```go type Record struct { @@ -89,9 +89,10 @@ type Record struct { **Notes**: -- Only applies to pointer, slice, and map fields +- Only applies to pointer, slice, map, and interface fields - When `nullable=false`, serializing a nil value will cause an error -- Default is `false` (no null flag written) +- Xlang mode defaults top-level struct fields to nullable only for pointer and `optional` carrier + fields. Native mode defaults pointer, slice, map, and interface fields to nullable. ### Reference Tracking @@ -295,7 +296,7 @@ type BadStruct struct { Field int `fory:"invalid=option=format"` } -f := fory.New() +f := fory.New(fory.WithXlang(true)) err := f.RegisterStruct(BadStruct{}, 1) // Error: ErrKindInvalidTag ``` @@ -311,7 +312,8 @@ Field configuration behaves differently depending on the serialization mode: **Xlang Mode**: -- **Nullable**: Only pointer types are nullable by default (slices and maps are NOT nullable) +- **Nullable**: Pointer and `optional.Optional[T]` fields are nullable by default (slices, + maps, and interfaces are NOT nullable unless tagged) - **Ref tracking**: Disabled by default (`ref` tag not set) You **need to configure fields** when: @@ -332,11 +334,11 @@ type User struct { ### Default Values Summary -| Option | Default | How to Enable | -| ---------- | ------- | ------------------------ | -| `nullable` | `false` | Use pointer types (`*T`) | -| `ref` | `false` | Add `fory:"ref"` tag | -| `id` | omitted | Add `fory:"id=N"` tag | +| Option | Default | How to Enable | +| ---------- | ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------ | +| `nullable` | Pointer and `optional.Optional[T]` fields in xlang mode; pointer, slice, map, and interface fields in native mode | Use `fory:"nullable"` or `fory:"nullable=false"` | +| `ref` | `false` | Add `fory:"ref"` tag | +| `id` | omitted | Add `fory:"id=N"` tag | ## Best Practices diff --git a/docs/guide/go/supported-types.md b/docs/guide/go/supported-types.md index 1020d627f3..1b6776c9eb 100644 --- a/docs/guide/go/supported-types.md +++ b/docs/guide/go/supported-types.md @@ -1,6 +1,6 @@ --- title: Supported Types -sidebar_position: 40 +sidebar_position: 50 id: supported_types license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -48,7 +48,7 @@ Fory uses variable-length integer encoding (varint) for better compression: - Platform `int` maps to `int32` on 32-bit, `int64` on 64-bit systems ```go -f := fory.New() +f := fory.New(fory.WithXlang(true)) // All integer types supported var i8 int8 = 127 @@ -82,7 +82,7 @@ struct field is dense numeric `array` data. | `[]I` (any/any) | LIST | Any interface type | ```go -f := fory.New() +f := fory.New(fory.WithXlang(true)) // Primitive root slice (optimized dense array payload) ints := []int32{1, 2, 3, 4, 5} @@ -118,7 +118,7 @@ data, _ = f.Serialize(dynamic) | `map[any]any` | MAP | Dynamic keys and values | ```go -f := fory.New() +f := fory.New(fory.WithXlang(true)) // String key maps m1 := map[string]string{"key": "value"} @@ -162,7 +162,7 @@ data, _ := f.Serialize(s) ```go import "time" -f := fory.New() +f := fory.New(fory.WithXlang(true)) // Timestamp t := time.Now() @@ -196,7 +196,7 @@ type User struct { password string // NOT serialized (unexported) } -f := fory.New() +f := fory.New(fory.WithXlang(true)) f.RegisterStruct(User{}, 1) user := &User{ID: 1, Name: "Alice", Age: 30, password: "secret"} @@ -218,7 +218,7 @@ type Company struct { Founded int32 } -f := fory.New() +f := fory.New(fory.WithXlang(true)) f.RegisterStruct(Address{}, 1) f.RegisterStruct(Company{}, 2) ``` @@ -231,7 +231,7 @@ f.RegisterStruct(Company{}, 2) | `**T` | Nested pointers supported | ```go -f := fory.New(fory.WithTrackRef(true)) +f := fory.New(fory.WithXlang(true), fory.WithTrackRef(true)) type Node struct { Value int32 @@ -268,7 +268,7 @@ f.Deserialize(data, &result) | `any` | UNION (31) | Polymorphic values | ```go -f := fory.New() +f := fory.New(fory.WithXlang(true)) // Serialize any var value any = "hello" @@ -294,7 +294,7 @@ func (c Circle) Area() float64 { return 3.14159 * c.Radius * c.Radius } -f := fory.New() +f := fory.New(fory.WithXlang(true)) f.RegisterStruct(Circle{}, 1) var shape Shape = Circle{Radius: 5.0} @@ -308,7 +308,7 @@ data, _ := f.Serialize(shape) | `[]byte` | BINARY (37) | Variable-length bytes | ```go -f := fory.New() +f := fory.New(fory.WithXlang(true)) data := []byte{0x01, 0x02, 0x03, 0x04} serialized, _ := f.Serialize(data) @@ -330,7 +330,7 @@ const ( StatusComplete Status = 2 ) -f := fory.New() +f := fory.New(fory.WithXlang(true)) f.RegisterEnum(Status(0), 1) status := StatusActive diff --git a/docs/guide/go/thread-safety.md b/docs/guide/go/thread-safety.md index f1e9d1fab5..df67d12cd4 100644 --- a/docs/guide/go/thread-safety.md +++ b/docs/guide/go/thread-safety.md @@ -1,6 +1,6 @@ --- title: Thread Safety -sidebar_position: 100 +sidebar_position: 110 id: thread_safety license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -26,7 +26,7 @@ This guide covers concurrent usage patterns for Fory Go, including the thread-sa The default `Fory` instance is **not thread-safe**: ```go -f := fory.New() +f := fory.New(fory.WithXlang(true)) // NOT SAFE: Concurrent access from multiple goroutines go func() { @@ -148,7 +148,7 @@ However, for best performance, register all types at startup before concurrent u With the default Fory, returned byte slices are views into the internal buffer: ```go -f := fory.New() +f := fory.New(fory.WithXlang(true)) data1, _ := f.Serialize(value1) // data1 is valid @@ -184,7 +184,7 @@ This is safer but has allocation overhead. ```go func BenchmarkNonThreadSafe(b *testing.B) { - f := fory.New() + f := fory.New(fory.WithXlang(true)) f.RegisterStruct(User{}, 1) user := &User{ID: 1, Name: "Alice"} @@ -215,7 +215,7 @@ For maximum performance with known goroutine count: ```go func worker(id int) { // Each worker has its own Fory instance - f := fory.New() + f := fory.New(fory.WithXlang(true)) f.RegisterStruct(User{}, 1) for task := range tasks { @@ -282,7 +282,7 @@ func handler(w http.ResponseWriter, r *http.Request) { ```go // WRONG: Race condition -var f = fory.New() +var f = fory.New(fory.WithXlang(true)) func handler1() { f.Serialize(value1) // Race! @@ -299,7 +299,7 @@ func handler2() { ```go // WRONG: Buffer invalidated on next call -f := fory.New() +f := fory.New(fory.WithXlang(true)) data, _ := f.Serialize(value1) savedData := data // Just copies the slice header! diff --git a/docs/guide/go/troubleshooting.md b/docs/guide/go/troubleshooting.md index 345c03563a..e671d40886 100644 --- a/docs/guide/go/troubleshooting.md +++ b/docs/guide/go/troubleshooting.md @@ -1,6 +1,6 @@ --- title: Troubleshooting -sidebar_position: 110 +sidebar_position: 120 id: troubleshooting license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -115,7 +115,8 @@ f2.RegisterStruct(User{}, 1) // Same ID! 1. **Enable compatible mode**: ```go -f := fory.New(fory.WithCompatible(true)) +// Add WithCompatible(true) to the same option set on every peer. +f := fory.New(/* existing options */, fory.WithCompatible(true)) ``` 2. **Ensure struct definitions match**: @@ -247,7 +248,7 @@ type Good struct { **Symptom**: Data deserializes but fields have wrong values. -**Cause**: Different field ordering between languages. In non-compatible mode, fields are sorted by their snake_case names. CamelCase field names (e.g., `FirstName`) are converted to snake_case (e.g., `first_name`) for sorting. +**Cause**: Different field ordering between languages. In schema-consistent mode, fields are sorted by their snake_case names. CamelCase field names (e.g., `FirstName`) are converted to snake_case (e.g., `first_name`) for sorting. **Solutions**: @@ -417,12 +418,12 @@ FORY_GO_JAVA_CI=1 mvn test -Dtest=org.apache.fory.xlang.GoXlangTest ```go func TestSchemaEvolution(t *testing.T) { - f1 := fory.New(fory.WithCompatible(true)) + f1 := fory.New() f1.RegisterStruct(UserV1{}, 1) data, _ := f1.Serialize(&UserV1{ID: 1, Name: "Alice"}) - f2 := fory.New(fory.WithCompatible(true)) + f2 := fory.New() f2.RegisterStruct(UserV2{}, 1) var result UserV2 diff --git a/docs/guide/go/type-registration.md b/docs/guide/go/type-registration.md index edf5e5e114..91a4bd3b45 100644 --- a/docs/guide/go/type-registration.md +++ b/docs/guide/go/type-registration.md @@ -1,6 +1,6 @@ --- title: Type Registration -sidebar_position: 30 +sidebar_position: 40 id: type_registration license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -39,7 +39,7 @@ type User struct { Name string } -f := fory.New() +f := fory.New(fory.WithXlang(true)) err := f.RegisterStruct(User{}, 1) if err != nil { panic(err) @@ -57,7 +57,7 @@ if err != nil { Register a struct with a type name string. This is more flexible but has higher serialization cost: ```go -f := fory.New() +f := fory.New(fory.WithXlang(true)) err := f.RegisterStructByName(User{}, "example.User") if err != nil { panic(err) @@ -85,7 +85,7 @@ const ( StatusComplete Status = 2 ) -f := fory.New() +f := fory.New(fory.WithXlang(true)) err := f.RegisterEnum(Status(0), 1) ``` @@ -100,7 +100,7 @@ err := f.RegisterEnumByName(Status(0), "example.Status") For types requiring custom serialization logic, register as extension types with a custom serializer: ```go -f := fory.New() +f := fory.New(fory.WithXlang(true)) // Register by ID err := f.RegisterExtension(CustomType{}, 1, &CustomSerializer{}) @@ -116,8 +116,8 @@ See [Custom Serializers](custom-serializers.md) for details on implementing the Type registration is per-Fory-instance: ```go -f1 := fory.New() -f2 := fory.New() +f1 := fory.New(fory.WithXlang(true)) +f2 := fory.New(fory.WithXlang(true)) // Types registered on f1 are NOT available on f2 f1.RegisterStruct(User{}, 1) @@ -131,7 +131,7 @@ f2.RegisterStruct(User{}, 1) Register types after creating a Fory instance and before any serialize/deserialize calls: ```go -f := fory.New() +f := fory.New(fory.WithXlang(true)) // Register before use f.RegisterStruct(User{}, 1) @@ -156,7 +156,7 @@ type Person struct { Address Address } -f := fory.New() +f := fory.New(fory.WithXlang(true)) // Register ALL struct types used in the object graph f.RegisterStruct(Address{}, 1) diff --git a/docs/guide/java/advanced-features.md b/docs/guide/java/advanced-features.md index 535cdf5941..c7d9af4060 100644 --- a/docs/guide/java/advanced-features.md +++ b/docs/guide/java/advanced-features.md @@ -1,6 +1,6 @@ --- title: Advanced Features -sidebar_position: 11 +sidebar_position: 16 id: advanced_features license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -19,59 +19,9 @@ license: | limitations under the License. --- -This page covers advanced features including zero-copy serialization, deep copy, memory management, and logging. - -## Zero-Copy Serialization - -Fory supports zero-copy serialization for efficient handling of large binary data: - -```java -import org.apache.fory.*; -import org.apache.fory.config.*; -import org.apache.fory.serializer.BufferObject; -import org.apache.fory.memory.MemoryBuffer; - -import java.util.*; -import java.util.stream.Collectors; - -public class ZeroCopyExample { - // Note that fory instance should be reused instead of creation every time. - static Fory fory = Fory.builder() - .withXlang(false) - .build(); - - public static void main(String[] args) { - List list = Arrays.asList("str", new byte[1000], new int[100], new double[100]); - Collection bufferObjects = new ArrayList<>(); - byte[] bytes = fory.serialize(list, e -> !bufferObjects.add(e)); - List buffers = bufferObjects.stream() - .map(BufferObject::toBuffer).collect(Collectors.toList()); - System.out.println(fory.deserialize(bytes, buffers)); - } -} -``` - -## Object Deep Copy - -Fory provides efficient deep copy functionality: - -### With Reference Tracking - -```java -Fory fory = Fory.builder().withRefCopy(true).build(); -SomeClass a = xxx; -SomeClass copied = fory.copy(a); -``` - -### Without Reference Tracking (Better Performance) - -When disabled, deep copy will ignore circular and shared references. Same reference of an object graph will be copied into different objects in one `Fory#copy`: - -```java -Fory fory = Fory.builder().withRefCopy(false).build(); -SomeClass a = xxx; -SomeClass copied = fory.copy(a); -``` +This page covers advanced Java runtime features that are not part of first-use serialization. +Java native-mode zero-copy serialization is documented in [Native Mode](native-mode.md), and deep +copy semantics are documented in [Object Copy](object-copy.md). ## Memory Allocation Customization @@ -178,7 +128,7 @@ public static final ThreadSafeFory FORY; static { LoggerFactory.useSlf4jLogging(true); - FORY = Fory.builder() + FORY = Fory.builder().withXlang(false) .buildThreadSafeFory(); } ``` @@ -205,4 +155,6 @@ static { - [Compression](compression.md) - Data compression options - [Configuration](configuration.md) - All ForyBuilder options -- [Cross-Language Serialization](cross-language.md) - XLANG mode +- [Native Mode](native-mode.md) - Java-only serialization, JDK hooks, and zero-copy buffers +- [Object Copy](object-copy.md) - Deep copy functionality +- [Cross-Language](cross-language.md) - Java xlang interoperability diff --git a/docs/guide/java/android-support.md b/docs/guide/java/android-support.md index c3b68088ef..90fb59e767 100644 --- a/docs/guide/java/android-support.md +++ b/docs/guide/java/android-support.md @@ -1,6 +1,6 @@ --- title: Android Support -sidebar_position: 14 +sidebar_position: 15 id: android_support license: | Licensed to the Apache Software Foundation (ASF) under one or more diff --git a/docs/guide/java/basic-serialization.md b/docs/guide/java/basic-serialization.md index 196a2073eb..511008e01b 100644 --- a/docs/guide/java/basic-serialization.md +++ b/docs/guide/java/basic-serialization.md @@ -19,113 +19,114 @@ license: | limitations under the License. --- -This page covers basic serialization patterns and Fory instance creation. +This page covers the Java xlang quickstart. Xlang mode is the default Java wire format and is the +right first choice for cross-language payloads. -## Creating Fory Instances +## Create a Fory Runtime -### Single-Thread Fory - -For single-threaded applications: +For a single-threaded xlang runtime, set the mode explicitly: ```java +import org.apache.fory.Fory; + Fory fory = Fory.builder() - .withXlang(false) - // enable reference tracking for shared/circular reference. - // Disable it will have better performance if no duplicate reference. - .withRefTracking(false) - .withCompatible(false) - // enable type forward/backward compatibility - // disable it for small size and better performance. - // .withCompatible(true) - // enable async multi-threaded compilation. - .withAsyncCompilation(true) - .build(); -byte[] bytes = fory.serialize(object); -System.out.println(fory.deserialize(bytes)); + .withXlang(true) + .requireClassRegistration(true) + .build(); ``` -### Thread-Safe Fory - -For multi-threaded applications: +For a thread-safe runtime, build `ThreadSafeFory` from the same builder: ```java +import org.apache.fory.ThreadSafeFory; + ThreadSafeFory fory = Fory.builder() - .withXlang(false) - // enable reference tracking for shared/circular reference. - // Disable it will have better performance if no duplicate reference. - .withRefTracking(false) - // compress int for smaller size - // .withIntCompressed(true) - // compress long for smaller size - // .withLongCompressed(true) - .withCompatible(false) - // enable type forward/backward compatibility - // disable it for small size and better performance. - // .withCompatible(true) - // enable async multi-threaded compilation. - .withAsyncCompilation(true) - .buildThreadSafeFory(); -byte[] bytes = fory.serialize(object); -System.out.println(fory.deserialize(bytes)); + .withXlang(true) + .requireClassRegistration(true) + .buildThreadSafeFory(); ``` -## Object Deep Copy +Default Java xlang mode also defaults to compatible schema mode, so independently deployed services +can add and remove fields when their schema metadata remains compatible. Use +`withCompatible(false)` only when every peer updates schema together and you want schema-consistent +xlang payloads. -Fory provides efficient deep copy functionality: +## Register Custom Types -For the full copy semantics, custom copy hooks, and troubleshooting guidance, see -[Object Copy](object-copy.md). - -### With Reference Tracking +Register application classes with the same type identity on every peer. Numeric IDs are compact and +fast, while namespace/type-name registration is easier to coordinate across independently owned +services. ```java -Fory fory = Fory.builder().withRefCopy(true).build(); -SomeClass a = xxx; -SomeClass copied = fory.copy(a); -``` +import org.apache.fory.annotation.ForyField; -### Without Reference Tracking (Better Performance) +public class User { + @ForyField(id = 0) + public String name; -When disabled, deep copy will ignore circular and shared references. Same reference of an object graph will be copied into different objects in one `Fory#copy`: + @ForyField(id = 1) + public int age; +} -```java -Fory fory = Fory.builder().withRefCopy(false).build(); -SomeClass a = xxx; -SomeClass copied = fory.copy(a); +Fory fory = Fory.builder() + .withXlang(true) + .requireClassRegistration(true) + .build(); + +fory.register(User.class, "example", "User"); ``` -## Serialization APIs +Use field IDs for long-lived schemas so field identity is stable even if Java field names change. +See [Schema Metadata](schema-metadata.md) for Java annotations, nullability, reference tracking, and +enum metadata. -### Basic Serialize/Deserialize +## Serialize And Deserialize ```java -// Serialize object to byte array -byte[] bytes = fory.serialize(object); +User user = new User(); +user.name = "Alice"; +user.age = 30; -// Deserialize byte array to object -Object obj = fory.deserialize(bytes); +byte[] bytes = fory.serialize(user); +User decoded = fory.deserialize(bytes, User.class); ``` -### Serialize/Deserialize with Type +When xlang bytes cross runtimes, every runtime must register the same type identity and compatible +field metadata. The shared rules live in [Xlang](../xlang/index.md), while Java-specific API calls +are in [Cross-Language](cross-language.md). -```java -// Serialize object -byte[] bytes = fory.serialize(object); +## Use Native Mode For Java-Only Traffic -// Deserialize with expected type -MyClass obj = fory.deserialize(bytes, MyClass.class); +For same-language Java/JVM traffic, native mode is usually the better fit: + +```java +Fory fory = Fory.builder() + .withXlang(false) + .build(); ``` +Native mode supports the broad Java object serialization surface, including JDK serialization hooks, +object copy, and native-mode zero-copy buffers. See [Native Mode](native-mode.md). + +## Common Options + +- `withRefTracking(true)` preserves shared references and circular references. +- `requireClassRegistration(true)` keeps the default registered-type policy. +- `withCompatible(true)` enables compatible mode explicitly for native-mode + schema evolution. Xlang mode already uses compatible mode by default. +- `withAsyncCompilation(true)` enables asynchronous serializer compilation where supported. + ## Best Practices 1. **Reuse Fory instances**: Creating Fory is expensive, always reuse instances 2. **Use appropriate thread safety**: Choose between single-thread and thread-safe based on your needs -3. **Register classes**: Register frequently used classes for better performance -4. **Configure reference tracking**: Disable if you don't have circular/shared references +3. **Register classes**: Keep type identity stable across every xlang peer +4. **Configure reference tracking**: Enable it only when the object graph needs identity or cycles ## Related Topics - [Configuration](configuration.md) - All ForyBuilder options -- [Object Copy](object-copy.md) - Deep-copy semantics and custom copy hooks -- [Type Registration](type-registration.md) - Class registration +- [Native Mode](native-mode.md) - Java-only serialization features +- [Schema Metadata](schema-metadata.md) - Field IDs, nullability, reference tracking, and enum IDs +- [Cross-Language](cross-language.md) - Java xlang interoperability - [Troubleshooting](troubleshooting.md) - Common API usage issues diff --git a/docs/guide/java/compression.md b/docs/guide/java/compression.md index 44be8f7293..d8df9160bd 100644 --- a/docs/guide/java/compression.md +++ b/docs/guide/java/compression.md @@ -1,6 +1,6 @@ --- title: Compression -sidebar_position: 7 +sidebar_position: 10 id: compression license: | Licensed to the Apache Software Foundation (ASF) under one or more diff --git a/docs/guide/java/configuration.md b/docs/guide/java/configuration.md index 2ce533a743..6cb89b43aa 100644 --- a/docs/guide/java/configuration.md +++ b/docs/guide/java/configuration.md @@ -1,6 +1,6 @@ --- title: Configuration -sidebar_position: 2 +sidebar_position: 3 id: configuration license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -32,7 +32,7 @@ This page documents all configuration options available through `ForyBuilder`. | `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()` | -| `compatible` | Type forward/backward compatibility config. `false`: class schema must be consistent between serialization peer and deserialization peer. `true`: class schema can be different between serialization peer and deserialization peer. They can add/delete fields independently. [See more](schema-evolution.md). | `false` | +| `compatible` | Type forward/backward compatibility config. `false`: class schema must be consistent between serialization peer and deserialization peer. `true`: class schema can be different between serialization peer and deserialization peer. They can add/delete fields independently. When unset, the default follows the wire mode: xlang mode uses `true`, and native mode uses `false`. [See more](schema-evolution.md). | xlang: `true`; native: `false` | | `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 compatible mode 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` | @@ -71,8 +71,7 @@ Fory fory = Fory.builder() ## Related Topics -- [Field Configuration](field-configuration.md) - `@ForyField`, `@Ignore`, and integer encoding annotations -- [Enum Configuration](enum-configuration.md) - `serializeEnumByName` and `@ForyEnumId` +- [Schema Metadata](schema-metadata.md) - `@ForyField`, `@Ignore`, integer encoding annotations, `serializeEnumByName`, and `@ForyEnumId` - [Schema Evolution](schema-evolution.md) - Compatible mode and meta sharing - [Compression](compression.md) - Int, long, and array compression details - [Type Registration](type-registration.md) - Class registration options diff --git a/docs/guide/java/cross-language.md b/docs/guide/java/cross-language.md index 3f43f3a278..e29f7b498e 100644 --- a/docs/guide/java/cross-language.md +++ b/docs/guide/java/cross-language.md @@ -1,6 +1,6 @@ --- title: Cross-Language Serialization -sidebar_position: 12 +sidebar_position: 4 id: cross_language license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -21,17 +21,16 @@ license: | Apache Fory™ supports seamless data exchange between Java and other languages (Python, Rust, Go, JavaScript, etc.) through the xlang serialization format. This enables multi-language microservices, polyglot data pipelines, and cross-platform data sharing. -## Enable Cross-Language Mode +## Create an Xlang Runtime -To serialize data for consumption by other languages, enable xlang mode: +Java defaults to xlang mode with compatible schema evolution. Set the mode explicitly in xlang examples: ```java import org.apache.fory.*; import org.apache.fory.config.*; -// Create Fory instance with XLANG mode Fory fory = Fory.builder() - .withXlang(true).withCompatible(true) + .withXlang(true) .withRefTracking(true) // Enable reference tracking for complex graphs .build(); ``` @@ -85,7 +84,7 @@ public record Person(String name, int age) {} public class Example { public static void main(String[] args) { Fory fory = Fory.builder() - .withXlang(true).withCompatible(true) + .withXlang(true) .withRefTracking(true) .build(); @@ -111,8 +110,7 @@ class Person: name: str age: pyfory.Int32 -# Create Fory in xlang mode -fory = pyfory.Fory(xlang=True, compatible=True, ref=True) +fory = pyfory.Fory(xlang=True, ref=True) # Register with the SAME name as Java fory.register_type(Person, typename="example.Person") @@ -124,7 +122,7 @@ print(f"{person.name}, {person.age}") # Output: Bob, 25 ## Handling Circular and Shared References -Cross-language mode supports circular and shared references when reference tracking is enabled: +Xlang mode supports circular and shared references when reference tracking is enabled: ```java public class Node { @@ -134,7 +132,7 @@ public class Node { } Fory fory = Fory.builder() - .withXlang(true).withCompatible(true) + .withXlang(true) .withRefTracking(true) // Required for circular references .build(); @@ -198,10 +196,10 @@ private @BFloat16Type short[] values; ```java public record UserData( - String name, // ✅ Compatible - int age, // ✅ Compatible - List tags, // ✅ Compatible - Map scores // ✅ Compatible + String name, // compatible + int age, // compatible + List tags, // compatible + Map scores // compatible ) {} ``` @@ -209,15 +207,15 @@ public record UserData( ```java public record UserData( - Optional name, // ❌ Not cross-language compatible - BigDecimal balance, // ❌ Limited support - EnumSet statuses // ❌ Java-specific collection + Optional name, // not cross-language compatible + BigDecimal balance, // limited support + EnumSet statuses // Java-specific collection ) {} ``` ## Performance Considerations -Cross-language mode has additional overhead compared to Java-only mode: +Xlang mode has additional overhead compared to Java native mode: - **Type metadata encoding**: Adds extra bytes per type - **Type resolution**: Requires name/ID lookup during deserialization @@ -226,12 +224,12 @@ Cross-language mode has additional overhead compared to Java-only mode: - Use **ID-based registration** when possible (smaller encoding) - **Disable reference tracking** if you don't need circular references (`withRefTracking(false)`) -- **Use Java mode** (`withXlang(false)`) when only Java serialization is needed +- **Use native mode** (`withXlang(false)`) when only Java serialization is needed ## Cross-Language Best Practices 1. **Consistent Registration**: Ensure all services register types with identical IDs/names -2. **Version Compatibility**: Use compatible mode for schema evolution across services +2. **Version Compatibility**: Keep compatible mode for schema evolution across services ## Troubleshooting Cross-Language Serialization @@ -247,7 +245,7 @@ Cross-language mode has additional overhead compared to Java-only mode: ### Data corruption or unexpected values -- Verify both sides enable xlang mode +- Verify both sides use xlang payloads - Ensure both sides have compatible Fory versions ## See Also diff --git a/docs/guide/java/custom-serializers.md b/docs/guide/java/custom-serializers.md index 6079e176e5..4816ad1dd9 100644 --- a/docs/guide/java/custom-serializers.md +++ b/docs/guide/java/custom-serializers.md @@ -1,6 +1,6 @@ --- title: Custom Serializers -sidebar_position: 4 +sidebar_position: 12 id: custom_serializers license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -68,7 +68,7 @@ public final class FooSerializer extends Serializer implements Shareable { Register it with a `Config`-based constructor when the serializer is shareable: ```java -Fory fory = Fory.builder().build(); +Fory fory = Fory.builder().withXlang(false).build(); fory.registerSerializer(Foo.class, new FooSerializer(fory.getConfig())); ``` @@ -195,7 +195,7 @@ public final class CustomMapSerializer> extends MapSerialize ## Registration ```java -Fory fory = Fory.builder().build(); +Fory fory = Fory.builder().withXlang(false).build(); fory.registerSerializer(Foo.class, new FooSerializer(fory.getConfig())); fory.registerSerializer( diff --git a/docs/guide/java/enum-configuration.md b/docs/guide/java/enum-configuration.md deleted file mode 100644 index 99a1bfce9d..0000000000 --- a/docs/guide/java/enum-configuration.md +++ /dev/null @@ -1,111 +0,0 @@ ---- -title: Enum Configuration -sidebar_position: 6 -id: enum_configuration -license: | - 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. ---- - -This page explains how Java enum serialization is configured in Apache Fory. - -## Default Enum Behavior - -Java enums can be serialized in two modes: - -1. **By numeric tag**: the default behavior -2. **By enum name**: enabled with `serializeEnumByName(true)` - -Numeric tags are always used in xlang mode. In native Java mode, `serializeEnumByName(true)` -switches enum serialization to names instead of numeric tags. - -## Serialize Enums by Name - -Use `serializeEnumByName(true)` when native Java peers should match enum constants by name instead -of numeric tag. - -```java -Fory fory = Fory.builder() - .withXlang(false) - .serializeEnumByName(true) - .build(); -``` - -This mode is useful when declaration order is unstable but enum names remain fixed. It only affects -native Java mode. Xlang still uses numeric tags. - -## Stable Numeric Enum IDs - -Without `serializeEnumByName(true)`, Java enums are serialized by numeric tag. The default tag is -the declaration ordinal. When an enum needs stable ids that do not depend on declaration order, -annotate exactly one id source with `@ForyEnumId`, or annotate every enum constant with explicit -tag values. - -```java -import org.apache.fory.annotation.ForyEnumId; - -enum Status { - Unknown(10), - Running(20), - Finished(30); - - private final int id; - - Status(int id) { - this.id = id; - } - - @ForyEnumId - public int getId() { - return id; - } -} -``` - -Java also supports annotating one enum instance field with `@ForyEnumId`, or annotating every enum -constant directly such as `@ForyEnumId(10) Unknown`. - -### `@ForyEnumId` Styles - -`@ForyEnumId` supports exactly three configuration styles: - -1. Annotate one enum instance field and store the numeric id there. -2. Annotate one zero-argument public instance method such as `getId()`. -3. Annotate every enum constant directly with an explicit value such as `@ForyEnumId(10) Unknown`. - -### Validation Rules - -1. Use exactly one of those three styles for a given enum. -2. Field and method annotations must leave `value()` at its default `-1`. -3. Enum-constant annotations must appear on every constant once any constant uses `@ForyEnumId`. -4. All ids must be non-negative, unique, and fit in Java `int`. - -### Lookup Behavior - -1. Without `@ForyEnumId`, Fory writes the declaration ordinal. -2. With `@ForyEnumId`, Fory writes the configured stable numeric tag instead. -3. Small dense tags use an array lookup internally; sparse larger tags fall back to a map. - -## Choosing Between Name and Numeric Modes - -- Use **enum names** when the enum is Java-only and constant names are the intended compatibility key. -- Use **numeric tags** when cross-language payloads or stable explicit ids matter. -- Use **`@ForyEnumId`** when declaration order may change but the numeric wire ids must stay stable. - -## Related Topics - -- [Configuration](configuration.md) - `serializeEnumByName` and other runtime options -- [Field Configuration](field-configuration.md) - `@ForyField`, `@Ignore`, and integer encoding annotations -- [Cross-Language](cross-language.md) - Xlang enum interoperability diff --git a/docs/guide/java/graalvm-support.md b/docs/guide/java/graalvm-support.md index aa3e2527c3..1f2503fe1e 100644 --- a/docs/guide/java/graalvm-support.md +++ b/docs/guide/java/graalvm-support.md @@ -1,6 +1,6 @@ --- title: GraalVM Support -sidebar_position: 13 +sidebar_position: 14 id: graalvm_support license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -66,7 +66,7 @@ public class Example { static Fory fory; static { - fory = Fory.builder().build(); + fory = Fory.builder().withXlang(false).build(); fory.register(MyClass.class); fory.register(AnotherClass.class); // Compile all serializers at build time @@ -105,12 +105,12 @@ class, for example: Args = --initialize-at-build-time=com.example.Example ``` -| Scenario | Without Feature | With Feature | -| ------------------------------- | ---------------------------- | ------------------ | -| Public classes with no-arg ctor | ✅ Works | ✅ Works | -| Private constructors | ❌ Needs reflect-config.json | ✅ Auto-registered | -| Private inner records | ❌ Needs reflect-config.json | ✅ Auto-registered | -| Dynamic proxies | ❌ Needs manual config | ✅ Auto-registered | +| Scenario | Without Feature | With Feature | +| ------------------------------- | ------------------------- | --------------- | +| Public classes with no-arg ctor | Works | Works | +| Private constructors | Needs reflect-config.json | Auto-registered | +| Private inner records | Needs reflect-config.json | Auto-registered | +| Dynamic proxies | Needs manual config | Auto-registered | ### Example with Private Record @@ -122,7 +122,7 @@ public class Example { static Fory fory; static { - fory = Fory.builder().build(); + fory = Fory.builder().withXlang(false).build(); fory.register(PrivateRecord.class); fory.ensureSerializersCompiled(); } @@ -146,7 +146,7 @@ public class ProxyExample { static Fory fory; static { - fory = Fory.builder().build(); + fory = Fory.builder().withXlang(false).build(); // Register the exact interface list used by Proxy.newProxyInstance(...) GraalvmSupport.registerProxySupport(MyService.class, Audited.class); fory.ensureSerializersCompiled(); diff --git a/docs/guide/java/index.md b/docs/guide/java/index.md index 3e4d29d493..336e76257c 100644 --- a/docs/guide/java/index.md +++ b/docs/guide/java/index.md @@ -19,7 +19,7 @@ license: | limitations under the License. --- -Apache Fory™ provides blazingly fast Java object serialization with JIT compilation and zero-copy techniques. When only Java object serialization is needed, this mode delivers better performance compared to cross-language object graph serialization. +Apache Fory™ provides blazingly fast Java object serialization with JIT compilation and zero-copy techniques. Java supports both xlang mode and native mode. Xlang mode is the default cross-language wire format and uses compatible schema evolution. Native mode is the Java-only wire format for same-language object serialization, JDK serialization replacement behavior, framework replacement, and Java-native object graph features. ## Features @@ -68,7 +68,8 @@ public class Example { SomeClass object = new SomeClass(); // Note that Fory instances should be reused between // multiple serializations of different objects. - Fory fory = Fory.builder().withXlang(false) + Fory fory = Fory.builder() + .withXlang(true) .requireClassRegistration(true) .build(); // Registering types can reduce class name serialization overhead, but not mandatory. @@ -91,7 +92,7 @@ public class Example { public static void main(String[] args) { SomeClass object = new SomeClass(); ThreadSafeFory fory = Fory.builder() - .withXlang(false) + .withXlang(true) .buildThreadSafeFory(); fory.register(SomeClass.class, 1); byte[] bytes = fory.serialize(object); @@ -108,8 +109,8 @@ import org.apache.fory.config.*; public class Example { private static final ThreadSafeFory fory = Fory.builder() - .withXlang(false) - .buildThreadSafeFory(); + .withXlang(true) + .buildThreadSafeFory(); static { fory.register(SomeClass.class, 1); @@ -123,6 +124,14 @@ public class Example { } ``` +## Xlang Mode And Native Mode + +Use xlang mode for cross-language payloads and schemas shared with non-Java runtimes. It is the default Java wire mode, and Java examples that use it set `.withXlang(true)` explicitly so the mode choice is visible. + +Use native mode for Java-only traffic. Native mode is selected with `.withXlang(false)`, uses schema-consistent payloads unless compatible mode is enabled, and owns Java-specific object behavior such as JDK serialization hooks, `Externalizable`, dynamic object graphs, object copy, and Java native-mode zero-copy buffers. It is optimized for the JVM type system and supports a broader Java object surface than xlang mode. If you are replacing JDK serialization, Kryo, FST, Hessian, or Java-only Protocol Buffers payloads, start with native mode. + +See [Native Mode](native-mode.md) for Java-only serialization details and [Cross-Language Serialization](cross-language.md) for Java xlang registration and interoperability rules. + ## Thread Safety Fory provides two thread-safe runtime styles: @@ -134,9 +143,8 @@ This is the default choice. It uses a fixed-size shared `ThreadPoolFory` sized t ```java ThreadSafeFory fory = Fory.builder() - .withXlang(false) + .withXlang(true) .withRefTracking(false) - .withCompatible(false) .withAsyncCompilation(true) .buildThreadSafeFory(); ``` @@ -150,7 +158,7 @@ platform thread, or when you want to pin that choice regardless of JDK version: ```java ThreadSafeFory fory = Fory.builder() - .withXlang(false) + .withXlang(true) .buildThreadLocalFory(); fory.register(SomeClass.class, 1); byte[] bytes = fory.serialize(object); @@ -166,9 +174,8 @@ pooled instance is already in use; the runtime does not key cached instances by ```java ThreadSafeFory fory = Fory.builder() - .withXlang(false) + .withXlang(true) .withRefTracking(false) - .withCompatible(false) .withAsyncCompilation(true) .buildThreadSafeForyPool(poolSize); ``` @@ -178,31 +185,28 @@ ThreadSafeFory fory = Fory.builder() ```java // Single-thread Fory Fory fory = Fory.builder() - .withXlang(false) + .withXlang(true) .withRefTracking(false) - .withCompatible(false) .withAsyncCompilation(true) .build(); // Thread-safe Fory (thread-safe Fory backed by a pool of Fory instances) ThreadSafeFory fory = Fory.builder() - .withXlang(false) + .withXlang(true) .withRefTracking(false) - .withCompatible(false) .withAsyncCompilation(true) .buildThreadSafeFory(); // Explicit thread-local runtime ThreadSafeFory threadLocalFory = Fory.builder() - .withXlang(false) + .withXlang(true) .buildThreadLocalFory(); ``` ## Next Steps - [Configuration](configuration.md) - Learn about ForyBuilder options -- [Field Configuration](field-configuration.md) - `@ForyField`, `@Ignore`, and integer encoding annotations -- [Enum Configuration](enum-configuration.md) - `serializeEnumByName` and `@ForyEnumId` +- [Schema Metadata](schema-metadata.md) - `@ForyField`, `@Ignore`, integer encoding annotations, `serializeEnumByName`, and `@ForyEnumId` - [Basic Serialization](basic-serialization.md) - Detailed serialization patterns - [Object Copy](object-copy.md) - Deep-copy Java object graphs in memory - [Compression](compression.md) - Integer, long, and array compression options diff --git a/docs/guide/java/migration.md b/docs/guide/java/migration.md deleted file mode 100644 index e17152936b..0000000000 --- a/docs/guide/java/migration.md +++ /dev/null @@ -1,120 +0,0 @@ ---- -title: Migration Guide -sidebar_position: 15 -id: migration -license: | - 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. ---- - -This page covers migration strategies from JDK serialization and upgrading between Fory versions. - -## JDK Serialization Migration - -If you use JDK serialization before and can't upgrade your client and server at the same time (which is common for online applications), Fory provides a utility method `org.apache.fory.serializer.JavaSerializer.serializedByJDK` to check whether the binary was generated by JDK serialization. - -You can use the following pattern to make existing serialization protocol-aware, then upgrade serialization to Fory in an async rolling-up way: - -```java -if (JavaSerializer.serializedByJDK(bytes)) { - ObjectInputStream objectInputStream = xxx; - return objectInputStream.readObject(); -} else { - return fory.deserialize(bytes); -} -``` - -This allows you to: - -1. Deploy new code that can read both JDK and Fory serialized data -2. Gradually migrate serialization to Fory -3. Eventually remove JDK serialization support - -## Upgrade Fory - -### Minor Version Upgrades - -Currently, binary compatibility is ensured for minor versions only. For example, if you are using Fory `v0.2.0`, binary compatibility will be provided if you upgrade to Fory `v0.2.1`. - -If you upgrade by minor version, or you won't have data serialized by older Fory, you can upgrade Fory directly without any special handling. - -### Major Version Upgrades - -If you upgrade to Fory `v0.4.1` from `v0.2.x`, no binary compatibility is ensured. - -Most of the time there is no need to upgrade Fory to a newer major version—the current version is fast and compact enough, and we provide some minor fixes for recent older versions. - -### Versioning Serialized Data - -If you do want to upgrade Fory for better performance and smaller size, you need to write the Fory version as a header to serialized data using code like the following to keep binary compatibility: - -#### Serialization with Version Header - -```java -MemoryBuffer buffer = xxx; -buffer.writeVarInt32(2); // Write Fory version -fory.serialize(buffer, obj); -``` - -#### Deserialization with Version Header - -```java -MemoryBuffer buffer = xxx; -int foryVersion = buffer.readVarInt32(); -Fory fory = getFory(foryVersion); -fory.deserialize(buffer); -``` - -`getFory` is a method to load the corresponding Fory version. You can shade and relocate different versions of Fory to different packages, and load Fory by version. - -### Shading Example - -```xml - - - org.apache.maven.plugins - maven-shade-plugin - - - shade-fory-v1 - package - - shade - - - - - org.apache.fory - com.myapp.fory.v1 - - - - - - -``` - -## Best Practices - -1. **Test thoroughly before upgrading**: Ensure compatibility with existing serialized data -2. **Use version headers for long-term storage**: If data persists across Fory versions -3. **Maintain backward compatibility**: Keep old Fory versions available for reading legacy data -4. **Monitor after upgrade**: Watch for deserialization errors in production - -## Related Topics - -- [Configuration](configuration.md) - ForyBuilder options -- [Schema Evolution](schema-evolution.md) - Handling class changes -- [Troubleshooting](troubleshooting.md) - Common migration issues diff --git a/docs/guide/java/native-mode.md b/docs/guide/java/native-mode.md new file mode 100644 index 0000000000..9091caf0f9 --- /dev/null +++ b/docs/guide/java/native-mode.md @@ -0,0 +1,188 @@ +--- +title: Native Mode +sidebar_position: 2 +id: native_mode +license: | + 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. +--- + +Java native mode is the Java-only wire format selected with `withXlang(false)`. Use it when both +writer and reader are Java/JVM services and you want the broad Java object serialization surface: +JDK serialization hooks, dynamic object graphs, optional class-registration policies, object copy, +and Java-native collection and wrapper handling. Native mode is optimized for the JVM type system, +so it is the right starting point for Java/JVM-only replacements of JDK serialization, Kryo, FST, +Hessian, or Java-only Protocol Buffers payloads. + +Use xlang mode, the default, when bytes must be read by other Fory runtimes. + +## Create a Native-Mode Runtime + +```java +import org.apache.fory.Fory; + +Fory fory = Fory.builder() + .withXlang(false) + .requireClassRegistration(true) + .withRefTracking(true) + .build(); + +byte[] bytes = fory.serialize(object); +Object decoded = fory.deserialize(bytes); +``` + +Native mode defaults to schema-consistent serialization. Enable compatible mode only when Java +classes evolve independently across writer and reader deployments: + +```java +Fory fory = Fory.builder() + .withXlang(false) + .withCompatible(true) + .build(); +``` + +## Java Serialization Framework Replacement + +Java native mode supports the JDK serialization hooks that are part of many existing Java object +models and is the Fory mode to use when replacing Java-only serialization frameworks: + +- `writeObject` and `readObject` +- `writeReplace` and `readResolve` +- `readObjectNoData` +- `Externalizable` + +Use native mode when replacing JDK serialization, Kryo, FST, Hessian, or Java-only Protocol +Buffers payloads. Use xlang mode only when the bytes must be read by non-Java Fory runtimes. + +```java +public class MyClass implements Serializable { + private void writeObject(ObjectOutputStream out) throws IOException { + // Custom serialization logic. + } + + private void readObject(ObjectInputStream in) throws IOException { + // Custom deserialization logic. + } + + private Object writeReplace() { + return this; + } + + private Object readResolve() { + return this; + } +} +``` + +When an application must read data that may be either JDK `ObjectOutputStream` bytes or Fory +native-mode bytes, `JavaSerializer.serializedByJDK` can identify the JDK payload before falling +back to Fory: + +```java +if (JavaSerializer.serializedByJDK(bytes)) { + ObjectInputStream objectInputStream = ...; + return objectInputStream.readObject(); +} +return fory.deserialize(bytes); +``` + +Use this bridge only at boundaries that actually accept both formats. Native-mode Fory payloads +should otherwise be written and read by Fory directly. + +## Object Graphs And Reference Tracking + +Native mode supports shared references and circular references when reference tracking is enabled: + +```java +Fory fory = Fory.builder() + .withXlang(false) + .withRefTracking(true) + .build(); +``` + +Disable reference tracking only for value-shaped graphs where identity and cycles are not part of +the data model. + +## Object Copy + +Fory can deep-copy Java objects without materializing a byte array. For full copy semantics, custom +copy hooks, and troubleshooting, see [Object Copy](object-copy.md). + +```java +Fory fory = Fory.builder() + .withXlang(false) + .withRefCopy(true) + .build(); + +MyClass copy = fory.copy(original); +``` + +## Zero-Copy Serialization + +Native mode supports out-of-band `BufferObject` payloads for large binary values and primitive +arrays: + +```java +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; +import org.apache.fory.Fory; +import org.apache.fory.memory.MemoryBuffer; +import org.apache.fory.serializer.BufferObject; + +Fory fory = Fory.builder() + .withXlang(false) + .build(); + +List value = Arrays.asList("str", new byte[1000], new int[100], new double[100]); +Collection bufferObjects = new ArrayList<>(); +byte[] bytes = fory.serialize(value, bufferObject -> !bufferObjects.add(bufferObject)); +List buffers = bufferObjects.stream() + .map(BufferObject::toBuffer) + .collect(Collectors.toList()); + +Object decoded = fory.deserialize(bytes, buffers); +``` + +The callback returns `false` for buffers that should be sent out-of-band. The main byte array still +contains the root object graph and references the buffers in callback order. + +## Registration And Security + +Class registration is enabled by default. Register application classes before serializing them: + +```java +Fory fory = Fory.builder() + .withXlang(false) + .requireClassRegistration(true) + .build(); + +fory.register(MyClass.class); +``` + +`requireClassRegistration(false)` is available for trusted environments that need dynamic class +loading, but deserializing unregistered classes from untrusted input is unsafe. Keep class +registration enabled for service boundaries unless a `TypeChecker` or allow-list policy owns the +trust decision. + +## Related Topics + +- [Basic Serialization](basic-serialization.md) - Xlang-first Java quickstart +- [Configuration](configuration.md) - Java builder options +- [Schema Evolution](schema-evolution.md) - Compatible and schema-consistent mode +- [Object Copy](object-copy.md) - Deep-copy semantics +- [GraalVM Support](graalvm-support.md) - Native-image platform support diff --git a/docs/guide/java/object-copy.md b/docs/guide/java/object-copy.md index e31c1564d7..8f7712a8ca 100644 --- a/docs/guide/java/object-copy.md +++ b/docs/guide/java/object-copy.md @@ -158,7 +158,7 @@ Enabling one does not automatically enable the other. If your application both s copies graphs with shared or circular references, configure both options explicitly. ```java -Fory fory = Fory.builder() +Fory fory = Fory.builder().withXlang(false) .withRefTracking(true) .withRefCopy(true) .build(); @@ -187,7 +187,7 @@ import org.apache.fory.Fory; public class Example { public static void main(String[] args) { - Fory fory = Fory.builder() + Fory fory = Fory.builder().withXlang(false) .requireClassRegistration(true) .withRefCopy(true) .build(); @@ -331,7 +331,7 @@ Use this approach when copy behavior belongs with a serializer rather than the d If copy fails on a cyclic object graph, enable `withRefCopy(true)`: ```java -Fory fory = Fory.builder() +Fory fory = Fory.builder().withXlang(false) .withRefCopy(true) .build(); ``` @@ -344,7 +344,7 @@ If the same source object is copied into multiple distinct target objects, `with disabled. Turn it on: ```java -Fory fory = Fory.builder() +Fory fory = Fory.builder().withXlang(false) .withRefCopy(true) .build(); ``` diff --git a/docs/guide/java/row-format.md b/docs/guide/java/row-format.md index b99bf4527c..dd43df8258 100644 --- a/docs/guide/java/row-format.md +++ b/docs/guide/java/row-format.md @@ -1,6 +1,6 @@ --- title: Row Format -sidebar_position: 14 +sidebar_position: 11 id: row_format license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -166,16 +166,16 @@ struct Foo { FORY_STRUCT(Foo, f1, f2, f3, f4); }; -fory::encoder::RowEncoder encoder; -encoder.Encode(foo); -auto row = encoder.GetWriter().ToRow(); +fory::row::encoder::RowEncoder encoder; +encoder.encode(foo); +auto row = encoder.get_writer().to_row(); // Zero-copy random access -auto f2_array = row->GetArray(1); -auto f4_array = row->GetArray(3); -auto bar10 = f4_array->GetStruct(10); -int64_t value = bar10->GetArray(1)->GetInt64(5); -std::string str = bar10->GetString(0); +auto f2_array = row->get_array(1); +auto f4_array = row->get_array(3); +auto bar10 = f4_array->get_struct(10); +int64_t value = bar10->get_array(1)->get_int64(5); +std::string str = bar10->get_string(0); ``` ## Performance Comparison @@ -189,6 +189,6 @@ std::string str = bar10->GetString(0); ## Related Topics -- [Cross-Language Serialization](cross-language.md) - XLANG mode +- [Cross-Language Serialization](cross-language.md) - xlang mode - [Advanced Features](advanced-features.md) - Zero-copy serialization - [Row Format Specification](https://fory.apache.org/docs/specification/row_format_spec) - Protocol details diff --git a/docs/guide/java/schema-evolution.md b/docs/guide/java/schema-evolution.md index b99a3c5465..76dbdf500b 100644 --- a/docs/guide/java/schema-evolution.md +++ b/docs/guide/java/schema-evolution.md @@ -1,6 +1,6 @@ --- title: Schema Evolution -sidebar_position: 8 +sidebar_position: 6 id: schema_evolution license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -27,18 +27,21 @@ In many systems, the schema of a class used for serialization may change over ti ### Default Mode -In Java-native mode (`xlang=false`), Fory serializes objects using schema-consistent mode by default. This mode assumes that the deserialization process uses the same class schema as the serialization process, minimizing payload overhead. However, if there is a schema inconsistency, deserialization will fail. +In Java native mode (`xlang=false`), Fory serializes objects using schema-consistent mode by default. This mode assumes that the deserialization process uses the same class schema as the serialization process, minimizing payload overhead. However, if there is a schema inconsistency, deserialization will fail. -In cross-language mode (`xlang=true`), Fory defaults to compatible mode because schemas can diverge more easily across independently deployed services and language implementations. +In xlang mode, Fory defaults to compatible mode because schemas can diverge more easily across independently deployed services and language implementations. ### Compatible Mode -If the schema is expected to change, to make deserialization succeed (i.e., schema forward/backward compatibility), users must configure Fory with `ForyBuilder#withCompatible(true)`. Cross-language mode already uses this setting by default; it is still recommended to set it explicitly in examples and service configuration. +If a Java-native mode schema is expected to change, configure Fory with +`ForyBuilder#withCompatible(true)` so deserialization can tolerate added, removed, or reordered +fields. Xlang mode already uses compatible mode by default, so xlang services do not need this +option unless they previously overrode the schema mode. In this compatible mode, deserialization can handle schema changes such as missing or extra fields, allowing it to succeed even when the serialization and deserialization processes have different class schemas. ```java -Fory fory = Fory.builder() +Fory fory = Fory.builder().withXlang(false) .withCompatible(true) .build(); @@ -56,8 +59,8 @@ This compatible mode involves serializing class metadata into the serialized out - `Evolution.ENABLED`: require schema evolution metadata for this class. Registration or type resolution fails if the Fory instance cannot emit that metadata. - `Evolution.DISABLED`: force fixed-schema `STRUCT/NAMED_STRUCT` encoding even when compatible metadata is otherwise enabled. -Use `@ForyStruct(evolution = Evolution.DISABLED)` for fixed-schema structs. The legacy boolean -form `@ForyStruct(evolving = false)` is still supported as a fixed-schema opt-out. +Use `@ForyStruct(evolution = Evolution.DISABLED)` for fixed-schema structs. The boolean shorthand +`@ForyStruct(evolving = false)` is also supported as a fixed-schema opt-out. If a class schema is stable and will not change, opt out of schema evolution on that class to avoid compatible metadata overhead: @@ -124,7 +127,7 @@ contexts must be created for each Fory instance. If you need a different classlo 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). +For more details, please refer to the [Meta Sharing specification](https://fory.apache.org/docs/specification/java_serialization_spec#meta-share). ## Deserialize Unknown Classes @@ -134,7 +137,7 @@ When enabled and metadata sharing is enabled, Fory will store the deserialized d If this data is sent to another process and the class exists in this process, the data will be deserialized into the object of this type without losing any information. -If metadata sharing is not enabled, the new class data will be skipped and a `UnknownSkipClass` stub object will be returned. +If metadata sharing is not enabled, the new class data is skipped and Fory returns an `UnknownEmptyStruct` marker object. ## Copy/Map Object from One Type to Another @@ -163,9 +166,9 @@ public class StructMappingExample { double f3; } - static ThreadSafeFory fory1 = Fory.builder() + static ThreadSafeFory fory1 = Fory.builder().withXlang(false) .withCompatible(true).buildThreadSafeFory(); - static ThreadSafeFory fory2 = Fory.builder() + static ThreadSafeFory fory2 = Fory.builder().withXlang(false) .withCompatible(true).buildThreadSafeFory(); static { @@ -207,7 +210,7 @@ public class DeserializeIntoType { double f3; } - static ThreadSafeFory fory = Fory.builder() + static ThreadSafeFory fory = Fory.builder().withXlang(false) .withCompatible(true).buildThreadSafeFory(); public static void main(String[] args) { @@ -220,14 +223,14 @@ public class DeserializeIntoType { ## Configuration -| Option | Description | Default | -| ------------------------- | -------------------------------------- | ------------------------- | -| `compatibleMode` | `SCHEMA_CONSISTENT` or `COMPATIBLE` | `SCHEMA_CONSISTENT` | -| `checkClassVersion` | Check class schema consistency | `false` | -| `metaShareEnabled` | Enable meta sharing | `true` if Compatible mode | -| `scopedMetaShareEnabled` | Scoped meta share per serialization | `true` if Compatible mode | -| `deserializeUnknownClass` | Handle non-existent or unknown classes | `true` if Compatible mode | -| `metaCompressor` | Compressor for meta compression | `DeflaterMetaCompressor` | +| Option | Description | Default | +| ------------------------- | -------------------------------------- | ------------------------------------------------ | +| `compatibleMode` | `SCHEMA_CONSISTENT` or `COMPATIBLE` | xlang: `COMPATIBLE`; native: `SCHEMA_CONSISTENT` | +| `checkClassVersion` | Check class schema consistency | `false` | +| `metaShareEnabled` | Enable meta sharing | `true` if Compatible mode | +| `scopedMetaShareEnabled` | Scoped meta share per serialization | `true` if Compatible mode | +| `deserializeUnknownClass` | Handle non-existent or unknown classes | `true` if Compatible mode | +| `metaCompressor` | Compressor for meta compression | `DeflaterMetaCompressor` | ## Best Practices @@ -239,5 +242,5 @@ public class DeserializeIntoType { ## Related Topics - [Configuration](configuration.md) - All ForyBuilder options -- [Cross-Language Serialization](cross-language.md) - XLANG mode +- [Cross-Language Serialization](cross-language.md) - xlang mode - [Troubleshooting](troubleshooting.md) - Common schema issues diff --git a/docs/guide/java/field-configuration.md b/docs/guide/java/schema-metadata.md similarity index 90% rename from docs/guide/java/field-configuration.md rename to docs/guide/java/schema-metadata.md index d625eeccad..49b5eccdd6 100644 --- a/docs/guide/java/field-configuration.md +++ b/docs/guide/java/schema-metadata.md @@ -1,7 +1,7 @@ --- -title: Field Configuration -sidebar_position: 5 -id: field_configuration +title: Schema Metadata +sidebar_position: 7 +id: schema_metadata license: | Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -551,6 +551,68 @@ public class Data { } ``` +## Enum Metadata + +Java enums are serialized by numeric tag in xlang mode. The default tag is the declaration ordinal. +When an enum needs stable ids that do not depend on declaration order, annotate exactly one id +source with `@ForyEnumId`, or annotate every enum constant with explicit tag values. + +```java +import org.apache.fory.annotation.ForyEnumId; + +enum Status { + Unknown(10), + Running(20), + Finished(30); + + private final int id; + + Status(int id) { + this.id = id; + } + + @ForyEnumId + public int getId() { + return id; + } +} +``` + +Java also supports annotating one enum instance field with `@ForyEnumId`, or annotating every enum +constant directly, such as `@ForyEnumId(10) Unknown`. + +`@ForyEnumId` supports exactly three configuration styles: + +1. Annotate one enum instance field and store the numeric id there. +2. Annotate one zero-argument public instance method such as `getId()`. +3. Annotate every enum constant directly with an explicit value such as `@ForyEnumId(10) Unknown`. + +Validation rules: + +1. Use exactly one of those three styles for a given enum. +2. Field and method annotations must leave `value()` at its default `-1`. +3. Enum-constant annotations must appear on every constant once any constant uses `@ForyEnumId`. +4. All ids must be non-negative, unique, and fit in Java `int`. + +Lookup behavior: + +1. Without `@ForyEnumId`, Fory writes the declaration ordinal. +2. With `@ForyEnumId`, Fory writes the configured stable numeric tag instead. +3. Small dense tags use an array lookup internally; sparse larger tags fall back to a map. + +Use `serializeEnumByName(true)` only for Java native-mode peers that should match enum constants by +name instead of numeric tag: + +```java +Fory fory = Fory.builder() + .withXlang(false) + .serializeEnumByName(true) + .build(); +``` + +This runtime option does not change xlang enum encoding; xlang uses numeric enum tags. Prefer +`@ForyEnumId` for cross-language payloads or any schema where numeric wire ids must stay stable. + ## Native Mode vs Xlang Mode Field configuration behaves differently depending on the serialization mode: @@ -651,6 +713,5 @@ public class User { - [Basic Serialization](basic-serialization.md) - Getting started with Fory serialization - [Configuration](configuration.md) - Runtime builder options -- [Enum Configuration](enum-configuration.md) - `@ForyEnumId` and enum name/tag behavior - [Schema Evolution](schema-evolution.md) - Compatible mode and schema evolution - [Cross-Language](cross-language.md) - Interoperability with Python, Rust, C++, Go diff --git a/docs/guide/java/static-generated-serializers.md b/docs/guide/java/static-generated-serializers.md index 0d84a635e0..93eb2a72e2 100644 --- a/docs/guide/java/static-generated-serializers.md +++ b/docs/guide/java/static-generated-serializers.md @@ -1,6 +1,6 @@ --- title: Static Generated Serializers -sidebar_position: 15 +sidebar_position: 8 id: static_generated_serializers license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -78,7 +78,7 @@ public class Order { The processor generates serializer classes in the same Java package as the annotated class. For `Order`, the generated classes are: -- `Order_ForySerializer` for cross-language mode. +- `Order_ForySerializer` for xlang mode. - `Order_ForyNativeSerializer` for Java native mode. For a static nested type such as `Outer.Inner`, the generated top-level classes are diff --git a/docs/guide/java/troubleshooting.md b/docs/guide/java/troubleshooting.md index ab97d98c54..435528f610 100644 --- a/docs/guide/java/troubleshooting.md +++ b/docs/guide/java/troubleshooting.md @@ -1,6 +1,6 @@ --- title: Troubleshooting -sidebar_position: 16 +sidebar_position: 17 id: troubleshooting license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -30,15 +30,13 @@ In such cases, you can invoke `ForyBuilder#withClassVersionCheck` to create Fory ```java // Enable class version check to diagnose issues Fory fory = Fory.builder() - .withXlang(false) .withClassVersionCheck(true) .build(); -// If ClassNotCompatibleException is thrown, use compatible mode -Fory fory = Fory.builder() - .withXlang(false) - .withCompatible(true) - .build(); +// If ClassNotCompatibleException is thrown, add compatible mode to the +// same builder configuration on every peer. +ForyBuilder builder = Fory.builder() + .withCompatible(true); ``` **Note**: compatible mode has more performance and space cost. For xlang mode it is the default and recommended setting. Use schema-consistent mode only if your classes are always consistent between serialization and deserialization, or if all services deploy schema changes at the same time. @@ -54,7 +52,7 @@ Use `serialize` with one of the `deserialize` overloads: **Wrong usage example:** ```java -// ❌ Wrong: deserialize with an incompatible target class +// Wrong: deserialize with an incompatible target class byte[] bytes = fory.serialize(struct1); Struct2 result = fory.deserialize(bytes, Struct2.class); // May throw ClassCastException ``` @@ -200,4 +198,4 @@ FORY_LOG_LEVEL=INFO mvn test -Dtest=org.apache.fory.TestClass#testMethod - [Configuration](configuration.md) - All ForyBuilder options - [Schema Evolution](schema-evolution.md) - Compatible mode details - [Type Registration](type-registration.md) - Registration best practices -- [Migration Guide](migration.md) - Upgrading Fory versions +- [Native Mode](native-mode.md) - Java-only serialization features diff --git a/docs/guide/java/type-registration.md b/docs/guide/java/type-registration.md index 66daf8aa1a..f05a7665db 100644 --- a/docs/guide/java/type-registration.md +++ b/docs/guide/java/type-registration.md @@ -1,6 +1,6 @@ --- -title: Type Registration & Security -sidebar_position: 3 +title: Type Registration +sidebar_position: 5 id: type_registration license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -65,7 +65,7 @@ If you invoke `ForyBuilder#requireClassRegistration(false)` to disable class reg For example, you can allow classes started with `org.example.*`: ```java -Fory fory = Fory.builder() +Fory fory = Fory.builder().withXlang(false) .requireClassRegistration(false) .withTypeChecker((typeResolver, className) -> className.startsWith("org.example.")) .build(); @@ -78,7 +78,7 @@ Fory provides a `org.apache.fory.resolver.AllowListChecker` which is an allowed/ ```java AllowListChecker checker = new AllowListChecker(AllowListChecker.CheckLevel.STRICT); checker.allowClass("org.example.*"); -ThreadSafeFory fory = Fory.builder() +ThreadSafeFory fory = Fory.builder().withXlang(false) .requireClassRegistration(false) .withTypeChecker(checker) .buildThreadSafeFory(); diff --git a/docs/guide/java/virtual-threads.md b/docs/guide/java/virtual-threads.md index 764cd52430..1dbadbf4cf 100644 --- a/docs/guide/java/virtual-threads.md +++ b/docs/guide/java/virtual-threads.md @@ -1,7 +1,7 @@ --- title: Virtual Threads -sidebar_position: 10 -id: java_virtual_threads +sidebar_position: 13 +id: virtual_threads license: | Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -33,7 +33,7 @@ When you use virtual threads, always use Fory's binary input/output APIs: Typical usage: ```java -ThreadSafeFory fory = Fory.builder() +ThreadSafeFory fory = Fory.builder().withXlang(false) .requireClassRegistration(false) .buildThreadSafeFory(); diff --git a/docs/guide/javascript/configuration.md b/docs/guide/javascript/configuration.md new file mode 100644 index 0000000000..aad60758dc --- /dev/null +++ b/docs/guide/javascript/configuration.md @@ -0,0 +1,104 @@ +--- +title: Configuration +sidebar_position: 2 +id: configuration +license: | + 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. +--- + +Fory JavaScript is an xlang-only runtime. `new Fory()` writes xlang payloads and uses compatible +schema evolution by default. There is no native-mode switch in the JavaScript API. + +## Basic Configuration + +```ts +import Fory from "@apache-fory/core"; + +const fory = new Fory(); +``` + +Create one `Fory` instance per application area and reuse it. Registration generates and caches +serializer code for each schema. + +## Constructor Options + +```ts +import Fory from "@apache-fory/core"; +import hps from "@apache-fory/hps"; + +const fory = new Fory({ + ref: true, + compatible: true, + maxDepth: 100, + maxBinarySize: 64 * 1024 * 1024, + maxCollectionSize: 1_000_000, + hps, +}); +``` + +| Option | Default | Description | +| -------------------------- | ----------- | ------------------------------------------------------------------------------------- | +| `ref` | `false` | Enable reference tracking for shared or circular object graphs | +| `compatible` | `true` | Allow field additions/removals without breaking existing messages | +| `maxDepth` | `50` | Maximum nesting depth. Must be `>= 2`. Increase for deeply nested structures | +| `maxBinarySize` | 64 MiB | Maximum bytes accepted for any single binary field | +| `maxCollectionSize` | `1_000_000` | Maximum elements accepted in any list, set, or map | +| `useSliceString` | `false` | Optional string-reading optimization for Node.js. Leave at default unless benchmarked | +| `hps` | unset | Optional fast string helper from `@apache-fory/hps` (Node.js 20+) | +| `hooks.afterCodeGenerated` | unset | Callback to inspect the generated serializer code, useful for debugging | + +## Reference Tracking + +Global reference tracking must be enabled before field-level reference metadata can take effect: + +```ts +const fory = new Fory({ ref: true }); +``` + +Then mark reference-tracked fields in the schema, for example with +`Type.struct("example.node").setTrackingRef(true)`. See [References](references.md) and +[Schema Metadata](schema-metadata.md). + +## Compatible Schema Evolution + +Compatible mode is the default: + +```ts +const fory = new Fory(); +``` + +Use this default for rolling upgrades, independently deployed services, and cross-language payloads. +You can opt out for one stable struct with `evolving: false`; see +[Schema Evolution](schema-evolution.md). + +## Optional HPS String Path + +`@apache-fory/hps` provides an optional Node.js string fast path: + +```ts +import hps from "@apache-fory/hps"; + +const fory = new Fory({ hps }); +``` + +Leave this unset unless you run on Node.js 20+ and have benchmarked your workload. + +## Related Topics + +- [Basic Serialization](basic-serialization.md) +- [Schema Metadata](schema-metadata.md) +- [Schema Evolution](schema-evolution.md) +- [References](references.md) diff --git a/docs/guide/javascript/cross-language.md b/docs/guide/javascript/cross-language.md index 0e1c700347..b93176d521 100644 --- a/docs/guide/javascript/cross-language.md +++ b/docs/guide/javascript/cross-language.md @@ -1,6 +1,6 @@ --- title: Cross-Language Serialization -sidebar_position: 80 +sidebar_position: 20 id: cross_language license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -23,8 +23,8 @@ Fory JavaScript serializes to the same binary format as the Java, Python, Go, Ru Things to keep in mind: -- The Fory JavaScript runtime reads and writes cross-language payloads only (it does not support any language-native format). -- Out-of-band mode is not currently supported. +- The Fory JavaScript runtime reads and writes cross-language payloads only (it does not support any native-mode format). +- JavaScript does not support out-of-band mode. ## Requirements for a Successful Round Trip @@ -33,14 +33,14 @@ For a message to survive a round trip between JavaScript and another runtime: 1. **Same type identity** on both sides — same numeric ID, or same `namespace + typeName`. 2. **Compatible field types** — a `Type.int32()` field in JavaScript matches Java `int`, Go `int32`, C# `int`. 3. **Same nullability** — if one side marks a field nullable, the other should too. -4. **Same `compatible` mode** if using schema evolution. +4. Compatible schema evolution on both sides. JavaScript enables it by default. 5. **Same reference tracking config** if your data has shared or circular references. ## Step-by-Step: JavaScript to Another Runtime 1. Define the JavaScript schema with the same type name or numeric ID used by the other runtime. 2. Register the schema in both runtimes. -3. Match field types, nullability, and `compatible` settings. +3. Match field types, nullability, and schema-evolution settings. 4. Test a real payload end-to-end before shipping. JavaScript side: @@ -76,7 +76,8 @@ On the other side, register the same `example.message` type (same name or same n Fory matches fields by name. When models are defined in multiple languages, keep field names consistent — or at minimum use a naming scheme that maps unambiguously across languages (e.g. `snake_case` everywhere). -When using `compatible: true` for schema evolution, field order differences are tolerated, but the names themselves must still match. +With the default compatible schema evolution, field order differences are tolerated, but the names +themselves must still match. ## Numeric Types diff --git a/docs/guide/javascript/index.md b/docs/guide/javascript/index.md index 170588baa4..ac6b02b2bb 100644 --- a/docs/guide/javascript/index.md +++ b/docs/guide/javascript/index.md @@ -98,37 +98,18 @@ Create one `Fory` instance per application and reuse it — creating a new one f ## Configuration -```ts -import Fory from "@apache-fory/core"; -import hps from "@apache-fory/hps"; - -const fory = new Fory({ - ref: true, - compatible: true, - maxDepth: 100, - maxBinarySize: 64 * 1024 * 1024, - maxCollectionSize: 1_000_000, - hps, -}); -``` - -| Option | Default | Description | -| -------------------------- | ----------- | ------------------------------------------------------------------------------------- | -| `ref` | `false` | Enable reference tracking for shared or circular object graphs | -| `compatible` | `false` | Allow field additions/removals without breaking existing messages | -| `maxDepth` | `50` | Maximum nesting depth. Must be `>= 2`. Increase for deeply nested structures | -| `maxBinarySize` | 64 MiB | Maximum bytes accepted for any single binary field | -| `maxCollectionSize` | `1_000_000` | Maximum elements accepted in any list, set, or map | -| `useSliceString` | `false` | Optional string-reading optimization for Node.js. Leave at default unless benchmarked | -| `hps` | unset | Optional fast string helper from `@apache-fory/hps` (Node.js 20+) | -| `hooks.afterCodeGenerated` | unset | Callback to inspect the generated serializer code — useful for debugging | +Fory JavaScript is xlang-only. `new Fory()` uses compatible schema evolution by default. Configure +reference tracking, size limits, and optional Node.js string acceleration through constructor +options; see [Configuration](configuration.md). ## Documentation | Topic | Description | | --------------------------------------------- | ------------------------------------------------------- | | [Basic Serialization](basic-serialization.md) | Core APIs and everyday usage | +| [Configuration](configuration.md) | Runtime options, compatible mode, limits, and HPS | | [Type Registration](type-registration.md) | Numeric IDs, names, decorators, and schema registration | +| [Schema Metadata](schema-metadata.md) | Type builders, field options, and decorators | | [Supported Types](supported-types.md) | Primitive, collection, time, enum, and struct mappings | | [References](references.md) | Shared references and circular object graphs | | [Schema Evolution](schema-evolution.md) | Compatible mode and evolving structs | diff --git a/docs/guide/javascript/schema-evolution.md b/docs/guide/javascript/schema-evolution.md index e46414a53e..13ab85b3a7 100644 --- a/docs/guide/javascript/schema-evolution.md +++ b/docs/guide/javascript/schema-evolution.md @@ -21,15 +21,15 @@ license: | Schema evolution lets different versions of your service exchange messages safely — a v2 writer can produce a message a v1 reader still understands, and vice versa. -## Two Modes +## Compatible And Schema-Consistent Evolution - **Compatible mode** (default): writes extra field metadata so readers can skip unknown fields and tolerate missing ones. Good for independent deployments, rolling upgrades, and xlang services. - **Schema-consistent mode**: more compact, but both sides must have exactly the same schema. Use it only when schemas do not change, or when all services update together. -## Enable Compatible Mode +## Default Compatible Mode ```ts -const fory = new Fory({ compatible: true }); +const fory = new Fory(); ``` Use this when: @@ -63,7 +63,7 @@ const readerType = Type.struct( ); ``` -With `compatible: true`, the reader ignores fields it does not know about, and fills unknown fields with default values. +With compatible mode, the reader ignores fields it does not know about, and fills unknown fields with default values. ## Opting Out of Evolution for One Struct @@ -84,10 +84,10 @@ const fixedType = Type.struct( | | Schema-consistent | Compatible | | ------------------------------- | ----------------- | ------------------- | -| Services always update together | ✔ best choice | works, but wasteful | -| Independent deployments | will break | ✔ best choice | -| Smallest possible messages | ✔ | slightly larger | -| Rolling upgrades | risky | ✔ safe | +| Services always update together | best choice | works, but wasteful | +| Independent deployments | will break | best choice | +| Smallest possible messages | best choice | slightly larger | +| Rolling upgrades | risky | safe | ## Cross-Language Requirement diff --git a/docs/guide/javascript/schema-metadata.md b/docs/guide/javascript/schema-metadata.md new file mode 100644 index 0000000000..73da7106af --- /dev/null +++ b/docs/guide/javascript/schema-metadata.md @@ -0,0 +1,163 @@ +--- +title: Schema Metadata +sidebar_position: 35 +id: schema_metadata +license: | + 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. +--- + +JavaScript schema metadata is declared with `Type.*` builders or TypeScript decorators. Metadata +defines type identity, field types, nullability, reference tracking, dynamic fields, and per-struct +schema evolution behavior. + +## Type Identity + +Structs and enums can use a numeric ID or a namespace/name pair. Pick one identity strategy for a +type and use it consistently in every runtime that reads or writes the payload. + +```ts +import { Type } from "@apache-fory/core"; + +const byId = Type.struct( + { typeId: 1001 }, + { + id: Type.int64(), + name: Type.string(), + }, +); + +const byName = Type.struct( + { namespace: "example", typeName: "user" }, + { + id: Type.int64(), + name: Type.string(), + }, +); +``` + +## Decorator Metadata + +Decorators keep the schema next to a TypeScript class declaration: + +```ts +@Type.struct({ typeName: "example.user" }) +class User { + @Type.int64() + id!: bigint; + + @Type.string() + name!: string; +} +``` + +The decorator metadata is equivalent to the builder metadata registered with `fory.register(...)`. + +## Field Types + +Use explicit scalar builders for stable contracts: + +```ts +Type.int8(); +Type.int16(); +Type.int32(); +Type.int64(); // JavaScript value is bigint +Type.uint32(); +Type.uint64(); // JavaScript value is bigint +Type.float16(); +Type.bfloat16(); +Type.float32(); +Type.float64(); +Type.string(); +Type.binary(); +``` + +Use collection builders for nested values: + +```ts +Type.list(Type.string()); +Type.map(Type.string(), Type.int32()); +Type.set(Type.string()); +Type.int32Array(); +Type.float64Array(); +``` + +## Nullability + +Fields are non-nullable unless the schema says otherwise: + +```ts +const userType = Type.struct("example.user", { + name: Type.string(), + email: Type.string().setNullable(true), +}); +``` + +Passing `null` to a non-nullable field throws. + +## Reference Tracking + +When the same object instance can appear in multiple fields, or when an object graph can be +circular, enable global reference tracking and mark reference-tracked fields: + +```ts +import Fory, { Type } from "@apache-fory/core"; + +const fory = new Fory({ ref: true }); + +const nodeType = Type.struct("example.node", { + next: Type.struct("example.node").setNullable(true).setTrackingRef(true), +}); +``` + +Field-level reference metadata has no effect unless `new Fory({ ref: true })` is also set. + +## Dynamic Fields + +Use `Type.any()` when a field can hold different Fory values at runtime: + +```ts +const eventType = Type.struct("example.event", { + kind: Type.string(), + payload: Type.any(), +}); +``` + +For a struct field with a declared type, `.setDynamic(Dynamic.FALSE)` always treats values as the +declared type and `.setDynamic(Dynamic.TRUE)` always writes the runtime type. The default +`Dynamic.AUTO` is appropriate for most fields. + +## Per-Struct Schema Evolution + +JavaScript uses compatible schema evolution by default. For a stable struct that should omit +evolution metadata, set `evolving: false`: + +```ts +const fixedType = Type.struct( + { typeId: 1002, evolving: false }, + { + name: Type.string(), + }, +); +``` + +Both writer and reader must agree on `evolving: false`. + +## Related Topics + +- [Configuration](configuration.md) +- [Type Registration](type-registration.md) +- [Supported Types](supported-types.md) +- [Schema Evolution](schema-evolution.md) diff --git a/docs/guide/javascript/troubleshooting.md b/docs/guide/javascript/troubleshooting.md index 624591adec..f39003be19 100644 --- a/docs/guide/javascript/troubleshooting.md +++ b/docs/guide/javascript/troubleshooting.md @@ -23,9 +23,10 @@ This page covers common problems when using Fory JavaScript. ## Cannot deserialize a non-cross-language payload -The Fory JavaScript runtime only reads Fory cross-language payloads. If the producer is a Java or Go service using a language-native format, the JavaScript side cannot decode it. +The Fory JavaScript runtime only reads Fory cross-language payloads. If the producer is a Java or Go service using a native-mode format, the JavaScript side cannot decode it. -Fix: switch the producer to the cross-language mode. For Java, use `.withXlang(true).withCompatible(true)`; for Go, use `WithXlang(true), WithCompatible(true)`. +Fix: switch the producer to xlang payloads. Java and Go use xlang by default; keep compatible mode +enabled unless every peer uses the same schema. ## `maxDepth must be an integer >= 2` diff --git a/docs/guide/javascript/type-registration.md b/docs/guide/javascript/type-registration.md index e922e8ff30..bb086fde0a 100644 --- a/docs/guide/javascript/type-registration.md +++ b/docs/guide/javascript/type-registration.md @@ -142,38 +142,10 @@ const order = deserialize(bytes); Store and reuse this pair — it is the fast path. -## Field Options +## Field Metadata -### Nullable fields - -If a field can be `null`, mark it explicitly. Passing `null` to a non-nullable field throws. - -```ts -Type.string().setNullable(true); -``` - -### Reference tracking on a field - -Needed when the same object instance can appear in multiple fields (see [References](references.md)): - -```ts -Type.struct("example.node").setTrackingRef(true); -``` - -This only has an effect when `new Fory({ ref: true })` is also set. - -### Polymorphic fields - -Use `Type.any()` when a field can hold different types at runtime: - -```ts -const eventType = Type.struct("example.event", { - kind: Type.string(), - payload: Type.any(), -}); -``` - -For fine-grained control over how a specific struct field handles its runtime type, you can call `.setDynamic(Dynamic.FALSE)` (always treat as the declared type) or `.setDynamic(Dynamic.TRUE)` (always write the runtime type). The default (`Dynamic.AUTO`) is correct for the vast majority of cases. +Field nullability, reference tracking, dynamic field behavior, numeric widths, and per-struct +schema-evolution metadata are covered in [Schema Metadata](schema-metadata.md). ## Choosing IDs vs Names @@ -196,5 +168,6 @@ For a message to round-trip between JavaScript and another runtime, both sides m ## Related Topics - [Basic Serialization](basic-serialization.md) +- [Schema Metadata](schema-metadata.md) - [Schema Evolution](schema-evolution.md) - [Cross-Language](cross-language.md) diff --git a/docs/guide/kotlin/android-support.md b/docs/guide/kotlin/android-support.md index ab808d8b9c..6ab7feb452 100644 --- a/docs/guide/kotlin/android-support.md +++ b/docs/guide/kotlin/android-support.md @@ -1,6 +1,6 @@ --- title: Android Support -sidebar_position: 5 +sidebar_position: 6 id: android_support license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -55,18 +55,18 @@ rules must be packaged with that library artifact. ## Runtime Setup -Create the runtime with `ForyKotlin.builder()`, then register application -classes through the Kotlin `register` extension or the normal Fory Java -registration APIs. +Create the runtime with `ForyKotlin.builder().withXlang(true)`, then register application classes +through the Kotlin `register` extension or the normal Fory Java registration +APIs. ```kotlin import org.apache.fory.kotlin.ForyKotlin import org.apache.fory.kotlin.register val fory = ForyKotlin.builder() - .withXlang(true) - .requireClassRegistration(true) - .build() + .withXlang(true) + .requireClassRegistration(true) + .build() fory.register("example", "User") ``` diff --git a/docs/guide/kotlin/fory-creation.md b/docs/guide/kotlin/configuration.md similarity index 72% rename from docs/guide/kotlin/fory-creation.md rename to docs/guide/kotlin/configuration.md index e663b60a2c..5defacc3c6 100644 --- a/docs/guide/kotlin/fory-creation.md +++ b/docs/guide/kotlin/configuration.md @@ -1,7 +1,7 @@ --- -title: Fory Creation +title: Configuration sidebar_position: 1 -id: fory_creation +id: configuration license: | Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -19,16 +19,32 @@ license: | limitations under the License. --- -This page covers Kotlin-specific requirements for creating Fory instances. +This page covers Kotlin-specific runtime configuration and Fory instance creation. -## Basic Setup +## Xlang Setup -When using Fory for Kotlin serialization, create the runtime with `ForyKotlin.builder()`: +Fory Kotlin follows the Java builder default: xlang mode with compatible schema +evolution. Use this path for cross-language Kotlin payloads, schema IDL +generated Kotlin models, and KSP-generated xlang serializers. ```kotlin import org.apache.fory.kotlin.ForyKotlin val fory = ForyKotlin.builder() + .withXlang(true) + .requireClassRegistration(true) + .build() +``` + +## Native Mode Setup + +For same-language Kotlin/JVM payloads that need native JVM object behavior, use +native mode explicitly: + +```kotlin +import org.apache.fory.kotlin.ForyKotlin + +val fory = ForyKotlin.builder().withXlang(false) .requireClassRegistration(true) .build() ``` @@ -45,6 +61,7 @@ import org.apache.fory.kotlin.ForyKotlin object ForyHolder { val fory: Fory = ForyKotlin.builder() + .withXlang(true) .requireClassRegistration(true) .build() } @@ -60,6 +77,7 @@ import org.apache.fory.kotlin.ForyKotlin object ForyHolder { val fory: ThreadSafeFory = ForyKotlin.builder() + .withXlang(true) .requireClassRegistration(true) .buildThreadSafeFory() } @@ -70,6 +88,7 @@ object ForyHolder { ```kotlin // Thread-safe Fory val fory: ThreadSafeFory = ForyKotlin.builder() + .withXlang(true) .requireClassRegistration(true) .buildThreadSafeFory() ``` @@ -78,15 +97,15 @@ val fory: ThreadSafeFory = ForyKotlin.builder() All configuration options from Fory Java are available. See [Java Configuration](../java/configuration.md) for the complete list. -Common options for Kotlin: +Common options for Kotlin native-mode payloads: ```kotlin import org.apache.fory.kotlin.ForyKotlin -val fory = ForyKotlin.builder() +val fory = ForyKotlin.builder().withXlang(false) // Enable reference tracking for circular references .withRefTracking(true) - // Enable schema evolution support + // Enable schema evolution support for native-mode payloads .withCompatible(true) // Enable async compilation for better startup performance .withAsyncCompilation(true) diff --git a/docs/guide/kotlin/default-values.md b/docs/guide/kotlin/default-values.md index ed61407b63..3f37893eea 100644 --- a/docs/guide/kotlin/default-values.md +++ b/docs/guide/kotlin/default-values.md @@ -1,6 +1,6 @@ --- title: Default Values -sidebar_position: 3 +sidebar_position: 4 id: default_values license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -19,7 +19,7 @@ license: | limitations under the License. --- -Fory supports Kotlin data class default values during deserialization when using compatible mode. This feature enables forward/backward compatibility when data class schemas evolve. +Fory supports Kotlin data class default values during native-mode deserialization when compatible mode is enabled. This feature enables forward/backward compatibility when data class schemas evolve. ## Overview @@ -31,10 +31,12 @@ When a Kotlin data class has parameters with default values, Fory can: ## Usage -This feature is automatically enabled when: +This feature is available when: -- Compatible mode is enabled (`withCompatible(true)`) -- The runtime is built with `ForyKotlin.builder()` or `Fory.builder().withModule(ForyKotlin)` +- Native mode is explicitly configured with compatible mode + (`withXlang(false).withCompatible(true)`) +- The runtime is built through `ForyKotlin.builder()` or `Fory.builder().withModule(ForyKotlin)` + with native mode enabled - A field is missing from the serialized data but exists in the target class with a default value ## Example @@ -49,7 +51,7 @@ data class User(val name: String, val age: Int) data class UserV2(val name: String, val age: Int, val email: String = "default@example.com") fun main() { - val fory = ForyKotlin.builder() + val fory = ForyKotlin.builder().withXlang(false) .withCompatible(true) .build() fory.register(User::class.java) @@ -90,7 +92,7 @@ data class ConfigV2( val retryCount: Int = 3 ) -val fory = ForyKotlin.builder() +val fory = ForyKotlin.builder().withXlang(false) .withCompatible(true) .build() @@ -130,4 +132,4 @@ val deserialized = fory.deserialize(serialized) as PersonV2 ## Related Topics - [Schema Evolution](../java/schema-evolution.md) - Forward/backward compatibility in Java -- [Fory Creation](fory-creation.md) - Setting up Fory with compatible mode +- [Configuration](configuration.md) - Setting up Fory with compatible mode diff --git a/docs/guide/kotlin/index.md b/docs/guide/kotlin/index.md index 46bee95a03..f7a822b51d 100644 --- a/docs/guide/kotlin/index.md +++ b/docs/guide/kotlin/index.md @@ -19,7 +19,7 @@ license: | limitations under the License. --- -Apache Fory™ Kotlin provides optimized serializers for Kotlin types, built on top of Fory Java. Most standard Kotlin types work out of the box with the default Fory Java implementation, while Fory Kotlin adds additional support for Kotlin-specific types. +Apache Fory™ Kotlin provides optimized serializers for Kotlin types, built on top of Fory Java. It supports xlang mode for cross-language payloads and native mode for Kotlin/JVM-only object serialization. Most standard Kotlin types work out of the box with the default Fory Java implementation, while Fory Kotlin adds additional support for Kotlin-specific types. Supported types include: @@ -72,8 +72,10 @@ data class Person(val name: String, val id: Long, val github: String) data class Point(val x: Int, val y: Int, val z: Int) fun main() { - // Create Fory instance (should be reused) + // Create Fory instance (should be reused). Kotlin follows the Java default: + // xlang mode with compatible schema evolution. val fory: ThreadSafeFory = ForyKotlin.builder() + .withXlang(true) .requireClassRegistration(true) .buildThreadSafeFory() @@ -86,6 +88,14 @@ fun main() { } ``` +## Xlang Mode And Native Mode + +Use xlang mode for cross-language payloads and schemas shared with other Fory runtimes. Xlang mode is the default Kotlin wire mode through the JVM builder, and Kotlin examples that use it set `.withXlang(true)` explicitly so the mode choice is visible. + +Use native mode for Kotlin/JVM-only traffic. Native mode is selected with `.withXlang(false)`, uses schema-consistent payloads unless compatible mode is enabled, and inherits the JVM native-mode object serialization path from Fory Java while adding Kotlin-specific serializers for data classes, unsigned values, ranges, stdlib types, and generated serializers. It is optimized for JVM and Kotlin type systems and is the right path for same-language Kotlin/JVM framework replacement payloads. + +See [Configuration](configuration.md) for Kotlin builder setup and [Java Native Mode](../java/native-mode.md) for the full JVM native-mode behavior. + ## Built on Fory Java Fory Kotlin is built on top of Fory Java. Most configuration options, features, and concepts from Fory Java apply directly to Kotlin. Refer to the Java documentation for: @@ -100,8 +110,9 @@ Fory Kotlin is built on top of Fory Java. Most configuration options, features, ## Kotlin-Specific Documentation -- [Fory Creation](fory-creation.md) - Kotlin-specific Fory setup requirements +- [Configuration](configuration.md) - Kotlin-specific Fory setup requirements - [Type Serialization](type-serialization.md) - Serializing Kotlin types +- [Schema Metadata](schema-metadata.md) - Kotlin annotations, nullability, references, and integer metadata - [Default Values](default-values.md) - Kotlin data class default values support - [Static Generated Serializers](static-generated-serializers.md) - KSP xlang/schema serializer generation - [Android Support](android-support.md) - Android setup, R8 behavior, and release-build validation diff --git a/docs/guide/kotlin/schema-metadata.md b/docs/guide/kotlin/schema-metadata.md new file mode 100644 index 0000000000..7b182ad01b --- /dev/null +++ b/docs/guide/kotlin/schema-metadata.md @@ -0,0 +1,124 @@ +--- +title: Schema Metadata +sidebar_position: 3 +id: schema_metadata +license: | + 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. +--- + +Kotlin schema metadata is used by the KSP-generated xlang serializers. Reuse the Java Fory +annotations for schema concepts, and use Kotlin type-use annotations only when you need Kotlin +specific integer encoding metadata. + +## Struct Fields + +Annotate Kotlin schema classes with `@ForyStruct` and constructor properties with +`@ForyField(id = N)`: + +```kotlin +import org.apache.fory.annotation.ForyField +import org.apache.fory.annotation.ForyStruct +import org.apache.fory.kotlin.Fixed +import org.apache.fory.kotlin.VarInt + +@ForyStruct +data class User( + @ForyField(id = 1) + val id: @Fixed UInt, + + @ForyField(id = 2) + val score: @VarInt Long, + + @ForyField(id = 3) + val tags: List, +) +``` + +Use `@ForyField(id = 1)` on constructor properties. `@field:ForyField(id = 1)` is also accepted +for field-backed properties. Do not use `@get:ForyField` or `@set:ForyField`; accessors are not +schema fields and the processor rejects them. + +## Nullability + +Use Kotlin `?` to describe nullable schema positions. Nullability is preserved inside collections +and maps: + +```kotlin +@ForyStruct +data class NullabilityExample( + @ForyField(id = 1) + val names: List, + + @ForyField(id = 2) + val optionalNames: List, + + @ForyField(id = 3) + val nullableList: List?, +) +``` + +Do not use Fory `@Nullable` in hand-written constructor-based Kotlin structs. The KSP processor +reads nullability from Kotlin source and rejects conflicting nullable annotations. + +## Reference Tracking + +Kotlin generated serializers preserve `@Ref` metadata for fields, list elements, and map values: + +```kotlin +import org.apache.fory.annotation.Ref + +@ForyStruct +data class Node( + @ForyField(id = 1) + val children: List<@Ref Node>, + + @ForyField(id = 2) + @Ref + val parent: Node?, +) +``` + +Global reference tracking still comes from the runtime configuration. See +[Configuration](configuration.md). + +## Integer Encoding + +Kotlin type-use encoding annotations map to Fory xlang integer encodings: + +| Annotation | Valid Kotlin types | +| ---------- | ------------------------------ | +| `@Fixed` | `Int`, `Long`, `UInt`, `ULong` | +| `@VarInt` | `Int`, `Long`, `UInt`, `ULong` | +| `@Tagged` | `Long`, `ULong` | + +Without an annotation, xlang `Int`, `Long`, `UInt`, and `ULong` use varint encoding. + +## Collections And Dense Arrays + +Collection declarations carry schema shape, not JVM implementation identity. `List` is +encoded as `list` and `Map` is encoded as `map`. + +Dense primitive and unsigned array fields are supported, including `BooleanArray`, `ByteArray`, +`IntArray`, `LongArray`, `FloatArray`, `DoubleArray`, `UByteArray`, `UShortArray`, `UIntArray`, and +`ULongArray`. `ByteArray` is encoded as Fory `binary` unless the type use is annotated with Java +`@ArrayType`. + +## Related Topics + +- [Static Generated Serializers](static-generated-serializers.md) +- [Configuration](configuration.md) +- [Default Values](default-values.md) +- [Android Support](android-support.md) diff --git a/docs/guide/kotlin/static-generated-serializers.md b/docs/guide/kotlin/static-generated-serializers.md index c98c1b3c16..f24e598e17 100644 --- a/docs/guide/kotlin/static-generated-serializers.md +++ b/docs/guide/kotlin/static-generated-serializers.md @@ -1,6 +1,6 @@ --- title: Static Generated Serializers -sidebar_position: 4 +sidebar_position: 5 id: static_generated_serializers license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -249,9 +249,9 @@ import org.apache.fory.kotlin.ForyKotlin import org.apache.fory.kotlin.register val fory = ForyKotlin.builder() - .withXlang(true) - .requireClassRegistration(true) - .build() + .withXlang(true) + .requireClassRegistration(true) + .build() fory.register("example", "User") ``` diff --git a/docs/guide/kotlin/type-serialization.md b/docs/guide/kotlin/type-serialization.md index c0bd051b42..1acb10db94 100644 --- a/docs/guide/kotlin/type-serialization.md +++ b/docs/guide/kotlin/type-serialization.md @@ -19,7 +19,9 @@ license: | limitations under the License. --- -This page covers serialization of Kotlin-specific types. +This page covers serialization of Kotlin-specific JVM types in native mode. For +cross-language Kotlin models, use the xlang path described in +[Static Generated Serializers](static-generated-serializers.md). ## Setup @@ -28,7 +30,7 @@ All examples assume the following setup: ```kotlin import org.apache.fory.kotlin.ForyKotlin -val fory = ForyKotlin.builder() +val fory = ForyKotlin.builder().withXlang(false) .requireClassRegistration(false) .build() ``` diff --git a/docs/guide/python/basic-serialization.md b/docs/guide/python/basic-serialization.md index 5350453779..aea4764adf 100644 --- a/docs/guide/python/basic-serialization.md +++ b/docs/guide/python/basic-serialization.md @@ -19,7 +19,8 @@ license: | limitations under the License. --- -This page covers basic serialization patterns in pyfory. +This page covers the Python xlang quickstart. `pyfory.Fory()` defaults to xlang mode with +compatible schema evolution; examples set `xlang=True` explicitly so the mode choice is visible. ## Basic Object Serialization @@ -28,10 +29,9 @@ Serialize and deserialize Python objects with a simple API: ```python import pyfory -# Create Fory instance -fory = pyfory.Fory(xlang=True, compatible=True) +fory = pyfory.Fory(xlang=True) -# Serialize any Python object +# Serialize xlang-compatible values data = fory.dumps({"name": "Alice", "age": 30, "scores": [95, 87, 92]}) # Deserialize back to Python object @@ -43,7 +43,7 @@ print(obj) # {'name': 'Alice', 'age': 30, 'scores': [95, 87, 92]} ## Custom Class Serialization -Fory automatically handles dataclasses and custom types: +Use dataclasses and type annotations for stable xlang payloads: ```python import pyfory @@ -53,13 +53,12 @@ from typing import List, Dict @dataclass class Person: name: str - age: int - scores: List[int] + age: pyfory.Int32 + scores: List[pyfory.Int32] metadata: Dict[str, str] -# Python mode - supports all Python types including dataclasses -fory = pyfory.Fory(xlang=False, ref=True) -fory.register(Person) +fory = pyfory.Fory(xlang=True, ref=True) +fory.register(Person, typename="example.Person") person = Person("Bob", 25, [88, 92, 85], {"team": "engineering"}) data = fory.serialize(person) result = fory.deserialize(data) @@ -68,56 +67,23 @@ print(result) # Person(name='Bob', age=25, ...) ## Reference Tracking & Circular References -Handle shared references and circular dependencies safely: +Handle repeated references safely when the payload uses xlang-compatible types: ```python import pyfory -f = pyfory.Fory(ref=True) # Enable reference tracking +f = pyfory.Fory(xlang=True, ref=True) -# Handle circular references safely -class Node: - def __init__(self, value): - self.value = value - self.children = [] - self.parent = None +shared = ["shared"] +value = [shared, shared] -root = Node("root") -child = Node("child") -child.parent = root # Circular reference -root.children.append(child) - -# Serializes without infinite recursion -data = f.serialize(root) +data = f.serialize(value) result = f.deserialize(data) -assert result.children[0].parent is result # Reference preserved +assert result[0] is result[1] ``` -## API Reference - -### Serialization Methods - -```python -# Serialize to bytes -data: bytes = fory.serialize(obj) -data: bytes = fory.dumps(obj) # Alias - -# Deserialize from bytes -obj = fory.deserialize(data) -obj = fory.loads(data) # Alias -``` - -### With Out-of-Band Buffers - -```python -# Serialize with buffer callback -buffer_objects = [] -data = fory.serialize(obj, buffer_callback=buffer_objects.append) - -# Deserialize with buffers -buffers = [obj.getbuffer() for obj in buffer_objects] -obj = fory.deserialize(data, buffers=buffers) -``` +For arbitrary Python object graphs, local classes, functions, and methods, use +[Python Native Mode](python-native.md). ## Performance Tips @@ -128,13 +94,13 @@ obj = fory.deserialize(data, buffers=buffers) ```python # Good: Reuse instance -fory = pyfory.Fory() +fory = pyfory.Fory(xlang=True) for obj in objects: data = fory.dumps(obj) # Bad: Create new instance each time for obj in objects: - fory = pyfory.Fory() # Wasteful! + fory = pyfory.Fory(xlang=True) # Wasteful! data = fory.dumps(obj) ``` @@ -143,3 +109,4 @@ for obj in objects: - [Configuration](configuration.md) - Fory parameters - [Type Registration](type-registration.md) - Registration patterns - [Python Native Mode](python-native.md) - Functions and lambdas +- [Out-of-Band Serialization](out-of-band.md) - Buffer callback APIs diff --git a/docs/guide/python/configuration.md b/docs/guide/python/configuration.md index b8ae54f8a5..7ed680ebb0 100644 --- a/docs/guide/python/configuration.md +++ b/docs/guide/python/configuration.md @@ -1,6 +1,6 @@ --- title: Configuration -sidebar_position: 2 +sidebar_position: 3 id: configuration license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -19,7 +19,9 @@ license: | limitations under the License. --- -This page covers Fory configuration parameters and language modes. +This page covers Python runtime configuration. `pyfory.Fory()` defaults to xlang mode with +compatible schema evolution. Native mode is selected explicitly with `xlang=False` and defaults to +schema-consistent payloads. ## Fory Class @@ -29,10 +31,10 @@ The main serialization interface: class Fory: def __init__( self, - xlang: bool = False, + xlang: bool = True, ref: bool = False, strict: bool = True, - compatible: bool = False, + compatible: Optional[bool] = None, max_depth: int = 50, policy: DeserializationPolicy = None, field_nullable: bool = False, @@ -53,17 +55,17 @@ class ThreadSafeFory: ## Parameters -| Parameter | Type | Default | Description | -| ----------------- | ------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `xlang` | `bool` | `False` | Enable cross-language serialization. When `False`, enables Python-native mode supporting all Python objects. When `True`, enables cross-language mode compatible with Java, Go, Rust, etc. | -| `ref` | `bool` | `False` | Enable reference tracking for shared/circular references. Disable for better performance if your data has no shared references. | -| `strict` | `bool` | `True` | Require type registration for security. **Highly recommended** for production. Only disable in trusted environments. | -| `compatible` | `bool` | `False` | Enable schema evolution in cross-language mode, allowing fields to be added/removed while maintaining compatibility. | -| `max_depth` | `int` | `50` | Maximum deserialization depth for security, preventing stack overflow attacks. | -| `policy` | `DeserializationPolicy \| None` | `None` | Deserialization policy used for security checks. Strongly recommended when `strict=False`. | -| `field_nullable` | `bool` | `False` | Treat dataclass fields as nullable by default (regardless of `xlang`). | -| `meta_compressor` | `Any` | `None` | Optional metadata compressor used for compatible-mode metadata encoding. | -| `fory_factory` | `Callable \| None` | `None` | `ThreadSafeFory` factory hook. When set, `ThreadSafeFory` creates instances via this callback; otherwise it forwards `**kwargs` to `Fory` construction. | +| Parameter | Type | Default | Description | +| ----------------- | ------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `xlang` | `bool` | `True` | Use xlang mode. Set `False` for Python native mode. | +| `ref` | `bool` | `False` | Enable reference tracking for shared/circular references. Disable for better performance if your data has no shared references. | +| `strict` | `bool` | `True` | Require type registration for security. Keep this enabled for production unless a policy owns trust decisions. | +| `compatible` | `bool \| None` | `None` | Schema evolution mode. `None` follows the wire mode: xlang defaults to compatible mode, while native mode defaults to schema-consistent mode. | +| `max_depth` | `int` | `50` | Maximum deserialization depth for security, preventing stack overflow attacks. | +| `policy` | `DeserializationPolicy \| None` | `None` | Deserialization policy used for security checks. Strongly recommended when `strict=False`. | +| `field_nullable` | `bool` | `False` | Treat dataclass fields as nullable by default. | +| `meta_compressor` | `Any` | `None` | Optional metadata compressor used for compatible-mode metadata encoding. | +| `fory_factory` | `Callable \| None` | `None` | `ThreadSafeFory` factory hook. When set, `ThreadSafeFory` creates instances via this callback; otherwise it forwards `**kwargs` to `Fory` construction. | ## Key Methods @@ -76,108 +78,89 @@ obj = fory.deserialize(data) data: bytes = fory.dumps(obj) obj = fory.loads(data) -# Type registration by id (for Python mode) +# Type registration by id fory.register(MyClass, type_id=123) fory.register(MyClass, type_id=123, serializer=custom_serializer) -# Type registration by name (for cross-language mode) +# Type registration by name fory.register(MyClass, typename="my.package.MyClass") fory.register(MyClass, typename="my.package.MyClass", serializer=custom_serializer) ``` -## Language Modes Comparison +## Xlang And Native Mode Comparison -| Feature | Python Mode (`xlang=False`) | Cross-Language Mode (`xlang=True, compatible=True`) | -| --------------------- | ------------------------------------ | --------------------------------------------------- | -| **Use Case** | Pure Python applications | Multi-language systems | -| **Compatibility** | Python only | Java, Go, Rust, C++, JavaScript, etc. | -| **Supported Types** | All Python types | Cross-language compatible types only | -| **Functions/Lambdas** | ✓ Supported | ✗ Not allowed | -| **Local Classes** | ✓ Supported | ✗ Not allowed | -| **Dynamic Classes** | ✓ Supported | ✗ Not allowed | -| **Schema Evolution** | ✓ Supported (with `compatible=True`) | ✓ Supported (with `compatible=True`) | -| **Performance** | Extremely fast | Very fast | -| **Data Size** | Compact | Compact with type metadata | +| Feature | Native mode (`xlang=False`) | Xlang mode (default) | +| ------------------- | ---------------------------------------------- | ------------------------------------- | +| Use case | Python-only applications | Multi-language systems | +| Compatibility | Python only | Java, Go, Rust, C++, JavaScript, etc. | +| Supported types | Python object surface | Cross-language compatible types | +| Functions/lambdas | Supported with trusted dynamic deserialization | Not allowed | +| Local classes | Supported with trusted dynamic deserialization | Not allowed | +| Dynamic classes | Supported with trusted dynamic deserialization | Not allowed | +| Schema mode default | Schema-consistent | Compatible | -## Python Mode (`xlang=False`) +## Xlang Mode -Python mode supports all Python types including functions, classes, and closures: +Xlang mode is the default and restricts payloads to types compatible across Fory runtimes: ```python import pyfory -# Full Python compatibility mode -fory = pyfory.Fory(xlang=False, ref=True, strict=False) - -# Supports ALL Python objects: -data = fory.dumps({ - 'function': lambda x: x * 2, # Functions and lambdas - 'class': type('Dynamic', (), {}), # Dynamic classes - 'method': str.upper, # Methods - 'nested': {'circular_ref': None} # Circular references (when ref=True) -}) - -# Drop-in replacement for pickle/cloudpickle -import pickle -obj = [1, 2, {"nested": [3, 4]}] -assert fory.loads(fory.dumps(obj)) == pickle.loads(pickle.dumps(obj)) +fory = pyfory.Fory(xlang=True, ref=True) +fory.register(MyDataClass, typename="com.example.MyDataClass") +data = fory.serialize(MyDataClass(field1="value", field2=42)) ``` -## Cross-Language Mode (`xlang=True, compatible=True`) +Use `compatible=False` only when every xlang peer updates schema together and you want +schema-consistent xlang payloads. -Cross-language mode restricts types to those compatible across all Fory implementations: +## Native Mode ```python import pyfory -# Cross-language compatibility mode -f = pyfory.Fory(xlang=True, compatible=True, ref=True) - -# Only supports cross-language compatible types -f.register(MyDataClass, typename="com.example.MyDataClass") - -# Data can be read by Java, Go, Rust, etc. -data = f.serialize(MyDataClass(field1="value", field2=42)) +fory = pyfory.Fory(xlang=False, ref=True, strict=False) ``` +Native mode supports Python-specific object features such as functions, local classes, methods, +`__reduce__`, and `__getstate__`. It defaults to schema-consistent mode. Set +`compatible=True` only when Python-only deployments need schema evolution. + ## Example Configurations -### Production Configuration +### Xlang Service ```python import pyfory -# Recommended settings for production fory = pyfory.Fory( - xlang=False, # Use True if you need cross-language support - ref=False, # Enable if you have shared/circular references - strict=True, # CRITICAL: Always True in production - compatible=False, # Native mode; xlang=True defaults to compatible=True - max_depth=20 # Adjust based on your data structure depth + xlang=True, + ref=False, + strict=True, + max_depth=20, ) -# Register all types upfront -fory.register(UserModel, type_id=100) -fory.register(OrderModel, type_id=101) -fory.register(ProductModel, type_id=102) +fory.register(UserModel, typename="example.User") ``` -### Development Configuration +### Native Mode With Dynamic Types ```python import pyfory -# Development settings (more permissive) fory = pyfory.Fory( xlang=False, ref=True, - strict=False, # Allow any type for development - max_depth=1000 # Higher limit for development + strict=False, + max_depth=1000, ) ``` +Use `strict=False` only for trusted data, preferably with a `policy=` deserialization policy. + ## Related Topics - [Basic Serialization](basic-serialization.md) - Using configured Fory - [Type Registration](type-registration.md) - Registration patterns +- [Python Native Mode](python-native.md) - Python-only object serialization - [Security](security.md) - Security best practices diff --git a/docs/guide/python/cross-language.md b/docs/guide/python/cross-language.md index ab8f5cab61..b1ed319ab7 100644 --- a/docs/guide/python/cross-language.md +++ b/docs/guide/python/cross-language.md @@ -1,6 +1,6 @@ --- title: Cross-Language Serialization -sidebar_position: 10 +sidebar_position: 4 id: cross_language license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -21,13 +21,13 @@ license: | `pyfory` supports cross-language object graph serialization, allowing you to serialize data in Python and deserialize it in Java, Go, Rust, or other supported languages. -## Enable Cross-Language Mode +## Create an Xlang Runtime -To use xlang mode, create `Fory` with `xlang=True, compatible=True`: +Python defaults to xlang mode with compatible schema evolution. Set the mode explicitly in xlang examples: ```python import pyfory -fory = pyfory.Fory(xlang=True, compatible=True, ref=False, strict=True) +fory = pyfory.Fory(xlang=True, ref=False, strict=True) ``` ## Cross-Language Example @@ -38,8 +38,7 @@ fory = pyfory.Fory(xlang=True, compatible=True, ref=False, strict=True) import pyfory from dataclasses import dataclass -# Cross-language mode for interoperability -f = pyfory.Fory(xlang=True, compatible=True, ref=True) +f = pyfory.Fory(xlang=True, ref=True) # Register type for cross-language compatibility @dataclass @@ -65,7 +64,7 @@ public class Person { } Fory fory = Fory.builder() - .withXlang(true).withCompatible(true) + .withXlang(true) .withRefTracking(true) .build(); @@ -85,9 +84,7 @@ struct Person { age: i32, } -let mut fory = Fory::builder() - .compatible(true) - .xlang(true).build(); +let mut fory = Fory::builder().xlang(true).build(); fory.register_by_name::("example", "Person"); let person: Person = fory.deserialize(&binary_data)?; @@ -195,7 +192,7 @@ Fory row-format schemas. ## Differences from Python Native Mode -The binary protocol and API are similar to `pyfory`'s python-native mode, but Python-native mode can serialize any Python object—including global functions, local functions, lambdas, local classes, and types with customized serialization using `__getstate__/__reduce__/__reduce_ex__`, which are **not allowed** in xlang mode. +The binary protocol and API are similar to `pyfory`'s Python native mode, but Python native mode can serialize any Python object—including global functions, local functions, lambdas, local classes, and types with customized serialization using `__getstate__/__reduce__/__reduce_ex__`, which are **not allowed** in xlang mode. ## See Also @@ -206,6 +203,6 @@ The binary protocol and API are similar to `pyfory`'s python-native mode, but Py ## Related Topics -- [Configuration](configuration.md) - XLANG mode settings +- [Configuration](configuration.md) - xlang mode settings - [Schema Evolution](schema-evolution.md) - Compatible mode - [Type Registration](type-registration.md) - Registration patterns diff --git a/docs/guide/python/custom-serializers.md b/docs/guide/python/custom-serializers.md index e5ea516b12..5edc35a22a 100644 --- a/docs/guide/python/custom-serializers.md +++ b/docs/guide/python/custom-serializers.md @@ -1,6 +1,6 @@ --- title: Custom Serializers -sidebar_position: 4 +sidebar_position: 12 id: custom_serializers license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -23,7 +23,7 @@ Implement custom serialization logic for specialized types. ## Implementing Custom Serializers -Implement `write/read` once for both Python and cross-language modes: +Implement `write/read` once for both Python native and xlang modes: ```python import pyfory @@ -50,7 +50,7 @@ class FooSerializer(Serializer): f2 = read_context.read_string() return Foo(f1, f2) -f = pyfory.Fory() +f = pyfory.Fory(xlang=False) f.register(Foo, type_id=100, serializer=FooSerializer(f.type_resolver, Foo)) # Now Foo uses your custom serializer @@ -115,14 +115,14 @@ value = buffer.read_bool() - External types from other packages - Types with special serialization requirements -- Legacy data format compatibility +- Existing data format compatibility - Performance-critical custom encoding - Types that don't work well with automatic serialization ## Registering Custom Serializers ```python -fory = pyfory.Fory() +fory = pyfory.Fory(xlang=False) # Register with type_id fory.register(MyClass, type_id=100, serializer=MySerializer(fory.type_resolver, MyClass)) diff --git a/docs/guide/python/index.md b/docs/guide/python/index.md index d0580943ed..7a7323b4ea 100644 --- a/docs/guide/python/index.md +++ b/docs/guide/python/index.md @@ -21,21 +21,21 @@ license: | **Apache Fory™** is a blazing fast multi-language serialization framework powered by **JIT compilation** and **zero-copy** techniques, providing up to **ultra-fast performance** while maintaining ease of use and safety. -`pyfory` provides the Python implementation of Apache Fory™, offering both high-performance object serialization and advanced row-format capabilities for data processing tasks. +`pyfory` provides the Python implementation of Apache Fory™, offering xlang mode for cross-language payloads, native mode for Python-only object serialization, and advanced row-format capabilities for data processing tasks. ## Key Features ### Flexible Serialization Modes -- **Python native Mode**: Full Python compatibility, drop-in replacement for pickle/cloudpickle -- **Cross-Language Mode**: Optimized for multi-language data exchange +- **Xlang mode**: Default cross-language wire format with compatible schema evolution +- **Python native mode**: Same-language mode and drop-in replacement for pickle/cloudpickle - **Row Format**: Zero-copy row format for analytics workloads ### Versatile Serialization Features -- **Shared/circular reference support** for complex object graphs in both Python-native and cross-language modes +- **Reference tracking** for shared xlang schema objects and Python native-mode circular graphs - **Polymorphism support** for customized types with automatic type dispatching -- **Schema evolution** support for backward/forward compatibility when using dataclasses in cross-language mode +- **Schema evolution** support for backward/forward compatibility when using dataclasses in xlang mode - **Out-of-band buffer support** for zero-copy serialization of large data structures like NumPy arrays and Pandas DataFrames, compatible with pickle protocol 5 ### Blazing Fast Performance @@ -92,8 +92,8 @@ class Person: name: str age: int -# Create thread-safe Fory instance -fory = pyfory.ThreadSafeFory(xlang=False, ref=True) +# Create a thread-safe xlang Fory instance +fory = pyfory.ThreadSafeFory(xlang=True, ref=True) fory.register(Person) # Use in multiple threads safely @@ -132,8 +132,8 @@ class Person: name: str age: int -# Create Fory instance -fory = pyfory.Fory(xlang=False, ref=True) +# Create an xlang Fory instance +fory = pyfory.Fory(xlang=True, ref=True) fory.register(Person) person = Person("Alice", 30) @@ -142,18 +142,26 @@ result = fory.deserialize(data) print(result) # Person(name='Alice', age=30) ``` +## Xlang Mode And Native Mode + +Use xlang mode for cross-language payloads and dataclass schemas shared with other Fory runtimes. Xlang mode is the default Python wire mode, and Python examples that use it set `xlang=True` explicitly so the mode choice is visible. + +Use native mode for Python-only traffic. Native mode is selected with `xlang=False`, uses schema-consistent payloads unless compatible mode is enabled, and owns pickle/cloudpickle-style behavior such as functions, lambdas, classes, methods, `__reduce__`, `__getstate__`, and out-of-band pickle protocol 5 buffers. It is optimized for Python's type system and supports a broader Python object surface than xlang mode, so use it when replacing pickle or cloudpickle. + +See [Python Native Mode](python-native.md) for Python-only serialization details and [Cross-Language](cross-language.md) for Python xlang registration and interoperability rules. + ## Next Steps - [Configuration](configuration.md) - Fory parameters and modes - [Basic Serialization](basic-serialization.md) - Basic usage patterns - [Python Native Mode](python-native.md) - Functions, lambdas, classes -- [Cross-Language](cross-language.md) - XLANG mode +- [Cross-Language](cross-language.md) - xlang mode - [Row Format](row-format.md) - Zero-copy row format - [Security](security.md) - Security best practices ## Links -- **Documentation**: https://fory.apache.org/docs/latest/python_guide/ +- **Documentation**: https://fory.apache.org/docs/guide/python/ - **GitHub**: https://github.com/apache/fory - **PyPI**: https://pypi.org/project/pyfory/ - **Issues**: https://github.com/apache/fory/issues diff --git a/docs/guide/python/migration.md b/docs/guide/python/migration.md deleted file mode 100644 index 56fcfd77f2..0000000000 --- a/docs/guide/python/migration.md +++ /dev/null @@ -1,85 +0,0 @@ ---- -title: Migration Guide -sidebar_position: 12 -id: migration -license: | - 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. ---- - -This page covers migration from pickle and JSON to pyfory. - -## From Pickle - -Replace pickle with Fory for better performance while keeping the same API: - -```python -# Before (pickle) -import pickle -data = pickle.dumps(obj) -result = pickle.loads(data) - -# After (Fory - drop-in replacement with better performance) -import pyfory -f = pyfory.Fory(xlang=False, ref=True, strict=False) -data = f.dumps(obj) # Faster and more compact -result = f.loads(data) # Faster deserialization - -# Benefits: -# - 2-10x faster serialization -# - 2-5x faster deserialization -# - Up to 3x smaller data size -# - Same API, better performance -``` - -## From JSON - -Unlike JSON, Fory supports arbitrary Python types including functions: - -```python -# Before (JSON - limited types) -import json -data = json.dumps({"name": "Alice", "age": 30}) -result = json.loads(data) - -# After (Fory - all Python types) -import pyfory -f = pyfory.Fory() -data = f.dumps({"name": "Alice", "age": 30, "func": lambda x: x}) -result = f.loads(data) -``` - -## Key Differences - -| Feature | pickle | JSON | pyfory | -| -------------- | ---------- | ------- | ---------------- | -| Performance | Moderate | Slow | Fast | -| Data Size | Large | Large | Compact | -| Type Support | All Python | Limited | All Python | -| Cross-Language | No | Yes | Yes (xlang mode) | -| Security | Low | High | Configurable | - -## Migration Tips - -1. **Start with strict=False** to ensure compatibility -2. **Add type registration** for performance and security -3. **Test thoroughly** before deploying -4. **Monitor performance** improvements - -## Related Topics - -- [Configuration](configuration.md) - Fory parameters -- [Python Native Mode](python-native.md) - Pickle replacement features -- [Security](security.md) - Security best practices diff --git a/docs/guide/python/numpy-integration.md b/docs/guide/python/numpy-integration.md index ea20fd893d..ba7f5e1ede 100644 --- a/docs/guide/python/numpy-integration.md +++ b/docs/guide/python/numpy-integration.md @@ -1,6 +1,6 @@ --- title: NumPy & Pandas -sidebar_position: 8 +sidebar_position: 10 id: numpy_integration license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -29,7 +29,7 @@ Large arrays use zero-copy when possible: import pyfory import numpy as np -f = pyfory.Fory() +f = pyfory.Fory(xlang=False) # Numpy arrays are supported natively arrays = { diff --git a/docs/guide/python/out-of-band.md b/docs/guide/python/out-of-band.md index 791ad1aaff..3feea3425f 100644 --- a/docs/guide/python/out-of-band.md +++ b/docs/guide/python/out-of-band.md @@ -1,6 +1,6 @@ --- title: Out-of-Band Serialization -sidebar_position: 7 +sidebar_position: 9 id: out_of_band license: | Licensed to the Apache Software Foundation (ASF) under one or more diff --git a/docs/guide/python/python-native.md b/docs/guide/python/python-native.md index 90e36ccfe4..51046e695e 100644 --- a/docs/guide/python/python-native.md +++ b/docs/guide/python/python-native.md @@ -1,6 +1,6 @@ --- title: Python Native Mode -sidebar_position: 5 +sidebar_position: 2 id: native_mode license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -19,13 +19,16 @@ license: | limitations under the License. --- -`pyfory` provides a Python-native serialization mode that offers the same functionality as pickle/cloudpickle, but with **significantly better performance, smaller data size, and enhanced security features**. +`pyfory` provides a Python native mode for same-language Python traffic. It covers the pickle and +cloudpickle-style object surface while keeping xlang mode, the default, focused on cross-language +payloads. Native mode is optimized for Python's type system and is the mode to use when replacing +pickle or cloudpickle for Python-only payloads. ## Overview -The binary protocol and API are similar to Fory's xlang mode, but Python-native mode can serialize any Python object—including global functions, local functions, lambdas, local classes and types with customized serialization using `__getstate__/__reduce__/__reduce_ex__`, which are not allowed in xlang mode. +The binary protocol and API are similar to Fory's xlang mode, but Python native mode can serialize any Python object—including global functions, local functions, lambdas, local classes and types with customized serialization using `__getstate__/__reduce__/__reduce_ex__`, which are not allowed in xlang mode. -To use Python-native mode, create `Fory` with `xlang=False`: +To use Python native mode, create `Fory` with `xlang=False`: ```python import pyfory @@ -34,12 +37,15 @@ fory = pyfory.Fory(xlang=False, ref=False, strict=True) ## Drop-in Replacement for Pickle/Cloudpickle -`pyfory` can serialize any Python object with the following configuration: +`pyfory` can serialize Python objects that are outside the xlang type system with the following +configuration. Use this mode when the payload stays in Python and you want Fory as the +pickle/cloudpickle replacement: - **For circular references**: Set `ref=True` to enable reference tracking - **For functions/classes**: Set `strict=False` to allow deserialization of dynamic types -**⚠️ Security Warning**: When `strict=False`, Fory will deserialize arbitrary types, which can pose security risks if data comes from untrusted sources. Only use `strict=False` in controlled environments where you trust the data source completely. +Security warning: when `strict=False`, Fory can deserialize arbitrary Python types. Use this only +with trusted data, and provide a `policy=` deserialization policy when dynamic types are required. ### Common Usage @@ -66,6 +72,11 @@ data = fory.dumps(person) print(fory.loads(data)) # Person(name='Bob', age=25) ``` +Native mode can replace pickle and cloudpickle for Python-only object graphs and can serialize +richer values than JSON, including functions, local classes, methods, and objects that implement +`__reduce__`, `__reduce_ex__`, `__getstate__`, or `__setstate__`. These Python-specific features +are not valid xlang payloads. + ## Serialize Global Functions Capture and serialize functions defined at module level. Fory deserialize and return same function object: @@ -200,6 +211,6 @@ print(f"Pickle: {timeit.timeit(lambda: pickle.dumps(obj), number=1000):.3f}s") ## Related Topics -- [Configuration](configuration.md) - Python mode configuration +- [Configuration](configuration.md) - Python native mode configuration - [Out-of-Band Serialization](out-of-band.md) - Zero-copy buffers - [Security](security.md) - DeserializationPolicy diff --git a/docs/guide/python/row-format.md b/docs/guide/python/row-format.md index a6784b350e..008551e698 100644 --- a/docs/guide/python/row-format.md +++ b/docs/guide/python/row-format.md @@ -156,16 +156,16 @@ struct Foo { FORY_STRUCT(Foo, f1, f2, f3, f4); }; -fory::encoder::RowEncoder encoder; -encoder.Encode(foo); -auto row = encoder.GetWriter().ToRow(); +fory::row::encoder::RowEncoder encoder; +encoder.encode(foo); +auto row = encoder.get_writer().to_row(); // Zero-copy random access without full deserialization -auto f2_array = row->GetArray(1); // Access f2 list -auto f4_array = row->GetArray(3); // Access f4 list -auto bar10 = f4_array->GetStruct(10); // Access 11th Bar -int64_t value = bar10->GetArray(1)->GetInt64(5); // Access 6th element of bar.f2 -std::string str = bar10->GetString(0); // Access bar.f1 +auto f2_array = row->get_array(1); // Access f2 list +auto f4_array = row->get_array(3); // Access f4 list +auto bar10 = f4_array->get_struct(10); // Access 11th Bar +int64_t value = bar10->get_array(1)->get_int64(5); // Access 6th element of bar.f2 +std::string str = bar10->get_string(0); // Access bar.f1 ``` ## Installation @@ -186,6 +186,6 @@ pip install pyfory[format] ## Related Topics -- [Cross-Language Serialization](cross-language.md) - XLANG mode +- [Cross-Language Serialization](cross-language.md) - xlang mode - [Basic Serialization](basic-serialization.md) - Object serialization - [Row Format Specification](https://fory.apache.org/docs/specification/row_format_spec) - Protocol details diff --git a/docs/guide/python/schema-evolution.md b/docs/guide/python/schema-evolution.md index bfd0a7482a..199d636fb0 100644 --- a/docs/guide/python/schema-evolution.md +++ b/docs/guide/python/schema-evolution.md @@ -1,6 +1,6 @@ --- title: Schema Evolution -sidebar_position: 6 +sidebar_position: 8 id: schema_evolution license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -19,14 +19,16 @@ license: | limitations under the License. --- -Apache Fory™ supports schema evolution in Compatible mode, allowing fields to be added/removed while maintaining compatibility. +Apache Fory™ supports schema evolution in compatible mode, allowing fields to be added or removed +while maintaining compatibility. Xlang mode enables compatible mode by default. In native mode, +set `compatible=True` explicitly when Python-only payloads need schema evolution. -## Enable Compatible Mode +## Xlang Default ```python import pyfory -f = pyfory.Fory(xlang=True, compatible=True) +f = pyfory.Fory(xlang=True) ``` ## Disable Evolution for Stable Classes @@ -60,9 +62,9 @@ from dataclasses import dataclass @dataclass class User: name: str - age: int + age: pyfory.Int32 -f = pyfory.Fory(xlang=True, compatible=True) +f = pyfory.Fory(xlang=True) f.register(User, typename="User") data = f.dumps(User("Alice", 30)) @@ -70,7 +72,7 @@ data = f.dumps(User("Alice", 30)) @dataclass class User: name: str - age: int + age: pyfory.Int32 email: str = "unknown@example.com" # New field with default # Can still deserialize old data diff --git a/docs/guide/python/field-configuration.md b/docs/guide/python/schema-metadata.md similarity index 97% rename from docs/guide/python/field-configuration.md rename to docs/guide/python/schema-metadata.md index 66b7f52ad8..4583a9d7cc 100644 --- a/docs/guide/python/field-configuration.md +++ b/docs/guide/python/schema-metadata.md @@ -1,7 +1,7 @@ --- -title: Field Configuration -sidebar_position: 5 -id: field_configuration +title: Schema Metadata +sidebar_position: 7 +id: schema_metadata license: | Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -138,7 +138,8 @@ class Record: ## Reference Tracking (`ref`) -Enable reference tracking for fields that may be shared or circular: +Enable reference tracking for fields that may be shared. Circular Python object +graphs require Python native mode with global reference tracking enabled. ```python @dataclass @@ -162,8 +163,9 @@ class CircularRef: **Notes**: -- Reference tracking only takes effect when `Fory(ref=True)` is set globally -- Field-level `ref=True` AND global `ref=True` must both be enabled +- Global `Fory(ref=True)` must be enabled. +- Field-level `ref=True` and global `ref=True` must both be enabled for schema + fields. ## Skipping Fields (`ignore`) @@ -357,7 +359,7 @@ class Document: def main(): - fory = pyfory.Fory(xlang=True, compatible=True, ref=True) + fory = pyfory.Fory(xlang=True, ref=True) fory.register_type(Document, type_id=100) doc = Document( @@ -448,7 +450,7 @@ In native mode, you typically **don't need to configure field annotations** unle - Optimize performance by disabling unnecessary ref tracking ```python -# Native mode: works without field configuration +# Native mode: works without schema metadata @dataclass class User: id: int = 0 diff --git a/docs/guide/python/security.md b/docs/guide/python/security.md index 5338d7c2de..1bcd441d70 100644 --- a/docs/guide/python/security.md +++ b/docs/guide/python/security.md @@ -1,6 +1,6 @@ --- title: Security Best Practices -sidebar_position: 9 +sidebar_position: 6 id: security license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -30,7 +30,6 @@ import pyfory # Recommended production settings f = pyfory.Fory( - xlang=False, # or True for cross-language ref=True, # Handle circular references strict=True, # IMPORTANT: Prevent malicious data max_depth=100 # Prevent deep recursion attacks @@ -61,7 +60,6 @@ if os.getenv('ENV') == 'development': else: # Production configuration (security hardened) fory = pyfory.Fory( - xlang=False, ref=True, strict=True, # CRITICAL: Require registration max_depth=100 # Reasonable limit diff --git a/docs/guide/python/troubleshooting.md b/docs/guide/python/troubleshooting.md index 168927fe8a..5ec222371a 100644 --- a/docs/guide/python/troubleshooting.md +++ b/docs/guide/python/troubleshooting.md @@ -48,15 +48,24 @@ print(pyfory.ENABLE_FORY_CYTHON_SERIALIZATION) # Should be True ```python # Use explicit type registration with consistent naming -f = pyfory.Fory(xlang=True, compatible=True) +f = pyfory.Fory(xlang=True) f.register(MyClass, typename="com.package.MyClass") # Use same name in all languages ``` ### Circular Reference Errors or Duplicate Data +Registered xlang schema objects and Python native objects both require reference tracking when +object identity or cycles matter: + +```python +# Enable reference tracking for registered schema objects. +f = pyfory.Fory(ref=True) +``` + +For arbitrary Python object graphs with circular references, use Python native mode: + ```python -# Enable reference tracking -f = pyfory.Fory(ref=True) # Required for circular references +f = pyfory.Fory(xlang=False, ref=True, strict=False) # Example with circular reference class Node: @@ -77,14 +86,14 @@ assert result.next.next is result # Circular reference preserved ### Schema Evolution Not Working ```python -# Enable compatible mode for schema evolution -f = pyfory.Fory(xlang=True, compatible=True) +# Keep your existing wire mode and enable compatible schema evolution. +f = pyfory.Fory(compatible=True) # Version 1: Original class @dataclass class User: name: str - age: int + age: pyfory.Int32 f.register(User, typename="User") data = f.dumps(User("Alice", 30)) @@ -93,7 +102,7 @@ data = f.dumps(User("Alice", 30)) @dataclass class User: name: str - age: int + age: pyfory.Int32 email: str = "unknown@example.com" # New field with default # Can still deserialize old data diff --git a/docs/guide/python/type-registration.md b/docs/guide/python/type-registration.md index ff0d04dbb8..8565ff608a 100644 --- a/docs/guide/python/type-registration.md +++ b/docs/guide/python/type-registration.md @@ -1,6 +1,6 @@ --- -title: Type Registration & Security -sidebar_position: 3 +title: Type Registration +sidebar_position: 5 id: type_registration license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -19,128 +19,67 @@ license: | limitations under the License. --- -This page covers type registration mechanisms and security configurations. +This page covers Python type registration APIs. Use [Security](security.md) for +strict-mode policy, max-depth limits, and trusted-data guidance. ## Type Registration -In strict mode, only registered types can be deserialized. This prevents arbitrary code execution: +Register xlang classes by type name so other runtimes can resolve the same +schema identity: ```python +from dataclasses import dataclass import pyfory -# Strict mode (recommended for production) -f = pyfory.Fory(strict=True) +fory = pyfory.Fory(xlang=True, strict=True) -class SafeClass: - def __init__(self, data): - self.data = data +@dataclass +class User: + name: str + age: pyfory.Int32 -# Must register types in strict mode -f.register(SafeClass, typename="com.example.SafeClass") - -# Now serialization works -obj = SafeClass("safe data") -data = f.serialize(obj) -result = f.deserialize(data) - -# Unregistered types will raise an exception -class UnsafeClass: - pass - -# This will fail in strict mode -try: - f.serialize(UnsafeClass()) -except Exception as e: - print("Security protection activated!") +fory.register(User, typename="example.User") ``` -## Registration Patterns - -### Pattern 1: Simple Registration +For Python native mode, numeric type IDs are the compact same-language +registration path: ```python +import pyfory + +fory = pyfory.Fory(xlang=False, strict=True) fory.register(MyClass, type_id=100) ``` -### Pattern 2: Cross-Language with Typename +## Registration Patterns + +Use the registration form that matches the payload contract: ```python +# Xlang: stable namespace/type-name identity fory.register(MyClass, typename="com.example.MyClass") -``` -### Pattern 3: With Custom Serializer +# Native mode: compact numeric identity +fory.register(MyClass, type_id=100) -```python +# Custom serializer fory.register(MyClass, type_id=100, serializer=MySerializer(fory.type_resolver, MyClass)) -``` -### Pattern 4: Batch Registration - -```python +# Batch registration type_id = 100 for model_class in [User, Order, Product, Invoice]: fory.register(model_class, type_id=type_id) type_id += 1 ``` -## Security Modes - -### Strict Mode (Recommended) - -```python -# Always use strict=True in production -fory = pyfory.Fory(strict=True) - -# Explicitly register allowed types -fory.register(UserModel, type_id=100) -fory.register(OrderModel, type_id=101) -``` - -### Non-Strict Mode - -**⚠️ Security Warning**: When `strict=False`, Fory will deserialize arbitrary types, which can pose security risks if data comes from untrusted sources. Only use `strict=False` in controlled environments where you trust the data source completely. - -```python -# Only in trusted environments -fory = pyfory.Fory(xlang=False, ref=True, strict=False) -``` - -If you do need to use `strict=False`, configure a `DeserializationPolicy`: - -```python -from pyfory import DeserializationPolicy - -class SafePolicy(DeserializationPolicy): - def validate_class(self, cls, is_local, **kwargs): - # Block dangerous modules - if cls.__module__ in {'subprocess', 'os', '__builtin__'}: - raise ValueError(f"Blocked: {cls}") - return None - -fory = pyfory.Fory(xlang=False, strict=False, policy=SafePolicy()) -``` - -## Max Depth Protection - -Limit deserialization depth to prevent stack overflow attacks: - -```python -fory = pyfory.Fory( - strict=True, - max_depth=100 # Adjust based on your data structure depth -) -``` - -## Best Practices +## Strict Mode Relationship -1. **Always use `strict=True` in production** -2. **Use `type_id` for performance**, `typename` for flexibility -3. **Register all types upfront** before any serialization -4. **Set appropriate `max_depth`** based on your data structures -5. **Use `DeserializationPolicy`** when `strict=False` is necessary +With `strict=True`, deserialization accepts only registered types. Register all +application classes before serializing or deserializing payloads, and keep the +same registration IDs or names on every peer that shares those payloads. ## Related Topics - [Configuration](configuration.md) - Fory parameters -- [Security](security.md) - DeserializationPolicy details +- [Security](security.md) - Strict mode, deserialization policies, and size limits - [Custom Serializers](custom-serializers.md) - Custom serialization diff --git a/docs/guide/rust/basic-serialization.md b/docs/guide/rust/basic-serialization.md index 63bb9a3551..12da159a67 100644 --- a/docs/guide/rust/basic-serialization.md +++ b/docs/guide/rust/basic-serialization.md @@ -54,9 +54,9 @@ struct Address { country: String, } -let mut fory = Fory::default(); -fory.register::
(100); -fory.register::(200); +let mut fory = Fory::builder().xlang(true).build(); +fory.register_by_name::
("example", "Address").unwrap(); +fory.register_by_name::("example", "Person").unwrap(); let person = Person { name: "John Doe".to_string(), @@ -72,7 +72,7 @@ let person = Person { ]), }; -let bytes = fory.serialize(&person); +let bytes = fory.serialize(&person).unwrap(); let decoded: Person = fory.deserialize(&bytes)?; assert_eq!(person, decoded); ``` @@ -162,7 +162,7 @@ fory = { version = "0.13", features = ["chrono"] } ```rust use fory::{Fory, Reader}; -let mut fory = Fory::default(); +let mut fory = Fory::builder().xlang(true).build(); fory.register::(1)?; let obj = MyStruct { /* ... */ }; diff --git a/docs/guide/rust/configuration.md b/docs/guide/rust/configuration.md index c405894fab..93cf1d38a8 100644 --- a/docs/guide/rust/configuration.md +++ b/docs/guide/rust/configuration.md @@ -19,37 +19,42 @@ license: | limitations under the License. --- -This page covers Fory configuration options and serialization modes. +This page covers Rust runtime configuration. `Fory::builder().xlang(true).build()` selects xlang mode with +compatible schema evolution. Native mode is selected explicitly with `.xlang(false)` and defaults to +schema-consistent payloads. -## Serialization Modes +## Wire Modes Apache Fory™ supports two serialization modes: -### Compatible Mode (xlang default) +### Xlang Mode -Compatible mode is recommended for cross-language services because schemas can diverge more -easily across languages: +Xlang mode is selected with `.xlang(true)` and uses the cross-language wire +format. Compatible schema evolution is the xlang default and is recommended for +cross-language services because schemas can diverge more easily across +languages: ```rust -let fory = Fory::builder().xlang(true).compatible(true).build(); +let fory = Fory::builder().xlang(true).build(); ``` -### SchemaConsistent Mode - -Type declarations must match exactly between peers: +Use `.compatible(false)` only for xlang payloads where every peer updates the +same schema together: ```rust let fory = Fory::builder().xlang(true).compatible(false).build(); ``` -### Compatible Mode +### Native Mode -For native Rust-only payloads, compatible mode is still explicit: +For Rust-only payloads, native mode is explicit and schema-consistent by default: ```rust -let fory = Fory::builder().compatible(true).build(); +let fory = Fory::builder().xlang(false).build(); ``` +Add `.compatible(true)` only when Rust-only deployments need schema evolution. + ## Configuration ### Maximum Dynamic Object Nesting Depth @@ -59,13 +64,13 @@ Apache Fory™ provides protection against stack overflow from deeply nested dyn **Default configuration:** ```rust -let fory = Fory::default(); // max_dyn_depth = 5 +let fory = Fory::builder().xlang(true).build(); // max_dyn_depth = 5 ``` **Custom depth limit:** ```rust -let fory = Fory::builder().max_dyn_depth(10).build(); // Allow up to 10 levels +let fory = Fory::builder().xlang(true).max_dyn_depth(10).build(); // Allow up to 10 levels ``` **When to adjust:** @@ -83,14 +88,12 @@ let fory = Fory::builder().max_dyn_depth(10).build(); // Allow up to 10 levels Note: Static data types (non-dynamic types) are secure by nature and not subject to depth limits, as their structure is known at compile time. -### Cross-Language Mode +### Explicit Xlang Examples -Enable cross-language serialization: +Set `.xlang(true)` explicitly for xlang serialization examples: ```rust -let fory = Fory::builder() - .compatible(true) - .xlang(true).build(); +let fory = Fory::builder().xlang(true).build(); ``` ## Builder Pattern @@ -98,37 +101,35 @@ let fory = Fory::builder() ```rust use fory::Fory; -// Default configuration -let fory = Fory::default(); +// Default xlang configuration +let fory = Fory::builder().xlang(true).build(); -// Compatible mode for schema evolution -let fory = Fory::builder().compatible(true).build(); +// Native mode for Rust-only traffic +let fory = Fory::builder().xlang(false).build(); -// Cross-language mode -let fory = Fory::builder() - .compatible(true) - .xlang(true).build(); +// Native mode with schema evolution +let fory = Fory::builder().xlang(false).compatible(true).build(); // Custom depth limit -let fory = Fory::builder().max_dyn_depth(10).build(); +let fory = Fory::builder().xlang(true).max_dyn_depth(10).build(); // Combined configuration let fory = Fory::builder() - .compatible(true) .xlang(true) + .compatible(true) .max_dyn_depth(10).build(); ``` ## Configuration Summary -| Option | Description | Default | -| -------------------- | --------------------------------------- | ------- | -| `compatible(bool)` | Enable schema evolution | `false` | -| `xlang(bool)` | Enable cross-language mode | `false` | -| `max_dyn_depth(u32)` | Maximum nesting depth for dynamic types | `5` | +| Option | Description | Default | +| -------------------- | --------------------------------------- | ------------------------------ | +| `compatible(bool)` | Enable schema evolution | xlang: `true`; native: `false` | +| `xlang(bool)` | Use xlang mode | `true` | +| `max_dyn_depth(u32)` | Maximum nesting depth for dynamic types | `5` | ## Related Topics - [Basic Serialization](basic-serialization.md) - Using configured Fory - [Schema Evolution](schema-evolution.md) - Compatible mode details -- [Cross-Language](cross-language.md) - XLANG mode +- [Cross-Language](cross-language.md) - xlang mode diff --git a/docs/guide/rust/cross-language.md b/docs/guide/rust/cross-language.md index 4223276a28..dab15eeb63 100644 --- a/docs/guide/rust/cross-language.md +++ b/docs/guide/rust/cross-language.md @@ -1,6 +1,6 @@ --- title: Cross-Language Serialization -sidebar_position: 8 +sidebar_position: 3 id: cross_language license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -21,21 +21,21 @@ license: | Apache Fory™ supports seamless data exchange across multiple languages including Java, Python, C++, Go, and JavaScript. -## Enable Cross-Language Mode +## Create an Xlang Runtime + +Rust defaults to xlang mode with compatible schema evolution. Set the mode explicitly in xlang examples: ```rust use fory::Fory; -// Enable cross-language mode -let mut fory = Fory::builder() - .compatible(true) - .xlang(true).build(); +// Use xlang mode +let mut fory = Fory::builder().xlang(true).build(); // Register types with consistent IDs across languages -fory.register::(100); +fory.register::(100)?; // Or use name-based registration -fory.register_by_name::("com.example", "MyStruct"); +fory.register_by_name::("com.example", "MyStruct")?; ``` ## Type Registration for Cross-Language @@ -45,11 +45,9 @@ fory.register_by_name::("com.example", "MyStruct"); For fast, compact serialization with consistent IDs across languages: ```rust -let mut fory = Fory::builder() - .compatible(true) - .xlang(true).build(); +let mut fory = Fory::builder().xlang(true).build(); -fory.register::(100); // Same ID in Java, Python, etc. +fory.register::(100)?; // Same ID in Java, Python, etc. ``` ### Register by Name @@ -57,7 +55,7 @@ fory.register::(100); // Same ID in Java, Python, etc. For more flexible type naming: ```rust -fory.register_by_name::("com.example", "User"); +fory.register_by_name::("com.example", "User")?; ``` ## Cross-Language Example @@ -74,11 +72,9 @@ struct Person { age: i32, } -let mut fory = Fory::builder() - .compatible(true) - .xlang(true).build(); +let mut fory = Fory::builder().xlang(true).build(); -fory.register::(100); +fory.register::(100)?; let person = Person { name: "Alice".to_string(), @@ -101,7 +97,7 @@ public class Person { } Fory fory = Fory.builder() - .withXlang(true).withCompatible(true) + .withXlang(true) .withRefTracking(true) .build(); @@ -121,7 +117,7 @@ class Person: name: str age: pyfory.Int32 -fory = pyfory.Fory(xlang=True, compatible=True, ref=True) +fory = pyfory.Fory(xlang=True, ref=True) fory.register_type(Person, type_id=100) # Same ID as Rust person = fory.deserialize(bytes_from_rust) @@ -175,7 +171,7 @@ explicit array field attribute when the schema is dense `array`. ## Best Practices 1. **Use consistent type IDs** across all languages -2. **Enable compatible mode** for schema evolution +2. **Keep compatible mode** for schema evolution 3. **Register all types** before serialization 4. **Test cross-language** compatibility during development @@ -188,6 +184,6 @@ explicit array field attribute when the schema is dense `array`. ## Related Topics -- [Configuration](configuration.md) - XLANG mode configuration +- [Configuration](configuration.md) - xlang mode configuration - [Schema Evolution](schema-evolution.md) - Compatible mode - [Type Registration](type-registration.md) - Registration methods diff --git a/docs/guide/rust/custom-serializers.md b/docs/guide/rust/custom-serializers.md index 508e37e570..8320a0dd88 100644 --- a/docs/guide/rust/custom-serializers.md +++ b/docs/guide/rust/custom-serializers.md @@ -1,6 +1,6 @@ --- title: Custom Serializers -sidebar_position: 4 +sidebar_position: 9 id: custom_serializers license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -25,7 +25,7 @@ For types that don't support `#[derive(ForyStruct)]`, implement the `Serializer` - External types from other crates - Types with special serialization requirements -- Legacy data format compatibility +- Existing data format compatibility - Performance-critical custom encoding ## Implementing the Serializer Trait @@ -79,14 +79,14 @@ impl ForyDefault for CustomType { ## Registering Custom Serializers ```rust -let mut fory = Fory::default(); -fory.register_serializer::(100); +let mut fory = Fory::builder().xlang(false).build(); +fory.register_serializer::(100)?; let custom = CustomType { value: 42, name: "test".to_string(), }; -let bytes = fory.serialize(&custom); +let bytes = fory.serialize(&custom)?; let decoded: CustomType = fory.deserialize(&bytes)?; assert_eq!(custom, decoded); ``` diff --git a/docs/guide/rust/index.md b/docs/guide/rust/index.md index 3b32eac6dd..a4377461d4 100644 --- a/docs/guide/rust/index.md +++ b/docs/guide/rust/index.md @@ -21,17 +21,17 @@ license: | **Apache Fory™** is a blazing fast multi-language serialization framework powered by **JIT compilation** and **zero-copy** techniques, providing up to **ultra-fast performance** while maintaining ease of use and safety. -The Rust implementation provides versatile and high-performance serialization with automatic memory management and compile-time type safety. +The Rust implementation provides versatile and high-performance serialization with automatic memory management and compile-time type safety. It supports both xlang mode for cross-language payloads and native mode for Rust-only payloads. ## Why Apache Fory™ Rust? -- **🔥 Blazingly Fast**: Zero-copy deserialization and optimized binary protocols -- **🌍 Cross-Language**: Seamlessly serialize/deserialize data across Java, Python, C++, Go, JavaScript, and Rust -- **🎯 Type-Safe**: Compile-time type checking with derive macros -- **🔄 Circular References**: Automatic tracking of shared and circular references with `Rc`/`Arc` and weak pointers -- **🧬 Polymorphic**: Serialize trait objects with `Box`, `Rc`, and `Arc` -- **📦 Schema Evolution**: Compatible mode for independent schema changes -- **⚡ Two Formats**: Object graph serialization and zero-copy row-based format +- **Fast binary encoding**: Zero-copy deserialization and optimized binary protocols +- **Cross-language**: Seamlessly serialize/deserialize data across Java, Python, C++, Go, JavaScript, and Rust +- **Type-safe**: Compile-time type checking with derive macros +- **Circular references**: Automatic tracking of shared and circular references with `Rc`/`Arc` and weak pointers +- **Polymorphic**: Serialize trait objects with `Box`, `Rc`, and `Arc` +- **Schema evolution**: Compatible mode for independent schema changes +- **Two formats**: Object graph serialization and zero-copy row-based format ## Crates @@ -64,7 +64,7 @@ struct User { } fn main() -> Result<(), Error> { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(true).build(); fory.register::(1)?; let user = User { @@ -90,6 +90,14 @@ fn main() -> Result<(), Error> { } ``` +## Xlang Mode And Native Mode + +Use xlang mode for cross-language payloads and schemas shared with other Fory runtimes. Xlang mode is the default Rust wire mode, and Rust examples that use it set `.xlang(true)` explicitly so the mode choice is visible. + +Use native mode for Rust-only traffic. Native mode is selected with `.xlang(false)`, uses schema-consistent payloads unless compatible mode is enabled, and keeps Rust object serialization on the Rust runtime path. It is optimized for Rust's type system and covers Rust-specific object features such as trait objects and shared-reference patterns that are not portable xlang payloads. + +See [Cross-Language Serialization](cross-language.md) for Rust xlang registration and interoperability rules, and [Configuration](configuration.md) for native-mode builder options. + ## Thread Safety Apache Fory™ Rust is fully thread-safe: `Fory` implements both `Send` and `Sync`, so one configured instance can be shared across threads for concurrent work. The internal read/write context pools are lazily initialized with thread-safe primitives, letting worker threads reuse buffers without coordination. @@ -106,7 +114,7 @@ struct Item { } fn main() -> Result<(), Error> { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(true).build(); fory.register::(1000)?; let fory = Arc::new(fory); @@ -180,5 +188,5 @@ fory-derive/ # Procedural macros - [Basic Serialization](basic-serialization.md) - Object graph serialization - [References](references.md) - Shared and circular references - [Polymorphism](polymorphism.md) - Trait object serialization -- [Cross-Language](cross-language.md) - XLANG mode +- [Cross-Language](cross-language.md) - xlang mode - [Row Format](row-format.md) - Zero-copy row-based format diff --git a/docs/guide/rust/polymorphism.md b/docs/guide/rust/polymorphism.md index f3b7bbbb1b..2c1e7f8c61 100644 --- a/docs/guide/rust/polymorphism.md +++ b/docs/guide/rust/polymorphism.md @@ -1,6 +1,6 @@ --- title: Trait Object Serialization -sidebar_position: 6 +sidebar_position: 7 id: polymorphism license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -64,10 +64,10 @@ struct Zoo { star_animal: Box, } -let mut fory = Fory::builder().compatible(true).build(); -fory.register::(100); -fory.register::(101); -fory.register::(102); +let mut fory = Fory::builder().xlang(false).compatible(true).build(); +fory.register::(100)?; +fory.register::(101)?; +fory.register::(102)?; let zoo = Zoo { star_animal: Box::new(Dog { @@ -76,7 +76,7 @@ let zoo = Zoo { }), }; -let bytes = fory.serialize(&zoo); +let bytes = fory.serialize(&zoo)?; let decoded: Zoo = fory.deserialize(&bytes)?; assert_eq!(decoded.star_animal.name(), "Buddy"); @@ -107,7 +107,7 @@ let dog_rc: Rc = Rc::new(Dog { let dog_any: Rc = dog_rc.clone(); // Serialize the Any wrapper -let bytes = fory.serialize(&dog_any); +let bytes = fory.serialize(&dog_any)?; let decoded: Rc = fory.deserialize(&bytes)?; // Downcast back to the concrete type @@ -129,7 +129,7 @@ let dog_arc: Arc = Arc::new(Dog { // Convert to Arc let dog_any: Arc = dog_arc.clone(); -let bytes = fory.serialize(&dog_any); +let bytes = fory.serialize(&dog_any)?; let decoded: Arc = fory.deserialize(&bytes)?; // Downcast to concrete type @@ -153,10 +153,10 @@ struct AnimalShelter { registry: HashMap>, } -let mut fory = Fory::builder().compatible(true).build(); -fory.register::(100); -fory.register::(101); -fory.register::(102); +let mut fory = Fory::builder().xlang(false).compatible(true).build(); +fory.register::(100)?; +fory.register::(101)?; +fory.register::(102)?; let shelter = AnimalShelter { animals_rc: vec![ @@ -174,7 +174,7 @@ let shelter = AnimalShelter { ]), }; -let bytes = fory.serialize(&shelter); +let bytes = fory.serialize(&shelter)?; let decoded: AnimalShelter = fory.deserialize(&bytes)?; assert_eq!(decoded.animals_rc[0].name(), "Rex"); @@ -197,7 +197,7 @@ let dog_rc: Rc = Rc::new(Dog { }); let wrapper = AnimalRc::from(dog_rc); -let bytes = fory.serialize(&wrapper); +let bytes = fory.serialize(&wrapper)?; let decoded: AnimalRc = fory.deserialize(&bytes)?; // Unwrap back to Rc @@ -211,7 +211,7 @@ let dog_arc: Arc = Arc::new(Dog { }); let wrapper = AnimalArc::from(dog_arc); -let bytes = fory.serialize(&wrapper); +let bytes = fory.serialize(&wrapper)?; let decoded: AnimalArc = fory.deserialize(&bytes)?; let unwrapped: Arc = decoded.unwrap(); diff --git a/docs/guide/rust/references.md b/docs/guide/rust/references.md index f18d11cd17..98ed922b7a 100644 --- a/docs/guide/rust/references.md +++ b/docs/guide/rust/references.md @@ -1,6 +1,6 @@ --- title: Shared & Circular References -sidebar_position: 5 +sidebar_position: 6 id: references license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -35,7 +35,7 @@ When the same object is referenced multiple times, Fory serializes it only once use fory::Fory; use std::rc::Rc; -let fory = Fory::default(); +let fory = Fory::builder().xlang(false).build(); // Create a shared value let shared = Rc::new(String::from("shared_value")); @@ -44,7 +44,7 @@ let shared = Rc::new(String::from("shared_value")); let data = vec![shared.clone(), shared.clone(), shared.clone()]; // The shared value is serialized only once -let bytes = fory.serialize(&data); +let bytes = fory.serialize(&data)?; let decoded: Vec> = fory.deserialize(&bytes)?; // Verify reference identity is preserved @@ -64,12 +64,12 @@ For thread-safe shared references, use `Arc`: use fory::Fory; use std::sync::Arc; -let fory = Fory::default(); +let fory = Fory::builder().xlang(false).build(); let shared = Arc::new(String::from("shared_value")); let data = vec![shared.clone(), shared.clone()]; -let bytes = fory.serialize(&data); +let bytes = fory.serialize(&data)?; let decoded: Vec> = fory.deserialize(&bytes)?; assert!(Arc::ptr_eq(&decoded[0], &decoded[1])); @@ -102,8 +102,8 @@ struct Node { children: Vec>>, } -let mut fory = Fory::default(); -fory.register::(2000); +let mut fory = Fory::builder().xlang(false).build(); +fory.register::(2000)?; // Build a parent-child tree let parent = Rc::new(RefCell::new(Node { @@ -128,7 +128,7 @@ parent.borrow_mut().children.push(child1.clone()); parent.borrow_mut().children.push(child2.clone()); // Serialize and deserialize the circular structure -let bytes = fory.serialize(&parent); +let bytes = fory.serialize(&parent)?; let decoded: Rc> = fory.deserialize(&bytes)?; // Verify the circular relationship @@ -154,8 +154,8 @@ struct Node { children: Vec>>, } -let mut fory = Fory::default(); -fory.register::(6000); +let mut fory = Fory::builder().xlang(false).build(); +fory.register::(6000)?; let parent = Arc::new(Mutex::new(Node { val: 10, @@ -178,7 +178,7 @@ let child2 = Arc::new(Mutex::new(Node { parent.lock().unwrap().children.push(child1.clone()); parent.lock().unwrap().children.push(child2.clone()); -let bytes = fory.serialize(&parent); +let bytes = fory.serialize(&parent)?; let decoded: Arc> = fory.deserialize(&bytes)?; assert_eq!(decoded.lock().unwrap().children.len(), 2); diff --git a/docs/guide/rust/row-format.md b/docs/guide/rust/row-format.md index c0728f7464..a72202f574 100644 --- a/docs/guide/rust/row-format.md +++ b/docs/guide/rust/row-format.md @@ -1,6 +1,6 @@ --- title: Row Format -sidebar_position: 9 +sidebar_position: 10 id: row_format license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -71,7 +71,7 @@ let profile = UserProfile { }; // Serialize to row format -let row_data = to_row(&profile); +let row_data = to_row(&profile).unwrap(); // Zero-copy deserialization - no object allocation! let row = from_row::(&row_data); @@ -85,13 +85,13 @@ assert_eq!(row.is_active(), true); // Access collections efficiently let scores = row.scores(); assert_eq!(scores.size(), 4); -assert_eq!(scores.get(0), 95); -assert_eq!(scores.get(1), 87); +assert_eq!(scores.get(0).unwrap(), 95); +assert_eq!(scores.get(1).unwrap(), 87); let prefs = row.preferences(); assert_eq!(prefs.keys().size(), 2); -assert_eq!(prefs.keys().get(0), "language"); -assert_eq!(prefs.values().get(0), "en"); +assert_eq!(prefs.keys().get(0).unwrap(), "language"); +assert_eq!(prefs.values().get(0).unwrap(), "en"); ``` ## How It Works diff --git a/docs/guide/rust/schema-evolution.md b/docs/guide/rust/schema-evolution.md index e6dbfced1d..db3b40d1d2 100644 --- a/docs/guide/rust/schema-evolution.md +++ b/docs/guide/rust/schema-evolution.md @@ -1,6 +1,6 @@ --- title: Schema Evolution -sidebar_position: 7 +sidebar_position: 8 id: schema_evolution license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -47,11 +47,11 @@ struct PersonV2 { metadata: HashMap, } -let mut fory1 = Fory::builder().compatible(true).build(); -fory1.register::(1); +let mut fory1 = Fory::builder().xlang(false).compatible(true).build(); +fory1.register::(1)?; -let mut fory2 = Fory::builder().compatible(true).build(); -fory2.register::(1); +let mut fory2 = Fory::builder().xlang(false).compatible(true).build(); +fory2.register::(1)?; let person_v1 = PersonV1 { name: "Alice".to_string(), @@ -60,7 +60,7 @@ let person_v1 = PersonV1 { }; // Serialize with V1 -let bytes = fory1.serialize(&person_v1); +let bytes = fory1.serialize(&person_v1)?; // Deserialize with V2 - missing fields get default values let person_v2: PersonV2 = fory2.deserialize(&bytes)?; @@ -120,7 +120,7 @@ enum Value { Object { name: String, value: i32 }, } -let mut fory = Fory::default(); +let mut fory = Fory::builder().xlang(false).build(); fory.register::(1)?; let value = Value::Object { name: "score".to_string(), value: 100 }; @@ -155,7 +155,7 @@ enum NewEvent { KeyPress(String), // New variant } -let mut fory = Fory::builder().compatible(true).build(); +let mut fory = Fory::builder().xlang(false).compatible(true).build(); // Serialize with old schema let old_bytes = fory.serialize(&OldEvent::Click { x: 100, y: 200 })?; @@ -180,7 +180,7 @@ assert!(matches!(new_event, NewEvent::Click { x: 100, y: 200, timestamp: 0 })); ## Tuple Support -Apache Fory™ supports tuples up to 22 elements out of the box with efficient serialization in both compatible and non-compatible modes. +Apache Fory™ supports tuples up to 22 elements out of the box with efficient serialization in both compatible and schema-consistent modes. **Features:** @@ -188,15 +188,15 @@ Apache Fory™ supports tuples up to 22 elements out of the box with efficient s - Heterogeneous type support (each element can be a different type) - Schema evolution in Compatible mode (handles missing/extra elements) -**Serialization modes:** +**Schema modes:** -1. **Non-compatible mode**: Serializes elements sequentially without collection headers for minimal overhead +1. **Schema-consistent mode**: Serializes elements sequentially without collection headers for minimal overhead 2. **Compatible mode**: Uses collection protocol with type metadata for schema evolution ```rust use fory::{Fory, Error}; -let mut fory = Fory::default(); +let mut fory = Fory::builder().xlang(false).build(); // Tuple with heterogeneous types let data: (i32, String, bool, Vec) = ( diff --git a/docs/guide/rust/field-configuration.md b/docs/guide/rust/schema-metadata.md similarity index 96% rename from docs/guide/rust/field-configuration.md rename to docs/guide/rust/schema-metadata.md index c5cc57b701..d87192c343 100644 --- a/docs/guide/rust/field-configuration.md +++ b/docs/guide/rust/schema-metadata.md @@ -1,7 +1,7 @@ --- -title: Field Configuration +title: Schema Metadata sidebar_position: 5 -id: field_configuration +id: schema_metadata license: | Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -281,7 +281,7 @@ struct Document { } fn main() { - let fory = fory::Fory::default(); + let fory = fory::Fory::builder().xlang(false).build(); let doc = Document { title: "My Document".to_string(), @@ -294,7 +294,7 @@ fn main() { internal_state: "secret".to_string(), // Will be skipped }; - let bytes = fory.serialize(&doc); + let bytes = fory.serialize(&doc).unwrap(); let decoded: Document = fory.deserialize(&bytes).unwrap(); } ``` @@ -331,7 +331,7 @@ struct Bad3 { ## Cross-Language Compatibility -When serializing data to be read by other languages (Java, C++, Go, Python), use field configuration to match encoding expectations: +When serializing data to be read by other languages (Java, C++, Go, Python), use schema metadata to match encoding expectations: ```rust #[derive(ForyStruct)] @@ -449,8 +449,8 @@ struct User { | `nullable` | `nullable` or `nullable = bool` | Control null flag writing | All fields | | `ref` | `ref` or `ref = bool` | Control reference tracking | `Rc`, `Arc`, weak types | | `encoding` | `encoding = varint/fixed/tagged` | Integer encoding method | `i32`, `u32`, `i64`, `u64` | -| `list` | `list(element(...))` | Element field configuration | `Vec` | -| `map` | `map(key(...), value(...))` | Key/value field configuration | `HashMap` | +| `list` | `list(element(...))` | Element schema metadata | `Vec` | +| `map` | `map(key(...), value(...))` | Key/value schema metadata | `HashMap` | ## Related Topics diff --git a/docs/guide/rust/troubleshooting.md b/docs/guide/rust/troubleshooting.md index 90afa1369c..97f3350532 100644 --- a/docs/guide/rust/troubleshooting.md +++ b/docs/guide/rust/troubleshooting.md @@ -1,6 +1,6 @@ --- title: Troubleshooting -sidebar_position: 10 +sidebar_position: 11 id: troubleshooting license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -51,7 +51,11 @@ Confirm that: - Ensure field types match across versions ```rust -let fory = Fory::builder().compatible(true).build(); +// Add compatible(true) to the same builder configuration on every peer. +let fory = Fory::builder() + // existing options + .compatible(true) + .build(); ``` ## Debugging Techniques diff --git a/docs/guide/rust/type-registration.md b/docs/guide/rust/type-registration.md index 0eedec6476..85a55af8e3 100644 --- a/docs/guide/rust/type-registration.md +++ b/docs/guide/rust/type-registration.md @@ -1,6 +1,6 @@ --- title: Type Registration -sidebar_position: 3 +sidebar_position: 4 id: type_registration license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -35,7 +35,7 @@ struct User { age: i32, } -let mut fory = Fory::default(); +let mut fory = Fory::builder().xlang(false).build(); fory.register::(1)?; let user = User { @@ -52,9 +52,7 @@ let decoded: User = fory.deserialize(&bytes)?; For cross-language compatibility, register with namespace and type name: ```rust -let mut fory = Fory::builder() - .compatible(true) - .xlang(true).build(); +let mut fory = Fory::builder().xlang(true).build(); // Register with symbolic type identity fory.register_by_name::("com.example", "MyStruct")?; @@ -65,7 +63,7 @@ fory.register_by_name::("com.example", "MyStruct")?; For types that need custom serialization logic: ```rust -let mut fory = Fory::default(); +let mut fory = Fory::builder().xlang(false).build(); fory.register_serializer::(100)?; ``` @@ -75,13 +73,13 @@ Rust registration APIs use explicit IDs or explicit namespace/type names. Keep t ```rust // Serializer side -let mut fory = Fory::default(); +let mut fory = Fory::builder().xlang(false).build(); fory.register::(1)?; fory.register::(2)?; fory.register::(3)?; // Deserializer side - MUST use the same ID mapping -let mut fory = Fory::default(); +let mut fory = Fory::builder().xlang(false).build(); fory.register::(1)?; fory.register::(2)?; fory.register::(3)?; @@ -95,7 +93,7 @@ Perform all registrations before spawning threads: use std::sync::Arc; use std::thread; -let mut fory = Fory::default(); +let mut fory = Fory::builder().xlang(false).build(); fory.register::(1)?; fory.register::(2)?; @@ -122,5 +120,5 @@ let handles: Vec<_> = (0..4) ## Related Topics - [Configuration](configuration.md) - Fory builder options -- [Cross-Language](cross-language.md) - XLANG mode registration +- [Cross-Language](cross-language.md) - xlang mode registration - [Custom Serializers](custom-serializers.md) - Custom serialization diff --git a/docs/guide/scala/fory-creation.md b/docs/guide/scala/configuration.md similarity index 69% rename from docs/guide/scala/fory-creation.md rename to docs/guide/scala/configuration.md index c65e384df0..fe0490fb55 100644 --- a/docs/guide/scala/fory-creation.md +++ b/docs/guide/scala/configuration.md @@ -1,7 +1,7 @@ --- -title: Fory Creation +title: Configuration sidebar_position: 1 -id: fory_creation +id: configuration license: | Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -19,23 +19,46 @@ license: | limitations under the License. --- -This page covers Scala-specific requirements for creating Fory instances. +This page covers Scala-specific runtime configuration and Fory instance creation. -## Basic Setup +## Xlang Setup -When using Fory for Scala serialization, you must: +Fory Scala follows the Java builder default: xlang mode with compatible schema +evolution. Use this path for cross-language Scala payloads, schema IDL generated +Scala models, and macro-derived xlang serializers. -1. Create the runtime with `ForyScala.builder()`, or install `ForyScala` with `Fory.builder().withModule(ForyScala)`. +```scala +import org.apache.fory.scala.ForyScala + +val fory = ForyScala.builder() + .withXlang(true) + .build() +``` + +Register application classes before serialization: + +```scala +fory.register(classOf[Person]) +fory.register(classOf[Point]) +``` + +## Native Mode Setup + +For same-language Scala/JVM payloads that need native JVM object behavior, you +must: + +1. Create the runtime with `ForyScala.builder().withXlang(false)`, or install + `ForyScala` with `Fory.builder().withXlang(false).withModule(ForyScala)`. 2. Register application classes before serialization. ```scala import org.apache.fory.scala.ForyScala -val fory = ForyScala.builder() +val fory = ForyScala.builder().withXlang(false) .build() ``` -## Registering Scala Internal Types +### Registering Scala Internal Types Depending on the object types you serialize, you may need to register some Scala internal types: @@ -46,19 +69,19 @@ fory.register(Class.forName("scala.Enumeration.Val")) To avoid such registration, you can disable class registration: ```scala -val fory = ForyScala.builder() +val fory = ForyScala.builder().withXlang(false) .requireClassRegistration(false) .build() ``` > **Note**: Disabling class registration allows deserialization of unknown types. This is more flexible but may be insecure if the classes contain malicious code. -## Reference Tracking +### Reference Tracking Circular references are common in Scala. Reference tracking should be enabled with `withRefTracking(true)`: ```scala -val fory = ForyScala.builder() +val fory = ForyScala.builder().withXlang(false) .withRefTracking(true) .build() ``` @@ -77,6 +100,7 @@ import org.apache.fory.scala.ForyScala object ForyHolder { val fory: Fory = ForyScala.builder() + .withXlang(true) .build() } ``` @@ -91,6 +115,7 @@ import org.apache.fory.scala.ForyScala object ForyHolder { val fory: ThreadSafeFory = ForyScala.builder() + .withXlang(true) .buildThreadSafeFory() } ``` @@ -99,25 +124,25 @@ object ForyHolder { All configuration options from Fory Java are available. See [Java Configuration](../java/configuration.md) for the complete list. -Common options for Scala: +Common options for Scala native-mode payloads: ```scala import org.apache.fory.scala.ForyScala -val fory = ForyScala.builder() +val fory = ForyScala.builder().withXlang(false) // Enable reference tracking for circular references .withRefTracking(true) - // Enable schema evolution support + // Enable schema evolution support for native-mode payloads .withCompatible(true) // Enable async compilation for better startup performance .withAsyncCompilation(true) .build() ``` -## Cross-Language Mode +## Xlang Mode -For Scala xlang or schema IDL generated code, enable xlang and register the -generated schema module: +For Scala xlang or schema IDL generated code, use the default xlang mode and +register the generated schema module: ```scala import org.apache.fory.scala.ForyScala @@ -125,7 +150,6 @@ import example.ExampleForyModule val fory = ForyScala.builder() .withXlang(true) - .withCompatible(true) .withRefTracking(true) .withModule(ExampleForyModule) .build() diff --git a/docs/guide/scala/default-values.md b/docs/guide/scala/default-values.md index 5991e63516..87406ef50c 100644 --- a/docs/guide/scala/default-values.md +++ b/docs/guide/scala/default-values.md @@ -1,6 +1,6 @@ --- title: Default Values -sidebar_position: 3 +sidebar_position: 4 id: default_values license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -19,7 +19,7 @@ license: | limitations under the License. --- -Fory supports Scala class default values during deserialization when using compatible mode. This feature enables forward/backward compatibility when case classes or regular Scala classes have default parameters. +Fory supports Scala class default values during native-mode deserialization when compatible mode is enabled. This feature enables forward/backward compatibility when case classes or regular Scala classes have default parameters. ## Overview @@ -47,9 +47,10 @@ Fory supports default values for: ## Usage -This feature is automatically enabled when: +This feature is available when: -- Compatible mode is enabled (`withCompatible(true)`) +- Native mode is explicitly configured with compatible mode + (`withXlang(false).withCompatible(true)`) - The target class is detected as a Scala class with default values - A field is missing from the serialized data but exists in the target class @@ -68,7 +69,7 @@ case class PersonV1(name: String) // Class WITH default values (for deserialization) case class PersonV2(name: String, age: Int = 25, city: String = "Unknown") -val fory = ForyScala.builder() +val fory = ForyScala.builder().withXlang(false) .withCompatible(true) .build() @@ -97,7 +98,7 @@ class EmployeeV2( val department: String = "Engineering" ) -val fory = ForyScala.builder() +val fory = ForyScala.builder().withXlang(false) .withCompatible(true) .build() @@ -128,7 +129,7 @@ case class ConfigV2( enabled: Boolean = true ) -val fory = ForyScala.builder() +val fory = ForyScala.builder().withXlang(false) .withCompatible(true) .build() @@ -154,7 +155,7 @@ object Models { case class PersonV2(name: String, address: Address = Address("DefaultStreet")) } -val fory = ForyScala.builder() +val fory = ForyScala.builder().withXlang(false) .withCompatible(true) .build() @@ -169,4 +170,4 @@ val deserialized = fory.deserialize(serialized).asInstanceOf[Models.PersonV2] ## Related Topics - [Schema Evolution](../java/schema-evolution.md) - Forward/backward compatibility in Java -- [Fory Creation](fory-creation.md) - Setting up Fory with compatible mode +- [Configuration](configuration.md) - Setting up Fory with compatible mode diff --git a/docs/guide/scala/index.md b/docs/guide/scala/index.md index ffa479737d..775f705551 100644 --- a/docs/guide/scala/index.md +++ b/docs/guide/scala/index.md @@ -19,7 +19,7 @@ license: | limitations under the License. --- -Apache Fory™ Scala provides optimized serializers for Scala types, built on top of Fory Java. It supports all Scala object serialization: +Apache Fory™ Scala provides optimized serializers for Scala types, built on top of Fory Java. It supports xlang mode for cross-language payloads and native mode for Scala/JVM-only object serialization. It supports all Scala object serialization: - `case` class serialization - `pojo/bean` class serialization @@ -63,6 +63,7 @@ case class Point(x: Int, y: Int, z: Int) object ScalaExample { val fory: Fory = ForyScala.builder() + .withXlang(true) .build() fory.register(classOf[Person]) @@ -76,6 +77,14 @@ object ScalaExample { } ``` +## Xlang Mode And Native Mode + +Use xlang mode for cross-language payloads and schemas shared with other Fory runtimes. Xlang mode is the default Scala wire mode through the JVM builder, and Scala examples that use it set `.withXlang(true)` explicitly so the mode choice is visible. + +Use native mode for Scala/JVM-only traffic. Native mode is selected with `.withXlang(false)`, uses schema-consistent payloads unless compatible mode is enabled, and inherits the JVM native-mode object serialization path from Fory Java while adding Scala-specific serializers for case classes, collections, tuples, options, and enumerations. It is optimized for JVM and Scala type systems and is the right path for same-language Scala/JVM framework replacement payloads. + +See [Configuration](configuration.md) for Scala builder setup and [Java Native Mode](../java/native-mode.md) for the full JVM native-mode behavior. + ## Built on Fory Java Fory Scala is built on top of Fory Java. Most configuration options, features, and concepts from Fory Java apply directly to Scala. Refer to the Java documentation for: @@ -90,7 +99,8 @@ Fory Scala is built on top of Fory Java. Most configuration options, features, a ## Scala-Specific Documentation -- [Fory Creation](fory-creation.md) - Scala-specific Fory setup requirements +- [Configuration](configuration.md) - Scala-specific Fory setup requirements - [Type Serialization](type-serialization.md) - Serializing Scala types +- [Schema Metadata](schema-metadata.md) - Scala annotations, references, enum IDs, and union metadata - [Default Values](default-values.md) - Scala class default values support - [Schema IDL And Xlang](schema-idl.md) - Scala 3 generated models and macro-derived xlang serializers diff --git a/docs/guide/scala/schema-idl.md b/docs/guide/scala/schema-idl.md index 1396ee2e43..f0087eae99 100644 --- a/docs/guide/scala/schema-idl.md +++ b/docs/guide/scala/schema-idl.md @@ -1,6 +1,6 @@ --- title: Schema IDL And Xlang -sidebar_position: 4 +sidebar_position: 5 id: schema_idl license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -35,7 +35,6 @@ import example.ExampleForyModule val fory = ForyScala.builder() .withXlang(true) - .withCompatible(true) .withRefTracking(true) .withModule(ExampleForyModule) .build() @@ -47,7 +46,7 @@ creating a custom runtime, or use the generated no-argument `toBytes` and Generated helpers register message type identities before installing message serializers. This two-phase order lets mutually recursive message graphs build -descriptor metadata through the normal `TypeResolver` path without placeholder +descriptor metadata through the normal `TypeResolver` path without temporary serializers or Scala-specific registration state in Java core. Enums and unions are registered with their serializers directly because their derived serializers own case dispatch. diff --git a/docs/guide/scala/schema-metadata.md b/docs/guide/scala/schema-metadata.md new file mode 100644 index 0000000000..1f96964c01 --- /dev/null +++ b/docs/guide/scala/schema-metadata.md @@ -0,0 +1,116 @@ +--- +title: Schema Metadata +sidebar_position: 3 +id: schema_metadata +license: | + 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. +--- + +Scala schema metadata is used by schema IDL generated code and Scala 3 macro-derived xlang +serializers. Metadata is declared with the shared JVM Fory annotations and Scala compile-time type +information. + +## Struct Fields + +Schema messages can use `@ForyStruct` and `@ForyField(id = N)`: + +```scala +import org.apache.fory.annotation.{ForyField, ForyStruct} +import org.apache.fory.scala.ForySerializer + +@ForyStruct +final case class Person( + @ForyField(id = 1) name: String, + @ForyField(id = 2) email: Option[String] +) derives ForySerializer +``` + +Schema `optional T` fields are represented as `Option[T]`. + +## Reference Tracking + +Reference tracking uses the shared JVM `@Ref` annotation. Use field or constructor-parameter +`@Ref` for a top-level `ref T` field, and type-use `T @Ref` for nested collection or map payloads: + +```scala +import org.apache.fory.annotation.{ForyField, ForyStruct, Ref} + +@ForyStruct +final class Node() derives ForySerializer { + @ForyField(id = 1) + var children: List[Node @Ref] = List.empty + + @Ref + @ForyField(id = 2) + var parent: Option[Node] = None +} +``` + +## Enum IDs + +IDL enums generate Scala 3 enums. Stable Fory enum IDs come from case-level `@ForyEnumId` metadata: + +```scala +import org.apache.fory.annotation.ForyEnumId + +enum Status { + @ForyEnumId(0) + case Unknown + + @ForyEnumId(1) + case Ok +} +``` + +Generated registration uses `ScalaSerializers.registerEnum(...)` so these stable IDs are used in +xlang mode. + +## Unions + +IDL unions generate Scala 3 ADT enums with `@ForyUnion` and `@ForyCase` metadata: + +```scala +import org.apache.fory.annotation.{ForyCase, ForyUnion, UInt32Type} +import org.apache.fory.config.Int32Encoding +import org.apache.fory.scala.ForySerializer + +@ForyUnion +enum SearchTarget derives ForySerializer { + @ForyCase(id = 0) + case UnknownCase(caseId: Int, value: Any) + + @ForyCase(id = 1) + case UserCase(value: User) + + @ForyCase(id = 2) + case FixedIdCase(value: Long @UInt32Type(encoding = Int32Encoding.FIXED)) +} +``` + +Schema-defined union cases must use positive IDs. Case ID `0` is reserved for the unknown-case +carrier used when a reader sees a newer positive case ID. + +## Generated Metadata Source + +The Scala macro builds descriptor metadata from Scala compile-time types, including nested +generics, `Option`, arrays, scalar encoding annotations, nullability, and `@Ref` metadata. Java +reflection is not the source of truth for generated Scala metadata. + +## Related Topics + +- [Schema IDL And Xlang](schema-idl.md) +- [Configuration](configuration.md) +- [Default Values](default-values.md) diff --git a/docs/guide/scala/type-serialization.md b/docs/guide/scala/type-serialization.md index 87661515c1..00a8574a84 100644 --- a/docs/guide/scala/type-serialization.md +++ b/docs/guide/scala/type-serialization.md @@ -19,7 +19,9 @@ license: | limitations under the License. --- -This page covers serialization of Scala-specific types. +This page covers serialization of Scala-specific JVM types in native mode. For +cross-language Scala models, use the xlang path described in +[Schema IDL And Xlang](schema-idl.md). ## Setup @@ -29,7 +31,7 @@ All examples assume the following setup: import org.apache.fory.Fory import org.apache.fory.scala.ForyScala -val fory = ForyScala.builder() +val fory = ForyScala.builder().withXlang(false) .build() ``` diff --git a/docs/guide/swift/configuration.md b/docs/guide/swift/configuration.md index 8a1b9d7ae9..4875e3e797 100644 --- a/docs/guide/swift/configuration.md +++ b/docs/guide/swift/configuration.md @@ -19,26 +19,32 @@ license: | limitations under the License. --- -This page covers `ForyConfig` and recommended runtime presets. +This page covers `Config` and recommended runtime presets. -## ForyConfig +## Config `Fory` is configured with: ```swift -public struct ForyConfig { - public var xlang: Bool - public var trackRef: Bool - public var compatible: Bool +public struct Config { + public let trackRef: Bool + public let compatible: Bool + public let checkClassVersion: Bool + public let maxCollectionSize: Int + public let maxBinarySize: Int + public let maxDepth: Int } ``` Default configuration: ```swift -let fory = Fory() // xlang=true, ref=false, compatible=true +let fory = Fory() // ref=false, compatible=true ``` +Swift supports the xlang wire format only, so there is no `xlang` option in +`Config` or the `Fory` initializer. + ## Threading `Fory` is single-threaded and optimized to reuse one read/write context pair on the calling thread. @@ -46,17 +52,6 @@ Reuse one instance per thread and do not use the same instance concurrently. ## Options -### `xlang` - -Controls cross-language protocol mode. - -- `true`: Use xlang wire format (default) -- `false`: Use Swift-native mode - -```swift -let fory = Fory(xlang: true, compatible: true) -``` - ### `trackRef` Enables shared/circular reference tracking for reference-trackable types. @@ -65,7 +60,7 @@ Enables shared/circular reference tracking for reference-trackable types. - `true`: Preserve object identity for class/reference graphs ```swift -let fory = Fory(xlang: true, ref: true, compatible: true) +let fory = Fory(ref: true) ``` ### `compatible` @@ -76,7 +71,25 @@ Enables compatible schema mode for evolution across versions. - `true`: Compatible mode (supports add/remove/reorder fields) ```swift -let fory = Fory(xlang: true, ref: false, compatible: true) +let fory = Fory() +``` + +### `checkClassVersion` + +Controls class-version validation in schema-consistent mode. When omitted, it +defaults to `true` when `compatible=false` and `false` when `compatible=true`. + +```swift +let fory = Fory(compatible: false, checkClassVersion: true) +``` + +### Size and Depth Limits + +`maxCollectionSize`, `maxBinarySize`, and `maxDepth` bound decoded payload size +and nesting depth. + +```swift +let fory = Fory(maxCollectionSize: 1_000_000, maxBinarySize: 64 * 1024 * 1024, maxDepth: 5) ``` ## Recommended Presets @@ -84,17 +97,17 @@ let fory = Fory(xlang: true, ref: false, compatible: true) ### Local, strict schema ```swift -let fory = Fory(xlang: false, ref: false, compatible: false) +let fory = Fory(ref: false, compatible: false) ``` ### Cross-language service payloads ```swift -let fory = Fory(xlang: true, ref: false, compatible: true) +let fory = Fory() ``` ### Graph/object identity workloads ```swift -let fory = Fory(xlang: true, ref: true, compatible: true) +let fory = Fory(ref: true) ``` diff --git a/docs/guide/swift/cross-language.md b/docs/guide/swift/cross-language.md index 441276624b..ff2bb6133f 100644 --- a/docs/guide/swift/cross-language.md +++ b/docs/guide/swift/cross-language.md @@ -1,6 +1,6 @@ --- title: Cross-Language Serialization -sidebar_position: 9 +sidebar_position: 3 id: cross_language license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -24,7 +24,7 @@ Fory Swift can exchange payloads with other Fory runtimes using the xlang protoc ## Recommended Cross-language Configuration ```swift -let fory = Fory(xlang: true, ref: false, compatible: true) +let fory = Fory() ``` ## Register Types with Shared Identity @@ -38,7 +38,7 @@ struct Order { var amount: Double = 0 } -let fory = Fory(xlang: true, compatible: true) +let fory = Fory() fory.register(Order.self, id: 100) ``` @@ -51,7 +51,7 @@ try fory.register(Order.self, namespace: "com.example", name: "Order") ## Cross-language Rules - Keep type registration mapping consistent across languages -- Use compatible mode when independently evolving schemas +- Keep compatible mode enabled when independently evolving schemas. Swift enables it by default. - Register all user-defined concrete types used by dynamic fields (`Any`, `any Serializer`) ## Lists and Dense Arrays @@ -60,22 +60,22 @@ Swift `Array` fields map to Fory `list` unless field metadata explicitly requests dense `array`. Use `array` only for one-dimensional bool or numeric data. -| Fory schema | Swift field metadata sketch | -| ----------------- | ---------------------------------------------------------- | -| `list` | `@ListField(element: .int32()) var ids: [Int32]` | -| `array` | `@ArrayField(element: .bool()) var flags: [Bool]` | -| `array` | `@ArrayField(element: .int8()) var values: [Int8]` | -| `array` | `@ArrayField(element: .int16()) var values: [Int16]` | -| `array` | `@ArrayField(element: .int32()) var values: [Int32]` | -| `array` | `@ArrayField(element: .int64()) var values: [Int64]` | -| `array` | `@ArrayField(element: .uint8()) var values: [UInt8]` | -| `array` | `@ArrayField(element: .uint16()) var values: [UInt16]` | -| `array` | `@ArrayField(element: .uint32()) var values: [UInt32]` | -| `array` | `@ArrayField(element: .uint64()) var values: [UInt64]` | -| `array` | `@ArrayField(element: .float16()) var values: [Float16]` | -| `array` | `@ArrayField(element: .bfloat16()) var values: [BFloat16]` | -| `array` | `@ArrayField(element: .float32()) var values: [Float]` | -| `array` | `@ArrayField(element: .float64()) var values: [Double]` | +| Fory schema | Swift field metadata sketch | +| ----------------- | -------------------------------------------------------- | +| `list` | `@ListField(element: .int32()) var ids: [Int32]` | +| `array` | `@ArrayField(element: .bool) var flags: [Bool]` | +| `array` | `@ArrayField(element: .int8) var values: [Int8]` | +| `array` | `@ArrayField(element: .int16) var values: [Int16]` | +| `array` | `@ArrayField(element: .int32()) var values: [Int32]` | +| `array` | `@ArrayField(element: .int64()) var values: [Int64]` | +| `array` | `@ArrayField(element: .uint8) var values: [UInt8]` | +| `array` | `@ArrayField(element: .uint16) var values: [UInt16]` | +| `array` | `@ArrayField(element: .uint32()) var values: [UInt32]` | +| `array` | `@ArrayField(element: .uint64()) var values: [UInt64]` | +| `array` | `@ArrayField(element: .float16) var values: [Float16]` | +| `array` | `@ArrayField(element: .bfloat16) var values: [BFloat16]` | +| `array` | `@ArrayField(element: .float32) var values: [Float]` | +| `array` | `@ArrayField(element: .float64) var values: [Double]` | ## Swift IDL Workflow @@ -95,7 +95,7 @@ Generated Swift code includes: Use generated registration before cross-language serialization: ```swift -let fory = Fory(xlang: true, ref: true, compatible: true) +let fory = Fory(ref: true) try Addressbook.ForyRegistration.register(fory) let payload = try fory.serialize(book) diff --git a/docs/guide/swift/custom-serializers.md b/docs/guide/swift/custom-serializers.md index b6723aae7e..2ced166780 100644 --- a/docs/guide/swift/custom-serializers.md +++ b/docs/guide/swift/custom-serializers.md @@ -1,6 +1,6 @@ --- title: Custom Serializers -sidebar_position: 4 +sidebar_position: 9 id: custom_serializers license: | Licensed to the Apache Software Foundation (ASF) under one or more @@ -25,7 +25,7 @@ For types that cannot or should not use Fory model macros, implement `Serializer - External types with strict wire compatibility requirements - Specialized compact encodings -- Legacy payload migration paths +- Existing payload adaptation paths - Highly tuned hot-path serialization ## Implementing `Serializer` diff --git a/docs/guide/swift/index.md b/docs/guide/swift/index.md index 93334b4898..e96e785fff 100644 --- a/docs/guide/swift/index.md +++ b/docs/guide/swift/index.md @@ -52,14 +52,13 @@ targets: [ - [Configuration](configuration.md) - [Basic Serialization](basic-serialization.md) +- [Cross-Language Serialization](cross-language.md) +- [Schema Metadata](schema-metadata.md) - [Type Registration](type-registration.md) - [Custom Serializers](custom-serializers.md) -- [Field Configuration](field-configuration.md) - [Shared and Circular References](references.md) - [Polymorphism and Dynamic Types](polymorphism.md) - [Schema Evolution](schema-evolution.md) -- [Cross-Language Serialization](cross-language.md) -- [Row Format Status](row-format.md) - [Troubleshooting](troubleshooting.md) ## Quick Example diff --git a/docs/guide/swift/references.md b/docs/guide/swift/references.md index edf62216ce..3a1f14ab15 100644 --- a/docs/guide/swift/references.md +++ b/docs/guide/swift/references.md @@ -19,12 +19,12 @@ license: | limitations under the License. --- -Swift reference tracking is controlled by `ForyConfig.trackRef`. +Swift reference tracking is controlled by `Config.trackRef`. ## Enable Reference Tracking ```swift -let fory = Fory(xlang: true, ref: true, compatible: false) +let fory = Fory(ref: true, compatible: false) ``` When enabled, reference-trackable types preserve identity and cycles. @@ -58,7 +58,7 @@ final class AnimalPair { } } -let fory = Fory(xlang: true, ref: true, compatible: true) +let fory = Fory(ref: true) fory.register(Animal.self, id: 200) fory.register(AnimalPair.self, id: 201) @@ -92,7 +92,7 @@ final class Node { } } -let fory = Fory(xlang: true, ref: true, compatible: true) +let fory = Fory(ref: true) fory.register(Node.self, id: 300) let node = Node(value: 7) diff --git a/docs/guide/swift/row-format.md b/docs/guide/swift/row-format.md deleted file mode 100644 index 4143b68c80..0000000000 --- a/docs/guide/swift/row-format.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -title: Row Format -sidebar_position: 10 -id: row_format -license: | - 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. ---- - -Swift currently focuses on object-graph serialization (`Fory.serialize` / `Fory.deserialize`). - -## Current Status - -- Object graph serialization: supported -- Cross-language xlang object protocol: supported -- Swift row-format APIs: not exposed yet - -## Recommended Approach Today - -- Use object serialization for application payloads -- Use compatible mode for schema evolution across service versions -- Use xlang mode for interoperability with other Fory runtimes - -## Future Direction - -Row-format support in Swift can be added without changing existing object serialization APIs. Until then, treat row-format features as unavailable in Swift runtime code. diff --git a/docs/guide/swift/schema-evolution.md b/docs/guide/swift/schema-evolution.md index c7ccad1f8a..f7fe019136 100644 --- a/docs/guide/swift/schema-evolution.md +++ b/docs/guide/swift/schema-evolution.md @@ -19,12 +19,12 @@ license: | limitations under the License. --- -Fory supports schema evolution through compatible mode. +Fory supports schema evolution through compatible mode, which is enabled by default in Swift. -## Enable Compatible Mode +## Default Compatible Mode ```swift -let fory = Fory(xlang: true, ref: false, compatible: true) +let fory = Fory() ``` ## Example: Evolving a Struct @@ -46,10 +46,10 @@ struct PersonV2 { var phone: String? = nil // added field } -let writer = Fory(xlang: true, compatible: true) +let writer = Fory() writer.register(PersonV1.self, id: 1) -let reader = Fory(xlang: true, compatible: true) +let reader = Fory() reader.register(PersonV2.self, id: 1) let v1 = PersonV1(name: "alice", age: 30, address: "main st") diff --git a/docs/guide/swift/field-configuration.md b/docs/guide/swift/schema-metadata.md similarity index 88% rename from docs/guide/swift/field-configuration.md rename to docs/guide/swift/schema-metadata.md index fc6da8020f..79c4fa35d1 100644 --- a/docs/guide/swift/field-configuration.md +++ b/docs/guide/swift/schema-metadata.md @@ -1,7 +1,7 @@ --- -title: Field Configuration -sidebar_position: 5 -id: field_configuration +title: Schema Metadata +sidebar_position: 4 +id: schema_metadata license: | Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with @@ -19,7 +19,7 @@ license: | limitations under the License. --- -This page covers macro-level field configuration in Swift. +This page covers macro-level schema metadata in Swift. ## Available Macro Attributes @@ -27,7 +27,7 @@ This page covers macro-level field configuration in Swift. - `@ForyEnum` on C-style enum models - `@ForyUnion` and `@ForyCase` on associated-value enum models - `@ForyField(encoding: ...)` on numeric fields -- `@ListField`, `@SetField`, and `@MapField` for nested collection field metadata +- `@ListField`, `@ArrayField`, `@SetField`, and `@MapField` for collection field metadata ## `@ForyField(encoding:)` @@ -59,8 +59,9 @@ Compile-time validation rejects unsupported combinations (for example, `Int32` w ## Nested Collection Field Metadata -Use `@ListField`, `@SetField`, and `@MapField` when a nested field needs type-specific -wire metadata, such as fixed or tagged integer encoding inside a container. +Use `@ListField`, `@ArrayField`, `@SetField`, and `@MapField` when a collection field +needs type-specific wire metadata, such as fixed or tagged integer encoding inside a +container. Use `@ArrayField` for dense non-null bool, integer, and floating-point arrays. ```swift @ForyStruct @@ -68,6 +69,9 @@ struct NestedMetrics: Equatable { @ListField(element: .encoding(.fixed)) var values: [Int32?] = [] + @ArrayField(element: .int32()) + var denseValues: [Int32] = [] + @SetField(element: .encoding(.fixed)) var ids: Set = [] diff --git a/docs/guide/swift/troubleshooting.md b/docs/guide/swift/troubleshooting.md index 02f768dddf..4e1f91e24b 100644 --- a/docs/guide/swift/troubleshooting.md +++ b/docs/guide/swift/troubleshooting.md @@ -44,9 +44,11 @@ Fix: ### `Invalid data: xlang bitmap mismatch` -Cause: serializer and deserializer use different `xlang` settings. +Cause: the input was produced by a peer runtime that did not write the xlang +wire format Swift expects. -Fix: configure both sides with matching `xlang` mode. +Fix: configure the peer serializer to write xlang format. Swift already uses +xlang format and has no native-mode switch. ### `Invalid data: class version hash mismatch` diff --git a/docs/guide/swift/type-registration.md b/docs/guide/swift/type-registration.md index 56297c10cb..8021535e5a 100644 --- a/docs/guide/swift/type-registration.md +++ b/docs/guide/swift/type-registration.md @@ -1,6 +1,6 @@ --- title: Type Registration -sidebar_position: 3 +sidebar_position: 5 id: type_registration license: | Licensed to the Apache Software Foundation (ASF) under one or more diff --git a/docs/guide/xlang/field-nullability.md b/docs/guide/xlang/field-nullability.md index 5d891fc7d4..1566efab26 100644 --- a/docs/guide/xlang/field-nullability.md +++ b/docs/guide/xlang/field-nullability.md @@ -108,8 +108,8 @@ public class Person { } Fory fory = Fory.builder() - .withXlang(true).withCompatible(true) - .build(); + .withXlang(true) + .build(); fory.register(Person.class, "example.Person"); ``` @@ -131,7 +131,7 @@ class Person: nickname: Optional[str] = None # Can be None bio: Optional[str] = None # Can be None -fory = pyfory.Fory(xlang=True, compatible=True) +fory = pyfory.Fory(xlang=True) fory.register_type(Person, typename="example.Person") ``` @@ -167,7 +167,7 @@ type Person struct { Bio *string // Can be nil } -fory := forygo.NewFory(forygo.WithXlang(true), forygo.WithCompatible(true)) +fory := forygo.NewFory(forygo.WithXlang(true)) fory.RegisterStructByName(Person{}, "example.Person") ``` diff --git a/docs/guide/xlang/field-reference-tracking.md b/docs/guide/xlang/field-reference-tracking.md index 68405d3edc..c3755b81a8 100644 --- a/docs/guide/xlang/field-reference-tracking.md +++ b/docs/guide/xlang/field-reference-tracking.md @@ -35,22 +35,22 @@ Reference tracking enables: ```java Fory fory = Fory.builder() - .withXlang(true).withCompatible(true) - .withRefTracking(true) + .withXlang(true) + .withRefTracking(true) .build(); ``` ### Python ```python -fory = pyfory.Fory(xlang=True, compatible=True, ref=True) +fory = pyfory.Fory(xlang=True, ref=True) ``` ### Go ```go fory := forygo.NewFory( - forygo.WithXlang(true), forygo.WithCompatible(true), + forygo.WithXlang(true), forygo.WithTrackRef(true), ) ``` @@ -58,14 +58,14 @@ fory := forygo.NewFory( ### C++ ```cpp -auto fory = fory::Fory::builder().xlang(true).compatible(true).track_ref(true).build(); +auto fory = fory::serialization::Fory::builder().xlang(true).track_ref(true).build(); ``` ### Rust ```rust let fory = Fory::builder() - .xlang(true).compatible(true) + .xlang(true) .track_ref(true).build(); ``` @@ -75,9 +75,8 @@ let fory = Fory::builder() import org.apache.fory.scala.ForyScala val fory = ForyScala.builder() - .withXlang(true) - .withCompatible(true) - .withRefTracking(true) + .withXlang(true) + .withRefTracking(true) .build() ``` @@ -115,8 +114,8 @@ Key behavior: ```java // Reference tracking enabled, but non-nullable fields still skip ref flags Fory fory = Fory.builder() - .withXlang(true).withCompatible(true) - .withRefTracking(true) + .withXlang(true) + .withRefTracking(true) .build(); ``` @@ -174,7 +173,7 @@ FORY_STRUCT(Document, ``` To disable reference tracking for C++ entirely, set -`Fory::builder().track_ref(false).build()` on the serializer. +`Fory::builder().xlang(true).track_ref(false).build()` on the serializer. #### Rust: Field Attributes diff --git a/docs/guide/xlang/getting-started.md b/docs/guide/xlang/getting-started.md index 62f279b340..e21737606c 100644 --- a/docs/guide/xlang/getting-started.md +++ b/docs/guide/xlang/getting-started.md @@ -63,16 +63,18 @@ fory = "0.13" ### JavaScript ```bash -npm install @apache-fory/fory +npm install @apache-fory/core ``` ### C++ Use Bazel or CMake to build from source. See [C++ Guide](../cpp/index.md) for details. -## Enable Cross-Language Mode +## Create an Xlang Runtime -Each language requires enabling xlang mode to ensure binary compatibility across languages. +Xlang mode is the default for runtimes that expose a mode switch. Swift, C#, JavaScript/TypeScript, +and Dart only expose the xlang wire format. The examples below keep compatible schema evolution on +the default path and show only options that change another setting. ### Java @@ -81,9 +83,8 @@ import org.apache.fory.*; import org.apache.fory.config.*; Fory fory = Fory.builder() - .withXlang(true) // Enable cross-language mode - .withCompatible(true) // Recommended xlang default - .withRefTracking(true) // Optional: for circular references + .withXlang(true) + .withRefTracking(true) // Optional: for circular references .build(); ``` @@ -92,11 +93,10 @@ Fory fory = Fory.builder() ```python import pyfory -# Cross-language mode uses compatible=True by default; set it explicitly in examples. -fory = pyfory.Fory(xlang=True, compatible=True) +fory = pyfory.Fory(xlang=True) # Enable reference tracking when needed -fory = pyfory.Fory(xlang=True, compatible=True, ref=True) +fory = pyfory.Fory(xlang=True, ref=True) ``` ### Go @@ -104,9 +104,9 @@ fory = pyfory.Fory(xlang=True, compatible=True, ref=True) ```go import forygo "github.com/apache/fory/go/fory" -fory := forygo.NewFory(forygo.WithXlang(true), forygo.WithCompatible(true)) +fory := forygo.NewFory(forygo.WithXlang(true)) // Or with reference tracking -fory := forygo.NewFory(forygo.WithXlang(true), forygo.WithCompatible(true), forygo.WithTrackRef(true)) +fory := forygo.NewFory(forygo.WithXlang(true), forygo.WithTrackRef(true)) ``` ### Rust @@ -114,15 +114,15 @@ fory := forygo.NewFory(forygo.WithXlang(true), forygo.WithCompatible(true), fory ```rust use fory::Fory; -let fory = Fory::builder().xlang(true).compatible(true).build(); +let fory = Fory::builder().xlang(true).build(); ``` ### JavaScript ```javascript -import Fory from "@apache-fory/fory"; +import Fory, { Type } from "@apache-fory/core"; -const fory = new Fory({ compatible: true }); +const fory = new Fory(); ``` ### C++ @@ -132,10 +132,7 @@ const fory = new Fory({ compatible: true }); using namespace fory::serialization; -auto fory = Fory::builder() - .xlang(true) - .compatible(true) - .build(); +auto fory = Fory::builder().xlang(true).build(); ``` ## Type Registration @@ -175,7 +172,7 @@ struct Person { age: i32, } -let mut fory = Fory::builder().xlang(true).compatible(true).build(); +let mut fory = Fory::builder().xlang(true).build(); fory .register_by_name::("example", "Person") .expect("register Person"); @@ -184,19 +181,22 @@ fory **JavaScript:** ```javascript -const description = Type.object("example.Person", { - name: Type.string(), - age: Type.int32(), -}); -fory.registerSerializer(description); +const personType = Type.struct( + { typeName: "example.Person" }, + { + name: Type.string(), + age: Type.int32(), + }, +); +const { serialize, deserialize } = fory.register(personType); ``` **C++:** ```cpp -fory.register_struct("example.Person"); +fory.register_struct("example", "Person"); // For enums, use register_enum: -// fory.register_enum("example.Color"); +// fory.register_enum("example", "Color"); ``` ### Register by ID @@ -247,9 +247,7 @@ public class Person { public class HelloWorld { public static void main(String[] args) throws Exception { - Fory fory = Fory.builder() - .withXlang(true).withCompatible(true) - .build(); + Fory fory = Fory.builder().withXlang(true).build(); fory.register(Person.class, "example.Person"); Person person = new Person(); @@ -274,7 +272,7 @@ class Person: name: str age: pyfory.Int32 -fory = pyfory.Fory(xlang=True, compatible=True) +fory = pyfory.Fory(xlang=True) fory.register_type(Person, typename="example.Person") with open("person.bin", "rb") as f: diff --git a/docs/guide/xlang/index.md b/docs/guide/xlang/index.md index 1aa2958123..9b4e8866af 100644 --- a/docs/guide/xlang/index.md +++ b/docs/guide/xlang/index.md @@ -1,5 +1,5 @@ --- -title: Cross-Language Serialization Guide +title: Xlang Serialization Guide sidebar_position: 0 id: serialization_index license: | @@ -19,41 +19,48 @@ license: | limitations under the License. --- -Apache Fory™ xlang (cross-language) serialization enables seamless data exchange between different programming languages. Serialize data in one language and deserialize it in another without manual data conversion. You can use either direct native types (no IDL) or a schema-first Fory IDL workflow. +Apache Fory™ xlang serialization is the default wire format for cross-language payloads. Serialize +data in one language and deserialize it in another without manual conversion. You can use direct +language model types for small contracts, or use Fory IDL and code generation when a schema-first +workflow is a better fit. ## Features -- **No IDL Required**: Serialize objects directly with native language types -- **Multi-Language Support**: Java, Python, C++, Go, Rust, JavaScript all interoperate seamlessly -- **Reference Support**: Shared and circular references work across language boundaries -- **Schema Evolution**: Forward/backward compatibility when class definitions change -- **Zero-Copy**: Out-of-band serialization for large binary data -- **High Performance**: JIT compilation and optimized binary protocol +- **No IDL required**: Serialize objects directly with language model types. +- **Multi-language support**: Java, Python, C++, Go, Rust, JavaScript/TypeScript, C#, Swift, and Dart interoperate through the same xlang format. +- **Reference support**: Shared and circular references work across language boundaries when reference tracking is enabled in each runtime. +- **Schema evolution**: Compatible mode is the xlang default so readers can tolerate added, removed, or reordered fields. +- **Out-of-band buffers**: Language runtimes can expose zero-copy buffer paths for large binary data. +- **High performance**: Runtimes use generated serializers, JIT serializers, or optimized code paths where available. ## Supported Languages -| Language | Status | Package | -| ---------- | ------ | -------------------------------- | -| Java | ✅ | `org.apache.fory:fory-core` | -| Python | ✅ | `pyfory` | -| C++ | ✅ | Bazel/CMake build | -| Go | ✅ | `github.com/apache/fory/go/fory` | -| Rust | ✅ | `fory` crate | -| JavaScript | ✅ | `@apache-fory/fory` | +| Language | Status | Package or target | +| --------------------- | --------- | -------------------------------- | +| Java | Supported | `org.apache.fory:fory-core` | +| Python | Supported | `pyfory` | +| C++ | Supported | Bazel/CMake build | +| Go | Supported | `github.com/apache/fory/go/fory` | +| Rust | Supported | `fory` crate | +| JavaScript/TypeScript | Supported | `@apache-fory/core` | +| C# | Supported | `Apache.Fory` | +| Swift | Supported | Swift Package Manager target | +| Dart | Supported | `fory` package | ## When to Use Xlang Mode -**Use xlang mode when:** +Use xlang mode when: - Building multi-language microservices - Creating polyglot data pipelines - Sharing data between frontend (JavaScript) and backend (Java/Python/Go) -**Use language-native mode when:** +Use native mode for same-language traffic in Java, Scala, Kotlin, Python, C++, +Go, or Rust: - All serialization/deserialization happens in the same language -- Maximum performance is required (native mode is faster) -- You need language-specific features (Python pickle compatibility, Java serialization hooks) +- You need language-specific features such as Python pickle-style objects or Java serialization hooks +- You want native-mode schema-consistent payloads for same-language services ## Quick Example @@ -68,9 +75,7 @@ public class Person { public int age; } -Fory fory = Fory.builder() - .withXlang(true).withCompatible(true) - .build(); +Fory fory = Fory.builder().withXlang(true).build(); fory.register(Person.class, "example.Person"); Person person = new Person(); @@ -91,7 +96,7 @@ class Person: name: str age: pyfory.Int32 -fory = pyfory.Fory(xlang=True, compatible=True) +fory = pyfory.Fory(xlang=True) fory.register_type(Person, typename="example.Person") # Receive bytes from Java diff --git a/docs/guide/xlang/row_format.md b/docs/guide/xlang/row_format.md index c17dc9800c..afc256b7b3 100644 --- a/docs/guide/xlang/row_format.md +++ b/docs/guide/xlang/row_format.md @@ -185,6 +185,6 @@ Parent decoded = encoder.fromRow(row); ## See Also -- [Row Format Specification](https://fory.apache.org/docs/next/specification/fory_row_format_spec) - Binary format details +- [Row Format Specification](https://fory.apache.org/docs/specification/row_format_spec) - Binary format details - [Java Row Format Guide](../java/row-format.md) - Java-specific row format documentation - [Python Row Format Guide](../python/row-format.md) - Python-specific row format documentation diff --git a/docs/guide/xlang/serialization.md b/docs/guide/xlang/serialization.md index 68be9c5ef6..cd4e94bf54 100644 --- a/docs/guide/xlang/serialization.md +++ b/docs/guide/xlang/serialization.md @@ -44,7 +44,7 @@ import java.util.*; public class Example1 { public static void main(String[] args) { - Fory fory = Fory.builder().withXlang(true).withCompatible(true).build(); + Fory fory = Fory.builder().withXlang(true).build(); List list = ofArrayList(true, false, "str", -1.1, 1, new int[100], new double[20]); byte[] bytes = fory.serialize(list); // bytes can be deserialized by other languages @@ -66,7 +66,7 @@ public class Example1 { import pyfory import numpy as np -fory = pyfory.Fory(xlang=True, compatible=True) +fory = pyfory.Fory(xlang=True) object_list = [True, False, "str", -1.1, 1, np.full(100, 0, dtype=np.int32), np.full(20, 0.0, dtype=np.double)] data = fory.serialize(object_list) @@ -89,7 +89,7 @@ import "fmt" func main() { list := []any{true, false, "str", -1.1, 1, make([]int32, 10), make([]float64, 20)} - fory := forygo.NewFory(forygo.WithXlang(true), forygo.WithCompatible(true)) + fory := forygo.NewFory(forygo.WithXlang(true)) bytes, err := fory.Marshal(list) if err != nil { panic(err) @@ -120,17 +120,9 @@ func main() { ### JavaScript ```javascript -import Fory from "@apache-fory/fory"; +import Fory from "@apache-fory/core"; -/** - * @apache-fory/hps use v8's fast-calls-api that can be called directly by jit, - * ensure that the version of Node is 20 or above. - * Experimental feature, installation success cannot be guaranteed at this moment. - * If you are unable to install the module, replace it with `const hps = null;` - **/ -import hps from "@apache-fory/hps"; - -const fory = new Fory({ hps }); +const fory = new Fory(); const input = fory.serialize("hello fory"); const result = fory.deserialize(input); console.log(result); @@ -142,7 +134,7 @@ console.log(result); use fory::Fory; fn run() { - let fory = Fory::builder().xlang(true).compatible(true).build(); + let fory = Fory::builder().xlang(true).build(); let bin = fory.serialize(&"hello".to_string()).expect("serialize success"); let obj: String = fory.deserialize(&bin).expect("deserialize success"); assert_eq!("hello".to_string(), obj); @@ -203,7 +195,7 @@ public class Example2 { // mvn exec:java -Dexec.mainClass="org.apache.fory.examples.Example2" public static void main(String[] args) { - Fory fory = Fory.builder().withXlang(true).withCompatible(true).build(); + Fory fory = Fory.builder().withXlang(true).build(); fory.register(SomeClass1.class, "example.SomeClass1"); fory.register(SomeClass2.class, "example.SomeClass2"); byte[] bytes = fory.serialize(createObject()); @@ -247,7 +239,7 @@ class SomeClass2: if __name__ == "__main__": - f = pyfory.Fory(xlang=True, compatible=True) + f = pyfory.Fory(xlang=True) f.register_type(SomeClass1, typename="example.SomeClass1") f.register_type(SomeClass2, typename="example.SomeClass2") obj1 = SomeClass1(f1=True, f2={-1: 2}) @@ -298,7 +290,7 @@ func main() { F11 []int16 F12 []int16 } - serializer := forygo.NewFory(forygo.WithXlang(true), forygo.WithCompatible(true)) + serializer := forygo.NewFory(forygo.WithXlang(true)) if err := serializer.RegisterStructByName(SomeClass1{}, "example.SomeClass1"); err != nil { panic(err) } @@ -336,22 +328,17 @@ func main() { ### JavaScript ```javascript -import Fory, { Type, InternalSerializerType } from "@apache-fory/fory"; - -/** - * @apache-fory/hps use v8's fast-calls-api that can be called directly by jit, - * ensure that the version of Node is 20 or above. - * Experimental feature, installation success cannot be guaranteed at this moment. - * If you are unable to install the module, replace it with `const hps = null;` - **/ -import hps from "@apache-fory/hps"; +import Fory, { Type } from "@apache-fory/core"; // Describe data structures using JSON schema -const description = Type.object("example.foo", { - foo: Type.string(), -}); -const fory = new Fory({ hps }); -const { serialize, deserialize } = fory.registerSerializer(description); +const description = Type.struct( + { typeName: "example.foo" }, + { + foo: Type.string(), + }, +); +const fory = new Fory(); +const { serialize, deserialize } = fory.register(description); const input = serialize({ foo: "hello fory" }); const result = deserialize(input); console.log(result); @@ -408,7 +395,7 @@ fn complex_struct() { c6: 4.0, }; - let mut fory = Fory::builder().xlang(true).compatible(true).build(); + let mut fory = Fory::builder().xlang(true).build(); fory .register_by_name::("example", "foo2") .expect("register Animal"); @@ -449,8 +436,10 @@ public class ReferenceExample { // mvn exec:java -Dexec.mainClass="org.apache.fory.examples.ReferenceExample" public static void main(String[] args) { - Fory fory = Fory.builder().withXlang(true).withCompatible(true) - .withRefTracking(true).build(); + Fory fory = Fory.builder() + .withXlang(true) + .withRefTracking(true) + .build(); fory.register(SomeClass.class, "example.SomeClass"); byte[] bytes = fory.serialize(createObject()); // bytes can be deserialized by other languages @@ -470,7 +459,7 @@ class SomeClass: f2: Dict[str, str] f3: Dict[str, str] -fory = pyfory.Fory(xlang=True, compatible=True, ref=True) +fory = pyfory.Fory(xlang=True, ref=True) fory.register_type(SomeClass, typename="example.SomeClass") obj = SomeClass() obj.f2 = {"k1": "v1", "k2": "v2"} @@ -494,10 +483,7 @@ func main() { F2 map[string]string F3 map[string]string } - fory := forygo.NewFory( - forygo.WithXlang(true), forygo.WithCompatible(true), - forygo.WithTrackRef(true), - ) + fory := forygo.NewFory(forygo.WithXlang(true), forygo.WithTrackRef(true)) if err := fory.RegisterStruct(SomeClass{}, 65); err != nil { panic(err) } @@ -520,22 +506,15 @@ func main() { ### JavaScript ```javascript -import Fory, { Type } from "@apache-fory/fory"; -/** - * @apache-fory/hps use v8's fast-calls-api that can be called directly by jit, - * ensure that the version of Node is 20 or above. - * Experimental feature, installation success cannot be guaranteed at this moment. - * If you are unable to install the module, replace it with `const hps = null;` - **/ -import hps from "@apache-fory/hps"; - -const description = Type.object("example.foo", { +import Fory, { Type } from "@apache-fory/core"; + +const description = Type.struct("example.foo", { foo: Type.string(), - bar: Type.object("example.foo"), + bar: Type.struct("example.foo").setTrackingRef(true), }); -const fory = new Fory({ hps }); -const { serialize, deserialize } = fory.registerSerializer(description); +const fory = new Fory({ ref: true }); +const { serialize, deserialize } = fory.register(description); const data: any = { foo: "hello fory", }; diff --git a/docs/guide/xlang/troubleshooting.md b/docs/guide/xlang/troubleshooting.md index 9b5ab032fd..de2430e06c 100644 --- a/docs/guide/xlang/troubleshooting.md +++ b/docs/guide/xlang/troubleshooting.md @@ -180,14 +180,13 @@ StackOverflowError or RecursionError ```java // Java Fory fory = Fory.builder() - .withXlang(true).withCompatible(true) .withRefTracking(true) .build(); ``` ```python # Python -fory = pyfory.Fory(xlang=True, compatible=True, ref=True) +fory = pyfory.Fory(ref=True) ``` ### Duplicate Objects @@ -198,7 +197,7 @@ fory = pyfory.Fory(xlang=True, compatible=True, ref=True) **Solution:** Enable reference tracking if objects are shared within the graph. -## Language Mode Issues +## Xlang Type Issues ### Incompatible Types in Xlang Mode @@ -240,10 +239,10 @@ metadata must still align exactly. 1. Align the schemas carefully on every service and language: field names or field IDs, field order where schema-consistent mode requires it, type annotations, nullability, and type registration IDs/names. -2. Or use compatible mode on every peer, for example `withCompatible(true)` in Java, - `compatible=True` in Python, `compatible(true)` in Rust, or `WithCompatible(true)` in Go. +2. Xlang mode defaults to compatible mode in current runtimes. If a peer has explicitly selected + schema-consistent mode, remove that override or enable compatible mode on every peer. Compatible mode writes extra schema metadata, so payloads are larger, but it is recommended - for `xlang=true` services that may evolve independently. + for xlang services that may evolve independently. 3. Use schema-consistent mode only when schemas do not change, or when all services deploy the schema change at the same time. @@ -312,7 +311,7 @@ python deserializer.py data.bin 1. **Not registering types**: Always register custom types before use 2. **Inconsistent type names/IDs**: Use the same names/IDs across all languages -3. **Forgetting xlang mode**: Use `.withXlang(true).withCompatible(true)` in Java +3. **Mixing xlang and native payloads**: Keep every peer on the xlang wire format 4. **Wrong type annotations**: Use markers such as `pyfory.Int32` in Python 5. **Ignoring reference tracking**: Enable for circular/shared references diff --git a/docs/guide/xlang/zero-copy.md b/docs/guide/xlang/zero-copy.md index aa91bba5ae..2d37215d30 100644 --- a/docs/guide/xlang/zero-copy.md +++ b/docs/guide/xlang/zero-copy.md @@ -51,7 +51,7 @@ import java.util.stream.Collectors; public class ZeroCopyExample { public static void main(String[] args) { - Fory fory = Fory.builder().withXlang(true).withCompatible(true).build(); + Fory fory = Fory.builder().withXlang(true).build(); // Data with large arrays List list = List.of( @@ -84,7 +84,7 @@ import array import pyfory import numpy as np -fory = pyfory.Fory(xlang=True, compatible=True) +fory = pyfory.Fory(xlang=True) # Data with large arrays data = [ @@ -115,7 +115,7 @@ import forygo "github.com/apache/fory/go/fory" import "fmt" func main() { - serializer := forygo.NewFory(forygo.WithXlang(true), forygo.WithCompatible(true)) + serializer := forygo.NewFory(forygo.WithXlang(true)) // Data with large arrays list := []any{ @@ -149,12 +149,6 @@ func main() { } ``` -## JavaScript - -```javascript -// Zero-copy support coming soon -``` - ## Use Cases ### High-Performance Data Transfer diff --git a/docs/specification/java_serialization_spec.md b/docs/specification/java_serialization_spec.md index 2d759e1c8e..d0a7e18a06 100644 --- a/docs/specification/java_serialization_spec.md +++ b/docs/specification/java_serialization_spec.md @@ -143,38 +143,38 @@ The shared scalar IDs are: Java native built-ins start at ID `69`: -| ID | Name | Java type or serializer owner | -| --- | ---------------------------- | ---------------------------------------- | -| 69 | `VOID_ID` | `java.lang.Void` | -| 70 | `CHAR_ID` | `java.lang.Character` | -| 71 | `PRIMITIVE_VOID_ID` | `void` | -| 72 | `PRIMITIVE_BOOL_ID` | `boolean` | -| 73 | `PRIMITIVE_INT8_ID` | `byte` | -| 74 | `PRIMITIVE_CHAR_ID` | `char` | -| 75 | `PRIMITIVE_INT16_ID` | `short` | -| 76 | `PRIMITIVE_INT32_ID` | `int` | -| 77 | `PRIMITIVE_FLOAT32_ID` | `float` | -| 78 | `PRIMITIVE_INT64_ID` | `long` | -| 79 | `PRIMITIVE_FLOAT64_ID` | `double` | -| 80 | `PRIMITIVE_BOOLEAN_ARRAY_ID` | `boolean[]` | -| 81 | `PRIMITIVE_BYTE_ARRAY_ID` | `byte[]` | -| 82 | `PRIMITIVE_CHAR_ARRAY_ID` | `char[]` | -| 83 | `PRIMITIVE_SHORT_ARRAY_ID` | `short[]` | -| 84 | `PRIMITIVE_INT_ARRAY_ID` | `int[]` | -| 85 | `PRIMITIVE_FLOAT_ARRAY_ID` | `float[]` | -| 86 | `PRIMITIVE_LONG_ARRAY_ID` | `long[]` | -| 87 | `PRIMITIVE_DOUBLE_ARRAY_ID` | `double[]` | -| 88 | `STRING_ARRAY_ID` | `String[]` | -| 89 | `OBJECT_ARRAY_ID` | `Object[]` and object array serializers | -| 90 | `ARRAYLIST_ID` | `java.util.ArrayList` | -| 91 | `HASHMAP_ID` | `java.util.HashMap` | -| 92 | `HASHSET_ID` | `java.util.HashSet` | -| 93 | `CLASS_ID` | `java.lang.Class` | -| 94 | `EMPTY_OBJECT_ID` | Empty-object serializer | -| 95 | `LAMBDA_STUB_ID` | Lambda replacement stub | -| 96 | `JDK_PROXY_STUB_ID` | JDK proxy replacement stub | -| 97 | `REPLACE_STUB_ID` | `writeReplace`/`readResolve` replacement | -| 98 | `NONEXISTENT_META_SHARED_ID` | Unknown class placeholder | +| ID | Name | Java type or serializer owner | +| --- | ---------------------------- | --------------------------------------- | +| 69 | `VOID_ID` | `java.lang.Void` | +| 70 | `CHAR_ID` | `java.lang.Character` | +| 71 | `PRIMITIVE_VOID_ID` | `void` | +| 72 | `PRIMITIVE_BOOL_ID` | `boolean` | +| 73 | `PRIMITIVE_INT8_ID` | `byte` | +| 74 | `PRIMITIVE_CHAR_ID` | `char` | +| 75 | `PRIMITIVE_INT16_ID` | `short` | +| 76 | `PRIMITIVE_INT32_ID` | `int` | +| 77 | `PRIMITIVE_FLOAT32_ID` | `float` | +| 78 | `PRIMITIVE_INT64_ID` | `long` | +| 79 | `PRIMITIVE_FLOAT64_ID` | `double` | +| 80 | `PRIMITIVE_BOOLEAN_ARRAY_ID` | `boolean[]` | +| 81 | `PRIMITIVE_BYTE_ARRAY_ID` | `byte[]` | +| 82 | `PRIMITIVE_CHAR_ARRAY_ID` | `char[]` | +| 83 | `PRIMITIVE_SHORT_ARRAY_ID` | `short[]` | +| 84 | `PRIMITIVE_INT_ARRAY_ID` | `int[]` | +| 85 | `PRIMITIVE_FLOAT_ARRAY_ID` | `float[]` | +| 86 | `PRIMITIVE_LONG_ARRAY_ID` | `long[]` | +| 87 | `PRIMITIVE_DOUBLE_ARRAY_ID` | `double[]` | +| 88 | `STRING_ARRAY_ID` | `String[]` | +| 89 | `OBJECT_ARRAY_ID` | `Object[]` and object array serializers | +| 90 | `ARRAYLIST_ID` | `java.util.ArrayList` | +| 91 | `HASHMAP_ID` | `java.util.HashMap` | +| 92 | `HASHSET_ID` | `java.util.HashSet` | +| 93 | `CLASS_ID` | `java.lang.Class` | +| 94 | `EMPTY_OBJECT_ID` | Empty-object serializer | +| 95 | `LAMBDA_STUB_ID` | Lambda replacement type ID | +| 96 | `JDK_PROXY_STUB_ID` | JDK proxy replacement type ID | +| 97 | `REPLACE_STUB_ID` | `writeReplace`/`readResolve` type ID | +| 98 | `NONEXISTENT_META_SHARED_ID` | Unknown-class marker type ID | ### Registered, Named, and Unregistered Classes @@ -661,7 +661,7 @@ and Java serialization hooks: - `writeReplace`/`readResolve` values use replacement metadata and payloads owned by the replacement serializer. -- JDK proxy and lambda stubs use their registered native stub IDs. +- JDK proxy and lambda replacements use their registered native type IDs. - Types that require Java Object Serialization compatibility may be delegated to serializers that reproduce the required Java semantics inside a Fory object slot. @@ -672,10 +672,10 @@ metadata rules in this document. ## Unknown Classes When meta sharing is enabled and a reader does not have a local class for a -remote ClassDef, Java may materialize an unknown-class placeholder using -`NONEXISTENT_META_SHARED_ID`. The placeholder stores enough field data to -preserve and copy the unknown value according to the unknown-class serializer. -It does not make the unknown Java class available to user code. +remote ClassDef, Java may materialize an unknown-class value using +`NONEXISTENT_META_SHARED_ID`. The value stores enough field data to preserve and +copy the unknown value according to the unknown-class serializer. It does not +make the unknown Java class available to user code. ## Out-of-Band Buffers diff --git a/docs/specification/xlang_serialization_spec.md b/docs/specification/xlang_serialization_spec.md index 477fcef506..6e1784b402 100644 --- a/docs/specification/xlang_serialization_spec.md +++ b/docs/specification/xlang_serialization_spec.md @@ -354,7 +354,7 @@ Byte 0: Bitmap flags - Bits 2-7: reserved ``` -- **xlang flag** (bit 0): 1 when serialization uses Fory xlang format, 0 when serialization uses Fory language-native format. +- **xlang flag** (bit 0): 1 when serialization uses Fory xlang format, 0 when serialization uses a Fory native-mode format. - **oob flag** (bit 1): 1 when out-of-band serialization is enabled (BufferCallback is not null), 0 otherwise. - **reserved bits** (bits 2-7): must be zero. @@ -423,7 +423,7 @@ function read_ref_or_null(buffer): - Reference IDs are assigned sequentially starting from `0` - The ID is assigned when `REF_VALUE_FLAG` is written (first occurrence) - Objects are stored in a list/map indexed by their reference ID -- For reading, a placeholder slot is reserved before deserializing the object, then filled after +- For reading, a reference slot is reserved before deserializing the object, then filled after ### When Reference Tracking is Disabled diff --git a/examples/cpp/hello_world/README.md b/examples/cpp/hello_world/README.md index 786065f610..b844365912 100644 --- a/examples/cpp/hello_world/README.md +++ b/examples/cpp/hello_world/README.md @@ -114,7 +114,7 @@ struct Point { ```cpp auto fory = fory::serialization::Fory::builder() - .xlang(true) // Enable cross-language serialization + .xlang(true) .track_ref(false) // Disable reference tracking .build(); diff --git a/go/README.md b/go/README.md index 002b0b8998..8448cf3716 100644 --- a/go/README.md +++ b/go/README.md @@ -4,6 +4,11 @@ Fory is a blazingly fast multi-language serialization framework powered by just- For comprehensive documentation, see the [Fory Go Guide](https://fory.apache.org/docs/guide/go/). +Fory Go defaults to xlang mode for cross-language payloads. Use native mode +with `fory.WithXlang(false)` for Go-only traffic when you want Go struct, +pointer, interface, and runtime-type behavior without portable xlang +type-mapping constraints. + ## Installation **Requirements**: Go 1.24 or later @@ -29,8 +34,8 @@ type User struct { } func main() { - // Create a Fory instance - f := fory.New() + // Create an xlang Fory instance. + f := fory.New(fory.WithXlang(true)) // Register struct with a type ID if err := f.RegisterStruct(User{}, 1); err != nil { diff --git a/go/fory/README.md b/go/fory/README.md index 8a7ebafe55..603f1d6c03 100644 --- a/go/fory/README.md +++ b/go/fory/README.md @@ -27,8 +27,8 @@ type User struct { } func main() { - // Create a Fory instance - f := fory.New() + // Create an xlang Fory instance. + f := fory.New(fory.WithXlang(true)) // Register struct with a type ID if err := f.RegisterStruct(User{}, 1); err != nil { @@ -81,18 +81,23 @@ Fory Go supports configuration through functional options: ```go // Enable reference tracking for circular references -f := fory.New(fory.WithTrackRef(true)) +f := fory.New(fory.WithXlang(true), fory.WithTrackRef(true)) -// Enable compatible mode for schema evolution -f := fory.New(fory.WithCompatible(true)) +// Native mode for Go-only payloads. Use this when both sides are Go and the +// payload should follow Go's native type system. +f := fory.New(fory.WithXlang(false)) + +// Enable compatible mode for schema evolution in native mode +f := fory.New(fory.WithXlang(false), fory.WithCompatible(true)) // Set maximum nesting depth -f := fory.New(fory.WithMaxDepth(20)) +f := fory.New(fory.WithXlang(true), fory.WithMaxDepth(20)) // Combine multiple options f := fory.New( + fory.WithXlang(true), fory.WithTrackRef(true), - fory.WithCompatible(true), + fory.WithMaxDepth(20), ) ``` @@ -102,7 +107,7 @@ Fory Go enables seamless data exchange with Java, Python, C++, Rust, and JavaScr ```go // Go -f := fory.New() +f := fory.New(fory.WithXlang(true)) f.RegisterStructByName(User{}, "example.User") data, _ := f.Serialize(&User{ID: 1, Name: "Alice"}) // 'data' can be deserialized by Java, Python, etc. @@ -115,7 +120,7 @@ The default Fory instance is not thread-safe. For concurrent use: ```go import "github.com/apache/fory/go/fory/threadsafe" -f := threadsafe.New() +f := threadsafe.New(fory.WithXlang(true)) // Safe for concurrent use go func() { f.Serialize(value1) }() diff --git a/go/fory/array_primitive_test.go b/go/fory/array_primitive_test.go index c1b989efce..4017add568 100644 --- a/go/fory/array_primitive_test.go +++ b/go/fory/array_primitive_test.go @@ -27,7 +27,7 @@ import ( ) func TestPrimitiveArraySerializer(t *testing.T) { - f := NewFory() + f := NewFory(WithXlang(false)) // Test uint16 array t.Run("uint16_array", func(t *testing.T) { @@ -93,7 +93,7 @@ func TestPrimitiveArraySerializer(t *testing.T) { } func TestArraySliceInteroperability(t *testing.T) { - f := NewFory() + f := NewFory(WithXlang(false)) t.Run("array_to_slice", func(t *testing.T) { // Serialize Array [3]int32 @@ -149,7 +149,7 @@ func TestArraySliceInteroperability(t *testing.T) { } func TestFloat16Array(t *testing.T) { - f := NewFory() + f := NewFory(WithXlang(false)) t.Run("float16_array", func(t *testing.T) { arr := [3]float16.Float16{ diff --git a/go/fory/array_test.go b/go/fory/array_test.go index 2fe0a7461a..fe89d68dc2 100644 --- a/go/fory/array_test.go +++ b/go/fory/array_test.go @@ -39,7 +39,7 @@ func TestArrayDynSerializer(t *testing.T) { } func TestArrayDynSerializerRoundTrip(t *testing.T) { - f := NewFory() + f := NewFory(WithXlang(false)) t.Run("array of interfaces with strings", func(t *testing.T) { arr := [3]any{"hello", "world", "test"} diff --git a/go/fory/doc.go b/go/fory/doc.go index 055483ab1a..1095a8ba38 100644 --- a/go/fory/doc.go +++ b/go/fory/doc.go @@ -49,8 +49,8 @@ Create a Fory instance, register your types, and serialize: } func main() { - // Create a Fory instance - f := fory.New() + // Create an xlang Fory instance. + f := fory.New(fory.WithXlang(true)) // Register struct with a type ID if err := f.RegisterStruct(User{}, 1); err != nil { @@ -77,23 +77,22 @@ Create a Fory instance, register your types, and serialize: Fory uses a functional options pattern for configuration: - // Default configuration - f := fory.New() + // Xlang configuration + f := fory.New(fory.WithXlang(true)) // With options f := fory.New( + fory.WithXlang(true), // Select xlang mode explicitly fory.WithTrackRef(true), // Enable reference tracking for circular references - fory.WithCompatible(true), // Enable schema evolution fory.WithMaxDepth(30), // Set maximum nesting depth - fory.WithXlang(true), // Enable cross-language mode ) Configuration defaults: - TrackRef: false (reference tracking disabled) - MaxDepth: 20 (maximum nesting depth) - - IsXlang: false (cross-language mode disabled) - - Compatible: false (schema evolution disabled) + - IsXlang: true (xlang mode enabled) + - Compatible: true in the default xlang mode; false in native mode unless WithCompatible(true) is set # Type Registration @@ -163,7 +162,7 @@ Available tags: Enable reference tracking to handle circular references and shared objects: - f := fory.New(fory.WithTrackRef(true)) + f := fory.New(fory.WithXlang(true), fory.WithTrackRef(true)) type Node struct { Value int32 @@ -185,9 +184,10 @@ When reference tracking is enabled: # Schema Evolution -Enable compatible mode for forward/backward compatibility: +Native mode defaults to schema-consistent payloads. Enable compatible mode for +forward/backward compatibility in Go-only native payloads: - f := fory.New(fory.WithCompatible(true)) + f := fory.New(fory.WithXlang(false), fory.WithCompatible(true)) // V1: original struct type UserV1 struct { @@ -209,10 +209,10 @@ Compatible mode supports: # Cross-Language Serialization -Enable cross-language mode for interoperability with Java, Python, C++, Rust, +Go defaults to xlang mode for interoperability with Java, Python, C++, Rust, and JavaScript: - f := fory.New(fory.WithXlang(true), fory.WithCompatible(true)) + f := fory.New(fory.WithXlang(true)) f.RegisterStruct(User{}, 1) // Use same ID across all languages data, _ := f.Serialize(&User{ID: 1, Name: "Alice"}) @@ -231,10 +231,7 @@ threadsafe package: import "github.com/apache/fory/go/fory/threadsafe" // Create thread-safe instance - f := threadsafe.New( - fory.WithTrackRef(true), - fory.WithCompatible(true), - ) + f := threadsafe.New() // Safe for concurrent use go func() { @@ -253,7 +250,7 @@ The thread-safe wrapper: The default Fory instance reuses its internal buffer for zero-copy performance: - f := fory.New() + f := fory.New(fory.WithXlang(true)) data1, _ := f.Serialize(value1) // WARNING: data1 becomes invalid after next Serialize call! data2, _ := f.Serialize(value2) @@ -331,7 +328,7 @@ may change between service versions. For comprehensive documentation, see https://fory.apache.org/docs/guide/go/ Related specifications: - - Xlang Serialization: https://fory.apache.org/docs/specification/fory_xlang_serialization_spec + - Xlang Serialization: https://fory.apache.org/docs/specification/xlang_serialization_spec - Type Mapping: https://fory.apache.org/docs/specification/xlang_type_mapping */ package fory diff --git a/go/fory/fory.go b/go/fory/fory.go index 6b9c48eacc..abdfdc5d76 100644 --- a/go/fory/fory.go +++ b/go/fory/fory.go @@ -64,7 +64,7 @@ func defaultConfig() Config { return Config{ TrackRef: false, // Match Java's default: reference tracking disabled MaxDepth: 20, - IsXlang: false, + IsXlang: true, MaxCollectionSize: 1_000_000, MaxBinarySize: 64 * 1024 * 1024, MaxTypeFields: 10000, diff --git a/go/fory/fory_typed_test.go b/go/fory/fory_typed_test.go index e1263772cd..16e3d4e3b5 100644 --- a/go/fory/fory_typed_test.go +++ b/go/fory/fory_typed_test.go @@ -26,7 +26,7 @@ import ( // TestSerializeGenericPrimitives tests Serialize[T]/DeserializeWithCallbackBuffers[T] with primitives. // Both functions take pointers to avoid interface heap allocation and struct copy. func TestSerializeGenericPrimitives(t *testing.T) { - f := NewFory(WithRefTracking(true)) + f := NewFory(WithXlang(false), WithRefTracking(true)) t.Run("Bool", func(t *testing.T) { val := true @@ -133,7 +133,7 @@ func TestSerializeGenericPrimitives(t *testing.T) { } func TestDeserializeRejectsRootTypeMismatch(t *testing.T) { - f := NewFory() + f := NewFory(WithXlang(false)) data := []byte{0, 0xff, byte(STRING)} var result bool @@ -145,7 +145,7 @@ func TestDeserializeRejectsRootTypeMismatch(t *testing.T) { } func TestDeserializeRejectsRootPrimitiveSliceTypeMismatch(t *testing.T) { - f := NewFory() + f := NewFory(WithXlang(false)) data := []byte{0, 0xff, byte(BINARY)} var int32Result []int32 @@ -157,7 +157,7 @@ func TestDeserializeRejectsRootPrimitiveSliceTypeMismatch(t *testing.T) { } func TestDeserializeByteSliceAcceptsUint8ArrayRootType(t *testing.T) { - f := NewFory() + f := NewFory(WithXlang(false)) buf := NewByteBuffer(nil) buf.WriteByte(0) buf.WriteInt8(NotNullValueFlag) @@ -173,7 +173,7 @@ func TestDeserializeByteSliceAcceptsUint8ArrayRootType(t *testing.T) { // TestSerializeGenericComplex tests Serialize[T]/DeserializeWithCallbackBuffers[T] with complex types. // These fall back to reflection-based serialization. func TestSerializeGenericComplex(t *testing.T) { - f := NewFory(WithRefTracking(true)) + f := NewFory(WithXlang(false), WithRefTracking(true)) t.Run("Struct", func(t *testing.T) { type TestStruct struct { @@ -227,7 +227,7 @@ func TestSerializeGenericComplex(t *testing.T) { // TestSerializeDeserializeRoundTrip tests that serialized data can be correctly deserialized. func TestSerializeDeserializeRoundTrip(t *testing.T) { - f := NewFory(WithRefTracking(true)) + f := NewFory(WithXlang(false), WithRefTracking(true)) // Test that SerializeWithCallback[T] uses pointer-based fast path when available t.Run("TypedSerializerPath", func(t *testing.T) { diff --git a/go/fory/limit_test.go b/go/fory/limit_test.go index 64d32f7a3d..25cee403c2 100644 --- a/go/fory/limit_test.go +++ b/go/fory/limit_test.go @@ -26,10 +26,10 @@ func TestMaxCollectionSizeGuardrail(t *testing.T) { // 1. Test slice exceeding limit t.Run("Slice exceeds MaxCollectionSize", func(t *testing.T) { config := WithMaxCollectionSize(2) - f := NewFory(config) + f := NewFory(WithXlang(false), config) slice := []string{"a", "b", "c"} - fBase := NewFory() + fBase := NewFory(WithXlang(false)) bytes, _ := fBase.Serialize(slice) var decoded []string @@ -41,10 +41,10 @@ func TestMaxCollectionSizeGuardrail(t *testing.T) { // 2. Test map exceeding limit t.Run("Map exceeds MaxCollectionSize", func(t *testing.T) { config := WithMaxCollectionSize(2) - f := NewFory(config) + f := NewFory(WithXlang(false), config) m := map[int32]int32{1: 1, 2: 2, 3: 3} - fBase := NewFory() + fBase := NewFory(WithXlang(false)) bytes, _ := fBase.Serialize(m) var decoded map[int32]int32 @@ -56,7 +56,7 @@ func TestMaxCollectionSizeGuardrail(t *testing.T) { // 3. Test string is not affected by MaxCollectionSize t.Run("String unaffected by MaxCollectionSize", func(t *testing.T) { config := WithMaxCollectionSize(2) - f := NewFory(config) + f := NewFory(WithXlang(false), config) str := "hello world" // length 11 bytes, err := f.Serialize(str) @@ -73,11 +73,11 @@ func TestMaxBinarySizeGuardrail(t *testing.T) { // 1. Test binary (byte slice) exceeding limit t.Run("Byte slice exceeds MaxBinarySize", func(t *testing.T) { config := WithMaxBinarySize(5) - f := NewFory(config) + f := NewFory(WithXlang(false), config) // We can serialize a byte slice using standard serializer, then decode with the f instance slice := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} - fBase := NewFory() + fBase := NewFory(WithXlang(false)) bytes, _ := fBase.Serialize(slice) var decoded []byte @@ -89,7 +89,7 @@ func TestMaxBinarySizeGuardrail(t *testing.T) { // 2. Test string is not affected by MaxBinarySize t.Run("String unaffected by MaxBinarySize", func(t *testing.T) { config := WithMaxBinarySize(2) - f := NewFory(config) + f := NewFory(WithXlang(false), config) str := "hello world" // length 11 bytes, err := f.Serialize(str) diff --git a/go/fory/map_primitive_test.go b/go/fory/map_primitive_test.go index 7decca8b0a..8f22e8e561 100644 --- a/go/fory/map_primitive_test.go +++ b/go/fory/map_primitive_test.go @@ -24,7 +24,7 @@ import ( ) func TestPrimitiveMapReaderRejectsInvalidChunkSize(t *testing.T) { - f := NewFory() + f := NewFory(WithXlang(false)) buf := NewByteBuffer(nil) buf.WriteLength(1) buf.WriteUint8(KEY_DECL_TYPE | VALUE_DECL_TYPE) @@ -36,7 +36,7 @@ func TestPrimitiveMapReaderRejectsInvalidChunkSize(t *testing.T) { } func TestPrimitiveMapReaderRejectsUnexpectedTypeInfo(t *testing.T) { - f := NewFory() + f := NewFory(WithXlang(false)) buf := NewByteBuffer(nil) buf.WriteLength(1) buf.WriteUint8(0) @@ -50,7 +50,7 @@ func TestPrimitiveMapReaderRejectsUnexpectedTypeInfo(t *testing.T) { } func TestPrimitiveMapReaderRejectsNullChunks(t *testing.T) { - f := NewFory() + f := NewFory(WithXlang(false)) buf := NewByteBuffer(nil) buf.WriteLength(1) buf.WriteUint8(KEY_HAS_NULL) diff --git a/go/fory/slice_primitive_test.go b/go/fory/slice_primitive_test.go index 71b28df7d4..d9f1241b30 100644 --- a/go/fory/slice_primitive_test.go +++ b/go/fory/slice_primitive_test.go @@ -27,7 +27,7 @@ import ( ) func TestFloat16Slice(t *testing.T) { - f := NewFory() + f := NewFory(WithXlang(false)) t.Run("float16_slice", func(t *testing.T) { slice := []float16.Float16{ @@ -69,7 +69,7 @@ func TestFloat16Slice(t *testing.T) { } func TestBFloat16Slice(t *testing.T) { - f := NewFory() + f := NewFory(WithXlang(false)) t.Run("bfloat16_slice", func(t *testing.T) { slice := []bfloat16.BFloat16{ @@ -111,7 +111,7 @@ func TestBFloat16Slice(t *testing.T) { } func TestIntSlice(t *testing.T) { - f := NewFory() + f := NewFory(WithXlang(false)) t.Run("int_slice_large_numbers", func(t *testing.T) { slice := []int{1, -2, 3, -4, math.MaxInt, math.MinInt} @@ -149,7 +149,7 @@ func TestIntSlice(t *testing.T) { } func TestUintSlice(t *testing.T) { - f := NewFory() + f := NewFory(WithXlang(false)) t.Run("uint_slice_large_numbers", func(t *testing.T) { slice := []uint{1, 2, 3, 4, math.MaxUint} @@ -187,7 +187,7 @@ func TestUintSlice(t *testing.T) { } func TestInt8Slice(t *testing.T) { - f := NewFory() + f := NewFory(WithXlang(false)) t.Run("int8_slice_large_numbers", func(t *testing.T) { slice := []int8{1, -2, 3, -4, math.MaxInt8, math.MinInt8} @@ -225,7 +225,7 @@ func TestInt8Slice(t *testing.T) { } func TestInt16Slice(t *testing.T) { - f := NewFory() + f := NewFory(WithXlang(false)) t.Run("int16_slice_large_numbers", func(t *testing.T) { slice := []int16{1, -2, 3, -4, math.MaxInt16, math.MinInt16} @@ -263,7 +263,7 @@ func TestInt16Slice(t *testing.T) { } func TestInt32Slice(t *testing.T) { - f := NewFory() + f := NewFory(WithXlang(false)) t.Run("int32_slice_large_numbers", func(t *testing.T) { slice := []int32{1, -2, 3, -4, math.MaxInt32, math.MinInt32} @@ -301,7 +301,7 @@ func TestInt32Slice(t *testing.T) { } func TestInt64Slice(t *testing.T) { - f := NewFory() + f := NewFory(WithXlang(false)) t.Run("int64_slice_large_numbers", func(t *testing.T) { slice := []int64{1, -2, 3, -4, math.MaxInt64, math.MinInt64} @@ -339,7 +339,7 @@ func TestInt64Slice(t *testing.T) { } func TestUint16Slice(t *testing.T) { - f := NewFory() + f := NewFory(WithXlang(false)) t.Run("uint16_slice_large_numbers", func(t *testing.T) { slice := []uint16{1, 2, 3, 4, math.MaxUint16} @@ -377,7 +377,7 @@ func TestUint16Slice(t *testing.T) { } func TestUint32Slice(t *testing.T) { - f := NewFory() + f := NewFory(WithXlang(false)) t.Run("uint32_slice_large_numbers", func(t *testing.T) { slice := []uint32{1, 2, 3, 4, math.MaxUint32} @@ -415,7 +415,7 @@ func TestUint32Slice(t *testing.T) { } func TestUint64Slice(t *testing.T) { - f := NewFory() + f := NewFory(WithXlang(false)) t.Run("uint64_slice_large_numbers", func(t *testing.T) { slice := []uint64{1, 2, 3, 4, math.MaxUint64} diff --git a/go/fory/stream_test.go b/go/fory/stream_test.go index 0c2503f3cb..c332bdf1b6 100644 --- a/go/fory/stream_test.go +++ b/go/fory/stream_test.go @@ -30,7 +30,7 @@ type StreamTestStruct struct { } func TestStreamDeserialization(t *testing.T) { - f := New() + f := New(WithXlang(false)) f.RegisterStruct(&StreamTestStruct{}, 100) original := &StreamTestStruct{ @@ -76,7 +76,7 @@ func (r *slowReader) Read(p []byte) (n int, err error) { } func TestStreamDeserializationSlow(t *testing.T) { - f := New() + f := New(WithXlang(false)) f.RegisterStruct(&StreamTestStruct{}, 100) original := &StreamTestStruct{ @@ -107,7 +107,7 @@ func TestStreamDeserializationSlow(t *testing.T) { } func TestStreamDeserializationEOF(t *testing.T) { - f := New() + f := New(WithXlang(false)) f.RegisterStruct(&StreamTestStruct{}, 100) original := &StreamTestStruct{ @@ -136,7 +136,7 @@ func TestStreamDeserializationEOF(t *testing.T) { } func TestDeserializeFromStreamClearsReadMetadataOnError(t *testing.T) { - f := New(WithCompatible(true)) + f := New(WithXlang(false), WithCompatible(true)) f.typeResolver.metaStringResolver.dynamicIDToEnumString = append(f.typeResolver.metaStringResolver.dynamicIDToEnumString, emptyMetaStringBytes) f.metaContext.readTypeInfos = append(f.metaContext.readTypeInfos, &TypeInfo{}) @@ -159,7 +159,7 @@ func TestDeserializeFromStreamClearsReadMetadataOnError(t *testing.T) { } func TestInputStreamSequential(t *testing.T) { - f := New() + f := New(WithXlang(false)) // Register type in compatible mode to test Meta Sharing across sequential reads f.config.Compatible = true f.RegisterStruct(&StreamTestStruct{}, 100) @@ -178,7 +178,7 @@ func TestInputStreamSequential(t *testing.T) { data3, _ := f.Serialize(msg3) buf.Write(data3) - fDec := New() + fDec := New(WithXlang(false)) fDec.config.Compatible = true fDec.RegisterStruct(&StreamTestStruct{}, 100) diff --git a/go/fory/tests/generator_test.go b/go/fory/tests/generator_test.go index 893f718861..665749be3c 100644 --- a/go/fory/tests/generator_test.go +++ b/go/fory/tests/generator_test.go @@ -45,7 +45,7 @@ func TestValidationDemo(t *testing.T) { assert.Equal(t, true, original.E, "Original E should be true") // 2. SerializeWithCallback using generated code - f := fory.NewFory(fory.WithRefTracking(true)) + f := fory.NewFory(fory.WithXlang(false), fory.WithRefTracking(true)) data, err := f.Marshal(original) require.NoError(t, err, "Serialization should not fail") require.NotEmpty(t, data, "Serialized data should not be empty") @@ -78,7 +78,7 @@ func TestSliceDemo(t *testing.T) { assert.NotEmpty(t, original.BoolSlice, "BoolSlice should not be empty") // 2. SerializeWithCallback using generated code - f := fory.NewFory(fory.WithRefTracking(true)) + f := fory.NewFory(fory.WithXlang(false), fory.WithRefTracking(true)) data, err := f.Marshal(original) require.NoError(t, err, "Serialization should not fail") require.NotEmpty(t, data, "Serialized data should not be empty") @@ -116,7 +116,7 @@ func TestDynamicSliceDemo(t *testing.T) { assert.Equal(t, int64(12345), original.DynamicSlice[4], "Fifth element should be int64(12345)") // 2. SerializeWithCallback using generated code - f := fory.NewFory(fory.WithRefTracking(true)) + f := fory.NewFory(fory.WithXlang(false), fory.WithRefTracking(true)) data, err := f.Marshal(original) require.NoError(t, err, "Serialization should not fail") require.NotEmpty(t, data, "Serialized data should not be empty") diff --git a/go/fory/tests/generator_xlang_test.go b/go/fory/tests/generator_xlang_test.go index 473d4d77eb..f176cf4969 100644 --- a/go/fory/tests/generator_xlang_test.go +++ b/go/fory/tests/generator_xlang_test.go @@ -62,10 +62,12 @@ func TestValidationDemoXlang(t *testing.T) { } // Codegen mode (automatically uses full name) - foryForCodegen := forygo.NewFory(forygo.WithRefTracking(true)) + foryForCodegen := forygo.NewFory( + forygo.WithXlang(true), forygo.WithCompatible(false), forygo.WithRefTracking(true)) // Reflect mode (register with full name) - foryForReflect := forygo.NewFory(forygo.WithRefTracking(true)) + foryForReflect := forygo.NewFory( + forygo.WithXlang(true), forygo.WithCompatible(false), forygo.WithRefTracking(true)) err := foryForReflect.RegisterStructByName(ReflectStruct{}, expectedTypeTag) require.NoError(t, err, "Should be able to register ReflectStruct with full name") @@ -127,10 +129,12 @@ func TestSliceDemoXlang(t *testing.T) { } // Codegen mode - enable reference tracking - foryForCodegen := forygo.NewFory(forygo.WithRefTracking(true)) + foryForCodegen := forygo.NewFory( + forygo.WithXlang(true), forygo.WithCompatible(false), forygo.WithRefTracking(true)) // Reflect mode - enable reference tracking - foryForReflect := forygo.NewFory(forygo.WithRefTracking(true)) + foryForReflect := forygo.NewFory( + forygo.WithXlang(true), forygo.WithCompatible(false), forygo.WithRefTracking(true)) err := foryForReflect.RegisterStructByName(ReflectSliceStruct{}, expectedTypeTag) require.NoError(t, err, "Should be able to register ReflectSliceStruct with full name") @@ -201,10 +205,12 @@ func TestDynamicSliceDemoXlang(t *testing.T) { } // Codegen mode - enable reference tracking - foryForCodegen := forygo.NewFory(forygo.WithRefTracking(true)) + foryForCodegen := forygo.NewFory( + forygo.WithXlang(true), forygo.WithCompatible(false), forygo.WithRefTracking(true)) // Reflect mode - enable reference tracking - foryForReflect := forygo.NewFory(forygo.WithRefTracking(true)) + foryForReflect := forygo.NewFory( + forygo.WithXlang(true), forygo.WithCompatible(false), forygo.WithRefTracking(true)) err := foryForReflect.RegisterStructByName(ReflectDynamicStruct{}, expectedTypeTag) require.NoError(t, err, "Should be able to register ReflectDynamicStruct with full name") @@ -258,8 +264,10 @@ func TestMapDemoXlang(t *testing.T) { reflectInstance := codegenInstance // Create Fory instances with reference tracking enabled - foryForCodegen := forygo.NewFory(forygo.WithRefTracking(true)) - foryForReflect := forygo.NewFory(forygo.WithRefTracking(true)) + foryForCodegen := forygo.NewFory( + forygo.WithXlang(true), forygo.WithCompatible(false), forygo.WithRefTracking(true)) + foryForReflect := forygo.NewFory( + forygo.WithXlang(true), forygo.WithCompatible(false), forygo.WithRefTracking(true)) // No need to register MapDemo - it has codegen serializer automatically diff --git a/go/fory/threadsafe/fory_test.go b/go/fory/threadsafe/fory_test.go index 4cee6e1f26..c43e00d89d 100644 --- a/go/fory/threadsafe/fory_test.go +++ b/go/fory/threadsafe/fory_test.go @@ -26,7 +26,7 @@ import ( // TestFory tests the thread-safe Fory wrapper func TestFory(t *testing.T) { - f := New(fory.WithRefTracking(true)) + f := New(fory.WithXlang(false), fory.WithRefTracking(true)) t.Run("BasicSerialization", func(t *testing.T) { data, err := f.Serialize(int32(42)) @@ -90,7 +90,7 @@ func TestFory(t *testing.T) { // TestSerializeAny tests the Serialize/Deserialize methods func TestSerializeAny(t *testing.T) { - f := New(fory.WithRefTracking(true)) + f := New(fory.WithXlang(false), fory.WithRefTracking(true)) t.Run("Primitives", func(t *testing.T) { data, err := f.Serialize(int32(42)) @@ -115,7 +115,7 @@ func TestSerializeAny(t *testing.T) { // TestDeserialize tests the Deserialize generic function func TestDeserialize(t *testing.T) { - f := New(fory.WithRefTracking(true)) + f := New(fory.WithXlang(false), fory.WithRefTracking(true)) t.Run("Int32", func(t *testing.T) { val := int32(42) diff --git a/go/fory/type_def_test.go b/go/fory/type_def_test.go index 09db8d4bee..76123f9768 100644 --- a/go/fory/type_def_test.go +++ b/go/fory/type_def_test.go @@ -109,7 +109,7 @@ func TestTypeDefEncodingDecoding(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - fory := NewFory(WithRefTracking(false)) + fory := NewFory(WithXlang(false), WithRefTracking(false)) if err := fory.RegisterStructByName(tt.testStruct, tt.tagName); err != nil { t.Fatalf("Failed to register tag type: %v", err) @@ -221,7 +221,7 @@ type Item1 struct { // TestTypeDefNullableFields verifies that pointer fields are correctly encoded as nullable // and primitive fields are encoded as non-nullable in TypeDef func TestTypeDefNullableFields(t *testing.T) { - fory := NewFory(WithRefTracking(false)) + fory := NewFory(WithXlang(false), WithRefTracking(false)) // Register the type if err := fory.RegisterStructByName(Item1{}, "test.Item1"); err != nil { @@ -332,7 +332,7 @@ func TestTypeDefNullableFields(t *testing.T) { // returning an error instead of performing the unbounded make([]FieldDef, fieldCount) // allocation that would OOM-crash the process. func TestTypeDefFieldCountOOMPanic(t *testing.T) { - fory := NewFory() + fory := NewFory(WithXlang(false)) buffer := NewByteBuffer(make([]byte, 0, 8)) buffer.WriteByte(StructTypeDefFlag | SmallNumFieldsThreshold) @@ -346,7 +346,7 @@ func TestTypeDefFieldCountOOMPanic(t *testing.T) { } func TestTypeDefRejectsReservedGlobalHeaderBits(t *testing.T) { - fory := NewFory() + fory := NewFory(WithXlang(false)) buffer := NewByteBuffer(nil) buffer.WriteByte(StructTypeDefFlag) buffer.WriteVarUint32(0) @@ -359,7 +359,7 @@ func TestTypeDefRejectsReservedGlobalHeaderBits(t *testing.T) { } func TestTypeDefRejectsReservedNonStructKindBits(t *testing.T) { - fory := NewFory() + fory := NewFory(WithXlang(false)) body := []byte{0b0001_0000} frame, header := typeDefTestFrame(t, body, false) @@ -370,7 +370,7 @@ func TestTypeDefRejectsReservedNonStructKindBits(t *testing.T) { } func TestTypeDefRejectsTrailingMetadataBytes(t *testing.T) { - fory := NewFory() + fory := NewFory(WithXlang(false)) meta := NewByteBuffer(nil) meta.WriteByte(StructTypeDefFlag) meta.WriteVarUint32(0) @@ -407,7 +407,7 @@ func TestTypeDefExtendedFieldCountHeaderDoesNotSetRegisterByName(t *testing.T) { } func TestTypeDefRejectsMetadataHashMismatch(t *testing.T) { - fory := NewFory() + fory := NewFory(WithXlang(false)) body := typeDefTestBodyWithoutFields() buffer := NewByteBuffer(nil) buffer.WriteBinary(body) @@ -419,7 +419,7 @@ func TestTypeDefRejectsMetadataHashMismatch(t *testing.T) { } func TestTypeDefHeaderHashIncludesHeaderLowBits(t *testing.T) { - fory := NewFory() + fory := NewFory(WithXlang(false)) body := typeDefTestBodyWithoutFields() _, header := typeDefTestFrame(t, body, false) @@ -438,7 +438,7 @@ func TestTypeDefHeaderHashIncludesHeaderLowBits(t *testing.T) { } func TestTypeDefRejectsEncodedMetadataAboveMaxBinarySize(t *testing.T) { - fory := NewFory(WithMaxBinarySize(1)) + fory := NewFory(WithXlang(false), WithMaxBinarySize(1)) body := typeDefTestBodyWithoutFields() frame, header := typeDefTestFrame(t, body, false) @@ -450,7 +450,7 @@ func TestTypeDefRejectsEncodedMetadataAboveMaxBinarySize(t *testing.T) { func TestTypeDefRejectsCompressedMetadata(t *testing.T) { decoded := typeDefTestBodyWithoutFields() compressed := deflateTypeDefTestBody(t, decoded) - fory := NewFory(WithMaxBinarySize(4096)) + fory := NewFory(WithXlang(false), WithMaxBinarySize(4096)) frame, header := typeDefTestFrame(t, compressed, true) _, err := decodeTypeDef(fory, frame, header) @@ -459,7 +459,7 @@ func TestTypeDefRejectsCompressedMetadata(t *testing.T) { } func TestReadSharedTypeMetaCapsParsedTypeDefCache(t *testing.T) { - fory := NewFory(WithCompatible(true)) + fory := NewFory(WithXlang(false), WithCompatible(true)) require.NoError(t, fory.RegisterStructByName(SimpleStruct{}, "example.SimpleStruct")) typeDef, err := buildTypeDef(fory, reflect.ValueOf(SimpleStruct{})) require.NoError(t, err) @@ -485,7 +485,7 @@ func TestReadSharedTypeMetaCapsParsedTypeDefCache(t *testing.T) { } func TestDecodeTypeDefFallbackNamedTypeCacheRespectsCap(t *testing.T) { - fory := NewFory(WithCompatible(true)) + fory := NewFory(WithXlang(false), WithCompatible(true)) require.NoError(t, fory.RegisterStructByName(SimpleStruct{}, "example.SimpleStruct")) typeDef, err := buildTypeDef(fory, reflect.ValueOf(SimpleStruct{})) require.NoError(t, err) @@ -515,7 +515,7 @@ func TestDecodeTypeDefFallbackNamedTypeCacheRespectsCap(t *testing.T) { } func TestTypeDefRejectsNamespaceLengthBeyondMetadata(t *testing.T) { - fory := NewFory() + fory := NewFory(WithXlang(false)) meta := NewByteBuffer(nil) meta.WriteByte(StructTypeDefFlag | RegisterByNameFlag) meta.WriteByte(byte(BIG_NAME_THRESHOLD << 2)) @@ -528,7 +528,7 @@ func TestTypeDefRejectsNamespaceLengthBeyondMetadata(t *testing.T) { } func TestTypeDefRejectsFieldNameLengthBeyondMetadata(t *testing.T) { - fory := NewFory() + fory := NewFory(WithXlang(false)) meta := NewByteBuffer(nil) meta.WriteByte(StructTypeDefFlag | 1) meta.WriteVarUint32(0) diff --git a/go/fory/type_test.go b/go/fory/type_test.go index fe47b0fe38..37e6951705 100644 --- a/go/fory/type_test.go +++ b/go/fory/type_test.go @@ -70,7 +70,7 @@ func TestTypeResolver(t *testing.T) { } func TestCreateSerializerSliceTypes(t *testing.T) { - fory := NewFory() + fory := NewFory(WithXlang(false)) r := newTypeResolver(fory) tests := []struct { @@ -107,7 +107,7 @@ func TestCreateSerializerSliceTypes(t *testing.T) { } func TestCreateSerializerArrayTypes(t *testing.T) { - fory := NewFory() + fory := NewFory(WithXlang(false)) r := newTypeResolver(fory) var expectedIntArraySerializerType reflect.Type @@ -158,7 +158,7 @@ func TestCreateSerializerArrayTypes(t *testing.T) { } func TestGetSliceSerializerReducedPrecisionTypes(t *testing.T) { - fory := NewFory() + fory := NewFory(WithXlang(false)) r := newTypeResolver(fory) serializer, err := r.GetSliceSerializer(reflect.TypeOf([]float16.Float16{})) @@ -171,7 +171,7 @@ func TestGetSliceSerializerReducedPrecisionTypes(t *testing.T) { } func TestGetArraySerializerReducedPrecisionTypes(t *testing.T) { - fory := NewFory() + fory := NewFory(WithXlang(false)) r := newTypeResolver(fory) serializer, err := r.GetArraySerializer(reflect.TypeOf([4]float16.Float16{})) diff --git a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/AbstractClassExample.java b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/AbstractClassExample.java index 9273bcc318..9767ddfa58 100644 --- a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/AbstractClassExample.java +++ b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/AbstractClassExample.java @@ -147,6 +147,7 @@ public int hashCode() { FORY = Fory.builder() .withName(AbstractClassExample.class.getName()) + .withXlang(false) .registerGuavaTypes(false) .build(); // Register enum type - abstract enums need to be registered diff --git a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ArrayExample.java b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ArrayExample.java index 0d9da08611..fb680e7721 100644 --- a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ArrayExample.java +++ b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ArrayExample.java @@ -25,7 +25,8 @@ import org.apache.fory.util.Preconditions; public class ArrayExample { - private static final Fory FORY = Fory.builder().registerGuavaTypes(false).build(); + private static final Fory FORY = + Fory.builder().withXlang(false).registerGuavaTypes(false).build(); static { FORY.register(ArrayExample.class); diff --git a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/Benchmark.java b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/Benchmark.java index 832f41b6b5..e828a70a55 100644 --- a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/Benchmark.java +++ b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/Benchmark.java @@ -73,11 +73,11 @@ public static Object jdkDeserialize(byte[] data) { private static final Fory fory2; static { - fory1 = Fory.builder().withNumberCompressed(false).build(); + fory1 = Fory.builder().withXlang(false).withNumberCompressed(false).build(); fory1.register(Foo.class); fory1.register(Struct.class); fory1.ensureSerializersCompiled(); - fory2 = Fory.builder().withNumberCompressed(true).build(); + fory2 = Fory.builder().withXlang(false).withNumberCompressed(true).build(); fory2.register(Foo.class); fory2.register(Struct.class); fory2.ensureSerializersCompiled(); diff --git a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/CollectionExample.java b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/CollectionExample.java index 36d671d88a..b9dffbe01f 100644 --- a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/CollectionExample.java +++ b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/CollectionExample.java @@ -35,6 +35,7 @@ public class CollectionExample { fory = Fory.builder() .withName(CollectionExample.class.getName()) + .withXlang(false) .requireClassRegistration(true) .build(); } diff --git a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/CompatibleDependentSerializerExample.java b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/CompatibleDependentSerializerExample.java index 2caa7b2422..c56c5f2202 100644 --- a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/CompatibleDependentSerializerExample.java +++ b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/CompatibleDependentSerializerExample.java @@ -39,6 +39,7 @@ private static Fory createFory() { Fory fory = Fory.builder() .withName(CompatibleDependentSerializerExample.class.getName()) + .withXlang(false) .requireClassRegistration(true) .withCompatible(true) .build(); diff --git a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/CompatibleDifferentSchemaExample.java b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/CompatibleDifferentSchemaExample.java index dd43e11036..41ddae5cda 100644 --- a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/CompatibleDifferentSchemaExample.java +++ b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/CompatibleDifferentSchemaExample.java @@ -40,6 +40,7 @@ private static Fory createWriter() { Fory fory = Fory.builder() .withName(CompatibleDifferentSchemaExample.class.getName() + ".writer") + .withXlang(false) .withCompatible(true) .requireClassRegistration(true) .build(); @@ -52,6 +53,7 @@ private static Fory createReader() { Fory fory = Fory.builder() .withName(CompatibleDifferentSchemaExample.class.getName() + ".reader") + .withXlang(false) .withCompatible(true) .requireClassRegistration(true) .build(); diff --git a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/CompatibleExample.java b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/CompatibleExample.java index 0c2c49a6a5..58cd833fb6 100644 --- a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/CompatibleExample.java +++ b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/CompatibleExample.java @@ -31,6 +31,7 @@ public class CompatibleExample { private static Fory createFory() { Fory fory = Fory.builder() + .withXlang(false) .requireClassRegistration(true) .withCompatible(true) .withScopedMetaShare(false) 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 4b71f665c5..9e79ef345b 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 @@ -33,6 +33,7 @@ public class CompatibleThreadSafeExample { Fory f = Fory.builder() .withName(CompatibleThreadSafeExample.class.getName()) + .withXlang(false) .requireClassRegistration(true) .withCompatible(true) .build(); 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 d0301f7fe8..0c2a477495 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 @@ -33,6 +33,7 @@ public class EnsureSerializerExample { fory = Fory.builder() .withName(EnsureSerializerExample.class.getName()) + .withXlang(false) .requireClassRegistration(true) .build(); // register and generate serializer code. diff --git a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/Example.java b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/Example.java index 4d8bcf1a87..7e6c628c45 100644 --- a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/Example.java +++ b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/Example.java @@ -28,7 +28,12 @@ public class Example { static Fory fory; static { - fory = Fory.builder().withName(Example.class.getName()).requireClassRegistration(true).build(); + fory = + Fory.builder() + .withName(Example.class.getName()) + .withXlang(false) + .requireClassRegistration(true) + .build(); // register and generate serializer code. fory.register(Foo.class); fory.ensureSerializersCompiled(); diff --git a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ExceptionExample.java b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ExceptionExample.java index 8acf5aee1f..287f280d45 100644 --- a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ExceptionExample.java +++ b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ExceptionExample.java @@ -31,6 +31,7 @@ public class ExceptionExample { private static final Fory FORY = Fory.builder() .withName(ExceptionExample.class.getName()) + .withXlang(false) .requireClassRegistration(true) .withRefTracking(false) .build(); diff --git a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/FeatureTestExample.java b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/FeatureTestExample.java index e3b64bd582..c39d39412e 100644 --- a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/FeatureTestExample.java +++ b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/FeatureTestExample.java @@ -73,6 +73,7 @@ private static Fory createFory() { Fory fory = Fory.builder() .withName(FeatureTestExample.class.getName()) + .withXlang(false) .requireClassRegistration(true) .build(); fory.register(PrivateConstructorClass.class); diff --git a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ObjectStreamExample.java b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ObjectStreamExample.java index c41ef77f02..f7a9c29932 100644 --- a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ObjectStreamExample.java +++ b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ObjectStreamExample.java @@ -35,6 +35,7 @@ public class ObjectStreamExample extends AbstractMap { private static final Fory FORY = Fory.builder() .withName(ObjectStreamExample.class.getName() + "_compatible_async") + .withXlang(false) .registerGuavaTypes(false) .withRefTracking(true) .withCodegen(true) diff --git a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ProxyExample.java b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ProxyExample.java index ea79bc37d9..995dbca8dd 100644 --- a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ProxyExample.java +++ b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ProxyExample.java @@ -47,6 +47,7 @@ private static Fory createFory() { Fory fory = Fory.builder() .withName(ProxyExample.class.getName()) + .withXlang(false) .requireClassRegistration(true) .build(); // register and generate serializer code. diff --git a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ScopedCompatibleExample.java b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ScopedCompatibleExample.java index 2f7f88bf86..15dcdfc4fd 100644 --- a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ScopedCompatibleExample.java +++ b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/ScopedCompatibleExample.java @@ -32,6 +32,7 @@ private static Fory createFory() { Fory fory = Fory.builder() .withName(ScopedCompatibleExample.class.getName()) + .withXlang(false) .requireClassRegistration(true) .withCompatible(true) .build(); 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..1951b638b0 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 @@ -40,6 +40,7 @@ public class ThreadSafeExample { Fory f = Fory.builder() .withName(ThreadSafeExample.class.getName()) + .withXlang(false) .requireClassRegistration(true) .build(); // register and generate serializer code. diff --git a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/record/CompatibleRecordExample.java b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/record/CompatibleRecordExample.java index 88ca43adbf..441c8bb782 100644 --- a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/record/CompatibleRecordExample.java +++ b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/record/CompatibleRecordExample.java @@ -32,6 +32,7 @@ private static Fory createFory() { Fory fory = Fory.builder() .withName(CompatibleRecordExample.class.getName()) + .withXlang(false) .requireClassRegistration(true) .withCompatible(true) .build(); diff --git a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/record/RecordExample.java b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/record/RecordExample.java index 16946f59f6..def9035312 100644 --- a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/record/RecordExample.java +++ b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/record/RecordExample.java @@ -37,6 +37,7 @@ private static Fory createFory() { Fory fory = Fory.builder() .withName(RecordExample.class.getName()) + .withXlang(false) .requireClassRegistration(true) .build(); // register and generate serializer code. diff --git a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/record/RecordExample2.java b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/record/RecordExample2.java index 7811b06f2d..c7e1cb2c29 100644 --- a/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/record/RecordExample2.java +++ b/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/record/RecordExample2.java @@ -37,6 +37,7 @@ private static Fory createFory() { Fory fory = Fory.builder() .withName(RecordExample2.class.getName()) + .withXlang(false) .requireClassRegistration(true) .build(); // register and generate serializer code. diff --git a/integration_tests/idl_tests/swift/idl_package/Tests/IdlRoundTripTests/IdlRoundTripTests.swift b/integration_tests/idl_tests/swift/idl_package/Tests/IdlRoundTripTests/IdlRoundTripTests.swift index c2f4aa5fbc..15817d1de2 100644 --- a/integration_tests/idl_tests/swift/idl_package/Tests/IdlRoundTripTests/IdlRoundTripTests.swift +++ b/integration_tests/idl_tests/swift/idl_package/Tests/IdlRoundTripTests/IdlRoundTripTests.swift @@ -30,9 +30,9 @@ final class IdlRoundTripTests: XCTestCase { } func testEvolvingRoundTrip() throws { - let foryV1 = Fory(xlang: true, ref: false, compatible: true) + let foryV1 = Fory(ref: false, compatible: true) try Evolving1.ForyRegistration.register(foryV1) - let foryV2 = Fory(xlang: true, ref: false, compatible: true) + let foryV2 = Fory(ref: false, compatible: true) try Evolving2.ForyRegistration.register(foryV2) let messageV1 = Evolving1.EvolvingMessage(id: 1, name: "Alice", city: "NYC") @@ -100,7 +100,7 @@ final class IdlRoundTripTests: XCTestCase { } func testGeneratedFixedIntegerListsUseListMetadata() throws { - let fory = Fory(xlang: true, ref: false, compatible: true) + let fory = Fory(ref: false, compatible: true) try Example.ForyRegistration.register(fory) let maybeFloat16Values: [String: Float16?] = ["none": nil, "one": Float16(1)] let maybeBFloat16Values: [String: BFloat16?] = ["none": nil, "one": BFloat16(rawValue: 0x3F80)] @@ -136,7 +136,7 @@ final class IdlRoundTripTests: XCTestCase { } private func runIdlMatrixRoundTrip(compatible: Bool) throws { - let fory = Fory(xlang: true, ref: false, compatible: compatible) + let fory = Fory(ref: false, compatible: compatible) try Addressbook.ForyRegistration.register(fory) try AutoId.ForyRegistration.register(fory) try Collection.ForyRegistration.register(fory) @@ -245,7 +245,7 @@ final class IdlRoundTripTests: XCTestCase { ) } - let anyProtoFory = Fory(xlang: true, ref: false, compatible: compatible) + let anyProtoFory = Fory(ref: false, compatible: compatible) try AnyExamplePb.ForyRegistration.register(anyProtoFory) let anyProtoHolder = buildAnyProtoHolder() do { @@ -276,7 +276,7 @@ final class IdlRoundTripTests: XCTestCase { XCTAssertEqual(actual, expected) } - let refFory = Fory(xlang: true, ref: true, compatible: compatible) + let refFory = Fory(ref: true, compatible: compatible) try Tree.ForyRegistration.register(refFory) try GraphNamespace.ForyRegistration.register(refFory) try Root.ForyRegistration.register(refFory) diff --git a/integration_tests/jdk_compatibility_tests/src/test/java/org/apache/fory/integration_tests/ForyTest.java b/integration_tests/jdk_compatibility_tests/src/test/java/org/apache/fory/integration_tests/ForyTest.java index 4010b63452..19a2cb7934 100644 --- a/integration_tests/jdk_compatibility_tests/src/test/java/org/apache/fory/integration_tests/ForyTest.java +++ b/integration_tests/jdk_compatibility_tests/src/test/java/org/apache/fory/integration_tests/ForyTest.java @@ -30,7 +30,7 @@ public class ForyTest { @Test public void testMediaContent() { Sample object = new Sample().populate(false); - Fory fory = Fory.builder().requireClassRegistration(false).build(); + Fory fory = Fory.builder().withXlang(false).requireClassRegistration(false).build(); byte[] data = fory.serialize(object); Sample sample = (Sample) fory.deserialize(data); Assert.assertEquals(sample, object); @@ -39,7 +39,7 @@ public void testMediaContent() { @Test public void testSample() { MediaContent object = new MediaContent().populate(false); - Fory fory = Fory.builder().requireClassRegistration(false).build(); + Fory fory = Fory.builder().withXlang(false).requireClassRegistration(false).build(); byte[] data = fory.serialize(object); MediaContent mediaContent = (MediaContent) fory.deserialize(data); Assert.assertEquals(mediaContent, object); diff --git a/java/README.md b/java/README.md index 7051c4b825..800abe9061 100644 --- a/java/README.md +++ b/java/README.md @@ -8,7 +8,7 @@ Apache Fory™ Java provides blazingly-fast serialization for the Java ecosystem ## Features -### 🚀 High Performance +### High Performance - **JIT Code Generation**: Highly-extensible JIT framework generates serializer code at runtime using async multi-threaded compilation, delivering 20-170x speedup through: - Inlining variables to reduce memory access @@ -20,13 +20,13 @@ Apache Fory™ Java provides blazingly-fast serialization for the Java ecosystem - **Meta Sharing**: Cached class metadata reduces redundant type information - **SIMD Acceleration**: Java Vector API support for array operations (Java 16+) -### 🔧 Drop-in Replacement +### Drop-in Replacement - **100% JDK Serialization Compatible**: Supports `writeObject`/`readObject`/`writeReplace`/`readResolve`/`readObjectNoData`/`Externalizable` - **Java 8-24 Support**: Works across all modern Java versions including Java 17+ records - **GraalVM Native Image**: AOT compilation support without reflection configuration -### 🔄 Advanced Features +### Advanced Features - **Reference Tracking**: Automatic handling of shared and circular references - **Schema Evolution**: Forward/backward compatibility for class schema changes @@ -36,12 +36,12 @@ Apache Fory™ Java provides blazingly-fast serialization for the Java ecosystem ## Documentation -| Topic | Description | Source Doc Link | Website Doc Link | -| --------------------------- | -------------------------- | ------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------- | -| **Java Serialization** | Comprehensive usage guide | [java_serialization_guide.md](../docs/guide/java_serialization_guide.md) | [Java Serialization Guide](https://fory.apache.org/docs/docs/guide/java_serialization) | -| **GraalVM Native Image** | Native image support | [graalvm-support.md](../docs/guide/java/graalvm-support.md) | [GraalVM Support](https://fory.apache.org/docs/guide/java/graalvm_support) | -| **Java Serialization Spec** | Protocol specification | [java_serialization_spec.md](../docs/specification/java_serialization_spec.md) | [Java Serialization Spec](https://fory.apache.org/docs/specification/fory_java_serialization_spec) | -| **Java Benchmarks** | Performance data and plots | [java/README.md](../docs/benchmarks/java/README.md) | [Java Benchmarks](https://fory.apache.org/docs/benchmarks/java) | +| Topic | Description | Source Doc Link | Website Doc Link | +| --------------------------- | -------------------------------- | ------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------- | +| **Java Guide** | Java xlang and native mode usage | [docs/guide/java](../docs/guide/java) | [Java Guide](https://fory.apache.org/docs/guide/java/) | +| **GraalVM Native Image** | Native image support | [graalvm-support.md](../docs/guide/java/graalvm-support.md) | [GraalVM Support](https://fory.apache.org/docs/guide/java/graalvm_support) | +| **Java Serialization Spec** | Protocol specification | [java_serialization_spec.md](../docs/specification/java_serialization_spec.md) | [Java Serialization Spec](https://fory.apache.org/docs/specification/java_serialization_spec) | +| **Java Benchmarks** | Performance data and plots | [java/README.md](../docs/benchmarks/java/README.md) | [Java Benchmarks](https://fory.apache.org/docs/benchmarks/java) | ## Modules @@ -108,8 +108,10 @@ Create a Fory instance, register your classes, and start serializing objects. Re import org.apache.fory.*; import org.apache.fory.config.*; -// Create Fory instance (should be reused) +// Create Fory instance (should be reused). Java defaults to xlang mode with +// compatible schema evolution. Fory fory = Fory.builder() + .withXlang(true) .requireClassRegistration(true) .build(); @@ -132,8 +134,8 @@ For multi-threaded environments, use `ThreadSafeFory` which maintains a pool of import org.apache.fory.*; import org.apache.fory.config.*; -// Create thread-safe Fory instance -private static final ThreadSafeFory fory = Fory.builder().buildThreadSafeFory(); +// Create thread-safe xlang Fory instance +private static final ThreadSafeFory fory = Fory.builder().withXlang(true).buildThreadSafeFory(); static { fory.register(MyClass.class); @@ -144,13 +146,26 @@ byte[] bytes = fory.serialize(object); Object result = fory.deserialize(bytes); ``` -### Schema Evolution +### Native Mode -Enable schema compatibility mode to support forward and backward compatibility when your class definitions change over time: +Use native mode for Java-only payloads when you need JVM-specific object behavior such as JDK +serialization hooks, `Externalizable`, broader object graph support, or a replacement for JDK +serialization, Kryo, FST, Hessian, or Java-only Protocol Buffers payloads: ```java -// Enable forward/backward compatibility Fory fory = Fory.builder() + .withXlang(false) + .requireClassRegistration(true) + .build(); +``` + +### Schema Evolution + +Xlang mode enables compatible schema evolution by default. In native mode, enable compatible mode +when your class definitions change over time: + +```java +Fory fory = Fory.builder().withXlang(false) .withCompatible(true) .build(); @@ -164,7 +179,7 @@ Enable reference tracking to properly handle shared references and circular depe ```java // Enable reference tracking for circular/shared references -Fory fory = Fory.builder() +Fory fory = Fory.builder().withXlang(false) .withRefTracking(true) .build(); @@ -176,10 +191,10 @@ byte[] bytes = fory.serialize(node); ### Cross-Language Serialization -Use XLANG mode to serialize data that can be deserialized by other languages (Python, Rust, Go, etc.): +Use xlang mode, the Java default, to serialize data that can be deserialized by other languages +(Python, Rust, Go, etc.): ```java -// Use XLANG mode for cross-language compatibility Fory fory = Fory.builder() .withXlang(true) .withRefTracking(true) @@ -201,11 +216,11 @@ Configure Fory with various options to suit your specific use case: ```java Fory fory = Fory.builder() - // Language mode: JAVA (fastest) or XLANG (cross-language) + // Native mode for Java-only payloads. Omit this for xlang payloads. .withXlang(false) // Reference tracking for circular/shared references .withRefTracking(true) - // Schema evolution mode + // Schema evolution mode. Xlang already enables compatible mode by default. .withCompatible(true) // Compression options .withIntCompressed(true) @@ -222,13 +237,14 @@ Fory fory = Fory.builder() .build(); ``` -See [Java Serialization Guide](../docs/guide/java_serialization_guide.md) for detailed configuration options. +See the [Java guide](../docs/guide/java/) for detailed configuration options. ## Advanced Features ### JDK Serialization Compatibility -Fory fully supports JDK serialization APIs with 100x better performance. You can use standard Java serialization methods and Fory will handle them automatically: +In native mode, Fory supports JDK serialization APIs with much better performance. Use native mode +when replacing Java-only JDK serialization, Kryo, FST, Hessian, or Protocol Buffers payloads: ```java public class MyClass implements Serializable { @@ -314,7 +330,7 @@ Bar partialBar = barEncoder.fromRow(barStruct); // Deserialize only one Bar obj Foo deserializedFoo = encoder.fromRow(binaryRow); ``` -See [Row Format Guide](../docs/guide/row_format_guide.md) for more details. +See the [Java row-format guide](../docs/guide/java/row-format.md) for more details. ### Array Compression (Java 16+) @@ -323,7 +339,7 @@ Use SIMD-accelerated compression for integer and long arrays to reduce memory us ```java import org.apache.fory.simd.*; -Fory fory = Fory.builder() +Fory fory = Fory.builder().withXlang(false) .withIntArrayCompressed(true) .withLongArrayCompressed(true) .build(); @@ -401,7 +417,7 @@ mvn -T16 checkstyle:check 1. **Reuse Fory Instances**: Creating Fory is expensive; reuse instances across serializations 2. **Enable Compression**: For numeric-heavy data, enable int/long compression 3. **Disable Reference Tracking**: If no circular references exist, disable tracking for better performance -4. **Use Java Mode**: Leave xlang disabled when cross-language support isn't needed. Java mode reduces type metadata overhead and supports more Java-native types not available in xlang mode +4. **Use native mode**: For Java-only payloads, use `withXlang(false)`. Native mode reduces type metadata overhead and supports more Java-native types not available in xlang mode 5. **Warm Up**: Allow JIT compilation to complete before benchmarking 6. **Register Classes**: Class registration reduces metadata overhead 7. **Use SIMD**: Enable array compression on Java 16+ for numeric arrays diff --git a/java/fory-annotation-processor/src/test/java/org/apache/fory/annotation/processing/ForyStructProcessorTest.java b/java/fory-annotation-processor/src/test/java/org/apache/fory/annotation/processing/ForyStructProcessorTest.java index abef8487c6..2e4092e14e 100644 --- a/java/fory-annotation-processor/src/test/java/org/apache/fory/annotation/processing/ForyStructProcessorTest.java +++ b/java/fory-annotation-processor/src/test/java/org/apache/fory/annotation/processing/ForyStructProcessorTest.java @@ -76,6 +76,7 @@ public void testStaticSerializerSelectedWithCodegenDisabled() throws Exception { Fory fory = Fory.builder() + .withXlang(false) .withClassLoader(loader) .withCodegen(false) .requireClassRegistration(false) @@ -177,6 +178,7 @@ public void testPrivateFieldUsesAccessibleAccessors() throws Exception { invoke(type, value, "setName", String.class, "static"); Fory fory = Fory.builder() + .withXlang(false) .withClassLoader(loader) .withCodegen(false) .requireClassRegistration(false) @@ -344,6 +346,7 @@ public void testGeneratedDescriptorsCarryNestedTypeMetadata() throws Exception { Class serializerType = loader.loadClass("test.MetadataStruct_ForySerializer"); Fory fory = Fory.builder() + .withXlang(false) .withClassLoader(loader) .withCodegen(false) .requireClassRegistration(false) @@ -391,6 +394,7 @@ public void testGeneratedDescriptorsPreserveRefMetadataPresence() throws Excepti Class serializerType = loader.loadClass("test.RefMetadataStruct_ForyNativeSerializer"); Fory fory = Fory.builder() + .withXlang(false) .withClassLoader(loader) .withCodegen(false) .withRefTracking(true) @@ -435,6 +439,7 @@ public void testGeneratedUnsignedScalarWritesValidateRange() throws Exception { Class type = loader.loadClass("test.UnsignedStruct"); Fory fory = Fory.builder() + .withXlang(false) .withClassLoader(loader) .withCodegen(false) .requireClassRegistration(false) @@ -482,6 +487,7 @@ public void testRecordReadAndCopyUseCanonicalConstructor() throws Exception { .newInstance(5, "skip", "record"); Fory fory = Fory.builder() + .withXlang(false) .withClassLoader(loader) .withCodegen(false) .requireClassRegistration(false) @@ -540,6 +546,7 @@ public void testCompatibleReadUsesGeneratedSerializer() throws Exception { setField(writerType, value, "name", "old"); Fory writer = Fory.builder() + .withXlang(false) .withClassLoader(writerLoader) .withCodegen(false) .withMetaShare(true) @@ -553,6 +560,7 @@ public void testCompatibleReadUsesGeneratedSerializer() throws Exception { Class readerType = readerLoader.loadClass("test.EvolvingStruct"); Fory reader = Fory.builder() + .withXlang(false) .withClassLoader(readerLoader) .withCodegen(false) .withMetaShare(true) @@ -608,6 +616,7 @@ public void testStaticSerializerHandlesMonomorphicRecursiveField() throws Except Fory fory = Fory.builder() + .withXlang(false) .withClassLoader(loader) .withCodegen(false) .withRefTracking(true) @@ -639,6 +648,7 @@ public void testGeneratedDescriptorDiscoveryDoesNotSelectStaticSerializerWhenCod Class type = loader.loadClass("test.DescriptorStruct"); Fory fory = Fory.builder() + .withXlang(false) .withClassLoader(loader) .withCodegen(true) .requireClassRegistration(false) @@ -747,6 +757,7 @@ private static void assertStaticRuntimeRoundTrip(boolean compatible) throws Exce private static ThreadSafeFory threadSafeFory( ClassLoader classLoader, boolean codegen, boolean compatible) { return Fory.builder() + .withXlang(false) .withClassLoader(classLoader) .withCodegen(codegen) .withCompatible(compatible) 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 11158ab852..5dae3df2a2 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 @@ -70,7 +70,7 @@ public final class ForyBuilder { String name; boolean checkClassVersion = false; - boolean xlang = false; + boolean xlang = true; boolean trackingRef = false; boolean copyRef = false; boolean stringRefIgnored = true; @@ -113,8 +113,8 @@ public final class ForyBuilder { public ForyBuilder() {} /** - * Whether cross-language serialize the object. If you used fory for java only, please keep it in - * java mode, which will have much better performance. + * Whether cross-language serialize the object. If you use Fory for Java only, set {@link + * Language#JAVA} explicitly for Java native mode. */ public ForyBuilder withLanguage(Language language) { this.xlang = language == Language.XLANG; diff --git a/java/fory-core/src/test/java/javax/fory/test/ResolverValidateSerializerTest.java b/java/fory-core/src/test/java/javax/fory/test/ResolverValidateSerializerTest.java index 5e5612820e..47b9ed3750 100644 --- a/java/fory-core/src/test/java/javax/fory/test/ResolverValidateSerializerTest.java +++ b/java/fory-core/src/test/java/javax/fory/test/ResolverValidateSerializerTest.java @@ -157,7 +157,12 @@ public ValidMap onMapRead(Map map) { @Test public void testListAndMapSerializerRegistration() { - Fory fory = Fory.builder().withRefTracking(true).requireClassRegistration(false).build(); + Fory fory = + Fory.builder() + .withXlang(false) + .withRefTracking(true) + .requireClassRegistration(false) + .build(); assertThrows( IllegalArgumentException.class, () -> fory.registerSerializer(InvalidList.class, InvalidList.InvalidListSerializer.class)); diff --git a/java/fory-core/src/test/java/org/apache/fory/ForyTest.java b/java/fory-core/src/test/java/org/apache/fory/ForyTest.java index 2afe9cfc90..5a3041677f 100644 --- a/java/fory-core/src/test/java/org/apache/fory/ForyTest.java +++ b/java/fory-core/src/test/java/org/apache/fory/ForyTest.java @@ -90,7 +90,7 @@ public static Object[][] xlangConfig() { @Test public void typedDeserializeRejectsOutOfBandRootHeaderWithoutBuffers() { - Fory fory = Fory.builder().build(); + Fory fory = Fory.builder().withXlang(false).build(); byte[] bytes = fory.serialize(7); bytes[0] |= 0x02; assertThrows(IllegalArgumentException.class, () -> fory.deserialize(bytes, Integer.class)); @@ -122,7 +122,10 @@ public void primitivesTest(boolean referenceTracking, boolean xlang) { @Test(dataProvider = "referenceTrackingConfig") public void basicTest(boolean referenceTracking) { ForyBuilder builder = - Fory.builder().withRefTracking(referenceTracking).requireClassRegistration(false); + Fory.builder() + .withXlang(false) + .withRefTracking(referenceTracking) + .requireClassRegistration(false); Fory fory1 = builder.build(); Fory fory2 = builder.build(); assertEquals("str", serDe(fory1, fory2, "str")); @@ -423,10 +426,10 @@ public void testJDKSerializableCheck() { @Test public void testClassRegistration() { - Fory fory = Fory.builder().requireClassRegistration(true).build(); + Fory fory = Fory.builder().withXlang(false).requireClassRegistration(true).build(); class A {} assertThrows(InsecureException.class, () -> fory.serialize(new A())); - Fory fory1 = Fory.builder().requireClassRegistration(false).build(); + Fory fory1 = Fory.builder().withXlang(false).requireClassRegistration(false).build(); serDe(fory1, new A()); } @@ -440,7 +443,7 @@ private static class IgnoreFields { @Test public void testIgnoreFields() { - Fory fory = Fory.builder().requireClassRegistration(false).build(); + Fory fory = Fory.builder().withXlang(false).requireClassRegistration(false).build(); IgnoreFields o = serDe(fory, new IgnoreFields(1, 2, 3)); assertEquals(0, o.f1); assertEquals(0, o.f2); @@ -459,7 +462,7 @@ private static class ExposeFields { @Test public void testExposeFields() { - Fory fory = Fory.builder().requireClassRegistration(false).build(); + Fory fory = Fory.builder().withXlang(false).requireClassRegistration(false).build(); ImmutableMap map1 = ImmutableMap.of("1", 1); ImmutableMap map2 = ImmutableMap.of("2", 2); ExposeFields o = serDe(fory, new ExposeFields(1, 2, 3, map1, map2)); @@ -480,7 +483,7 @@ private static class ExposeFields2 { @Test public void testExposeFields2() { - Fory fory = Fory.builder().requireClassRegistration(false).build(); + Fory fory = Fory.builder().withXlang(false).requireClassRegistration(false).build(); assertThrowsCause(RuntimeException.class, () -> serDe(fory, new ExposeFields2(1, 2, 3))); } @@ -499,7 +502,7 @@ public void testClassGC() { } private void foryGC(WeakHashMap map) { - Fory fory = Fory.builder().requireClassRegistration(false).build(); + Fory fory = Fory.builder().withXlang(false).requireClassRegistration(false).build(); Class structClass1 = Struct.createStructClass("TestClassGC", 1, false); System.out.println(structClass1.hashCode()); Object struct1 = Struct.createPOJO(structClass1); @@ -549,7 +552,12 @@ public void write(WriteContext writeContext, UUID value) { @Test public void testRegisterPrivateSerializer() { - Fory fory = Fory.builder().withRefTracking(true).requireClassRegistration(false).build(); + Fory fory = + Fory.builder() + .withXlang(false) + .withRefTracking(true) + .requireClassRegistration(false) + .build(); fory.registerSerializer(UUID.class, new UUIDSerializer(fory.getTypeResolver())); DomainObject obj = new DomainObject(); obj.id = UUID.randomUUID(); @@ -563,7 +571,12 @@ class A { } A a = new A(); a.f = a; - Fory fory = Fory.builder().withRefTracking(false).requireClassRegistration(false).build(); + Fory fory = + Fory.builder() + .withXlang(false) + .withRefTracking(false) + .requireClassRegistration(false) + .build(); try { fory.serialize(a); throw new IllegalStateException("SerializationException not raised."); @@ -575,7 +588,12 @@ class A { @Test public void testPkgAccessLevelParentClass() { - Fory fory = Fory.builder().withRefTracking(true).requireClassRegistration(false).build(); + Fory fory = + Fory.builder() + .withXlang(false) + .withRefTracking(true) + .requireClassRegistration(false) + .build(); HashBasedTable table = HashBasedTable.create(2, 4); table.put("r", "c", 100); serDeCheckSerializer(fory, table, "Codec"); @@ -594,6 +612,7 @@ public PrintReadObject(boolean b) {} public void testPrintReadObjectsWhenFailed() { Fory fory = Fory.builder() + .withXlang(false) .withRefTracking(true) .withCodegen(false) .requireClassRegistration(false) @@ -611,6 +630,7 @@ public void testPrintReadObjectsWhenFailed() { public void testNullObjSerAndDe() { Fory fory = Fory.builder() + .withXlang(false) .withRefTracking(true) .requireClassRegistration(false) .withMetaShare(true) @@ -628,7 +648,7 @@ public void testNullObjSerAndDe() { public void testResetBufferToSizeLimit() { final int minBufferBytes = 64; final int limitInBytes = 1024; - Fory fory = Fory.builder().withBufferSizeLimitBytes(limitInBytes).build(); + Fory fory = Fory.builder().withXlang(false).withBufferSizeLimitBytes(limitInBytes).build(); final byte[] smallPayload = new byte[0]; final byte[] serializedSmall = fory.serialize(smallPayload); @@ -668,8 +688,10 @@ static class Struct2 { @Test public void testStructMapping() { - ThreadSafeFory fory1 = Fory.builder().withCompatible(true).buildThreadSafeFory(); - ThreadSafeFory fory2 = Fory.builder().withCompatible(true).buildThreadSafeFory(); + ThreadSafeFory fory1 = + Fory.builder().withXlang(false).withCompatible(true).buildThreadSafeFory(); + ThreadSafeFory fory2 = + Fory.builder().withXlang(false).withCompatible(true).buildThreadSafeFory(); fory1.register(Struct1.class); fory2.register(Struct2.class); Struct1 struct1 = new Struct1(10, "abc"); @@ -694,9 +716,19 @@ private Object maxDepthData() { @Test public void testMaxDepth() { - byte[] bytes = Fory.builder().requireClassRegistration(false).build().serialize(maxDepthData()); + byte[] bytes = + Fory.builder() + .withXlang(false) + .requireClassRegistration(false) + .build() + .serialize(maxDepthData()); Fory fory = - Fory.builder().requireClassRegistration(false).withName("fory1").withMaxDepth(3).build(); + Fory.builder() + .withXlang(false) + .requireClassRegistration(false) + .withName("fory1") + .withMaxDepth(3) + .build(); assertThrows(InsecureException.class, () -> fory.deserialize(bytes)); } @@ -712,9 +744,15 @@ public void testMaxDepthCodegen() { MaxDepth maxDepth = new MaxDepth( 1, new MaxDepth(2, new MaxDepth(3, new MaxDepth(4, new MaxDepth(5, maxDepthData()))))); - byte[] bytes = Fory.builder().requireClassRegistration(false).build().serialize(maxDepth); + byte[] bytes = + Fory.builder().withXlang(false).requireClassRegistration(false).build().serialize(maxDepth); Fory fory = - Fory.builder().requireClassRegistration(false).withName("fory2").withMaxDepth(3).build(); + Fory.builder() + .withXlang(false) + .requireClassRegistration(false) + .withName("fory2") + .withMaxDepth(3) + .build(); assertThrows(InsecureException.class, () -> fory.deserialize(bytes)); } } diff --git a/java/fory-core/src/test/java/org/apache/fory/JpmsOptionalClassLoadingTest.java b/java/fory-core/src/test/java/org/apache/fory/JpmsOptionalClassLoadingTest.java index 6c8a4a238b..3a14548cd0 100644 --- a/java/fory-core/src/test/java/org/apache/fory/JpmsOptionalClassLoadingTest.java +++ b/java/fory-core/src/test/java/org/apache/fory/JpmsOptionalClassLoadingTest.java @@ -65,7 +65,7 @@ private static String readFully(InputStream inputStream) throws IOException { public static final class NoJavaSqlMain { public static void main(String[] args) { - Fory fory = Fory.builder().requireClassRegistration(false).build(); + Fory fory = Fory.builder().withXlang(false).requireClassRegistration(false).build(); byte[] bytes = fory.serialize(new SampleValue("fory")); SampleValue value = (SampleValue) fory.deserialize(bytes); if (!"fory".equals(value.value)) { diff --git a/java/fory-core/src/test/java/org/apache/fory/StreamTest.java b/java/fory-core/src/test/java/org/apache/fory/StreamTest.java index 0b7d1d8bc0..e25b486493 100644 --- a/java/fory-core/src/test/java/org/apache/fory/StreamTest.java +++ b/java/fory-core/src/test/java/org/apache/fory/StreamTest.java @@ -107,7 +107,12 @@ public void testBufferStream() { @Test public void testBufferReset() { - Fory fory = Fory.builder().withRefTracking(true).requireClassRegistration(false).build(); + Fory fory = + Fory.builder() + .withXlang(false) + .withRefTracking(true) + .requireClassRegistration(false) + .build(); byte[] bytes = fory.serialize(new byte[1000 * 1000]); checkBuffer(fory); // assertEquals(fory.deserialize(bytes), new byte[1000 * 1000]); @@ -150,7 +155,7 @@ private void checkBuffer(Fory fory) { @Test public void testOutputStream() throws IOException { - Fory fory = Fory.builder().requireClassRegistration(false).build(); + Fory fory = Fory.builder().withXlang(false).requireClassRegistration(false).build(); ByteArrayOutputStream bas = new ByteArrayOutputStream(); BeanA beanA = BeanA.createBeanA(2); fory.serialize(bas, beanA); @@ -168,7 +173,7 @@ public void testOutputStream() throws IOException { newObj = fory.deserialize(buf); assertEquals(newObj, beanA); - fory = Fory.builder().requireClassRegistration(false).build(); + fory = Fory.builder().withXlang(false).requireClassRegistration(false).build(); // test reader buffer grow bis = new ByteArrayInputStream(bas.toByteArray()); stream = of(bis); @@ -185,7 +190,7 @@ public void testOutputStream() throws IOException { @Test public void testBufferedStream() throws IOException { - Fory fory = Fory.builder().requireClassRegistration(false).build(); + Fory fory = Fory.builder().withXlang(false).requireClassRegistration(false).build(); ByteArrayOutputStream bas = new ByteArrayOutputStream(); BeanA beanA = BeanA.createBeanA(2); fory.serialize(bas, beanA); @@ -205,7 +210,7 @@ public synchronized int read(byte[] b, int off, int len) throws IOException { newObj = fory.deserialize(stream); assertEquals(newObj, beanA); - fory = Fory.builder().requireClassRegistration(false).build(); + fory = Fory.builder().withXlang(false).requireClassRegistration(false).build(); // test reader buffer grow bis = new ByteArrayInputStream(bas.toByteArray()); stream = of(bis); @@ -223,7 +228,7 @@ public synchronized int read(byte[] b, int off, int len) throws IOException { @Test public void testOutputStreamWithType() throws IOException { - Fory fory = Fory.builder().requireClassRegistration(false).build(); + Fory fory = Fory.builder().withXlang(false).requireClassRegistration(false).build(); BeanA beanA = BeanA.createBeanA(2); ByteArrayOutputStream bas = new ByteArrayOutputStream(); fory.serialize(bas, beanA); @@ -244,7 +249,7 @@ public void testOutputStreamWithType() throws IOException { @Test public void testReadableChannel() throws IOException { - Fory fory = Fory.builder().requireClassRegistration(false).build(); + Fory fory = Fory.builder().withXlang(false).requireClassRegistration(false).build(); BeanA beanA = BeanA.createBeanA(2); { ByteArrayOutputStream bas = new ByteArrayOutputStream(); @@ -278,7 +283,7 @@ public void testReadableChannel() throws IOException { @Test public void testReadableChannelRequiresExactReads() throws IOException { - Fory fory = Fory.builder().requireClassRegistration(false).build(); + Fory fory = Fory.builder().withXlang(false).requireClassRegistration(false).build(); BeanA beanA = BeanA.createBeanA(2); byte[] serialized = fory.serialize(beanA); @@ -299,6 +304,7 @@ public void testReadableChannelRequiresExactReads() throws IOException { public void testScopedMetaShare() throws IOException { Fory fory = Fory.builder() + .withXlang(false) .requireClassRegistration(false) .withCompatible(true) .withScopedMetaShare(true) diff --git a/java/fory-core/src/test/java/org/apache/fory/ThreadSafeForyTest.java b/java/fory-core/src/test/java/org/apache/fory/ThreadSafeForyTest.java index a24c54b2e6..47fe199d7c 100644 --- a/java/fory-core/src/test/java/org/apache/fory/ThreadSafeForyTest.java +++ b/java/fory-core/src/test/java/org/apache/fory/ThreadSafeForyTest.java @@ -52,18 +52,19 @@ public class ThreadSafeForyTest extends ForyTestBase { @Test public void testBuildThreadSafeForyUsesThreadPoolFory() { - ThreadSafeFory fory = Fory.builder().requireClassRegistration(false).buildThreadSafeFory(); + ThreadSafeFory fory = + Fory.builder().withXlang(false).requireClassRegistration(false).buildThreadSafeFory(); assertTrue(fory instanceof ThreadPoolFory); } @Test public void testThreadSafeBuildersAssignGeneratedNames() { ThreadSafeFory threadSafe = - Fory.builder().requireClassRegistration(false).buildThreadSafeFory(); + Fory.builder().withXlang(false).requireClassRegistration(false).buildThreadSafeFory(); ThreadSafeFory threadLocal = - Fory.builder().requireClassRegistration(false).buildThreadLocalFory(); + Fory.builder().withXlang(false).requireClassRegistration(false).buildThreadLocalFory(); ThreadSafeFory threadPool = - Fory.builder().requireClassRegistration(false).buildThreadSafeForyPool(1); + Fory.builder().withXlang(false).requireClassRegistration(false).buildThreadSafeForyPool(1); String threadSafeName = threadSafe.execute(fory -> fory.getConfig().getName()); String threadLocalName = threadLocal.execute(fory -> fory.getConfig().getName()); @@ -77,6 +78,7 @@ public void testThreadSafeBuildersAssignGeneratedNames() { ThreadSafeFory named = Fory.builder() + .withXlang(false) .withName("explicit-thread-safe-name") .requireClassRegistration(false) .buildThreadSafeForyPool(1); @@ -99,7 +101,7 @@ public void testFunctionFactoryConstructorsUseBuilderProvidedClassLoader() { @Test public void testThreadSafeRuntimesShareRegistryAcrossRawForyInstances() throws Exception { ThreadLocalFory threadLocal = - Fory.builder().requireClassRegistration(false).buildThreadLocalFory(); + Fory.builder().withXlang(false).requireClassRegistration(false).buildThreadLocalFory(); AtomicReference threadLocalRegistry1 = new AtomicReference<>(); AtomicReference threadLocalRegistry2 = new AtomicReference<>(); Thread thread1 = @@ -113,7 +115,11 @@ public void testThreadSafeRuntimesShareRegistryAcrossRawForyInstances() throws E assertSame(threadLocalRegistry1.get(), threadLocalRegistry2.get()); ThreadPoolFory threadPool = - (ThreadPoolFory) Fory.builder().requireClassRegistration(false).buildThreadSafeForyPool(2); + (ThreadPoolFory) + Fory.builder() + .withXlang(false) + .requireClassRegistration(false) + .buildThreadSafeForyPool(2); CountDownLatch acquired = new CountDownLatch(2); CountDownLatch release = new CountDownLatch(1); AtomicReference threadPoolRegistry1 = new AtomicReference<>(); @@ -203,7 +209,7 @@ public void testRegistration() throws Exception { try { AtomicReference error = new AtomicReference<>(); ThreadSafeFory pooled = - Fory.builder().requireClassRegistration(true).buildThreadSafeForyPool(4); + Fory.builder().withXlang(false).requireClassRegistration(true).buildThreadSafeForyPool(4); pooled.register(BeanB.class); assertEquals(pooled.deserialize(pooled.serialize(bean)), bean); executor.execute( @@ -224,7 +230,8 @@ public void testRegistration() throws Exception { @Test public void testThreadPoolReusesForyAcrossThreads() throws InterruptedException { - ThreadSafeFory fory = Fory.builder().requireClassRegistration(false).buildThreadSafeForyPool(1); + ThreadSafeFory fory = + Fory.builder().withXlang(false).requireClassRegistration(false).buildThreadSafeForyPool(1); AtomicReference firstForyId = new AtomicReference<>(); AtomicReference secondForyId = new AtomicReference<>(); AtomicReference error = new AtomicReference<>(); @@ -361,8 +368,8 @@ public void testThreadLocalMetaShareWithPerThreadMetaContexts() throws Interrupt public void testSerializeDeserializeWithType() { for (ThreadSafeFory fory : new ThreadSafeFory[] { - Fory.builder().requireClassRegistration(false).buildThreadSafeFory(), - Fory.builder().requireClassRegistration(false).buildThreadSafeForyPool(2) + Fory.builder().withXlang(false).requireClassRegistration(false).buildThreadSafeFory(), + Fory.builder().withXlang(false).requireClassRegistration(false).buildThreadSafeForyPool(2) }) { byte[] bytes = fory.serialize("abc"); Assert.assertEquals(fory.deserialize(bytes, String.class), "abc"); @@ -374,15 +381,15 @@ public void testSerializeDeserializeWithType() { @Test public void testDeserializeByteBufferPreservesPositionAndLimit() { - Fory writer = Fory.builder().requireClassRegistration(false).build(); + Fory writer = Fory.builder().withXlang(false).requireClassRegistration(false).build(); String value = "thread-safe-byte-buffer"; byte[] payload = writer.serialize(value); for (BaseFory fory : new BaseFory[] { - Fory.builder().requireClassRegistration(false).build(), - Fory.builder().requireClassRegistration(false).buildThreadSafeFory(), - Fory.builder().requireClassRegistration(false).buildThreadLocalFory(), - Fory.builder().requireClassRegistration(false).buildThreadSafeForyPool(2) + Fory.builder().withXlang(false).requireClassRegistration(false).build(), + Fory.builder().withXlang(false).requireClassRegistration(false).buildThreadSafeFory(), + Fory.builder().withXlang(false).requireClassRegistration(false).buildThreadLocalFory(), + Fory.builder().withXlang(false).requireClassRegistration(false).buildThreadSafeForyPool(2) }) { for (ByteBuffer buffer : byteBufferViews(payload)) { int position = buffer.position(); @@ -456,16 +463,19 @@ public void testBuilderClassLoaderStaysFixed() throws Exception { ClassLoader loader = new CustomClassLoader(ClassLoader.getSystemClassLoader()); ThreadSafeFory threadSafe = Fory.builder() + .withXlang(false) .withClassLoader(loader) .requireClassRegistration(false) .buildThreadSafeFory(); ThreadSafeFory threadLocal = Fory.builder() + .withXlang(false) .withClassLoader(loader) .requireClassRegistration(false) .buildThreadLocalFory(); ThreadSafeFory threadPool = Fory.builder() + .withXlang(false) .withClassLoader(loader) .requireClassRegistration(false) .buildThreadSafeForyPool(2); @@ -484,7 +494,7 @@ public void testBuilderClassLoaderStaysFixed() throws Exception { @Test public void testSerializerRegister() { ThreadSafeFory threadSafeFory = - Fory.builder().requireClassRegistration(false).buildThreadSafeForyPool(2); + Fory.builder().withXlang(false).requireClassRegistration(false).buildThreadSafeForyPool(2); threadSafeFory.registerSerializer(Foo.class, FooSerializer.class); threadSafeFory.execute( fory -> { @@ -496,7 +506,8 @@ public void testSerializerRegister() { @Test public void testRegisterAfterSerializeThrowsException() { - ThreadSafeFory fory = Fory.builder().requireClassRegistration(true).buildThreadLocalFory(); + ThreadSafeFory fory = + Fory.builder().withXlang(false).requireClassRegistration(true).buildThreadLocalFory(); fory.register(BeanA.class); fory.serialize("ok"); Assert.assertThrows(ForyException.class, () -> fory.register(BeanB.class)); @@ -504,7 +515,7 @@ public void testRegisterAfterSerializeThrowsException() { @Test public void testRegisterAfterSerializeThrowsExceptionWithFory() { - Fory fory = Fory.builder().requireClassRegistration(true).build(); + Fory fory = Fory.builder().withXlang(false).requireClassRegistration(true).build(); fory.register(BeanA.class); fory.serialize("ok"); Assert.assertThrows(ForyException.class, () -> fory.register(BeanB.class)); @@ -512,7 +523,8 @@ public void testRegisterAfterSerializeThrowsExceptionWithFory() { @Test public void testRegisterAfterSerializeThrowsExceptionWithForyPool() { - ThreadSafeFory fory = Fory.builder().requireClassRegistration(true).buildThreadSafeForyPool(2); + ThreadSafeFory fory = + Fory.builder().withXlang(false).requireClassRegistration(true).buildThreadSafeForyPool(2); fory.register(BeanA.class); fory.serialize("ok"); Assert.assertThrows(ForyException.class, () -> fory.register(BeanB.class)); diff --git a/java/fory-core/src/test/java/org/apache/fory/builder/ObjectCodecBuilderTest.java b/java/fory-core/src/test/java/org/apache/fory/builder/ObjectCodecBuilderTest.java index 9812cf4307..d599e1c234 100644 --- a/java/fory-core/src/test/java/org/apache/fory/builder/ObjectCodecBuilderTest.java +++ b/java/fory-core/src/test/java/org/apache/fory/builder/ObjectCodecBuilderTest.java @@ -160,7 +160,11 @@ public static class SmallInlineFields { @Test(dataProvider = "compressNumber") public void testSmallStructInlinesFieldGroups(boolean compressNumber) { Fory fory = - Fory.builder().withNumberCompressed(compressNumber).requireClassRegistration(false).build(); + Fory.builder() + .withXlang(false) + .withNumberCompressed(compressNumber) + .requireClassRegistration(false) + .build(); ObjectCodecBuilder codecBuilder = new ObjectCodecBuilder(SmallInlineFields.class, fory); CompileUnit compileUnit = new CompileUnit( @@ -212,7 +216,7 @@ public void testNestedContainer() { @Test public void testAccessLevel() { - Fory fory = Fory.builder().requireClassRegistration(false).build(); + Fory fory = Fory.builder().withXlang(false).requireClassRegistration(false).build(); AccessBeans.PublicClass object = AccessBeans.createPublicClassObject(); serDeCheckSerializer(fory, object, "Codec"); } diff --git a/java/fory-core/src/test/java/org/apache/fory/codegen/CodeGeneratorTest.java b/java/fory-core/src/test/java/org/apache/fory/codegen/CodeGeneratorTest.java index 704315f211..80315b8fbd 100644 --- a/java/fory-core/src/test/java/org/apache/fory/codegen/CodeGeneratorTest.java +++ b/java/fory-core/src/test/java/org/apache/fory/codegen/CodeGeneratorTest.java @@ -114,7 +114,8 @@ public void tryDuplicateCompile() { public ClassLoader tryDuplicateCompile(ClassLoader loader) { CodeGenerator codeGenerator = CodeGenerator.getSharedCodeGenerator(loader); ObjectCodecBuilder codecBuilder = - new ObjectCodecBuilder(Foo.class, Fory.builder().requireClassRegistration(false).build()); + new ObjectCodecBuilder( + Foo.class, Fory.builder().withXlang(false).requireClassRegistration(false).build()); CompileUnit compileUnit = new CompileUnit( Foo.class.getPackage().getName(), @@ -130,7 +131,8 @@ public ClassLoader tryDuplicateCompile(ClassLoader loader) { public void tryDefineClassesInClassLoader() { ByteArrayClassLoader loader = new ByteArrayClassLoader(new HashMap<>()); ObjectCodecBuilder codecBuilder = - new ObjectCodecBuilder(Foo.class, Fory.builder().requireClassRegistration(false).build()); + new ObjectCodecBuilder( + Foo.class, Fory.builder().withXlang(false).requireClassRegistration(false).build()); CompileUnit compileUnit = new CompileUnit( Foo.class.getPackage().getName(), diff --git a/java/fory-core/src/test/java/org/apache/fory/config/ForyBuilderTest.java b/java/fory-core/src/test/java/org/apache/fory/config/ForyBuilderTest.java index 9604a71820..c72cfd02a6 100644 --- a/java/fory-core/src/test/java/org/apache/fory/config/ForyBuilderTest.java +++ b/java/fory-core/src/test/java/org/apache/fory/config/ForyBuilderTest.java @@ -84,9 +84,9 @@ public int hashCode() { @Test public void testCompatibleStateIsBooleanBacked() { - Fory compatible = new ForyBuilder().withCompatible(true).build(); - Fory compatibleFromBoolean = new ForyBuilder().withCompatible(true).build(); - Fory schemaConsistent = new ForyBuilder().withCompatible(false).build(); + Fory compatible = new ForyBuilder().withXlang(false).withCompatible(true).build(); + Fory compatibleFromBoolean = new ForyBuilder().withXlang(false).withCompatible(true).build(); + Fory schemaConsistent = new ForyBuilder().withXlang(false).withCompatible(false).build(); assertTrue(compatible.getConfig().isCompatible()); assertEquals(compatible.getConfig(), compatibleFromBoolean.getConfig()); @@ -96,6 +96,7 @@ public void testCompatibleStateIsBooleanBacked() { @Test public void testXlangDefaultsToCompatibleUnlessExplicitlySet() { + assertTrue(new ForyBuilder().xlang); Fory defaultXlang = new ForyBuilder().withXlang(true).build(); Fory explicitSchemaConsistent = new ForyBuilder().withCompatible(false).withXlang(true).withClassVersionCheck(true).build(); @@ -140,11 +141,11 @@ public void testXlangForcesProtocolIntegerEncodings() { @Test public void testCodegenDefaultsOnOrdinaryJvm() { - Fory defaultFory = new ForyBuilder().build(); - Fory explicitCodegen = new ForyBuilder().withCodegen(true).build(); - Fory interpreter = new ForyBuilder().withCodegen(false).build(); + Fory defaultFory = new ForyBuilder().withXlang(false).build(); + Fory explicitCodegen = new ForyBuilder().withXlang(false).withCodegen(true).build(); + Fory interpreter = new ForyBuilder().withXlang(false).withCodegen(false).build(); ThreadSafeFory threadSafeInterpreter = - new ForyBuilder().withCodegen(false).buildThreadSafeForyPool(1); + new ForyBuilder().withXlang(false).withCodegen(false).buildThreadSafeForyPool(1); assertTrue(defaultFory.getConfig().isCodeGenEnabled()); assertTrue(explicitCodegen.getConfig().isCodeGenEnabled()); @@ -181,10 +182,11 @@ private static String readFully(InputStream inputStream) throws IOException { public static final class GraalvmCodegenConfigMain { public static void main(String[] args) { - Fory defaultFory = new ForyBuilder().build(); - Fory explicitCodegen = new ForyBuilder().withCodegen(true).build(); - Fory interpreter = new ForyBuilder().withCodegen(false).build(); - Fory asyncRequested = new ForyBuilder().withCodegen(true).withAsyncCompilation(true).build(); + Fory defaultFory = new ForyBuilder().withXlang(false).build(); + Fory explicitCodegen = new ForyBuilder().withXlang(false).withCodegen(true).build(); + Fory interpreter = new ForyBuilder().withXlang(false).withCodegen(false).build(); + Fory asyncRequested = + new ForyBuilder().withXlang(false).withCodegen(true).withAsyncCompilation(true).build(); assertDisabled(defaultFory, "default"); assertDisabled(explicitCodegen, "explicit codegen"); diff --git a/java/fory-core/src/test/java/org/apache/fory/io/MemoryBufferObjectInputTest.java b/java/fory-core/src/test/java/org/apache/fory/io/MemoryBufferObjectInputTest.java index 576972a703..595989f6ac 100644 --- a/java/fory-core/src/test/java/org/apache/fory/io/MemoryBufferObjectInputTest.java +++ b/java/fory-core/src/test/java/org/apache/fory/io/MemoryBufferObjectInputTest.java @@ -35,7 +35,7 @@ public class MemoryBufferObjectInputTest extends ForyTestBase { @Test(dataProvider = "compressNumber") public void testForyStructInput(boolean compressNumber) throws IOException { - Fory fory = Fory.builder().withNumberCompressed(compressNumber).build(); + Fory fory = Fory.builder().withXlang(false).withNumberCompressed(compressNumber).build(); MemoryBuffer buffer = MemoryUtils.buffer(32); buffer.writeByte(1); if (compressNumber) { diff --git a/java/fory-core/src/test/java/org/apache/fory/io/MemoryBufferObjectOutputTest.java b/java/fory-core/src/test/java/org/apache/fory/io/MemoryBufferObjectOutputTest.java index ad458429c7..4b2a5c0b02 100644 --- a/java/fory-core/src/test/java/org/apache/fory/io/MemoryBufferObjectOutputTest.java +++ b/java/fory-core/src/test/java/org/apache/fory/io/MemoryBufferObjectOutputTest.java @@ -32,7 +32,7 @@ public class MemoryBufferObjectOutputTest { @Test public void testForyStructOutput() throws IOException { - Fory fory = Fory.builder().build(); + Fory fory = Fory.builder().withXlang(false).build(); MemoryBuffer buffer = MemoryUtils.buffer(32); fory.getWriteContext().prepare(buffer, null); try (MemoryBufferObjectOutput output = diff --git a/java/fory-core/src/test/java/org/apache/fory/meta/NativeTypeDefEncoderTest.java b/java/fory-core/src/test/java/org/apache/fory/meta/NativeTypeDefEncoderTest.java index 0a6bd94679..036a9c6dad 100644 --- a/java/fory-core/src/test/java/org/apache/fory/meta/NativeTypeDefEncoderTest.java +++ b/java/fory-core/src/test/java/org/apache/fory/meta/NativeTypeDefEncoderTest.java @@ -45,7 +45,7 @@ public class NativeTypeDefEncoderTest { @Test public void testBasicTypeDef() { - Fory fory = Fory.builder().withMetaShare(true).build(); + Fory fory = Fory.builder().withXlang(false).withMetaShare(true).build(); Class type = TypeDefTest.TestFieldsOrderClass1.class; List fieldsInfo = buildFieldsInfo((ClassResolver) fory.getTypeResolver(), type); MemoryBuffer buffer = @@ -63,7 +63,7 @@ public void testBigMetaEncoding() { new Class[] { MapFields.class, BeanA.class, Struct.createStructClass("TestBigMetaEncoding", 5) }) { - Fory fory = Fory.builder().withMetaShare(true).build(); + Fory fory = Fory.builder().withXlang(false).withMetaShare(true).build(); TypeDef typeDef = TypeDef.buildTypeDef(fory.getTypeResolver(), type); TypeDef typeDef1 = TypeDef.readTypeDef( @@ -91,7 +91,7 @@ public void testEmptySubClassSerializer() { @Test public void testBigClassNameObject() { - Fory fory = Fory.builder().withMetaShare(true).build(); + Fory fory = Fory.builder().withXlang(false).withMetaShare(true).build(); TypeDef typeDef = TypeDef.buildTypeDef( fory.getTypeResolver(), @@ -212,7 +212,7 @@ public void testPrependHeader() { @Test public void testDecodeRejectsReservedGlobalBits() { - Fory fory = Fory.builder().withMetaShare(true).build(); + Fory fory = Fory.builder().withXlang(false).withMetaShare(true).build(); TypeDef typeDef = TypeDef.buildTypeDef(fory.getTypeResolver(), Foo1.class); MemoryBuffer encoded = MemoryBuffer.fromByteArray(typeDef.getEncoded()); long header = encoded.readInt64(); @@ -228,7 +228,7 @@ public void testDecodeRejectsReservedGlobalBits() { @Test public void testDecodeRejectsTrailingTypeDefBodyBytes() { - Fory fory = Fory.builder().withMetaShare(true).build(); + Fory fory = Fory.builder().withXlang(false).withMetaShare(true).build(); TypeDef typeDef = TypeDef.buildTypeDef(fory.getTypeResolver(), Foo1.class); MemoryBuffer encoded = MemoryBuffer.fromByteArray(typeDef.getEncoded()); long header = encoded.readInt64(); @@ -247,7 +247,7 @@ public void testDecodeRejectsTrailingTypeDefBodyBytes() { @Test public void testDecodeRejectsParsedTypeDefWithMismatchedHash() { - Fory fory = Fory.builder().withMetaShare(true).build(); + Fory fory = Fory.builder().withXlang(false).withMetaShare(true).build(); TypeDef typeDef = TypeDef.buildTypeDef(fory.getTypeResolver(), Foo1.class); MemoryBuffer encoded = MemoryBuffer.fromByteArray(typeDef.getEncoded()); long header = encoded.readInt64(); @@ -261,7 +261,7 @@ public void testDecodeRejectsParsedTypeDefWithMismatchedHash() { @Test public void testDecodeRejectsBodyOnlyHeaderHash() { - Fory fory = Fory.builder().withMetaShare(true).build(); + Fory fory = Fory.builder().withXlang(false).withMetaShare(true).build(); TypeDef typeDef = TypeDef.buildTypeDef(fory.getTypeResolver(), Foo1.class); byte[] malformed = rewriteHeaderWithBodyOnlyHash(typeDef); @@ -272,7 +272,7 @@ public void testDecodeRejectsBodyOnlyHeaderHash() { @Test public void testDecodeRejectsHashConsistentMalformedTypeDefBody() { - Fory fory = Fory.builder().withMetaShare(true).build(); + Fory fory = Fory.builder().withXlang(false).withMetaShare(true).build(); MemoryBuffer body = MemoryBuffer.newHeapBuffer(1); body.writeByte(0); MemoryBuffer encoded = NativeTypeDefEncoder.prependHeader(body, false); @@ -338,13 +338,19 @@ private static int indexOf(byte[] bytes, byte[] needle, int fromIndex) { public void testAbstractParentClass() { Fory fory0 = Fory.builder() + .withXlang(false) .withMetaShare(true) .withScopedMetaShare(true) .withCompatible(true) .requireClassRegistration(false) .build(); Fory fory1 = - Fory.builder().withMetaShare(true).withScopedMetaShare(true).withCompatible(true).build(); + Fory.builder() + .withXlang(false) + .withMetaShare(true) + .withScopedMetaShare(true) + .withCompatible(true) + .build(); fory1.register(BaseAbstractClass.class); fory1.register(ChildClass.class); for (Fory fory : new Fory[] {fory0, fory1}) { @@ -419,7 +425,7 @@ public static class ClassWithMixedFields { @Test public void testBuildFieldsInfoWithDuplicateTagIds() { - Fory fory = Fory.builder().withMetaShare(true).build(); + Fory fory = Fory.builder().withXlang(false).withMetaShare(true).build(); Assert.assertThrows( IllegalArgumentException.class, @@ -430,7 +436,7 @@ public void testBuildFieldsInfoWithDuplicateTagIds() { @Test public void testBuildFieldsInfoWithValidTagIds() { - Fory fory = Fory.builder().withMetaShare(true).build(); + Fory fory = Fory.builder().withXlang(false).withMetaShare(true).build(); // Should not throw any exception List fieldsInfo = @@ -445,7 +451,7 @@ public void testBuildFieldsInfoWithValidTagIds() { @Test public void testBuildFieldsInfoWithMixedFields() { - Fory fory = Fory.builder().withMetaShare(true).build(); + Fory fory = Fory.builder().withXlang(false).withMetaShare(true).build(); Assert.assertThrows( IllegalArgumentException.class, diff --git a/java/fory-core/src/test/java/org/apache/fory/meta/TypeDefTest.java b/java/fory-core/src/test/java/org/apache/fory/meta/TypeDefTest.java index 17aae7de76..19e5378c3f 100644 --- a/java/fory-core/src/test/java/org/apache/fory/meta/TypeDefTest.java +++ b/java/fory-core/src/test/java/org/apache/fory/meta/TypeDefTest.java @@ -98,7 +98,7 @@ public void testFieldsOrder() { @Test public void testTypeDefSerialization() throws NoSuchFieldException { - Fory fory = Fory.builder().withMetaShare(true).build(); + Fory fory = Fory.builder().withXlang(false).withMetaShare(true).build(); { TypeDef typeDef = TypeDef.buildTypeDef( @@ -147,7 +147,7 @@ public void testTypeDefSerialization() throws NoSuchFieldException { @Test public void testDuplicateFieldsClass() { - Fory fory = Fory.builder().withMetaShare(true).build(); + Fory fory = Fory.builder().withXlang(false).withMetaShare(true).build(); { TypeDef typeDef = TypeDef.buildTypeDef( @@ -168,7 +168,7 @@ public void testDuplicateFieldsClass() { @Test public void testContainerClass() { - Fory fory = Fory.builder().withMetaShare(true).build(); + Fory fory = Fory.builder().withXlang(false).withMetaShare(true).build(); List fields = ReflectionUtils.getFields(ContainerClass.class, true); TypeDef typeDef = TypeDef.buildTypeDef((ClassResolver) fory.getTypeResolver(), ContainerClass.class, fields); @@ -183,7 +183,7 @@ public void testContainerClass() { @Test public void testInterface() { - Fory fory = Fory.builder().withMetaShare(true).build(); + Fory fory = Fory.builder().withXlang(false).withMetaShare(true).build(); TypeDef typeDef = TypeDef.buildTypeDef(fory.getTypeResolver(), Map.class); assertTrue(typeDef.getFieldsInfo().isEmpty()); assertFalse(typeDef.isStructSchemaKind()); @@ -191,7 +191,7 @@ public void testInterface() { @Test public void testTypeExtInfo() { - Fory fory = Fory.builder().withRefTracking(true).withMetaShare(true).build(); + Fory fory = Fory.builder().withXlang(false).withRefTracking(true).withMetaShare(true).build(); ClassResolver classResolver = (ClassResolver) fory.getTypeResolver(); assertTrue( classResolver.needToWriteRef( @@ -237,7 +237,7 @@ static class ClassWithValidTagIds { @Test public void testDuplicateTagIdsThrowsException() { - Fory fory = Fory.builder().withMetaShare(true).build(); + Fory fory = Fory.builder().withXlang(false).withMetaShare(true).build(); List fields = ReflectionUtils.getFields(ClassWithDuplicateTagIds.class, true); Assert.assertThrows( @@ -249,7 +249,7 @@ public void testDuplicateTagIdsThrowsException() { @Test public void testDuplicateTagIdsMultipleThrowsException() { - Fory fory = Fory.builder().withMetaShare(true).build(); + Fory fory = Fory.builder().withXlang(false).withMetaShare(true).build(); List fields = ReflectionUtils.getFields(ClassWithDuplicateTagIdsMultiple.class, true); Assert.assertThrows( @@ -263,7 +263,7 @@ public void testDuplicateTagIdsMultipleThrowsException() { @Test public void testValidTagIdsSucceeds() { - Fory fory = Fory.builder().withMetaShare(true).build(); + Fory fory = Fory.builder().withXlang(false).withMetaShare(true).build(); List fields = ReflectionUtils.getFields(ClassWithValidTagIds.class, true); // Should not throw any exception @@ -308,7 +308,7 @@ static class TargetClassWithMixedTags { @Test public void testGetDescriptorsWithDuplicateTagIds() { - Fory fory = Fory.builder().withMetaShare(true).build(); + Fory fory = Fory.builder().withXlang(false).withMetaShare(true).build(); // Build a TypeDef with valid fields (no duplicates in TypeDef itself) List sourceFields = ReflectionUtils.getFields(ClassWithValidTagIds.class, true); @@ -324,7 +324,7 @@ public void testGetDescriptorsWithDuplicateTagIds() { @Test public void testGetDescriptorsWithValidTags() { - Fory fory = Fory.builder().withMetaShare(true).build(); + Fory fory = Fory.builder().withXlang(false).withMetaShare(true).build(); // Build a TypeDef with tagged fields List sourceFields = ReflectionUtils.getFields(TargetClassWithValidTags.class, true); @@ -341,7 +341,7 @@ public void testGetDescriptorsWithValidTags() { @Test public void testGetDescriptorsWithMixedTags() { - Fory fory = Fory.builder().withMetaShare(true).build(); + Fory fory = Fory.builder().withXlang(false).withMetaShare(true).build(); // Build a TypeDef with mixed tagged and non-tagged fields List sourceFields = ReflectionUtils.getFields(TargetClassWithMixedTags.class, true); @@ -393,7 +393,7 @@ static class TargetClassWithDifferentNames { @Test public void testGetDescriptorsMatchesByTagNotName() { - Fory fory = Fory.builder().withMetaShare(true).build(); + Fory fory = Fory.builder().withXlang(false).withMetaShare(true).build(); // Build a TypeDef from source class with specific tag IDs List sourceFields = ReflectionUtils.getFields(SourceClassWithTags.class, true); @@ -428,7 +428,7 @@ static class TargetClassWithZeroTagId { @Test public void testGetDescriptorsWithDuplicateZeroTagIds() { - Fory fory = Fory.builder().withMetaShare(true).build(); + Fory fory = Fory.builder().withXlang(false).withMetaShare(true).build(); // Build a TypeDef with some fields List sourceFields = ReflectionUtils.getFields(ClassWithValidTagIds.class, true); @@ -448,7 +448,7 @@ static class EmptyClass { @Test public void testGetDescriptorsWithEmptyClass() { - Fory fory = Fory.builder().withMetaShare(true).build(); + Fory fory = Fory.builder().withXlang(false).withMetaShare(true).build(); // Build a TypeDef with no fields List sourceFields = ReflectionUtils.getFields(EmptyClass.class, true); @@ -479,7 +479,7 @@ static class InheritedChildWithDuplicateTag extends InheritedBaseClass { @Test public void testGetDescriptorsWithInheritance() { - Fory fory = Fory.builder().withMetaShare(true).build(); + Fory fory = Fory.builder().withXlang(false).withMetaShare(true).build(); // Build a TypeDef with inherited fields List sourceFields = ReflectionUtils.getFields(InheritedChildClass.class, true); @@ -497,7 +497,7 @@ public void testGetDescriptorsWithInheritance() { @Test public void testGetDescriptorsWithInheritedDuplicateTag() { - Fory fory = Fory.builder().withMetaShare(true).build(); + Fory fory = Fory.builder().withXlang(false).withMetaShare(true).build(); // Build a TypeDef with some fields List sourceFields = ReflectionUtils.getFields(InheritedBaseClass.class, true); diff --git a/java/fory-core/src/test/java/org/apache/fory/resolver/AllowListCheckerTest.java b/java/fory-core/src/test/java/org/apache/fory/resolver/AllowListCheckerTest.java index db5b7a280f..cd463d3db0 100644 --- a/java/fory-core/src/test/java/org/apache/fory/resolver/AllowListCheckerTest.java +++ b/java/fory-core/src/test/java/org/apache/fory/resolver/AllowListCheckerTest.java @@ -36,7 +36,7 @@ public class AllowListCheckerTest { @Test public void testCheckClass() { { - Fory fory = Fory.builder().requireClassRegistration(false).build(); + Fory fory = Fory.builder().withXlang(false).requireClassRegistration(false).build(); AllowListChecker checker = new AllowListChecker(AllowListChecker.CheckLevel.STRICT); fory.getTypeResolver().setTypeChecker(checker); assertThrows(InsecureException.class, () -> fory.serialize(new AllowListCheckerTest())); @@ -47,7 +47,7 @@ public void testCheckClass() { assertThrows(InsecureException.class, () -> fory.deserialize(bytes)); } { - Fory fory = Fory.builder().requireClassRegistration(false).build(); + Fory fory = Fory.builder().withXlang(false).requireClassRegistration(false).build(); AllowListChecker checker = new AllowListChecker(AllowListChecker.CheckLevel.WARN); fory.getTypeResolver().setTypeChecker(checker); byte[] bytes = fory.serialize(new AllowListCheckerTest()); @@ -60,7 +60,7 @@ public void testCheckClass() { @Test public void testCheckClassWildcard() { { - Fory fory = Fory.builder().requireClassRegistration(false).build(); + Fory fory = Fory.builder().withXlang(false).requireClassRegistration(false).build(); AllowListChecker checker = new AllowListChecker(AllowListChecker.CheckLevel.STRICT); fory.getTypeResolver().setTypeChecker(checker); assertThrows(InsecureException.class, () -> fory.serialize(new AllowListCheckerTest())); @@ -71,7 +71,7 @@ public void testCheckClassWildcard() { assertThrows(InsecureException.class, () -> fory.deserialize(bytes)); } { - Fory fory = Fory.builder().requireClassRegistration(false).build(); + Fory fory = Fory.builder().withXlang(false).requireClassRegistration(false).build(); AllowListChecker checker = new AllowListChecker(AllowListChecker.CheckLevel.WARN); fory.getTypeResolver().setTypeChecker(checker); byte[] bytes = fory.serialize(new AllowListCheckerTest()); @@ -85,7 +85,12 @@ public void testCheckClassWildcard() { public void testBuilderConfiguredChecker() { AllowListChecker checker = new AllowListChecker(AllowListChecker.CheckLevel.STRICT); checker.allowClass("org.apache.fory.*"); - Fory fory = Fory.builder().requireClassRegistration(false).withTypeChecker(checker).build(); + Fory fory = + Fory.builder() + .withXlang(false) + .requireClassRegistration(false) + .withTypeChecker(checker) + .build(); byte[] bytes = fory.serialize(new AllowListCheckerTest()); checker.disallowClass("org.apache.fory.*"); assertThrows(InsecureException.class, () -> fory.serialize(new AllowListCheckerTest())); @@ -102,13 +107,14 @@ public void testBuilderConfiguredCheckerSuppressesStartupWarning() { System.setOut(capture); LoggerFactory.setLogLevel(LogLevel.WARN_LEVEL); - Fory.builder().requireClassRegistration(false).build(); + Fory.builder().withXlang(false).requireClassRegistration(false).build(); assertTrue( new String(output.toByteArray(), StandardCharsets.UTF_8) .contains("Class registration isn't forced")); output.reset(); Fory.builder() + .withXlang(false) .requireClassRegistration(false) .withTypeChecker((resolver, className) -> true) .build(); @@ -129,6 +135,7 @@ public void testThreadSafeFory() { AllowListChecker checker = new AllowListChecker(AllowListChecker.CheckLevel.STRICT); ThreadSafeFory fory = Fory.builder() + .withXlang(false) .requireClassRegistration(false) .withTypeChecker(checker) .buildThreadSafeFory(); diff --git a/java/fory-core/src/test/java/org/apache/fory/resolver/ClassResolverTest.java b/java/fory-core/src/test/java/org/apache/fory/resolver/ClassResolverTest.java index dc59cfde03..2f723a8b78 100644 --- a/java/fory-core/src/test/java/org/apache/fory/resolver/ClassResolverTest.java +++ b/java/fory-core/src/test/java/org/apache/fory/resolver/ClassResolverTest.java @@ -506,7 +506,7 @@ public void testWriteClassName() { @Test public void testWriteClassNamesInSamePackage() { - Fory fory = Fory.builder().requireClassRegistration(false).build(); + Fory fory = Fory.builder().withXlang(false).requireClassRegistration(false).build(); MemoryBuffer buffer = MemoryBuffer.newHeapBuffer(32); withWriteContext( fory, diff --git a/java/fory-core/src/test/java/org/apache/fory/resolver/GraalvmRuntimeArrayTest.java b/java/fory-core/src/test/java/org/apache/fory/resolver/GraalvmRuntimeArrayTest.java index 449c6dc683..023796ea1e 100644 --- a/java/fory-core/src/test/java/org/apache/fory/resolver/GraalvmRuntimeArrayTest.java +++ b/java/fory-core/src/test/java/org/apache/fory/resolver/GraalvmRuntimeArrayTest.java @@ -62,6 +62,7 @@ public static final class GraalvmRuntimeArrayMain { public static void main(String[] args) { Fory fory = Fory.builder() + .withXlang(false) .withCodegen(false) .requireClassRegistration(true) .suppressClassRegistrationWarnings(true) diff --git a/java/fory-core/src/test/java/org/apache/fory/serializer/AndroidDynamicFeatureTest.java b/java/fory-core/src/test/java/org/apache/fory/serializer/AndroidDynamicFeatureTest.java index 55f9f4c861..6d08427b52 100644 --- a/java/fory-core/src/test/java/org/apache/fory/serializer/AndroidDynamicFeatureTest.java +++ b/java/fory-core/src/test/java/org/apache/fory/serializer/AndroidDynamicFeatureTest.java @@ -108,6 +108,7 @@ private static void verifyReflectiveGetter() { private static void verifyFory(boolean compressString) { Fory fory = Fory.builder() + .withXlang(false) .withCodegen(true) .withStringCompressed(compressString) .withRefTracking(true) diff --git a/java/fory-core/src/test/java/org/apache/fory/serializer/AndroidJvmRoundTripTest.java b/java/fory-core/src/test/java/org/apache/fory/serializer/AndroidJvmRoundTripTest.java index 3ce1ac3e63..5b93035622 100644 --- a/java/fory-core/src/test/java/org/apache/fory/serializer/AndroidJvmRoundTripTest.java +++ b/java/fory-core/src/test/java/org/apache/fory/serializer/AndroidJvmRoundTripTest.java @@ -187,6 +187,7 @@ private static void requireAndroidMode() { private static Fory newFory(RoundTripKind kind) { Fory fory = Fory.builder() + .withXlang(false) .withCodegen(true) .withRefTracking(true) .requireClassRegistration(false) diff --git a/java/fory-core/src/test/java/org/apache/fory/serializer/ArraySerializersTest.java b/java/fory-core/src/test/java/org/apache/fory/serializer/ArraySerializersTest.java index ec58608b4a..652a98e7c9 100644 --- a/java/fory-core/src/test/java/org/apache/fory/serializer/ArraySerializersTest.java +++ b/java/fory-core/src/test/java/org/apache/fory/serializer/ArraySerializersTest.java @@ -98,7 +98,7 @@ private static void registerTypes(Fory... forys) { @Test public void testDedicatedObjectArraySerializersAreRetained() { - Fory fory = Fory.builder().requireClassRegistration(false).build(); + Fory fory = Fory.builder().withXlang(false).requireClassRegistration(false).build(); assertTrue( (Object) fory.getSerializer(Object[].class) instanceof ArraySerializers.ObjectArraySerializer); @@ -304,6 +304,7 @@ public static void testPrimitiveArray(Fory fory1, Fory fory2) { public void testPrimitiveArrayReadRejectsOversizedBinaryPayload() { Fory fory = Fory.builder() + .withXlang(false) .withMaxBinarySize(4) .withIntArrayCompressed(true) .withLongArrayCompressed(true) @@ -330,7 +331,7 @@ public void testPrimitiveArrayReadRejectsOversizedBinaryPayload() { @Test public void testPrimitiveArrayReadRejectsUnalignedBinaryPayload() { - Fory fory = Fory.builder().withMaxBinarySize(64).build(); + Fory fory = Fory.builder().withXlang(false).withMaxBinarySize(64).build(); for (Class arrayType : new Class[] { char[].class, short[].class, int[].class, long[].class, float[].class, double[].class @@ -343,7 +344,7 @@ public void testPrimitiveArrayReadRejectsUnalignedBinaryPayload() { @Test public void testPrimitiveArrayReadRejectsTruncatedPayload() { - Fory fory = Fory.builder().withMaxBinarySize(64).build(); + Fory fory = Fory.builder().withXlang(false).withMaxBinarySize(64).build(); Class[] arrayTypes = new Class[] { boolean[].class, @@ -367,13 +368,17 @@ public void testPrimitiveArrayReadRejectsTruncatedPayload() { @Test public void testPrimitiveArrayReadRejectsNegativeDecodedBinaryPayload() { - Fory fixedWidthFory = Fory.builder().build(); + Fory fixedWidthFory = Fory.builder().withXlang(false).build(); assertThrows( DeserializationException.class, () -> readPrimitiveArrayRawPayload(fixedWidthFory, char[].class)); Fory compressedFory = - Fory.builder().withIntArrayCompressed(true).withLongArrayCompressed(true).build(); + Fory.builder() + .withXlang(false) + .withIntArrayCompressed(true) + .withLongArrayCompressed(true) + .build(); assertThrows( DeserializationException.class, () -> readPrimitiveArrayRawPayload(compressedFory, int[].class)); @@ -517,7 +522,12 @@ public GenericArrayWrapper(Class clazz, int capacity) { @SuppressWarnings("unchecked") @Test(dataProvider = "enableCodegen") public void testArrayPolyMorphic(boolean enableCodegen) { - Fory fory = Fory.builder().requireClassRegistration(false).withCodegen(enableCodegen).build(); + Fory fory = + Fory.builder() + .withXlang(false) + .requireClassRegistration(false) + .withCodegen(enableCodegen) + .build(); Object[] arr = new String[] {"a", "b"}; serDeCheck(fory, arr); @@ -572,6 +582,7 @@ public void testVariableLengthLongArray() { // Create Fory instance with variable-length encoding enabled for long arrays Fory fory = Fory.builder() + .withXlang(false) .requireClassRegistration(false) .withLongArrayCompressed(true) .withLongCompressed(Int64Encoding.VARINT) @@ -620,11 +631,16 @@ public void testVariableLengthLongArray() { public void testVariableLengthEncodingEfficiencyForSmallValues() { // Create a Fory instance with fixed-length encoding (compressLongArray disabled) Fory foryFixed = - Fory.builder().requireClassRegistration(false).withLongArrayCompressed(false).build(); + Fory.builder() + .withXlang(false) + .requireClassRegistration(false) + .withLongArrayCompressed(false) + .build(); // Create a Fory instance with variable-length encoding (compressLongArray enabled) Fory foryVariable = Fory.builder() + .withXlang(false) .requireClassRegistration(false) .withLongArrayCompressed(true) .withLongCompressed(Int64Encoding.VARINT) @@ -735,7 +751,12 @@ public void testVariableLengthEncodingEfficiencyForSmallValues() { @Test public void testVariableLengthIntArray() { // Create Fory instance with variable-length encoding enabled for int arrays - Fory fory = Fory.builder().requireClassRegistration(false).withIntArrayCompressed(true).build(); + Fory fory = + Fory.builder() + .withXlang(false) + .requireClassRegistration(false) + .withIntArrayCompressed(true) + .build(); // Test empty array int[] emptyArray = new int[0]; @@ -828,11 +849,19 @@ public void testXlangPrimitiveArrayIgnoresNativeCompressionFlags() { public void testVariableLengthIntArrayEncodingEfficiencyForSmallValues() { // Create a Fory instance with fixed-length encoding (compressIntArray disabled) Fory foryFixed = - Fory.builder().requireClassRegistration(false).withIntArrayCompressed(false).build(); + Fory.builder() + .withXlang(false) + .requireClassRegistration(false) + .withIntArrayCompressed(false) + .build(); // Create a Fory instance with variable-length encoding (compressIntArray enabled) Fory foryVariable = - Fory.builder().requireClassRegistration(false).withIntArrayCompressed(true).build(); + Fory.builder() + .withXlang(false) + .requireClassRegistration(false) + .withIntArrayCompressed(true) + .build(); // Create an array with many small values (0-127, which can be encoded in 1-2 bytes with varint) int arraySize = 10000; diff --git a/java/fory-core/src/test/java/org/apache/fory/serializer/BufferSerializersTest.java b/java/fory-core/src/test/java/org/apache/fory/serializer/BufferSerializersTest.java index c8913ad734..f2901e1c4f 100644 --- a/java/fory-core/src/test/java/org/apache/fory/serializer/BufferSerializersTest.java +++ b/java/fory-core/src/test/java/org/apache/fory/serializer/BufferSerializersTest.java @@ -31,7 +31,7 @@ public class BufferSerializersTest extends ForyTestBase { @Test public void testByteBuffer() { - Fory fory = Fory.builder().build(); + Fory fory = Fory.builder().withXlang(false).build(); ByteBuffer buffer1 = ByteBuffer.allocate(32); buffer1.putLong(1000L); ByteBufferUtil.rewind(buffer1); @@ -56,7 +56,7 @@ public void testByteBuffer(Fory fory) { @Test public void testByteBufferRejectsMalformedPayload() { - Fory fory = Fory.builder().build(); + Fory fory = Fory.builder().withXlang(false).build(); Serializer serializer = new BufferSerializers.ByteBufferSerializer(fory.getTypeResolver(), ByteBuffer.class); diff --git a/java/fory-core/src/test/java/org/apache/fory/serializer/EnumSerializerTest.java b/java/fory-core/src/test/java/org/apache/fory/serializer/EnumSerializerTest.java index 7e238d8954..3368f1cdde 100644 --- a/java/fory-core/src/test/java/org/apache/fory/serializer/EnumSerializerTest.java +++ b/java/fory-core/src/test/java/org/apache/fory/serializer/EnumSerializerTest.java @@ -176,7 +176,7 @@ public void testEnumSerializer(Fory fory) { @Test public void testEnumSerializationUsesOrdinalArrayByDefault() throws Exception { - Fory fory = Fory.builder().requireClassRegistration(false).build(); + Fory fory = Fory.builder().withXlang(false).requireClassRegistration(false).build(); EnumSerializer serializer = getEnumSerializer(fory, EnumFoo.class); assertEquals(writeEnumTag(fory, serializer, EnumFoo.B), 1); @@ -186,7 +186,7 @@ public void testEnumSerializationUsesOrdinalArrayByDefault() throws Exception { @Test public void testEnumSerializationUsesAnnotatedFieldId() { - Fory fory = Fory.builder().requireClassRegistration(false).build(); + Fory fory = Fory.builder().withXlang(false).requireClassRegistration(false).build(); EnumSerializer serializer = getEnumSerializer(fory, EnumWithIdField.class); assertEquals(writeEnumTag(fory, serializer, EnumWithIdField.B), 20); @@ -195,7 +195,7 @@ public void testEnumSerializationUsesAnnotatedFieldId() { @Test public void testEnumSerializationUsesAnnotatedMethodId() { - Fory fory = Fory.builder().requireClassRegistration(false).build(); + Fory fory = Fory.builder().withXlang(false).requireClassRegistration(false).build(); EnumSerializer serializer = getEnumSerializer(fory, EnumWithIdMethod.class); assertEquals(writeEnumTag(fory, serializer, EnumWithIdMethod.B), 200); @@ -204,7 +204,7 @@ public void testEnumSerializationUsesAnnotatedMethodId() { @Test public void testEnumSerializationUsesAnnotatedConstantId() { - Fory fory = Fory.builder().requireClassRegistration(false).build(); + Fory fory = Fory.builder().withXlang(false).requireClassRegistration(false).build(); EnumSerializer serializer = getEnumSerializer(fory, EnumWithConstantIds.class); assertEquals(writeEnumTag(fory, serializer, EnumWithConstantIds.B), 7); @@ -213,7 +213,7 @@ public void testEnumSerializationUsesAnnotatedConstantId() { @Test public void testEnumSerializationUsesSparseMapForLargeIds() throws Exception { - Fory fory = Fory.builder().requireClassRegistration(false).build(); + Fory fory = Fory.builder().withXlang(false).requireClassRegistration(false).build(); EnumSerializer serializer = getEnumSerializer(fory, EnumWithLargeIds.class); assertEquals(writeEnumTag(fory, serializer, EnumWithLargeIds.B), 8192); @@ -223,7 +223,7 @@ public void testEnumSerializationUsesSparseMapForLargeIds() throws Exception { @Test public void testEnumSerializationRejectsPartialConstantIds() { - Fory fory = Fory.builder().requireClassRegistration(false).build(); + Fory fory = Fory.builder().withXlang(false).requireClassRegistration(false).build(); assertThrows( IllegalArgumentException.class, () -> getEnumSerializer(fory, EnumWithPartialConstantIds.class)); @@ -231,14 +231,14 @@ public void testEnumSerializationRejectsPartialConstantIds() { @Test public void testEnumSerializationRejectsDuplicateIds() { - Fory fory = Fory.builder().requireClassRegistration(false).build(); + Fory fory = Fory.builder().withXlang(false).requireClassRegistration(false).build(); assertThrows( IllegalArgumentException.class, () -> getEnumSerializer(fory, EnumWithDuplicateIds.class)); } @Test public void testEnumSerializationRejectsConflictingIdStrategies() { - Fory fory = Fory.builder().requireClassRegistration(false).build(); + Fory fory = Fory.builder().withXlang(false).requireClassRegistration(false).build(); assertThrows( IllegalArgumentException.class, () -> getEnumSerializer(fory, EnumWithConflictingIdStrategies.class)); diff --git a/java/fory-core/src/test/java/org/apache/fory/serializer/JavaSerializerTest.java b/java/fory-core/src/test/java/org/apache/fory/serializer/JavaSerializerTest.java index f3bbedb7f2..f69260eeb1 100644 --- a/java/fory-core/src/test/java/org/apache/fory/serializer/JavaSerializerTest.java +++ b/java/fory-core/src/test/java/org/apache/fory/serializer/JavaSerializerTest.java @@ -74,7 +74,7 @@ public void testJdkSerializationMagicNumber() throws Exception { Assert.assertEquals(BigEndian.getShortB(bytes, 0), ObjectStreamConstants.STREAM_MAGIC); Assert.assertTrue(JavaSerializer.serializedByJDK(bytes)); Assert.assertTrue(JavaSerializer.serializedByJDK(ByteBuffer.wrap(bytes), 0)); - Fory fory = Fory.builder().build(); + Fory fory = Fory.builder().withXlang(false).build(); bytes = fory.serialize(1.1); Assert.assertFalse(JavaSerializer.serializedByJDK(bytes)); } diff --git a/java/fory-core/src/test/java/org/apache/fory/serializer/LambdaSerializerTest.java b/java/fory-core/src/test/java/org/apache/fory/serializer/LambdaSerializerTest.java index e925cb144d..099540b6fc 100644 --- a/java/fory-core/src/test/java/org/apache/fory/serializer/LambdaSerializerTest.java +++ b/java/fory-core/src/test/java/org/apache/fory/serializer/LambdaSerializerTest.java @@ -84,7 +84,7 @@ public void testLambdaCopy(Fory fory) { @Test public void testLambdaUnserializableMsg() { - Fory fory = Fory.builder().requireClassRegistration(false).build(); + Fory fory = Fory.builder().withXlang(false).requireClassRegistration(false).build(); Function function = String::valueOf; assertThrowsCause(UnsupportedOperationException.class, () -> fory.serialize(function)); try { diff --git a/java/fory-core/src/test/java/org/apache/fory/serializer/ObjectSerializerTest.java b/java/fory-core/src/test/java/org/apache/fory/serializer/ObjectSerializerTest.java index 9da2c6b135..162b984c6f 100644 --- a/java/fory-core/src/test/java/org/apache/fory/serializer/ObjectSerializerTest.java +++ b/java/fory-core/src/test/java/org/apache/fory/serializer/ObjectSerializerTest.java @@ -232,6 +232,7 @@ public static void main(String[] args) { Fory fory = Fory.builder() + .withXlang(false) .withCodegen(true) .withRefTracking(true) .requireClassRegistration(false) diff --git a/java/fory-core/src/test/java/org/apache/fory/serializer/PrimitiveSerializersTest.java b/java/fory-core/src/test/java/org/apache/fory/serializer/PrimitiveSerializersTest.java index 081fe749c3..a2904dbce6 100644 --- a/java/fory-core/src/test/java/org/apache/fory/serializer/PrimitiveSerializersTest.java +++ b/java/fory-core/src/test/java/org/apache/fory/serializer/PrimitiveSerializersTest.java @@ -262,6 +262,7 @@ public void testPrimitiveListAsCollectionFieldWithCodegen() { public void testPrimitiveListReadRejectsMalformedBinaryPayloadSize() { Fory fory = Fory.builder() + .withXlang(false) .withMaxBinarySize(4) .withIntArrayCompressed(true) .withLongArrayCompressed(true) @@ -278,13 +279,17 @@ public void testPrimitiveListReadRejectsMalformedBinaryPayloadSize() { @Test public void testPrimitiveListReadRejectsNegativeDecodedBinaryPayload() { - Fory fixedWidthFory = Fory.builder().build(); + Fory fixedWidthFory = Fory.builder().withXlang(false).build(); assertThrows( DeserializationException.class, () -> readPrimitiveListRawPayload(fixedWidthFory, Int16List.class)); Fory compressedFory = - Fory.builder().withIntArrayCompressed(true).withLongArrayCompressed(true).build(); + Fory.builder() + .withXlang(false) + .withIntArrayCompressed(true) + .withLongArrayCompressed(true) + .build(); assertThrows( DeserializationException.class, () -> readPrimitiveListRawPayload(compressedFory, Int32List.class)); diff --git a/java/fory-core/src/test/java/org/apache/fory/serializer/ReplaceResolveSerializerTest.java b/java/fory-core/src/test/java/org/apache/fory/serializer/ReplaceResolveSerializerTest.java index f8e0249403..027f006174 100644 --- a/java/fory-core/src/test/java/org/apache/fory/serializer/ReplaceResolveSerializerTest.java +++ b/java/fory-core/src/test/java/org/apache/fory/serializer/ReplaceResolveSerializerTest.java @@ -560,7 +560,7 @@ public Object readResolve() { @Test public void testInheritance() { - Fory fory = Fory.builder().requireClassRegistration(false).build(); + Fory fory = Fory.builder().withXlang(false).requireClassRegistration(false).build(); byte[] bytes = fory.serialize(new InheritanceTestClass((byte) 10)); InheritanceTestClass o = (InheritanceTestClass) fory.deserialize(bytes); assertEquals(o.f1, 10); diff --git a/java/fory-core/src/test/java/org/apache/fory/serializer/SerializersTest.java b/java/fory-core/src/test/java/org/apache/fory/serializer/SerializersTest.java index c9df3b8bf6..f25ed88c6c 100644 --- a/java/fory-core/src/test/java/org/apache/fory/serializer/SerializersTest.java +++ b/java/fory-core/src/test/java/org/apache/fory/serializer/SerializersTest.java @@ -67,7 +67,10 @@ public void testStringBuilder(boolean referenceTracking, boolean xlang) { @Test(dataProvider = "referenceTrackingConfig") public void testBigInt(boolean referenceTracking) { ForyBuilder builder = - Fory.builder().withRefTracking(referenceTracking).requireClassRegistration(false); + Fory.builder() + .withXlang(false) + .withRefTracking(referenceTracking) + .requireClassRegistration(false); Fory fory1 = builder.build(); Fory fory2 = builder.build(); assertEquals(BigInteger.valueOf(100), serDe(fory1, fory2, BigInteger.valueOf(100))); @@ -81,7 +84,12 @@ public void testBigInt(boolean referenceTracking) { @Test public void testBigNumberReadsRejectOversizedBinaryPayload() { - Fory fory = Fory.builder().withMaxBinarySize(1).requireClassRegistration(false).build(); + Fory fory = + Fory.builder() + .withXlang(false) + .withMaxBinarySize(1) + .requireClassRegistration(false) + .build(); assertThrows( DeserializationException.class, @@ -277,7 +285,7 @@ private Object writeReplace() { @Test public void testSerializeClass() { - Fory fory = Fory.builder().requireClassRegistration(false).build(); + Fory fory = Fory.builder().withXlang(false).requireClassRegistration(false).build(); // serialize both TestReplaceClassSerialization object and class. // Scala `object` native serialization will return ModuleSerializationProxy will write original // class. @@ -294,7 +302,7 @@ public void testSerializeClass() { @Test public void testEmptyObject() { - Fory fory = Fory.builder().requireClassRegistration(true).build(); + Fory fory = Fory.builder().withXlang(false).requireClassRegistration(true).build(); assertSame(serDe(fory, new Object()).getClass(), Object.class); } } diff --git a/java/fory-core/src/test/java/org/apache/fory/serializer/StringSerializerTest.java b/java/fory-core/src/test/java/org/apache/fory/serializer/StringSerializerTest.java index d9bf9be09b..95c41e6714 100644 --- a/java/fory-core/src/test/java/org/apache/fory/serializer/StringSerializerTest.java +++ b/java/fory-core/src/test/java/org/apache/fory/serializer/StringSerializerTest.java @@ -146,7 +146,12 @@ static void writeJDK8String(MemoryBuffer buffer, String value) { @Test public void testJavaStringSimple() { - Fory fory = Fory.builder().withStringCompressed(true).requireClassRegistration(false).build(); + Fory fory = + Fory.builder() + .withXlang(false) + .withStringCompressed(true) + .requireClassRegistration(false) + .build(); MemoryBuffer buffer = MemoryUtils.buffer(32); StringSerializer serializer = new StringSerializer(fory.getConfig()); { @@ -209,6 +214,7 @@ public void testCompressedStringEstimatedWrongSize() { public void testJavaString(boolean stringCompress, boolean writeNumUtf16BytesForUtf8Encoding) { Fory fory = Fory.builder() + .withXlang(false) .withStringCompressed(stringCompress) .withWriteNumUtf16BytesForUtf8Encoding(writeNumUtf16BytesForUtf8Encoding) .requireClassRegistration(false) @@ -235,6 +241,7 @@ public void testJavaStringOffHeap( boolean stringCompress, boolean writeNumUtf16BytesForUtf8Encoding) { Fory fory = Fory.builder() + .withXlang(false) .withStringCompressed(stringCompress) .withWriteNumUtf16BytesForUtf8Encoding(writeNumUtf16BytesForUtf8Encoding) .requireClassRegistration(false) @@ -332,7 +339,12 @@ public void testCompressJava8String() { if (JdkVersion.MAJOR_VERSION != 8) { throw new SkipException("Java 8 only"); } - Fory fory = Fory.builder().withStringCompressed(true).requireClassRegistration(false).build(); + Fory fory = + Fory.builder() + .withXlang(false) + .withStringCompressed(true) + .requireClassRegistration(false) + .build(); StringSerializer stringSerializer = (StringSerializer) fory.getTypeResolver().getSerializer(String.class); @@ -357,6 +369,7 @@ public void testCompressJava8String() { public void testReadUtf8String(boolean writeNumUtf16BytesForUtf8Encoding) { Fory fory = Fory.builder() + .withXlang(false) .withStringCompressed(true) .withWriteNumUtf16BytesForUtf8Encoding(writeNumUtf16BytesForUtf8Encoding) .requireClassRegistration(false) diff --git a/java/fory-core/src/test/java/org/apache/fory/serializer/URLSerializerTest.java b/java/fory-core/src/test/java/org/apache/fory/serializer/URLSerializerTest.java index 7945fc2e71..5035051921 100644 --- a/java/fory-core/src/test/java/org/apache/fory/serializer/URLSerializerTest.java +++ b/java/fory-core/src/test/java/org/apache/fory/serializer/URLSerializerTest.java @@ -42,7 +42,7 @@ public void testDefaultCopy(Fory fory) throws MalformedURLException { @Test public void testURLSerializer() throws MalformedURLException { - Fory fory = Fory.builder().build(); + Fory fory = Fory.builder().withXlang(false).build(); fory.registerSerializer(URL.class, URLSerializer.class); Assert.assertEquals( serDeCheckSerializer(fory, new URL("http://test"), "URLSerializer"), diff --git a/java/fory-core/src/test/java/org/apache/fory/serializer/collection/AndroidCollectionFeatureTest.java b/java/fory-core/src/test/java/org/apache/fory/serializer/collection/AndroidCollectionFeatureTest.java index a6806004f4..2d2e2d5736 100644 --- a/java/fory-core/src/test/java/org/apache/fory/serializer/collection/AndroidCollectionFeatureTest.java +++ b/java/fory-core/src/test/java/org/apache/fory/serializer/collection/AndroidCollectionFeatureTest.java @@ -140,6 +140,7 @@ private static String readFully(InputStream inputStream) throws IOException { private static Fory newCompatibleChildContainerFory() { return Fory.builder() + .withXlang(false) .withCodegen(true) .withRefTracking(true) .requireClassRegistration(false) @@ -179,6 +180,7 @@ public static void main(String[] args) throws Exception { Fory fory = Fory.builder() + .withXlang(false) .withCodegen(true) .withRefTracking(true) .requireClassRegistration(false) diff --git a/java/fory-core/src/test/java/org/apache/fory/serializer/collection/CollectionSerializersTest.java b/java/fory-core/src/test/java/org/apache/fory/serializer/collection/CollectionSerializersTest.java index e974b9b825..3f9e4b1d72 100644 --- a/java/fory-core/src/test/java/org/apache/fory/serializer/collection/CollectionSerializersTest.java +++ b/java/fory-core/src/test/java/org/apache/fory/serializer/collection/CollectionSerializersTest.java @@ -189,6 +189,7 @@ public static class BasicListNestedJIT { public void testBasicListNestedJIT(boolean referenceTracking) { Fory fory = Fory.builder() + .withXlang(false) .withRefTracking(referenceTracking) .withCodegen(true) .requireClassRegistration(false) @@ -1090,7 +1091,7 @@ public void testCollectionReadRejectsOversizedElementCount() { @Test public void testBitSetReadRejectsNegativeDecodedBinaryPayload() { - Fory fory = Fory.builder().build(); + Fory fory = Fory.builder().withXlang(false).build(); MemoryBuffer buffer = MemoryBuffer.newHeapBuffer(5); writeNegativeDecodedVarUInt32(buffer); ReadContext readContext = fory.getReadContext(); @@ -1549,7 +1550,12 @@ static class CollectionAbstractTest { @Test(dataProvider = "enableCodegen") public void testAbstractCollectionElementsSerialization(boolean enableCodegen) { - Fory fory = Fory.builder().withCodegen(enableCodegen).requireClassRegistration(false).build(); + Fory fory = + Fory.builder() + .withXlang(false) + .withCodegen(enableCodegen) + .requireClassRegistration(false) + .build(); { CollectionAbstractTest test = new CollectionAbstractTest(); test.fooList = new ArrayList<>(ImmutableList.of(new Foo1(), new Foo1())); @@ -1580,6 +1586,7 @@ public void testAbstractCollectionElementsSerialization(Fory fory) { public void testCollectionAllNullElements(boolean enableCodegen) { Fory fory = Fory.builder() + .withXlang(false) .withCodegen(true) .withRefTracking(true) .requireClassRegistration(false) @@ -1598,6 +1605,7 @@ public void testCollectionAllNullElements(boolean enableCodegen) { fory = Fory.builder() + .withXlang(false) .withCodegen(enableCodegen) .withRefTracking(true) .requireClassRegistration(false) diff --git a/java/fory-core/src/test/java/org/apache/fory/serializer/collection/MapSerializersTest.java b/java/fory-core/src/test/java/org/apache/fory/serializer/collection/MapSerializersTest.java index 501ca74df9..2db9d5c842 100644 --- a/java/fory-core/src/test/java/org/apache/fory/serializer/collection/MapSerializersTest.java +++ b/java/fory-core/src/test/java/org/apache/fory/serializer/collection/MapSerializersTest.java @@ -1007,7 +1007,7 @@ public static class StringKeyMap extends HashMap {} @Test public void testStringKeyMapSerializer() { // see https://github.com/apache/fory/issues/1170 - Fory fory = Fory.builder().withRefTracking(true).build(); + Fory fory = Fory.builder().withXlang(false).withRefTracking(true).build(); fory.registerSerializer(StringKeyMap.class, MapSerializers.StringKeyMapSerializer.class); { StringKeyMap> map = new StringKeyMap<>(); @@ -1212,7 +1212,7 @@ public void testNestedValueByPrivateMapSerializer() { @Test(dataProvider = "referenceTrackingConfig") public void testObjectKeyValueChunk(boolean referenceTrackingConfig) { - Fory fory = Fory.builder().withRefTracking(referenceTrackingConfig).build(); + Fory fory = Fory.builder().withXlang(false).withRefTracking(referenceTrackingConfig).build(); final Map differentKeyAndValueTypeMap = createDifferentKeyAndValueTypeMap(); final Serializer serializer = fory.getSerializer(differentKeyAndValueTypeMap.getClass()); @@ -1222,7 +1222,7 @@ public void testObjectKeyValueChunk(boolean referenceTrackingConfig) { @Test(dataProvider = "referenceTrackingConfig") public void testObjectKeyValueBigChunk(boolean referenceTrackingConfig) { - Fory fory = Fory.builder().withRefTracking(referenceTrackingConfig).build(); + Fory fory = Fory.builder().withXlang(false).withRefTracking(referenceTrackingConfig).build(); final Map differentKeyAndValueTypeMap = createDifferentKeyAndValueTypeMap(); for (int i = 0; i < 3000; i++) { differentKeyAndValueTypeMap.put("k" + i, i); diff --git a/java/fory-core/src/test/java/org/apache/fory/serializer/struct/FingerprintTest.java b/java/fory-core/src/test/java/org/apache/fory/serializer/struct/FingerprintTest.java index ee7e6c1404..7d6da02a2f 100644 --- a/java/fory-core/src/test/java/org/apache/fory/serializer/struct/FingerprintTest.java +++ b/java/fory-core/src/test/java/org/apache/fory/serializer/struct/FingerprintTest.java @@ -46,7 +46,7 @@ public static class RefWithoutForyFieldStruct { @Test public void testUnsignedScalarFieldsFingerprint() { - Fory fory = Fory.builder().build(); + Fory fory = Fory.builder().withXlang(false).build(); List descriptors = Descriptor.getDescriptors(UnsignedScalarFields.class); String fingerprint = Fingerprint.computeStructFingerprint(fory, descriptors); @@ -83,7 +83,7 @@ public void testUnsignedScalarFieldsFingerprint() { @Test public void testUnsignedArrayFieldsFingerprint() { - Fory fory = Fory.builder().build(); + Fory fory = Fory.builder().withXlang(false).build(); List descriptors = Descriptor.getDescriptors(UnsignedArrayFields.class); String fingerprint = Fingerprint.computeStructFingerprint(fory, descriptors); @@ -110,7 +110,7 @@ public void testUnsignedArrayFieldsFingerprint() { @Test public void testAllUnsignedFieldsFingerprint() { - Fory fory = Fory.builder().build(); + Fory fory = Fory.builder().withXlang(false).build(); List descriptors = Descriptor.getDescriptors(AllUnsignedFields.class); String fingerprint = Fingerprint.computeStructFingerprint(fory, descriptors); diff --git a/java/fory-core/src/test/java/org/apache/fory/type/DescriptorGrouperTest.java b/java/fory-core/src/test/java/org/apache/fory/type/DescriptorGrouperTest.java index 1fa2fd7e13..ff66f69ac9 100644 --- a/java/fory-core/src/test/java/org/apache/fory/type/DescriptorGrouperTest.java +++ b/java/fory-core/src/test/java/org/apache/fory/type/DescriptorGrouperTest.java @@ -76,7 +76,7 @@ private List createDescriptors() { @Test public void testComparatorByTypeAndName() { - Fory fory = Fory.builder().build(); + Fory fory = Fory.builder().withXlang(false).build(); List descriptors = new ArrayList<>(); descriptors.add( createDescriptor(TypeRef.of(Date.class), "z_timestamp", -1, "TestClass", false)); @@ -92,7 +92,7 @@ public void testComparatorByTypeAndName() { @Test public void testPrimitiveComparator() { - Fory fory = Fory.builder().build(); + Fory fory = Fory.builder().withXlang(false).build(); List descriptors = new ArrayList<>(); int index = 0; for (Class aClass : Primitives.allPrimitiveTypes()) { @@ -126,7 +126,7 @@ public void testPrimitiveCompressedComparator() { descriptors.add(createDescriptor(TypeRef.of(aClass), "f" + index++, -1, "TestClass", false)); } Collections.shuffle(descriptors, new Random(7)); - Fory fory = Fory.builder().build(); + Fory fory = Fory.builder().withXlang(false).build(); descriptors.sort(fory.getTypeResolver().getPrimitiveComparator()); List> classes = descriptors.stream().map(Descriptor::getRawType).collect(Collectors.toList()); @@ -167,7 +167,7 @@ public void testXlangPrimitiveComparatorUsesAscendingTypeIdTieBreaker() { @Test public void testGrouper() { - Fory fory = Fory.builder().build(); + Fory fory = Fory.builder().withXlang(false).build(); List descriptors = createDescriptors(); int index = 0; descriptors.add( @@ -273,7 +273,7 @@ public void testGrouper() { @Test public void testCompressedPrimitiveGrouper() { - Fory fory = Fory.builder().build(); + Fory fory = Fory.builder().withXlang(false).build(); DescriptorGrouper grouper = DescriptorGrouper.createDescriptorGrouper( d -> ReflectionUtils.isMonomorphic(d.getRawType()), @@ -371,7 +371,7 @@ public void testNormalizedTypeNameComparator() { @Test public void testComparatorUsesFieldIdentifierBeforeRawTypeName() { - Fory fory = Fory.builder().build(); + Fory fory = Fory.builder().withXlang(false).build(); Comparator comparator = ((ClassResolver) fory.getTypeResolver()).createTypeAndNameComparator(); Descriptor date = createDescriptor(TypeRef.of(Date.class), "z_date", -1, "TestClass", false); diff --git a/java/fory-extensions/src/test/java/org/apache/fory/extension/meta/TypeDefEncoderTest.java b/java/fory-extensions/src/test/java/org/apache/fory/extension/meta/TypeDefEncoderTest.java index 6fd3c0f882..27032beda9 100644 --- a/java/fory-extensions/src/test/java/org/apache/fory/extension/meta/TypeDefEncoderTest.java +++ b/java/fory-extensions/src/test/java/org/apache/fory/extension/meta/TypeDefEncoderTest.java @@ -44,7 +44,11 @@ static class TestFieldsOrderClass1 { @Test public void testBasicTypeDefZstdMetaCompressor() throws Exception { Fory fory = - Fory.builder().withMetaShare(true).withMetaCompressor(new ZstdMetaCompressor()).build(); + Fory.builder() + .withXlang(false) + .withMetaShare(true) + .withMetaCompressor(new ZstdMetaCompressor()) + .build(); Class type = TestFieldsOrderClass1.class; ClassResolver classResolver = (ClassResolver) fory.getTypeResolver(); List fieldsInfo = buildFieldsInfo(classResolver, type); diff --git a/java/fory-extensions/src/test/java/org/apache/fory/extension/serializer/ProtobufSerializerTest.java b/java/fory-extensions/src/test/java/org/apache/fory/extension/serializer/ProtobufSerializerTest.java index be35ca9e94..bedfabfbbe 100644 --- a/java/fory-extensions/src/test/java/org/apache/fory/extension/serializer/ProtobufSerializerTest.java +++ b/java/fory-extensions/src/test/java/org/apache/fory/extension/serializer/ProtobufSerializerTest.java @@ -27,7 +27,7 @@ public class ProtobufSerializerTest { @Test public void testByteString() { - Fory fory = Fory.builder().requireClassRegistration(false).build(); + Fory fory = Fory.builder().withXlang(false).requireClassRegistration(false).build(); Assert.assertEquals(fory.deserialize(fory.serialize(ByteString.empty())), ByteString.empty()); ByteString bytes = ByteString.copyFrom(new byte[] {1, 2, 3}); Assert.assertEquals(fory.deserialize(fory.serialize(bytes)), bytes); diff --git a/java/fory-latest-jdk-tests/src/test/java/org/apache/fory/VirtualThreadSafeForyTest.java b/java/fory-latest-jdk-tests/src/test/java/org/apache/fory/VirtualThreadSafeForyTest.java index 0ce1991a82..2584898243 100644 --- a/java/fory-latest-jdk-tests/src/test/java/org/apache/fory/VirtualThreadSafeForyTest.java +++ b/java/fory-latest-jdk-tests/src/test/java/org/apache/fory/VirtualThreadSafeForyTest.java @@ -46,6 +46,7 @@ public void testBuildThreadSafeForyUsesFixedBuilderClassLoader() throws Interrup ClassLoader classLoader = new CustomClassLoader(ClassLoader.getSystemClassLoader()); ThreadSafeFory fory = Fory.builder() + .withXlang(false) .withClassLoader(classLoader) .requireClassRegistration(false) .buildThreadSafeFory(); @@ -71,7 +72,11 @@ public void testBuildThreadSafeForyUsesFixedBuilderClassLoader() throws Interrup @Test public void testVirtualThreadsUseFixedSizeThreadPoolFory() throws Exception { ThreadPoolFory fory = - (ThreadPoolFory) Fory.builder().requireClassRegistration(false).buildThreadSafeForyPool(2); + (ThreadPoolFory) + Fory.builder() + .withXlang(false) + .requireClassRegistration(false) + .buildThreadSafeForyPool(2); int threadCount = 8; CountDownLatch acquired = new CountDownLatch(2); CountDownLatch release = new CountDownLatch(1); diff --git a/java/fory-latest-jdk-tests/src/test/java/org/apache/fory/integration_tests/ImmutableCollectionSerializersTest.java b/java/fory-latest-jdk-tests/src/test/java/org/apache/fory/integration_tests/ImmutableCollectionSerializersTest.java index e6c7029519..ae7db28118 100644 --- a/java/fory-latest-jdk-tests/src/test/java/org/apache/fory/integration_tests/ImmutableCollectionSerializersTest.java +++ b/java/fory-latest-jdk-tests/src/test/java/org/apache/fory/integration_tests/ImmutableCollectionSerializersTest.java @@ -43,7 +43,7 @@ public static Object[][] codegen() { @Test(dataProvider = "codegen") public void testImmutableCollections(boolean codegen) { - Fory fory = Fory.builder().withCodegen(codegen).build(); + Fory fory = Fory.builder().withXlang(false).withCodegen(codegen).build(); serDeCheck(fory, List.of()); serDeCheck(fory, List.of("A")); serDeCheck(fory, List.of("A", "B")); @@ -63,7 +63,7 @@ public void testImmutableCollections(boolean codegen) { @Test(dataProvider = "codegen") public void testImmutableCollectionStruct(boolean codegen) { - Fory fory = Fory.builder().withCodegen(codegen).build(); + Fory fory = Fory.builder().withXlang(false).withCodegen(codegen).build(); fory.register(MapFields.class); MapFields mapFields = new MapFields(); mapFields.map = Map.of(); @@ -77,7 +77,7 @@ public void testImmutableCollectionStruct(boolean codegen) { @Test public void testImmutableMapStruct() { - Fory fory = Fory.builder().build(); + Fory fory = Fory.builder().withXlang(false).build(); fory.register(CollectionFields.class); CollectionFields collectionFields = new CollectionFields(); collectionFields.collection = List.of(); diff --git a/java/fory-latest-jdk-tests/src/test/java/org/apache/fory/integration_tests/RecordSerializersTest.java b/java/fory-latest-jdk-tests/src/test/java/org/apache/fory/integration_tests/RecordSerializersTest.java index 30efd25f3f..57d12c66f0 100644 --- a/java/fory-latest-jdk-tests/src/test/java/org/apache/fory/integration_tests/RecordSerializersTest.java +++ b/java/fory-latest-jdk-tests/src/test/java/org/apache/fory/integration_tests/RecordSerializersTest.java @@ -82,7 +82,12 @@ public static Object[][] codegen() { @Test(dataProvider = "codegen") public void testSimpleRecord(boolean codegen) { - Fory fory = Fory.builder().requireClassRegistration(false).withCodegen(codegen).build(); + Fory fory = + Fory.builder() + .withXlang(false) + .requireClassRegistration(false) + .withCodegen(codegen) + .build(); Foo foo = new Foo(10, "abc", new ArrayList<>(Arrays.asList("a", "b")), 'x'); Assert.assertEquals(fory.deserialize(fory.serialize(foo)), foo); } @@ -141,6 +146,7 @@ private static ForyBuilder newNumberCompressedBuilder() { public void testSimpleRecordMetaShare(boolean codegen) { Fory fory = Fory.builder() + .withXlang(false) .requireClassRegistration(false) .withCodegen(codegen) .withMetaShare(true) @@ -168,6 +174,7 @@ public void testRecordCompatible(boolean codegen) throws Throwable { .invoke(1, "abc", ofArrayList("a", "b"), 'a', ofHashMap("a", 1)); Fory fory = Fory.builder() + .withXlang(false) .requireClassRegistration(false) .withCodegen(codegen) .withClassLoader(cls1.getClassLoader()) @@ -186,6 +193,7 @@ public void testRecordCompatible(boolean codegen) throws Throwable { RecordUtils.getRecordConstructor(cls2).f1.invoke(1, "abc", 'a', ofHashMap("a", 1)); Fory fory2 = Fory.builder() + .withXlang(false) .requireClassRegistration(false) .withCodegen(codegen) .withClassLoader(cls2.getClassLoader()) @@ -211,6 +219,7 @@ public void testRecordMetaShare(boolean codegen) throws Throwable { .invoke(1, "abc", ofArrayList("a", "b"), 'a', ofHashMap("a", 1)); Fory fory1 = Fory.builder() + .withXlang(false) .requireClassRegistration(false) .withCodegen(codegen) .withCompatible(true) @@ -227,6 +236,7 @@ public void testRecordMetaShare(boolean codegen) throws Throwable { RecordUtils.getRecordConstructor(cls2).f1.invoke("abc", 'a', ofHashMap("a", 1)); Fory fory2 = Fory.builder() + .withXlang(false) .requireClassRegistration(false) .withCodegen(codegen) .withCompatible(true) @@ -251,7 +261,12 @@ public void testRecordMetaShare(boolean codegen) throws Throwable { @Test(dataProvider = "codegen") public void testPrivateRecords(boolean codegen) { { - Fory fory = Fory.builder().requireClassRegistration(false).withCodegen(codegen).build(); + Fory fory = + Fory.builder() + .withXlang(false) + .requireClassRegistration(false) + .withCodegen(codegen) + .build(); Object o1 = Records.createPrivateRecord(11); Assert.assertEquals(fory.deserialize(fory.serialize(o1)), o1); Object o2 = Records.createPublicRecord(11, o1); @@ -260,6 +275,7 @@ public void testPrivateRecords(boolean codegen) { { Fory fory = Fory.builder() + .withXlang(false) .requireClassRegistration(false) .withCodegen(codegen) .withCompatible(true) @@ -272,6 +288,7 @@ public void testPrivateRecords(boolean codegen) { { Fory fory = Fory.builder() + .withXlang(false) .requireClassRegistration(false) .withCodegen(codegen) .withMetaShare(true) @@ -289,7 +306,7 @@ public void testPrivateRecords(boolean codegen) { @Test(dataProvider = "codegen") public void testPrivateRecord(boolean codegen) { - Fory fory = Fory.builder().withCodegen(codegen).build(); + Fory fory = Fory.builder().withXlang(false).withCodegen(codegen).build(); fory.register(PrivateRecord.class); byte[] serialized = fory.serialize(new PrivateRecord("foo")); // fails Object deserialized = fory.deserialize(serialized); @@ -300,7 +317,7 @@ private record PrivateRecord(String foo) {} @Test(dataProvider = "codegen") public void testCopy(boolean codegen) { - Fory fory = Fory.builder().withCodegen(codegen).build(); + Fory fory = Fory.builder().withXlang(false).withCodegen(codegen).build(); fory.register(Foo.class); fory.register(PrivateRecord.class); Assert.assertEquals(fory.copy(new PrivateRecord("foo")), new PrivateRecord("foo")); diff --git a/java/fory-simd/src/main/java/org/apache/fory/serializer/CompressedArraySerializers.java b/java/fory-simd/src/main/java/org/apache/fory/serializer/CompressedArraySerializers.java index 1db8e24e0f..ba4f2e247a 100644 --- a/java/fory-simd/src/main/java/org/apache/fory/serializer/CompressedArraySerializers.java +++ b/java/fory-simd/src/main/java/org/apache/fory/serializer/CompressedArraySerializers.java @@ -69,7 +69,7 @@ private static void validateBinarySize(int size, int maxBinarySize, int elemSize *

Example usage: * *

{@code
-   * Fory fory = Fory.builder()
+   * Fory fory = Fory.builder().withXlang(false)
    *     .withConfig(Config.compressIntArray(true).compressLongArray(true))
    *     .build();
    * CompressedArraySerializers.registerSerializers(fory);
@@ -107,7 +107,7 @@ static void registerIfEnabled(Fory fory) {
    * 

Example usage: * *

{@code
-   * ThreadSafeFory fory = Fory.builder()
+   * ThreadSafeFory fory = Fory.builder().withXlang(false)
    *     .withConfig(Config.compressIntArray(true).compressLongArray(true))
    *     .buildThreadSafeFory();
    * CompressedArraySerializers.registerSerializers(fory);
diff --git a/java/fory-simd/src/test/java/org/apache/fory/serializer/ArrayCompressionTest.java b/java/fory-simd/src/test/java/org/apache/fory/serializer/ArrayCompressionTest.java
index ef4f0ea4a7..6650264107 100644
--- a/java/fory-simd/src/test/java/org/apache/fory/serializer/ArrayCompressionTest.java
+++ b/java/fory-simd/src/test/java/org/apache/fory/serializer/ArrayCompressionTest.java
@@ -56,7 +56,8 @@ public Object[][] longArrayData() {
 
   @Test(dataProvider = "intArrayData")
   public void testIntArrayCompressionRoundTrip(String description, int[] originalArray) {
-    Fory foryWithCompression = new ForyBuilder().withIntArrayCompressed(true).build();
+    Fory foryWithCompression =
+        new ForyBuilder().withXlang(false).withIntArrayCompressed(true).build();
     CompressedArraySerializers.registerSerializers(foryWithCompression);
     byte[] serializedWithCompression = foryWithCompression.serialize(originalArray);
     int[] deserializedWithCompression =
@@ -69,7 +70,8 @@ public void testIntArrayCompressionRoundTrip(String description, int[] originalA
 
   @Test(dataProvider = "longArrayData")
   public void testLongArrayCompressionRoundTrip(String description, long[] originalArray) {
-    Fory foryWithCompression = new ForyBuilder().withLongArrayCompressed(true).build();
+    Fory foryWithCompression =
+        new ForyBuilder().withXlang(false).withLongArrayCompressed(true).build();
     CompressedArraySerializers.registerSerializers(foryWithCompression);
     byte[] serializedWithCompression = foryWithCompression.serialize(originalArray);
     long[] deserializedWithCompression =
@@ -83,11 +85,19 @@ public void testLongArrayCompressionRoundTrip(String description, long[] origina
   @Test
   public void testCompressionRatios() {
     Fory foryWithCompression =
-        new ForyBuilder().withIntArrayCompressed(true).withLongArrayCompressed(true).build();
+        new ForyBuilder()
+            .withXlang(false)
+            .withIntArrayCompressed(true)
+            .withLongArrayCompressed(true)
+            .build();
     CompressedArraySerializers.registerSerializers(foryWithCompression);
 
     Fory foryWithoutCompression =
-        new ForyBuilder().withIntArrayCompressed(false).withLongArrayCompressed(false).build();
+        new ForyBuilder()
+            .withXlang(false)
+            .withIntArrayCompressed(false)
+            .withLongArrayCompressed(false)
+            .build();
 
     // Test byte-range int array compression (should achieve ~4x compression)
     int[] byteRangeArray = createByteRangeArray(10_000);
@@ -123,7 +133,11 @@ public void testCompressionRatios() {
   @Test
   public void testLargeArrays() {
     Fory fory =
-        new ForyBuilder().withIntArrayCompressed(true).withLongArrayCompressed(true).build();
+        new ForyBuilder()
+            .withXlang(false)
+            .withIntArrayCompressed(true)
+            .withLongArrayCompressed(true)
+            .build();
     CompressedArraySerializers.registerSerializers(fory);
 
     // Test very large compressible arrays
diff --git a/java/fory-testsuite/src/test/java/org/apache/fory/test/ReadResolveCircularTest.java b/java/fory-testsuite/src/test/java/org/apache/fory/test/ReadResolveCircularTest.java
index 41f8b62158..37133b1be6 100644
--- a/java/fory-testsuite/src/test/java/org/apache/fory/test/ReadResolveCircularTest.java
+++ b/java/fory-testsuite/src/test/java/org/apache/fory/test/ReadResolveCircularTest.java
@@ -35,7 +35,12 @@ public void testReadResolveCircular() {
     c.addItem(new Item("Item 1"));
     c.addItem(new Item("Item 2"));
 
-    Fory fory = Fory.builder().requireClassRegistration(false).withRefTracking(true).build();
+    Fory fory =
+        Fory.builder()
+            .withXlang(false)
+            .requireClassRegistration(false)
+            .withRefTracking(true)
+            .build();
     byte[] bytes = fory.serialize(c);
     System.out.println(fory.deserialize(bytes));
     System.out.println(
diff --git a/javascript/README.md b/javascript/README.md
index 70ada5d353..ac8850cf25 100644
--- a/javascript/README.md
+++ b/javascript/README.md
@@ -101,10 +101,10 @@ Register the same `example.message` type on the other side using the peer runtim
 
 ## Schema Evolution
 
-Enable compatible mode for independent service deployments:
+Compatible mode is enabled by default for independent service deployments:
 
 ```ts
-const fory = new Fory({ compatible: true });
+const fory = new Fory();
 ```
 
 Readers can skip unknown fields and tolerate missing ones, supporting rolling upgrades and schema changes across services.
diff --git a/javascript/packages/core/README.md b/javascript/packages/core/README.md
index ed66ecda43..6c457c046c 100644
--- a/javascript/packages/core/README.md
+++ b/javascript/packages/core/README.md
@@ -111,10 +111,10 @@ const inventoryType = Type.struct("example.inventory", {
 
 ## Schema Evolution
 
-Enable compatible mode for independent service deployments:
+Compatible mode is the default and supports independent service deployments:
 
 ```ts
-const fory = new Fory({ compatible: true });
+const fory = new Fory();
 ```
 
 Readers skip unknown fields and tolerate missing ones, supporting rolling upgrades.
diff --git a/javascript/packages/core/lib/fory.ts b/javascript/packages/core/lib/fory.ts
index 1e6bd5e1ce..50a367a14c 100644
--- a/javascript/packages/core/lib/fory.ts
+++ b/javascript/packages/core/lib/fory.ts
@@ -92,6 +92,7 @@ export default class Fory {
       maxCollectionSize: config?.maxCollectionSize,
       hooks: config?.hooks || {},
       compatible: config?.compatible ?? true,
+      hps: config?.hps,
     };
   }
 
diff --git a/javascript/packages/hps/README.md b/javascript/packages/hps/README.md
index 77efd31a02..161c2a3472 100644
--- a/javascript/packages/hps/README.md
+++ b/javascript/packages/hps/README.md
@@ -43,7 +43,7 @@ console.log(result);
 // { foo: 'hello fory' }
 ```
 
-If `hps` is unavailable, omit it or pass `null`:
+If `hps` is unavailable, omit it:
 
 ```ts
 const fory = new Fory(); // works without hps
diff --git a/kotlin/README.md b/kotlin/README.md
index dbcfaf8135..637bf9e1ad 100644
--- a/kotlin/README.md
+++ b/kotlin/README.md
@@ -25,6 +25,7 @@ import org.apache.fory.kotlin.ForyKotlin
 data class User(val name: String, val id: UInt)
 
 val fory = ForyKotlin.builder()
+  .withXlang(true)
   .requireClassRegistration(true)
   .build()
 
@@ -34,6 +35,9 @@ fory.register(User::class.java)
 `fory-kotlin` supports Kotlin-specific runtime types such as unsigned
 primitives, unsigned arrays, Kotlin collection carriers, default constructor
 values, `kotlin.time.Duration`, `kotlin.text.Regex`, and `kotlin.uuid.Uuid`.
+Use `.withXlang(false)` for same-language Kotlin/JVM native-mode payloads when
+you need JVM-native object behavior and Kotlin-specific type support without
+portable xlang type-mapping constraints.
 
 ## KSP Xlang Serializers
 
diff --git a/kotlin/fory-kotlin/src/test/kotlin/org/apache/fory/serializer/kotlin/DefaultValueTest.kt b/kotlin/fory-kotlin/src/test/kotlin/org/apache/fory/serializer/kotlin/DefaultValueTest.kt
index 3416696610..62fa3ef5e3 100644
--- a/kotlin/fory-kotlin/src/test/kotlin/org/apache/fory/serializer/kotlin/DefaultValueTest.kt
+++ b/kotlin/fory-kotlin/src/test/kotlin/org/apache/fory/serializer/kotlin/DefaultValueTest.kt
@@ -131,7 +131,7 @@ class DefaultValueTest {
 
   @Test
   fun testDefaultValueDeserialization() {
-    val fory = ForyKotlin.builder().requireClassRegistration(false).withCompatible(true).build()
+    val fory = ForyKotlin.builder().withXlang(false).requireClassRegistration(false).withCompatible(true).build()
     val obj = ClassNoDefaults("test")
     val serialized = fory.serialize(obj)
     val deserialized = fory.deserialize(serialized, ClassWithDefaults::class.java)
diff --git a/python/README.md b/python/README.md
index e517a72cac..d9ba9c7077 100644
--- a/python/README.md
+++ b/python/README.md
@@ -11,38 +11,38 @@
 
 `pyfory` provides the Python implementation of Apache Fory™, offering both high-performance object serialization and advanced row-format capabilities for data processing tasks.
 
-## 🚀 Key Features
+## Key Features
 
-### 🔧 **Flexible Serialization Modes**
+### **Flexible Serialization Modes**
 
-- **Python native Mode**: Full Python compatibility, drop-in replacement for pickle/cloudpickle
-- **Cross-Language Mode**: Optimized for multi-language data exchange
+- **Xlang mode**: Default cross-language wire format with compatible schema evolution
+- **Python native mode**: Same-language mode and drop-in replacement for pickle/cloudpickle
 - **Row Format**: Zero-copy row format for analytics workloads
 
-### 🎯 Versatile Serialization Features
+### Versatile Serialization Features
 
-- **Shared/circular reference support** for complex object graphs in both Python-native and cross-language modes
+- **Shared/circular reference support** for complex object graphs in both Python native and xlang modes
 - **Polymorphism support** for customized types with automatic type dispatching
-- **Schema evolution** support for backward/forward compatibility when using dataclasses in cross-language mode
+- **Schema evolution** support for backward/forward compatibility when using dataclasses in xlang mode
 - **Out-of-band buffer support** for zero-copy serialization of large data structures like NumPy arrays and Pandas DataFrames, compatible with pickle protocol 5
 - **Reduced-precision xlang types** use reserved `pyfory.Float16` and `pyfory.BFloat16` annotations and native Python `float` values; dense array payloads use public wrappers such as `Float16Array` and `BFloat16Array`
 
-### ⚡ **Blazing Fast Performance**
+### Blazing Fast Performance
 
 - **Extremely fast performance** compared to other serialization frameworks
 - **Runtime code generation** and **Cython-accelerated** core implementation for optimal performance
 
-### 📦 Compact Data Size
+### Compact Data Size
 
 - **Compact object graph protocol** with minimal space overhead—up to 3× size reduction compared to pickle/cloudpickle
 - **Meta packing and sharing** to minimize type forward/backward compatibility space overhead
 
-### 🛡️ **Security & Safety**
+### **Security & Safety**
 
 - **Strict mode** prevents deserialization of untrusted types by type registration and checks.
 - **Reference tracking** for handling circular references safely
 
-## 📦 Installation
+## Installation
 
 ### Basic Installation
 
@@ -69,19 +69,24 @@ pip install -e ".[dev,format]"
 - **Python**: 3.8 or higher
 - **OS**: Linux, macOS, Windows
 
-## 🐍 Python Native Serialization
+## Python Native Serialization
 
-`pyfory` provides a Python-native serialization mode that offers the same functionality as pickle/cloudpickle, but with **significantly better performance, smaller data size, and enhanced security features**.
+`pyfory` provides a Python native mode for Python-only payloads. It is optimized for Python's type
+system and offers the same object surface as pickle/cloudpickle, but with **significantly better
+performance, smaller data size, and enhanced security features**.
 
-The binary protocol and API are similar to Fory's xlang mode, but Python-native mode can serialize any Python object—including global functions, local functions, lambdas, local classes and types with customized serialization using `__getstate__/__reduce__/__reduce_ex__`, which are not allowed in xlang mode.
+The binary protocol and API are similar to Fory's xlang mode, but Python native mode can serialize any Python object—including global functions, local functions, lambdas, local classes and types with customized serialization using `__getstate__/__reduce__/__reduce_ex__`, which are not allowed in xlang mode.
 
-To use Python-native mode, create `Fory` with `xlang=False`. This mode is optimized for pure Python applications:
+To use Python native mode, create `Fory` with `xlang=False`. Use this mode when replacing pickle or
+cloudpickle for pure Python applications:
 
 ```python
 import pyfory
 fory = pyfory.Fory(xlang=False, ref=False, strict=True)
 ```
 
+## Xlang Object Serialization
+
 ### Basic Object Serialization
 
 Serialize and deserialize Python objects with a simple API. This example shows serializing a dictionary with mixed types:
@@ -89,10 +94,10 @@ Serialize and deserialize Python objects with a simple API. This example shows s
 ```python
 import pyfory
 
-# Create Fory instance
-fory = pyfory.Fory(xlang=True, compatible=True)
+# Create an xlang Fory instance.
+fory = pyfory.Fory(xlang=True)
 
-# Serialize any Python object
+# Serialize xlang-compatible values
 data = fory.dumps({"name": "Alice", "age": 30, "scores": [95, 87, 92]})
 
 # Deserialize back to Python object
@@ -114,29 +119,28 @@ from typing import List, Dict
 @dataclass
 class Person:
     name: str
-    age: int
-    scores: List[int]
+    age: pyfory.Int32
+    scores: List[pyfory.Int32]
     metadata: Dict[str, str]
 
-# Python mode - supports all Python types including dataclasses
-fory = pyfory.Fory(xlang=False, ref=True)
-fory.register(Person)
+fory = pyfory.Fory(xlang=True, ref=True)
+fory.register(Person, typename="example.Person")
 person = Person("Bob", 25, [88, 92, 85], {"team": "engineering"})
 data = fory.serialize(person)
 result = fory.deserialize(data)
 print(result)  # Person(name='Bob', age=25, ...)
 ```
 
-### Drop-in Replacement for Pickle/Cloudpickle
+## Drop-in Replacement for Pickle/Cloudpickle
 
 `pyfory` can serialize any Python object with the following configuration:
 
 - **For circular references**: Set `ref=True` to enable reference tracking
 - **For functions/classes**: Set `strict=False` to allow deserialization of dynamic types
 
-**⚠️ Security Warning**: When `strict=False`, Fory will deserialize arbitrary types, which can pose security risks if data comes from untrusted sources. Only use `strict=False` in controlled environments where you trust the data source completely. If you do need to use `strict=False`, please configure a `DeserializationPolicy` when creating fory using `policy=your_policy` to controlling deserialization behavior.
+**Security Warning**: When `strict=False`, Fory will deserialize arbitrary types, which can pose security risks if data comes from untrusted sources. Only use `strict=False` in controlled environments where you trust the data source completely. If you do need to use `strict=False`, please configure a `DeserializationPolicy` when creating fory using `policy=your_policy` to controlling deserialization behavior.
 
-#### Common Usage
+### Common Usage
 
 Serialize common Python objects including dicts, lists, and custom classes without any registration:
 
@@ -163,7 +167,7 @@ data = fory.dumps(person)
 print(fory.loads(data))  # Person(name='Bob', age=25)
 ```
 
-#### Serialize Global Functions
+### Serialize Global Functions
 
 Capture and get functions defined at module level. Fory deserialize and return same function object:
 
@@ -434,20 +438,20 @@ for buffer_obj in buffer_objects:
 
 **Note**: For contiguous memory buffers (like bytes, numpy arrays), `getbuffer()` returns a zero-copy `memoryview`. For non-contiguous data, a copy may be created to ensure contiguity.
 
-## 🏃‍♂️ Cross-Language Object Graph Serialization
+## Cross-Language Object Graph Serialization
 
 `pyfory` supports cross-language object graph serialization, allowing you to serialize data in Python and deserialize it in Java, Go, Rust, or other supported languages.
 
-The binary protocol and API are similar to `pyfory`'s python-native mode, but Python-native mode can serialize any Python object—including global functions, local functions, lambdas, local classes, and types with customized serialization using `__getstate__/__reduce__/__reduce_ex__`, which are not allowed in xlang mode.
+The binary protocol and API are similar to `pyfory`'s Python native mode, but Python native mode can serialize any Python object—including global functions, local functions, lambdas, local classes, and types with customized serialization using `__getstate__/__reduce__/__reduce_ex__`, which are not allowed in xlang mode.
 
-To use xlang mode, create `Fory` with `xlang=True, compatible=True`. This mode is for xlang serialization applications:
+Xlang mode is the default. Set `xlang=True` explicitly in cross-language examples so the mode choice is visible:
 
 ```python
 import pyfory
-fory = pyfory.Fory(xlang=True, compatible=True, ref=False, strict=True)
+fory = pyfory.Fory(xlang=True, ref=False, strict=True)
 ```
 
-### Cross-Language Sserialization
+### Cross-Language Serialization
 
 Serialize data in Python and deserialize it in Java, Go, Rust, or other supported languages. Both sides must register the same type with matching names:
 
@@ -457,8 +461,8 @@ Serialize data in Python and deserialize it in Java, Go, Rust, or other supporte
 from dataclasses import dataclass
 import pyfory
 
-# Cross-language mode for interoperability
-f = pyfory.Fory(xlang=True, compatible=True, ref=True)
+# Xlang mode for interoperability
+f = pyfory.Fory(xlang=True, ref=True)
 
 # Register type for cross-language compatibility
 @dataclass
@@ -487,7 +491,7 @@ public class Person {
 }
 
 Fory fory = Fory.builder()
-    .withXlang(true).withCompatible(true)
+    .withXlang(true)
     .withRefTracking(true)
     .build();
 
@@ -495,9 +499,9 @@ fory.register(Person.class, "example.Person");
 Person person = (Person) fory.deserialize(binaryData);
 ```
 
-## 📊 Row Format - Zero-Copy Processing
+## Row Format - Zero-Copy Processing
 
-Apache Fury™ provides a random-access row format that enables reading nested fields from binary data without full deserialization. This drastically reduces overhead when working with large objects where only partial data access is needed. The format also supports memory-mapped files for ultra-low memory footprint.
+Apache Fory™ provides a random-access row format that enables reading nested fields from binary data without full deserialization. This drastically reduces overhead when working with large objects where only partial data access is needed. The format also supports memory-mapped files for ultra-low memory footprint.
 
 ### Basic Row Format Usage
 
@@ -636,27 +640,27 @@ for (int i = 0; i < 1000000; i++) {
 }
 
 // Encode to row format (cross-language compatible with Python/Java)
-fory::encoder::RowEncoder encoder;
-encoder.Encode(foo);
-auto row = encoder.GetWriter().ToRow();
+fory::row::encoder::RowEncoder encoder;
+encoder.encode(foo);
+auto row = encoder.get_writer().to_row();
 
 // Zero-copy random access without full deserialization
-auto f2_array = row->GetArray(1);                    // Access f2 list
-auto f4_array = row->GetArray(3);                    // Access f4 list
-auto bar10 = f4_array->GetStruct(10);                // Access 11th Bar
-int64_t value = bar10->GetArray(1)->GetInt64(5);    // Access 6th element of bar.f2
-std::string str = bar10->GetString(0);               // Access bar.f1
+auto f2_array = row->get_array(1);                   // Access f2 list
+auto f4_array = row->get_array(3);                   // Access f4 list
+auto bar10 = f4_array->get_struct(10);               // Access 11th Bar
+int64_t value = bar10->get_array(1)->get_int64(5);   // Access 6th element of bar.f2
+std::string str = bar10->get_string(0);              // Access bar.f1
 ```
 
 ### Key Benefits
 
 - **Zero-Copy Access**: Read nested fields without deserializing the entire object
 - **Memory Efficiency**: Memory-map large datasets directly from disk
-- **Cross-Language**: Binary format is compatible between Python, Java, and other Fury implementations
+- **Cross-Language**: Binary format is compatible between Python, Java, and other Fory implementations
 - **Partial Deserialization**: Deserialize only the specific elements you need
 - **High Performance**: Skip unnecessary data parsing for analytics and big data workloads
 
-## 🏗️ Core API Reference
+## Core API Reference
 
 ### Fory Class
 
@@ -666,10 +670,10 @@ The main serialization interface:
 class Fory:
     def __init__(
         self,
-        xlang: bool = False,
+        xlang: bool = True,
         ref: bool = False,
         strict: bool = True,
-        compatible: bool = False,
+        compatible: bool | None = None,
         max_depth: int = 50
     )
 ```
@@ -682,10 +686,10 @@ Thread-safe serialization interface using thread-local storage:
 class ThreadSafeFory:
     def __init__(
         self,
-        xlang: bool = False,
+        xlang: bool = True,
         ref: bool = False,
         strict: bool = True,
-        compatible: bool = False,
+        compatible: bool | None = None,
         max_depth: int = 50
     )
 ```
@@ -735,10 +739,10 @@ for t in threads: t.join()
 
 **Parameters:**
 
-- **`xlang`** (`bool`, default=`False`): Enable cross-language serialization. When `False`, enables Python-native mode supporting all Python objects. When `True`, enables cross-language mode compatible with Java, Go, Rust, etc.
+- **`xlang`** (`bool`, default=`True`): Use xlang mode. Set `False` for Python native mode supporting Python-specific objects.
 - **`ref`** (`bool`, default=`False`): Enable reference tracking for shared/circular references. Disable for better performance if your data has no shared references.
 - **`strict`** (`bool`, default=`True`): Require type registration for security. **Highly recommended** for production. Only disable in trusted environments.
-- **`compatible`** (`bool`, default follows `xlang`): Enable schema evolution in cross-language mode, allowing fields to be added/removed while maintaining compatibility. Cross-language mode defaults to `compatible=True`; set `compatible=False` only for schema-consistent deployments.
+- **`compatible`** (`bool`, default follows `xlang`): Enable schema evolution. Xlang mode defaults to compatible mode; native mode defaults to schema-consistent mode.
 - **`max_depth`** (`int`, default=`50`): Maximum deserialization depth for security, preventing stack overflow attacks.
 
 **Key Methods:**
@@ -752,37 +756,35 @@ obj = fory.deserialize(data)
 data: bytes = fory.dumps(obj)
 obj = fory.loads(data)
 
-# Type registration by id (for Python mode)
+# Type registration by id
 fory.register(MyClass, type_id=123)
 fory.register(MyClass, type_id=123, serializer=custom_serializer)
 
-# Type registration by name (for cross-language mode)
+# Type registration by name
 fory.register(MyClass, typename="my.package.MyClass")
 fory.register(MyClass, typename="my.package.MyClass", serializer=custom_serializer)
 ```
 
-### Language Modes Comparison
+### Xlang And Native Mode Comparison
 
-| Feature               | Python Mode (`xlang=False`)          | Cross-Language Mode (`xlang=True, compatible=True`) |
-| --------------------- | ------------------------------------ | --------------------------------------------------- |
-| **Use Case**          | Pure Python applications             | Multi-language systems                              |
-| **Compatibility**     | Python only                          | Java, Go, Rust, C++, JavaScript, etc.               |
-| **Supported Types**   | All Python types                     | Cross-language compatible types only                |
-| **Functions/Lambdas** | ✓ Supported                          | ✗ Not allowed                                       |
-| **Local Classes**     | ✓ Supported                          | ✗ Not allowed                                       |
-| **Dynamic Classes**   | ✓ Supported                          | ✗ Not allowed                                       |
-| **Schema Evolution**  | ✓ Supported (with `compatible=True`) | ✓ Supported (with `compatible=True`)                |
-| **Performance**       | Extremely fast                       | Very fast                                           |
-| **Data Size**         | Compact                              | Compact with type metadata                          |
+| Feature             | Native mode (`xlang=False`)                    | Xlang mode (default)                  |
+| ------------------- | ---------------------------------------------- | ------------------------------------- |
+| Use case            | Pure Python applications                       | Multi-language systems                |
+| Compatibility       | Python only                                    | Java, Go, Rust, C++, JavaScript, etc. |
+| Supported types     | Python object surface                          | Cross-language compatible types       |
+| Functions/lambdas   | Supported with trusted dynamic deserialization | Not allowed                           |
+| Local classes       | Supported with trusted dynamic deserialization | Not allowed                           |
+| Dynamic classes     | Supported with trusted dynamic deserialization | Not allowed                           |
+| Schema mode default | Schema-consistent                              | Compatible                            |
 
-#### Python Mode (`xlang=False`)
+#### Native Mode (`xlang=False`)
 
-Python mode supports all Python types including functions, classes, and closures. Perfect for pure Python applications:
+Python native mode supports Python-specific objects including functions, classes, and closures. Use it for Python-only applications:
 
 ```python
 import pyfory
 
-# Full Python compatibility mode
+# Python native mode
 fory = pyfory.Fory(xlang=False, ref=True, strict=False)
 
 # Supports ALL Python objects:
@@ -805,15 +807,14 @@ print(f"Fory: {timeit.timeit(lambda: fory.dumps(obj), number=1000):.3f}s")
 print(f"Pickle: {timeit.timeit(lambda: pickle.dumps(obj), number=1000):.3f}s")
 ```
 
-#### Cross-Language Mode (`xlang=True, compatible=True`)
+#### Xlang Mode
 
-Cross-language mode restricts types to those compatible across all Fory implementations. Use for multi-language systems:
+Xlang mode restricts types to those compatible across all Fory implementations. Use it for multi-language systems:
 
 ```python
 import pyfory
 
-# Cross-language compatibility mode
-f = pyfory.Fory(xlang=True, compatible=True, ref=True)
+f = pyfory.Fory(xlang=True, ref=True)
 
 # Only supports cross-language compatible types
 f.register(MyDataClass, typename="com.example.MyDataClass")
@@ -822,7 +823,7 @@ f.register(MyDataClass, typename="com.example.MyDataClass")
 data = f.serialize(MyDataClass(field1="value", field2=42))
 ```
 
-## 🔧 Advanced Features
+## Advanced Features
 
 ### Reference Tracking & Circular References
 
@@ -831,7 +832,7 @@ Handle shared references and circular dependencies safely. Set `ref=True` to ded
 ```python
 import pyfory
 
-f = pyfory.Fory(ref=True)  # Enable reference tracking
+f = pyfory.Fory(xlang=False, ref=True)  # Enable reference tracking
 
 # Handle circular references safely
 class Node:
@@ -851,7 +852,7 @@ result = f.deserialize(data)
 assert result.children[0].parent is result  # Reference preserved
 ```
 
-### Type Registration & Security
+### Type Registration
 
 In strict mode, only registered types can be deserialized. This prevents arbitrary code execution:
 
@@ -859,7 +860,7 @@ In strict mode, only registered types can be deserialized. This prevents arbitra
 import pyfory
 
 # Strict mode (recommended for production)
-f = pyfory.Fory(strict=True)
+f = pyfory.Fory(xlang=False, strict=True)
 
 class SafeClass:
     def __init__(self, data):
@@ -913,7 +914,7 @@ class FooSerializer(Serializer):
         f2 = buffer.read_string()
         return Foo(f1, f2)
 
-f = pyfory.Fory()
+f = pyfory.Fory(xlang=False)
 f.register(Foo, type_id=100, serializer=FooSerializer(f, Foo))
 
 # Now Foo uses your custom serializer
@@ -930,7 +931,7 @@ Fory natively supports numpy arrays with optimized serialization. Large arrays u
 import pyfory
 import numpy as np
 
-f = pyfory.Fory()
+f = pyfory.Fory(xlang=False)
 
 # Numpy arrays are supported natively
 arrays = {
@@ -946,7 +947,7 @@ result = f.deserialize(data)
 assert np.array_equal(arrays['matrix'], result['matrix'])
 ```
 
-## 💡 Best Practices
+## Best Practices
 
 ### Production Configuration
 
@@ -957,10 +958,10 @@ import pyfory
 
 # Recommended settings for production
 fory = pyfory.Fory(
-    xlang=False,        # Use True if you need cross-language support
+    xlang=False,        # Native mode for Python-only traffic
     ref=False,           # Enable if you have shared/circular references
     strict=True,        # CRITICAL: Always True in production
-    compatible=False,   # Native mode; xlang=True defaults to compatible=True
+    compatible=False,   # Native mode defaults to schema-consistent payloads
     max_depth=20       # Adjust based on your data structure depth
 )
 
@@ -982,13 +983,13 @@ Optimize serialization speed and memory usage with these guidelines:
 
 ```python
 # Good: Reuse instance
-fory = pyfory.Fory()
+fory = pyfory.Fory(xlang=False)
 for obj in objects:
     data = fory.dumps(obj)
 
 # Bad: Create new instance each time
 for obj in objects:
-    fory = pyfory.Fory()  # Wasteful!
+    fory = pyfory.Fory(xlang=False)  # Wasteful!
     data = fory.dumps(obj)
 ```
 
@@ -1042,49 +1043,7 @@ except Exception as e:
     print(f"Deserialization failed: {e}")
 ```
 
-## 🛠️ Migration Guide
-
-### From Pickle
-
-Replace pickle with Fory for better performance while keeping the same API:
-
-```python
-# Before (pickle)
-import pickle
-data = pickle.dumps(obj)
-result = pickle.loads(data)
-
-# After (Fory - drop-in replacement with better performance)
-import pyfory
-f = pyfory.Fory(xlang=False, ref=True, strict=False)
-data = f.dumps(obj)      # Faster and more compact
-result = f.loads(data)   # Faster deserialization
-
-# Benefits:
-# - 2-10x faster serialization
-# - 2-5x faster deserialization
-# - Up to 3x smaller data size
-# - Same API, better performance
-```
-
-### From JSON
-
-Unlike JSON, Fory supports arbitrary Python types including functions:
-
-```python
-# Before (JSON - limited types)
-import json
-data = json.dumps({"name": "Alice", "age": 30})
-result = json.loads(data)
-
-# After (Fory - all Python types)
-import pyfory
-f = pyfory.Fory()
-data = f.dumps({"name": "Alice", "age": 30, "func": lambda x: x})
-result = f.loads(data)
-```
-
-## 🚨 Security Best Practices
+## Security Best Practices
 
 ### Production Configuration
 
@@ -1095,7 +1054,6 @@ import pyfory
 
 # Recommended production settings
 f = pyfory.Fory(
-    xlang=False,   # or True for cross-language
     ref=True,      # Handle circular references
     strict=True,   # IMPORTANT: Prevent malicious data
     max_depth=100  # Prevent deep recursion attacks
@@ -1126,7 +1084,6 @@ if os.getenv('ENV') == 'development':
 else:
     # Production configuration (security hardened)
     fory = pyfory.Fory(
-        xlang=False,
         ref=True,
         strict=True,     # CRITICAL: Require registration
         max_depth=100    # Reasonable limit
@@ -1197,7 +1154,7 @@ result = fory.deserialize(data)  # Policy hooks will be invoked
 
 **See also:** `pyfory/policy.py` contains detailed documentation and examples for each hook.
 
-## 🐛 Troubleshooting
+## Troubleshooting
 
 ### Common Issues
 
@@ -1221,25 +1178,34 @@ print(pyfory.ENABLE_FORY_CYTHON_SERIALIZATION)  # Should be True
 # If False, Cython extension may not be compiled correctly
 # Reinstall with: pip install --force-reinstall --no-cache-dir pyfory
 
-# For debugging, you can disable Cython mode before importing
+# For debugging, you can disable the Cython implementation before importing
 import os
 os.environ['ENABLE_FORY_CYTHON_SERIALIZATION'] = '0'
-import pyfory  # Now uses pure Python mode
+import pyfory  # Now uses the pure Python implementation
 ```
 
 **Q: Cross-language compatibility issues**
 
 ```python
 # A: Use explicit type registration with consistent naming
-f = pyfory.Fory(xlang=True, compatible=True)
+f = pyfory.Fory(xlang=True)
 f.register(MyClass, typename="com.package.MyClass")  # Use same name in all languages
 ```
 
 **Q: Circular reference errors or duplicate data**
 
+Registered xlang schema objects and Python native objects both require reference tracking when
+object identity or cycles matter:
+
 ```python
-# A: Enable reference tracking
-f = pyfory.Fory(ref=True)  # Required for circular references
+# A: Enable reference tracking for registered schema objects
+f = pyfory.Fory(ref=True)
+```
+
+For arbitrary Python object graphs with circular references, use Python native mode:
+
+```python
+f = pyfory.Fory(xlang=False, ref=True, strict=False)
 
 # Example with circular reference
 class Node:
@@ -1274,8 +1240,8 @@ import pyfory  # Now uses pure Python implementation
 **Q: Schema evolution not working**
 
 ```python
-# A: Enable compatible mode for schema evolution
-f = pyfory.Fory(xlang=True, compatible=True)
+# A: Xlang mode defaults to compatible schema evolution.
+f = pyfory.Fory(xlang=True)
 
 # Version 1: Original class
 @dataclass
@@ -1312,7 +1278,7 @@ f.register(AnotherClass, type_id=101)
 f = pyfory.Fory(strict=False)  # Use only in trusted environments
 ```
 
-## 🤝 Contributing
+## Contributing
 
 Apache Fory™ is an open-source project under the Apache Software Foundation. We welcome all forms of contributions:
 
@@ -1325,7 +1291,7 @@ Apache Fory™ is an open-source project under the Apache Software Foundation. W
 
 > **For Contributors**: See [CONTRIBUTING.md](CONTRIBUTING.md) for comprehensive development setup instructions
 
-## 📄 License
+## License
 
 Apache License 2.0. See [LICENSE](https://github.com/apache/fory/blob/main/LICENSE) for details.
 
@@ -1333,19 +1299,19 @@ Apache License 2.0. See [LICENSE](https://github.com/apache/fory/blob/main/LICEN
 
 **Apache Fory™** - Blazing fast, secure, and versatile serialization for modern applications.
 
-## 🔗 Links
+## Links
 
-- **Documentation**: https://fory.apache.org/docs/latest/python_guide/
+- **Documentation**: https://fory.apache.org/docs/guide/python/
 - **GitHub**: https://github.com/apache/fory
 - **PyPI**: https://pypi.org/project/pyfory/
 - **Issues**: https://github.com/apache/fory/issues
 - **Slack**: https://join.slack.com/t/fory-project/shared_invite/zt-36g0qouzm-kcQSvV_dtfbtBKHRwT5gsw
-- **Benchmarks**: https://fory.apache.org/docs/latest/benchmarks/
+- **Benchmarks**: https://fory.apache.org/docs/benchmarks/
 
-## 🌟 Community
+## Community
 
 We welcome contributions! Whether it's bug reports, feature requests, documentation improvements, or code contributions, we appreciate your help.
 
-- Star the project on [GitHub](https://github.com/apache/fory) ⭐
-- Join our [Slack community](https://join.slack.com/t/fory-project/shared_invite/zt-36g0qouzm-kcQSvV_dtfbtBKHRwT5gsw) 💬
-- Follow us on [X/Twitter](https://x.com/ApacheFory) 🐦
+- Star the project on [GitHub](https://github.com/apache/fory)
+- Join our [Slack community](https://join.slack.com/t/fory-project/shared_invite/zt-36g0qouzm-kcQSvV_dtfbtBKHRwT5gsw)
+- Follow us on [X/Twitter](https://x.com/ApacheFory)
diff --git a/python/pyfory/_fory.py b/python/pyfory/_fory.py
index d09a555f89..4cc1a3a4dc 100644
--- a/python/pyfory/_fory.py
+++ b/python/pyfory/_fory.py
@@ -84,15 +84,16 @@ class Fory:
     High-performance cross-language serialization framework.
 
     Fory provides blazingly-fast serialization for Python objects with support for
-    both Python-native mode and cross-language mode. It handles complex object graphs,
-    reference tracking, and circular references automatically.
+    both Python native mode and xlang mode. Xlang mode handles registered schema
+    objects and cross-language reference metadata; Python native mode handles the
+    broader Python object graph surface, including circular Python objects.
 
-    In Python-native mode (xlang=False), Fory can serialize all Python objects
+    In Python native mode (xlang=False), Fory can serialize all Python objects
     including dataclasses, classes with custom serialization methods, and local
     functions/classes, making it a drop-in replacement for pickle.
 
-    In cross-language mode (xlang=True, compatible=True), Fory serializes objects in a format that
-    can be deserialized by other Fory-supported languages (Java, Go, Rust, C++, etc).
+    In xlang mode, the default, Fory serializes objects in a format that can be
+    deserialized by other Fory-supported languages (Java, Go, Rust, C++, etc.).
 
     Examples:
         >>> import pyfory
@@ -103,16 +104,10 @@ class Fory:
         ...     name: str
         ...     age: pyfory.Int32
         >>>
-        >>> # Python-native mode
-        >>> fory = pyfory.Fory()
-        >>> fory.register(Person)
+        >>> fory = pyfory.Fory(xlang=True)
+        >>> fory.register(Person, typename="example.Person")
         >>> data = fory.serialize(Person("Alice", 30))
         >>> person = fory.deserialize(data)
-        >>>
-        >>> # Cross-language mode
-        >>> fory_xlang = pyfory.Fory(xlang=True, compatible=True)
-        >>> fory_xlang.register(Person)
-        >>> data = fory_xlang.serialize(Person("Bob", 25))
 
     See Also:
         ThreadSafeFory: Thread-safe wrapper for concurrent usage
@@ -137,7 +132,7 @@ class Fory:
 
     def __init__(
         self,
-        xlang: bool = False,
+        xlang: bool = True,
         ref: bool = False,
         strict: bool = True,
         compatible: Optional[bool] = None,
@@ -152,15 +147,15 @@ def __init__(
         Initialize a Fory serialization instance.
 
         Args:
-            xlang: Enable cross-language serialization mode. When False (default), uses
-                Python-native mode supporting all Python objects (dataclasses, __reduce__,
+            xlang: Enable xlang mode. When False, uses
+                Python native mode supporting all Python objects (dataclasses, __reduce__,
                 local functions/classes). With ref=True and strict=False, serves as a
-                drop-in replacement for pickle. When True, uses cross-language format
+                drop-in replacement for pickle. When True, uses the xlang wire format
                 compatible with other Fory languages (Java, Go, Rust, etc), but Python-
                 specific features like functions and __reduce__ methods are not supported.
 
-            ref: Enable reference tracking for shared and circular references. When enabled,
-                duplicate objects are stored once and circular references are supported.
+            ref: Enable reference tracking for shared references and Python native-mode
+                circular references. When enabled, duplicate objects are stored once.
                 Disabled by default for better performance.
 
             strict: Require type registration before serialization (default: True). When
@@ -171,9 +166,10 @@ def __init__(
                 are allowed. We are not responsible for security risks when this option
                 is disabled without proper policy controls.
 
-            compatible: Enable schema evolution for cross-language serialization. Defaults
-                to True when xlang=True and False otherwise. When enabled, supports
-                forward/backward compatibility for dataclass field additions and removals.
+            compatible: Enable schema evolution. When omitted, xlang mode defaults to
+                compatible mode and Python native mode defaults to schema-consistent mode.
+                When enabled, supports forward/backward compatibility for dataclass field
+                additions and removals.
 
             max_depth: Maximum nesting depth for deserialization (default: 50). Raises
                 an exception if exceeded to prevent malicious deeply-nested data attacks.
@@ -197,11 +193,11 @@ def __init__(
                 payloads that claim extremely large binary sizes.
 
         Example:
-            >>> # Python-native mode with reference tracking
-            >>> fory = Fory(ref=True)
+            >>> # Python native mode with reference tracking
+            >>> fory = Fory(xlang=False, ref=True)
             >>>
-            >>> # Cross-language mode with schema evolution
-            >>> fory = Fory(xlang=True, compatible=True)
+            >>> # Xlang mode with compatible schema evolution
+            >>> fory = Fory(xlang=True)
         """
         compatible = xlang if compatible is None else compatible
         self.xlang = xlang
@@ -272,14 +268,14 @@ def register(
 
         Example:
             >>> # Register with type_id (recommended for performance)
-            >>> fory = Fory(xlang=True, compatible=True)
+            >>> fory = Fory(xlang=True)
             >>> fory.register(Person, type_id=100)
             >>>
             >>> # Register with namespace and typename (more flexible)
             >>> fory.register(Person, namespace="com.example", typename="Person")
             >>>
-            >>> # Python-native mode (no cross-language matching needed)
-            >>> fory = Fory()
+            >>> # Python native mode (no cross-language matching needed)
+            >>> fory = Fory(xlang=False)
             >>> fory.register(Person)
         """
         self.register_type(
@@ -323,14 +319,14 @@ def register_type(
 
         Example:
             >>> # Register with type_id (recommended for performance)
-            >>> fory = Fory(xlang=True, compatible=True)
+            >>> fory = Fory(xlang=True)
             >>> fory.register_type(Person, type_id=100)
             >>>
             >>> # Register with namespace and typename (more flexible)
             >>> fory.register_type(Person, namespace="com.example", typename="Person")
             >>>
-            >>> # Python-native mode (no cross-language matching needed)
-            >>> fory = Fory()
+            >>> # Python native mode (no cross-language matching needed)
+            >>> fory = Fory(xlang=False)
             >>> fory.register_type(Person)
         """
         return self.type_resolver.register_type(
@@ -373,7 +369,7 @@ def register_serializer(self, cls: type, serializer):
             serializer: Custom serializer instance implementing the Serializer protocol
 
         Example:
-            >>> fory = Fory()
+            >>> fory = Fory(xlang=False)
             >>> fory.register_serializer(MyClass, MyCustomSerializer())
         """
         self.type_resolver.register_serializer(cls, serializer)
@@ -455,7 +451,7 @@ def serialize(
             Serialized bytes if buffer is None, otherwise returns the provided buffer
 
         Example:
-            >>> fory = Fory()
+            >>> fory = Fory(xlang=False)
             >>> data = fory.serialize({"key": "value", "num": 42})
             >>> print(type(data))
             
@@ -531,7 +527,7 @@ def deserialize(
             The deserialized Python object
 
         Example:
-            >>> fory = Fory()
+            >>> fory = Fory(xlang=False)
             >>> data = fory.serialize({"key": "value"})
             >>> obj = fory.deserialize(data)
             >>> print(obj)
@@ -614,11 +610,11 @@ class ThreadSafeFory:
     serialization will raise a RuntimeError.
 
     Args:
-        xlang (bool): Whether to enable cross-language serialization. Defaults to False.
+        xlang (bool): Whether to enable xlang mode. Defaults to True.
         ref (bool): Whether to enable reference tracking. Defaults to False.
         strict (bool): Whether to require type registration. Defaults to True.
-        compatible (bool): Whether to enable compatible mode. Defaults to True when
-            xlang=True and False otherwise.
+        compatible (bool): Whether to enable compatible mode. Defaults to compatible mode
+            in xlang and schema-consistent mode in Python native mode.
         max_depth (int): Maximum depth for deserialization. Defaults to 50.
         max_collection_size (int): Maximum allowed size for collections and maps during
             deserialization. Defaults to 1,000,000.
@@ -626,7 +622,7 @@ class ThreadSafeFory:
             deserialization. Defaults to 64 MB.
 
     Example:
-        >>> import pyfury
+        >>> import pyfory
         >>> import threading
         >>> from dataclasses import dataclass
         >>>
@@ -636,7 +632,7 @@ class ThreadSafeFory:
         ...     age: int
         >>>
         >>> # Create thread-safe instance
-        >>> fory = pyfury.ThreadSafeFory()
+        >>> fory = pyfory.ThreadSafeFory(xlang=False)
         >>> fory.register(Person)
         >>>
         >>> # Use safely from multiple threads
diff --git a/python/pyfory/policy.py b/python/pyfory/policy.py
index a47f5d8ae9..5070821c0b 100644
--- a/python/pyfory/policy.py
+++ b/python/pyfory/policy.py
@@ -40,17 +40,17 @@ class DeserializationPolicy:
     +---------------------------+----------------------+----------------------------+
     | Security Feature          | pickle.Unpickler     | Fory DeserializationPolicy           |
     +---------------------------+----------------------+----------------------------+
-    | Class import control      | ✓ find_class()       | ✓ validate_class()         |
-    | Function import control   | ✗ (via find_class)   | ✓ validate_function()      |
-    | Method validation         | ✗                    | ✓ validate_method()        |
-    | Module import control     | ✗                    | ✓ validate_module()        |
-    | Instantiation control     | ✗                    | ✓ authorize_instantiation()|
-    | __reduce__ interception   | ✗                    | ✓ intercept_reduce_call()  |
-    | Post-reduce inspection    | ✗                    | ✓ inspect_reduced_object() |
-    | __setstate__ interception | ✗                    | ✓ intercept_setstate()     |
-    | Object replacement        | ✗                    | ✓ (return from validators) |
-    | State sanitization        | ✗                    | ✓ (modify in-place)        |
-    | Local class/function      | ✗                    | ✓ (is_local flag)          |
+    | Class import control      | find_class()         | validate_class()           |
+    | Function import control   | no (via find_class)  | validate_function()        |
+    | Method validation         | no                   | validate_method()          |
+    | Module import control     | no                   | validate_module()          |
+    | Instantiation control     | no                   | authorize_instantiation()  |
+    | __reduce__ interception   | no                   | intercept_reduce_call()    |
+    | Post-reduce inspection    | no                   | inspect_reduced_object()   |
+    | __setstate__ interception | no                   | intercept_setstate()       |
+    | Object replacement        | no                   | return from validators     |
+    | State sanitization        | no                   | modify in-place            |
+    | Local class/function      | no                   | is_local flag              |
     +---------------------------+----------------------+----------------------------+
 
     Example: Blocking subprocess.Popen with pickle vs Fory:
@@ -126,7 +126,7 @@ def intercept_reduce_call(self, callable_obj, args, **kwargs):
     ...         print(f"Reducing with {callable_obj.__name__}({args})")
     ...         return None  # Proceed normally
     ...
-    >>> fory = Fory(checker=SafeDeserializationPolicy())
+    >>> fory = Fory(xlang=False, strict=False, policy=SafeDeserializationPolicy())
 
     Thread Safety
     -------------
@@ -143,7 +143,7 @@ def intercept_reduce_call(self, callable_obj, args, **kwargs):
     See Also
     --------
     - Python's pickle module security warnings: https://docs.python.org/3/library/pickle.html
-    - Fory documentation on secure deserialization: docs/guide/security.md
+    - Fory Python security guide: https://fory.apache.org/docs/guide/python/security
     """
 
     # ============================================================================
@@ -188,8 +188,7 @@ def authorize_instantiation(self, cls, **kwargs):
             ...             raise ValueError(f"Class {cls.__name__} not whitelisted")
 
         Note:
-            This method was previously named check_read_allowed and check_create_object.
-            Those names are kept as aliases for backward compatibility.
+            `check_read_allowed` and `check_create_object` are aliases for this hook.
         """
         pass
 
@@ -215,7 +214,7 @@ def validate_class(self, cls, *, is_local: bool, **kwargs):
         - Block dangerous classes (subprocess.Popen, os.system, etc.)
         - Replace untrusted classes with safe alternatives
         - Validate that local classes match expected signatures
-        - Implement class versioning/migration logic
+        - Implement class versioning or adaptation logic
 
         Args:
             cls (type): The deserialized class object.
@@ -233,10 +232,10 @@ class will be used instead for deserialization.
             Exception: Raise any exception to reject the class and abort deserialization.
 
         Example:
-            >>> class MigrationChecker(DeserializationPolicy):
+            >>> class ClassAdapter(DeserializationPolicy):
             ...     def validate_class(self, cls, is_local, **kwargs):
-            ...         # Migrate old class to new class
-            ...         if cls.__name__ == 'OldUserClass':
+            ...         # Map a serialized class name to the current class.
+            ...         if cls.__name__ == 'ArchivedUserClass':
             ...             return NewUserClass
             ...         # Block dangerous classes
             ...         if cls.__module__ == 'subprocess':
@@ -244,8 +243,7 @@ class will be used instead for deserialization.
             ...         return None  # Accept
 
         Note:
-            This method was previously named check_class. That name is kept as an
-            alias for backward compatibility.
+            `check_class` is an alias for this hook.
         """
         pass
 
@@ -265,7 +263,7 @@ def validate_function(self, func, is_local: bool, **kwargs):
         ------------------
         - Block dangerous built-in functions (eval, exec, compile, __import__)
         - Validate that reconstructed functions have expected signatures
-        - Replace untrusted functions with safe stubs
+        - Replace untrusted functions with safe alternatives
         - Audit function imports for security logging
 
         Args:
@@ -291,8 +289,7 @@ def validate_function(self, func, is_local: bool, **kwargs):
             ...         return None
 
         Note:
-            This method was previously named check_function. That name is kept as an
-            alias for backward compatibility.
+            `check_function` is an alias for this hook.
         """
         pass
 
@@ -335,8 +332,7 @@ def validate_method(self, method, is_local: bool, **kwargs):
             ...         return None
 
         Note:
-            This method was previously named check_method. That name is kept as an
-            alias for backward compatibility.
+            `check_method` is an alias for this hook.
         """
         pass
 
@@ -380,8 +376,7 @@ def validate_module(self, module_name: str, **kwargs):
             ...         return None
 
         Note:
-            This method was previously named check_module. That name is kept as an
-            alias for backward compatibility.
+            `check_module` is an alias for this hook.
         """
         pass
 
@@ -441,18 +436,13 @@ def intercept_reduce_call(self, callable_obj, args, **kwargs):
             This is one of the most critical security hooks, as __reduce__ is the primary
             vector for arbitrary code execution in pickle-based attacks.
 
-            This method was previously named check_reduce_callable. That name is kept
-            as an alias for backward compatibility.
+            `check_reduce_callable` is an alias for this hook.
         """
         pass
 
-    # Backward compatibility aliases
+    # Hook aliases
     def check_reduce_callable(self, callable_obj, args, **kwargs):
-        """Deprecated: Use intercept_reduce_call instead.
-
-        This method is kept for backward compatibility. New code should use
-        intercept_reduce_call for clarity.
-        """
+        """Alias for intercept_reduce_call."""
         return self.intercept_reduce_call(callable_obj, args, **kwargs)
 
     def inspect_reduced_object(self, obj, **kwargs):
@@ -497,18 +487,13 @@ def inspect_reduced_object(self, obj, **kwargs):
         Note:
             This hook provides a last line of defense after reduce reconstruction.
 
-            This method was previously named check_restored_reduced_object. That name
-            is kept as an alias for backward compatibility.
+            `check_restored_reduced_object` is an alias for this hook.
         """
         pass
 
-    # Backward compatibility aliases
+    # Hook aliases
     def check_restored_reduced_object(self, obj, **kwargs):
-        """Deprecated: Use inspect_reduced_object instead.
-
-        This method is kept for backward compatibility. New code should use
-        inspect_reduced_object for clarity.
-        """
+        """Alias for inspect_reduced_object."""
         return self.inspect_reduced_object(obj, **kwargs)
 
     def intercept_setstate(self, obj, state, **kwargs):
@@ -562,18 +547,13 @@ def intercept_setstate(self, obj, state, **kwargs):
             This hook can modify the state dict in-place. Changes will be reflected
             when __setstate__ is called.
 
-            This method was previously named check_setstate. That name is kept as an
-            alias for backward compatibility.
+            `check_setstate` is an alias for this hook.
         """
         pass
 
-    # Backward compatibility alias
+    # Hook alias
     def check_setstate(self, obj, state, **kwargs):
-        """Deprecated: Use intercept_setstate instead.
-
-        This method is kept for backward compatibility. New code should use
-        intercept_setstate for clarity.
-        """
+        """Alias for intercept_setstate."""
         return self.intercept_setstate(obj, state, **kwargs)
 
 
diff --git a/python/pyfory/serialization.pyx b/python/pyfory/serialization.pyx
index 733156cbc9..5be3b44a0d 100644
--- a/python/pyfory/serialization.pyx
+++ b/python/pyfory/serialization.pyx
@@ -126,7 +126,7 @@ cdef class Config:
     config instance instead of mirroring them onto other owners.
 
     Attributes:
-        xlang: Enables cross-language wire format instead of Python-native mode.
+        xlang: Selects xlang wire format instead of Python native mode.
         track_ref: Enables reference tracking for shared and circular object graphs.
         strict: Requires type registration before serialization/deserialization.
         compatible: Enables compatible mode and schema-evolution metadata paths.
@@ -173,7 +173,7 @@ cdef class Config:
         Build a runtime config object for one Python or Cython Fory instance.
 
         Args:
-            xlang: Enable cross-language serialization mode.
+            xlang: Select xlang wire format.
             track_ref: Enable reference tracking for object graphs.
             strict: Require registered types on dynamic resolution paths.
             compatible: Enable compatible mode and meta-share flows.
@@ -834,7 +834,7 @@ cdef class Fory:
 
     def __init__(
         self,
-        xlang=False,
+        xlang=True,
         ref=False,
         strict=True,
         compatible=None,
@@ -849,11 +849,11 @@ cdef class Fory:
         Initialize a Cython-backed Fory runtime instance.
 
         Args:
-            xlang: Enable cross-language serialization mode.
+            xlang: Select xlang wire format.
             ref: Enable reference tracking for shared and circular references.
             strict: Require registered types on dynamic resolution paths.
             compatible: Enable compatible mode and meta-share type exchange. Defaults to
-                True when xlang=True and False otherwise.
+                compatible mode in xlang and schema-consistent mode in Python native mode.
             max_depth: Maximum allowed read depth before rejecting payloads.
             policy: Optional deserialization policy implementation.
             field_nullable: Treat struct fields as nullable by default.
diff --git a/python/pyfory/tests/test_class_serializer.py b/python/pyfory/tests/test_class_serializer.py
index 77fe3af8ce..eded41b96f 100644
--- a/python/pyfory/tests/test_class_serializer.py
+++ b/python/pyfory/tests/test_class_serializer.py
@@ -41,7 +41,7 @@ def __eq__(self, other):
     LocalClass = create_local_class()
 
     # Test basic serialization of the class type itself
-    fory = Fory(ref=True, strict=False)
+    fory = Fory(xlang=False, ref=True, strict=False)
 
     # Serialize the class type
     serialized = fory.serialize(LocalClass)
@@ -78,7 +78,7 @@ def get_multiplied_value(self):
     # Create a local class with closure
     LocalClassWithClosure = create_local_class_with_closure(3)
 
-    fory = Fory(ref=True, strict=False)
+    fory = Fory(xlang=False, ref=True, strict=False)
 
     # Serialize the class type
     serialized = fory.serialize(LocalClassWithClosure)
@@ -115,7 +115,7 @@ def get_value(self):
         return LocalDerivedClass
 
     LocalClass = create_local_class_with_inheritance()
-    fory = Fory(ref=True, strict=False)
+    fory = Fory(xlang=False, ref=True, strict=False)
 
     # Serialize and deserialize the class
     serialized = fory.serialize(LocalClass)
@@ -156,7 +156,7 @@ def get_info(self):
         return LocalClassWithVars
 
     LocalClass = create_class_with_vars()
-    fory = Fory(ref=True, strict=False)
+    fory = Fory(xlang=False, ref=True, strict=False)
 
     # Create some instances to modify class state
     LocalClass(1)  # This increments the counter
@@ -201,7 +201,7 @@ def get_outer(self):
         def create_inner(self, inner_val):
             return self.InnerGlobalClass(inner_val)
 
-    fory = Fory(ref=True, strict=False)
+    fory = Fory(xlang=False, ref=True, strict=False)
 
     # Test serializing the outer class
     serialized_outer = fory.serialize(OuterGlobalClass)
@@ -259,7 +259,7 @@ def inner_method(self):
 
         return OuterLocalClass
 
-    fory = Fory(ref=True, strict=False)
+    fory = Fory(xlang=False, ref=True, strict=False)
 
     # Create complex local class with nested closures
     ComplexLocalClass = create_complex_local_scenario(5)
@@ -304,7 +304,7 @@ def combined_method(self):
 
         return LocalMultipleInheritanceClass
 
-    fory = Fory(ref=True, strict=False)
+    fory = Fory(xlang=False, ref=True, strict=False)
 
     LocalClass = create_local_class_with_multiple_inheritance()
 
diff --git a/python/pyfory/tests/test_cross_language.py b/python/pyfory/tests/test_cross_language.py
index c5cf46e44d..5e0bef1d4c 100644
--- a/python/pyfory/tests/test_cross_language.py
+++ b/python/pyfory/tests/test_cross_language.py
@@ -449,7 +449,7 @@ class SomeClass:
 
 
 def test_custom_class_roundtrip():
-    fory = pyfory.Fory(ref=True)
+    fory = pyfory.Fory(xlang=True, compatible=False, ref=True)
     fory.register_type(SomeClass, typename="example.SomeClass")
     obj1 = SomeClass()
     obj1.f2 = {"k1": "v1", "k2": "v2"}
diff --git a/python/pyfory/tests/test_function.py b/python/pyfory/tests/test_function.py
index 738ef69d62..759104c6ae 100644
--- a/python/pyfory/tests/test_function.py
+++ b/python/pyfory/tests/test_function.py
@@ -20,7 +20,9 @@
 
 def test_lambda_functions_serialization():
     """Tests serialization of lambda functions."""
-    fory = pyfory.Fory()
+    fory = pyfory.Fory(
+        xlang=False,
+    )
     test_input = 5
 
     # Register the necessary types
@@ -45,7 +47,9 @@ def test_lambda_functions_serialization():
 
 def test_regular_functions_serialization():
     """Tests serialization of regular functions."""
-    fory = pyfory.Fory()
+    fory = pyfory.Fory(
+        xlang=False,
+    )
     test_input = 5
 
     def add_one(x):
@@ -74,7 +78,9 @@ def complex_function(a, b, c=10):
 
 def test_nested_functions_serialization():
     """Tests serialization of nested functions."""
-    fory = pyfory.Fory()
+    fory = pyfory.Fory(
+        xlang=False,
+    )
 
     # Register the necessary types
     fory.register_type(tuple)
@@ -99,7 +105,9 @@ def inner_function(y):
 
 def test_local_class_serialization():
     """Tests serialization of local classes."""
-    fory = pyfory.Fory()
+    fory = pyfory.Fory(
+        xlang=False,
+    )
 
     # Register the necessary types
     fory.register_type(tuple)
diff --git a/python/pyfory/tests/test_method.py b/python/pyfory/tests/test_method.py
index 346407ee03..29d7a00941 100644
--- a/python/pyfory/tests/test_method.py
+++ b/python/pyfory/tests/test_method.py
@@ -75,7 +75,7 @@ class TestMethodSerialization:
 
     def test_instance_method_serialization(self):
         """Test serialization of instance methods."""
-        fory = pyfory.Fory(strict=False, ref=True)
+        fory = pyfory.Fory(xlang=False, strict=False, ref=True)
 
         class TestClass:
             def __init__(self, value):
@@ -96,7 +96,7 @@ def instance_method(self):
 
     def test_classmethod_serialization(self):
         """Test serialization of class methods."""
-        fory = pyfory.Fory(strict=False, ref=True)
+        fory = pyfory.Fory(xlang=False, strict=False, ref=True)
 
         class TestClass:
             class_var = 42
@@ -116,7 +116,7 @@ def class_method(cls):
 
     def test_staticmethod_serialization(self):
         """Test serialization of static methods."""
-        fory = pyfory.Fory(strict=False, ref=True)
+        fory = pyfory.Fory(xlang=False, strict=False, ref=True)
 
         class TestClass:
             @staticmethod
@@ -134,7 +134,7 @@ def static_method():
 
     def test_method_with_args_serialization(self):
         """Test serialization of methods with arguments."""
-        fory = pyfory.Fory(strict=False, ref=True)
+        fory = pyfory.Fory(xlang=False, strict=False, ref=True)
 
         class TestClass:
             def __init__(self, base):
@@ -176,7 +176,7 @@ def subtract(a, b):
 
     def test_nested_class_method_serialization(self):
         """Test serialization of methods from nested classes."""
-        fory = pyfory.Fory(strict=False, ref=True)
+        fory = pyfory.Fory(xlang=False, strict=False, ref=True)
 
         class OuterClass:
             class InnerClass:
@@ -196,7 +196,7 @@ def inner_class_method(cls):
 
 def test_classmethod_serialization():
     """Standalone test for classmethod serialization - reproduces the original error."""
-    fory = pyfory.Fory(strict=False, ref=True)
+    fory = pyfory.Fory(xlang=False, strict=False, ref=True)
 
     class A:
         @classmethod
@@ -225,7 +225,7 @@ def g():
 
 def test_staticmethod_serialization():
     """Standalone test for staticmethod serialization."""
-    fory = pyfory.Fory(strict=False, ref=True)
+    fory = pyfory.Fory(xlang=False, strict=False, ref=True)
 
     class A:
         @staticmethod
@@ -243,7 +243,7 @@ def g():
 # Global class method tests
 def test_global_classmethod_serialization():
     """Test serialization of global class methods."""
-    fory = pyfory.Fory(strict=False, ref=True)
+    fory = pyfory.Fory(xlang=False, strict=False, ref=True)
 
     method = GlobalTestClass.class_method
     serialized = fory.serialize(method)
@@ -256,7 +256,7 @@ def test_global_classmethod_serialization():
 
 def test_global_classmethod_with_args():
     """Test serialization of global class methods with arguments."""
-    fory = pyfory.Fory(strict=False, ref=True)
+    fory = pyfory.Fory(xlang=False, strict=False, ref=True)
 
     method = GlobalTestClass.class_method_with_args
     serialized = fory.serialize(method)
@@ -269,7 +269,7 @@ def test_global_classmethod_with_args():
 
 def test_global_staticmethod_serialization():
     """Test serialization of global static methods."""
-    fory = pyfory.Fory(strict=False, ref=True)
+    fory = pyfory.Fory(xlang=False, strict=False, ref=True)
 
     method = GlobalTestClass.static_method
     serialized = fory.serialize(method)
@@ -281,7 +281,7 @@ def test_global_staticmethod_serialization():
 
 def test_global_staticmethod_with_args():
     """Test serialization of global static methods with arguments."""
-    fory = pyfory.Fory(strict=False, ref=True)
+    fory = pyfory.Fory(xlang=False, strict=False, ref=True)
 
     method = GlobalTestClass.static_method_with_args
     serialized = fory.serialize(method)
@@ -294,7 +294,7 @@ def test_global_staticmethod_with_args():
 
 def test_global_instance_method_serialization():
     """Test serialization of global instance methods."""
-    fory = pyfory.Fory(strict=False, ref=True)
+    fory = pyfory.Fory(xlang=False, strict=False, ref=True)
 
     obj = GlobalTestClass("test_value")
     method = obj.instance_method
@@ -307,7 +307,7 @@ def test_global_instance_method_serialization():
 
 def test_multiple_global_classes():
     """Test serialization of methods from multiple global classes."""
-    fory = pyfory.Fory(strict=False, ref=True)
+    fory = pyfory.Fory(xlang=False, strict=False, ref=True)
 
     # Test methods from different global classes
     method1 = GlobalTestClass.class_method
@@ -327,7 +327,7 @@ def test_multiple_global_classes():
 
 def test_global_class_inheritance():
     """Test serialization of methods from global classes with inheritance."""
-    fory = pyfory.Fory(strict=False, ref=True)
+    fory = pyfory.Fory(xlang=False, strict=False, ref=True)
 
     # Test inherited class method
     method = GlobalClassWithInheritance.inherited_class_method
@@ -348,7 +348,7 @@ def test_global_class_inheritance():
 
 def test_global_methods_without_ref_tracking():
     """Test serialization of global class methods without reference tracking."""
-    fory = pyfory.Fory(strict=False, ref=False)
+    fory = pyfory.Fory(xlang=False, strict=False, ref=False)
 
     # Global classes should work even without track_ref
     method = GlobalTestClass.class_method
@@ -361,7 +361,7 @@ def test_global_methods_without_ref_tracking():
 
 def test_global_method_collection():
     """Test serialization of collections containing global methods."""
-    fory = pyfory.Fory(strict=False, ref=True)
+    fory = pyfory.Fory(xlang=False, strict=False, ref=True)
 
     methods = [
         GlobalTestClass.class_method,
@@ -379,7 +379,7 @@ def test_global_method_collection():
 
 def test_global_method_in_dict():
     """Test serialization of dictionaries containing global methods."""
-    fory = pyfory.Fory(strict=False, ref=True)
+    fory = pyfory.Fory(xlang=False, strict=False, ref=True)
 
     method_dict = {
         "class_method": GlobalTestClass.class_method,
diff --git a/python/pyfory/tests/test_policy.py b/python/pyfory/tests/test_policy.py
index fe1be69556..9eb7d55f0c 100644
--- a/python/pyfory/tests/test_policy.py
+++ b/python/pyfory/tests/test_policy.py
@@ -140,7 +140,7 @@ class UnsafeClass:
         pass
 
     policy = BlockClassPolicy(blocked_class_names=["UnsafeClass"])
-    fory = Fory(ref=True, strict=False, policy=policy)
+    fory = Fory(xlang=False, ref=True, strict=False, policy=policy)
 
     # Serialize and deserialize the class type itself (not an instance)
     safe_data = fory.serialize(SafeClass)
@@ -164,7 +164,7 @@ def __reduce__(self):
             return (ReducibleClass, (self.value,))
 
     policy = BlockReduceCallPolicy(blocked_names=["ReducibleClass"])
-    fory = Fory(ref=True, strict=False, policy=policy)
+    fory = Fory(xlang=False, ref=True, strict=False, policy=policy)
     data = fory.serialize(ReducibleClass(42))
 
     with pytest.raises(ValueError, match="ReducibleClass is blocked"):
@@ -182,7 +182,7 @@ def __reduce__(self):
             return (ReducibleClass, (self.value,))
 
     policy = ReplaceObjectPolicy(replacement_value="REPLACED")
-    fory = Fory(ref=True, strict=False, policy=policy)
+    fory = Fory(xlang=False, ref=True, strict=False, policy=policy)
     data = fory.serialize(ReducibleClass(42))
 
     result = fory.deserialize(data)
@@ -204,7 +204,7 @@ def __setstate__(self, state):
             self.__dict__.update(state)
 
     policy = SanitizeStatePolicy()
-    fory = Fory(ref=False, strict=False, policy=policy)
+    fory = Fory(xlang=False, ref=False, strict=False, policy=policy)
     data = fory.serialize(SecretHolder("admin", "secret123"))
 
     result = fory.deserialize(data)
@@ -237,7 +237,7 @@ def __setstate__(self, state):
             self.__dict__.update(state)
 
     policy = CountingSanitizePolicy()
-    fory = Fory(ref=True, strict=False, policy=policy)
+    fory = Fory(xlang=False, ref=True, strict=False, policy=policy)
     data = fory.serialize(SecretReduceHolder())
 
     result = fory.deserialize(data)
@@ -254,7 +254,7 @@ def intercept_setstate(self, obj, state, **kwargs):
             raise ValueError("state blocked")
 
     FalseyState.bool_called = False
-    fory = Fory(ref=True, strict=False, policy=BlockSetStatePolicy())
+    fory = Fory(xlang=False, ref=True, strict=False, policy=BlockSetStatePolicy())
     data = fory.serialize(FalseyStatePayload())
 
     with pytest.raises(ValueError, match="state blocked"):
@@ -273,8 +273,8 @@ def intercept_setstate(self, obj, state, **kwargs):
     obj.value = 1
     ObjectSetAttrPayload.setattr_called = False
 
-    writer = Fory(ref=True, strict=False)
-    reader = Fory(ref=True, strict=False, policy=BlockSetStatePolicy())
+    writer = Fory(xlang=False, ref=True, strict=False)
+    reader = Fory(xlang=False, ref=True, strict=False, policy=BlockSetStatePolicy())
     writer.register(ObjectSetAttrPayload)
     reader.register(ObjectSetAttrPayload)
 
@@ -295,7 +295,7 @@ class LocalClass:
     LocalCls = make_local_class()
 
     policy = BlockClassPolicy(blocked_class_names=["LocalClass"])
-    fory = Fory(ref=True, strict=False, policy=policy)
+    fory = Fory(xlang=False, ref=True, strict=False, policy=policy)
 
     # Serialize the local class type
     data = fory.serialize(LocalCls)
@@ -315,7 +315,7 @@ def __reduce__(self):
             return (ReducibleClass, (self.value,))
 
     policy = BlockReduceCallPolicy(blocked_names=["ReducibleClass"])
-    fory = Fory(ref=True, strict=False, policy=policy)
+    fory = Fory(xlang=False, ref=True, strict=False, policy=policy)
 
     data = fory.serialize(ReducibleClass(42))
 
@@ -326,7 +326,7 @@ def __reduce__(self):
 def test_policy_allows_safe_operations():
     """Test that policy doesn't interfere with safe built-in types."""
     policy = BlockClassPolicy(blocked_class_names=[])
-    fory = Fory(ref=False, strict=False, policy=policy)
+    fory = Fory(xlang=False, ref=False, strict=False, policy=policy)
 
     assert fory.deserialize(fory.serialize(42)) == 42
     assert fory.deserialize(fory.serialize("test")) == "test"
@@ -361,7 +361,7 @@ def __reduce__(self):
             return (TestClass, (self.value,))
 
     policy = MultiHookPolicy()
-    fory = Fory(ref=True, strict=False, policy=policy)
+    fory = Fory(xlang=False, ref=True, strict=False, policy=policy)
 
     data = fory.serialize(TestClass(42))
     result = fory.deserialize(data)
@@ -390,7 +390,7 @@ def __reduce__(self):
             return (Outer, (self.inner,))
 
     policy = BlockReduceCallPolicy(blocked_names=["Inner"])
-    fory = Fory(ref=True, strict=False, policy=policy)
+    fory = Fory(xlang=False, ref=True, strict=False, policy=policy)
 
     data = fory.serialize(Outer(Inner(42)))
 
@@ -422,7 +422,7 @@ def authorize_instantiation(self, cls, **kwargs):
             return None
 
     policy = BlockInstantiationPolicy()
-    fory = Fory(ref=True, strict=False, policy=policy)
+    fory = Fory(xlang=False, ref=True, strict=False, policy=policy)
     with pytest.raises(ValueError, match="StatefulPayload blocked"):
         fory.deserialize(fory.serialize(StatefulPayload()))
     assert policy.authorize_instantiation_calls == 1
@@ -451,7 +451,7 @@ def authorize_instantiation(self, cls, **kwargs):
             return None
 
     policy = BlockInstantiationPolicy()
-    fory = Fory(ref=True, strict=False, policy=policy)
+    fory = Fory(xlang=False, ref=True, strict=False, policy=policy)
     with pytest.raises(ValueError, match="ReduceTarget blocked"):
         fory.deserialize(fory.serialize(ReducePayload()))
     assert policy.reduce_target_calls == 1
@@ -476,8 +476,8 @@ def authorize_instantiation(self, cls, **kwargs):
             return None
 
     policy = BlockInstantiationPolicy()
-    writer = Fory(ref=True, strict=True)
-    reader = Fory(ref=True, strict=True, policy=policy)
+    writer = Fory(xlang=False, ref=True, strict=True)
+    reader = Fory(xlang=False, ref=True, strict=True, policy=policy)
     writer.register(StrictDataClass)
     reader.register(StrictDataClass)
 
@@ -496,7 +496,7 @@ class ReturnModulePolicy(DeserializationPolicy):
         def validate_module(self, module_name, **kwargs):
             return collections
 
-    fory1 = Fory(ref=True, strict=False, policy=ReturnModulePolicy())
+    fory1 = Fory(xlang=False, ref=True, strict=False, policy=ReturnModulePolicy())
     data = fory1.serialize(json)
     assert fory1.deserialize(data) is collections
 
@@ -505,7 +505,7 @@ class RedirectPolicy(DeserializationPolicy):
         def validate_module(self, module_name, **kwargs):
             return "collections" if module_name == "json" else None
 
-    fory2 = Fory(ref=True, strict=False, policy=RedirectPolicy())
+    fory2 = Fory(xlang=False, ref=True, strict=False, policy=RedirectPolicy())
     assert fory2.deserialize(fory2.serialize(json)).__name__ == "collections"
 
     # Test 3: Raise to block module
@@ -513,7 +513,7 @@ class BlockPolicy(DeserializationPolicy):
         def validate_module(self, module_name, **kwargs):
             raise ValueError(f"Module {module_name} blocked")
 
-    fory3 = Fory(ref=True, strict=False, policy=BlockPolicy())
+    fory3 = Fory(xlang=False, ref=True, strict=False, policy=BlockPolicy())
     with pytest.raises(ValueError, match="blocked"):
         fory3.deserialize(fory3.serialize(json))
 
@@ -533,7 +533,7 @@ def validate_module(self, module_name, **kwargs):
             return None
 
     policy = BlockModulePolicy()
-    fory = Fory(ref=True, strict=False, policy=policy)
+    fory = Fory(xlang=False, ref=True, strict=False, policy=policy)
     with pytest.raises(ValueError, match="subprocess blocked"):
         fory.deserialize(fory.serialize(subprocess.Popen))
     assert policy.validate_module_calls == 1
@@ -556,7 +556,7 @@ def validate_function(self, func, is_local, **kwargs):
             return None
 
     policy = BlockMethodPolicy()
-    fory = Fory(ref=True, strict=False, policy=policy)
+    fory = Fory(xlang=False, ref=True, strict=False, policy=policy)
 
     with pytest.raises(ValueError, match="method blocked"):
         fory.deserialize(fory.serialize([].append))
@@ -584,7 +584,7 @@ def validate_method(self, method, is_local, **kwargs):
 
     obj = GuardedMethod()
     method = types.MethodType(GuardedMethod.run, obj)
-    fory = Fory(ref=True, strict=False, policy=BlockMethodPolicy())
+    fory = Fory(xlang=False, ref=True, strict=False, policy=BlockMethodPolicy())
     data = fory.serialize(method)
 
     GuardedMethod.getattribute_called = False
@@ -606,7 +606,7 @@ def validate_class(self, cls, is_local, **kwargs):
     PolicyGlobalClass.__module__ = "__main__"
     try:
         policy = CaptureClassPolicy()
-        fory = Fory(ref=True, strict=False, policy=policy)
+        fory = Fory(xlang=False, ref=True, strict=False, policy=policy)
         serializer = TypeSerializer(fory.type_resolver, type)
         read_context = FakeReadContext(policy, [0, __name__, "PolicyGlobalClass"])
 
@@ -630,7 +630,7 @@ def validate_method(self, method, is_local, **kwargs):
             raise ValueError("method blocked")
 
     policy = CaptureMethodPolicy()
-    fory = Fory(ref=True, strict=False, policy=policy)
+    fory = Fory(xlang=False, ref=True, strict=False, policy=policy)
     serializer = FunctionSerializer(fory.type_resolver, type(policy_global_function))
     read_context = FakeReadContext(policy, [0, LocalReceiver(), "run"])
 
@@ -653,7 +653,7 @@ def validate_method(self, method, is_local, **kwargs):
             raise ValueError("method blocked")
 
     policy = CaptureMethodPolicy()
-    fory = Fory(ref=True, strict=False, policy=policy)
+    fory = Fory(xlang=False, ref=True, strict=False, policy=policy)
     serializer = NativeFuncMethodSerializer(fory.type_resolver, type(policy_global_function))
     read_context = FakeReadContext(policy, ["run", False, LocalReceiver()])
 
@@ -679,7 +679,7 @@ def validate_function(self, func, is_local, **kwargs):
             return None
 
     policy = BlockClassPolicy()
-    fory = Fory(ref=True, strict=False, policy=policy)
+    fory = Fory(xlang=False, ref=True, strict=False, policy=policy)
     serializer = FunctionSerializer(fory.type_resolver, type(policy_global_function))
     read_context = FakeReadContext(policy, [1, "subprocess", "Popen"])
 
@@ -704,7 +704,7 @@ def validate_function(self, func, is_local, **kwargs):
             return None
 
     policy = MethodPolicy()
-    fory = Fory(ref=True, strict=False, policy=policy)
+    fory = Fory(xlang=False, ref=True, strict=False, policy=policy)
     serializer = FunctionSerializer(fory.type_resolver, type(policy_global_function))
     read_context = FakeReadContext(policy, [1, __name__, "policy_global_bound_method"])
 
@@ -727,7 +727,7 @@ def validate_function(self, func, is_local, **kwargs):
     policy_global_function.__module__ = "__main__"
     try:
         policy = CaptureFunctionPolicy()
-        fory = Fory(ref=True, strict=False, policy=policy)
+        fory = Fory(xlang=False, ref=True, strict=False, policy=policy)
         serializer = FunctionSerializer(fory.type_resolver, type(policy_global_function))
         read_context = FakeReadContext(policy, [1, __name__, "policy_global_function"])
 
@@ -754,7 +754,7 @@ def validate_function(self, func, is_local, **kwargs):
             return None
 
     policy = BlockClassPolicy()
-    fory = Fory(ref=True, strict=False, policy=policy)
+    fory = Fory(xlang=False, ref=True, strict=False, policy=policy)
     serializer = NativeFuncMethodSerializer(fory.type_resolver, type(policy_global_function))
     read_context = FakeReadContext(policy, ["Popen", True, "subprocess"])
 
@@ -779,7 +779,7 @@ def validate_function(self, func, is_local, **kwargs):
             return None
 
     policy = MethodPolicy()
-    fory = Fory(ref=True, strict=False, policy=policy)
+    fory = Fory(xlang=False, ref=True, strict=False, policy=policy)
     serializer = NativeFuncMethodSerializer(fory.type_resolver, type(policy_global_function))
     read_context = FakeReadContext(policy, ["policy_global_bound_method", True, __name__])
 
@@ -802,7 +802,7 @@ def validate_function(self, func, is_local, **kwargs):
     policy_global_function.__module__ = "__main__"
     try:
         policy = CaptureFunctionPolicy()
-        fory = Fory(ref=True, strict=False, policy=policy)
+        fory = Fory(xlang=False, ref=True, strict=False, policy=policy)
         serializer = NativeFuncMethodSerializer(fory.type_resolver, type(policy_global_function))
         read_context = FakeReadContext(policy, ["policy_global_function", True, __name__])
 
@@ -826,7 +826,7 @@ def validate_module(self, module_name, **kwargs):
             return None
 
     policy = BlockModulePolicy()
-    fory = Fory(ref=True, strict=False, policy=policy)
+    fory = Fory(xlang=False, ref=True, strict=False, policy=policy)
     with pytest.raises(ValueError, match="function module blocked"):
         fory.deserialize(fory.serialize(policy_global_function))
     assert policy.validate_module_calls == 1
@@ -849,7 +849,7 @@ def validate_module(self, module_name, **kwargs):
             return None
 
     policy = BlockModulePolicy()
-    fory = Fory(ref=True, strict=False, policy=policy)
+    fory = Fory(xlang=False, ref=True, strict=False, policy=policy)
     with pytest.raises(ValueError, match="local function module blocked"):
         fory.deserialize(fory.serialize(local_function))
     assert policy.validate_module_calls == 1
@@ -870,7 +870,7 @@ def validate_module(self, module_name, **kwargs):
             return None
 
     policy = BlockModulePolicy()
-    fory = Fory(ref=True, strict=False, policy=policy)
+    fory = Fory(xlang=False, ref=True, strict=False, policy=policy)
     with pytest.raises(ValueError, match="time blocked"):
         fory.deserialize(fory.serialize(time.time))
     assert policy.validate_module_calls == 1
@@ -890,7 +890,7 @@ def validate_module(self, module_name, **kwargs):
             return None
 
     policy = BlockModulePolicy()
-    fory = Fory(ref=True, strict=False, policy=policy)
+    fory = Fory(xlang=False, ref=True, strict=False, policy=policy)
     from pyfory.registry import SharedRegistry, TypeResolver
 
     resolver = TypeResolver(fory.config, shared_registry=SharedRegistry())
@@ -918,7 +918,7 @@ def validate_class(self, cls, is_local, **kwargs):
             return None
 
     policy = BlockClassPolicy()
-    fory = Fory(ref=True, strict=False, policy=policy)
+    fory = Fory(xlang=False, ref=True, strict=False, policy=policy)
     from pyfory.registry import SharedRegistry, TypeResolver
 
     resolver = TypeResolver(fory.config, shared_registry=SharedRegistry())
@@ -950,7 +950,7 @@ def validate_module(self, module_name, **kwargs):
             return None
 
     policy = BlockModulePolicy()
-    fory = Fory(ref=True, strict=False, policy=policy)
+    fory = Fory(xlang=False, ref=True, strict=False, policy=policy)
     with pytest.raises(ValueError, match="subprocess blocked"):
         fory.deserialize(fory.serialize(GlobalNamePayload()))
     assert policy.validate_module_calls == 1
@@ -979,7 +979,7 @@ def validate_class(self, cls, is_local, **kwargs):
             return None
 
     policy = BlockClassPolicy()
-    fory = Fory(ref=True, strict=False, policy=policy)
+    fory = Fory(xlang=False, ref=True, strict=False, policy=policy)
     with pytest.raises(ValueError, match="subprocess.Popen blocked"):
         fory.deserialize(fory.serialize(GlobalNamePayload()))
     assert policy.validate_module_calls == 1
@@ -1009,7 +1009,7 @@ def validate_function(self, func, is_local, **kwargs):
             return None
 
     policy = BlockFunctionPolicy()
-    fory = Fory(ref=True, strict=False, policy=policy)
+    fory = Fory(xlang=False, ref=True, strict=False, policy=policy)
     with pytest.raises(ValueError, match="eval blocked"):
         fory.deserialize(fory.serialize(GlobalNamePayload()))
     assert policy.validate_module_calls == 1
@@ -1042,7 +1042,7 @@ def validate_function(self, func, is_local, **kwargs):
             return None
 
     policy = MethodPolicy()
-    fory = Fory(ref=True, strict=False, policy=policy)
+    fory = Fory(xlang=False, ref=True, strict=False, policy=policy)
     with pytest.raises(ValueError, match="method blocked"):
         fory.deserialize(fory.serialize(GlobalNamePayload()))
     assert policy.validate_module_calls == 1
diff --git a/python/pyfory/tests/test_serializer.py b/python/pyfory/tests/test_serializer.py
index 3afe47cc52..1a84da1461 100644
--- a/python/pyfory/tests/test_serializer.py
+++ b/python/pyfory/tests/test_serializer.py
@@ -455,8 +455,8 @@ def test_timestamp_serializer(xlang):
 def test_ref_tracking(xlang):
     fory = Fory(xlang=xlang, ref=True)
 
-    # Circular reference test - only works for Python language mode
-    # XLANG mode doesn't support true circular references during deserialization
+    # Circular reference test - only works for Python native mode.
+    # Xlang mode doesn't support true circular references during deserialization
     # because the object must be registered after it's fully constructed
     if not xlang:
         simple_list = []
@@ -477,7 +477,7 @@ def test_ref_tracking(xlang):
         "dict2_0": dict2,
         "dict2_1": dict2,
     }
-    # Circular reference in dict3 - only works for Python language mode
+    # Circular reference in dict3 - only works for Python native mode
     if not xlang:
         dict3["dict3_0"] = dict3
         dict3["dict3_1"] = dict3
diff --git a/python/pyfory/tests/test_size_guardrails.py b/python/pyfory/tests/test_size_guardrails.py
index 9b17610eef..c33d9ce133 100644
--- a/python/pyfory/tests/test_size_guardrails.py
+++ b/python/pyfory/tests/test_size_guardrails.py
@@ -101,7 +101,12 @@ def test_tuple_limit(self, ref, data, limit, should_fail):
             assert roundtrip(data, limit, xlang=False, ref=ref) == data
 
     def test_default_limit_is_one_million(self):
-        assert Fory().max_collection_size == 1_000_000
+        assert (
+            Fory(
+                xlang=False,
+            ).max_collection_size
+            == 1_000_000
+        )
 
     def test_dataclass_list_field_exceeds_limit(self):
         @dataclass
@@ -119,8 +124,8 @@ class Container:
     def test_object_field_count_exceeds_limit(self):
         obj = ObjectPayload()
         obj.value = 1
-        writer = Fory(ref=True, strict=False)
-        reader = Fory(ref=True, strict=False, max_collection_size=0)
+        writer = Fory(xlang=False, ref=True, strict=False)
+        reader = Fory(xlang=False, ref=True, strict=False, max_collection_size=0)
         writer.register(ObjectPayload)
         reader.register(ObjectPayload)
 
@@ -134,8 +139,8 @@ class LocalPayload:
 
             return LocalPayload
 
-        writer = Fory(ref=True, strict=False)
-        reader = Fory(ref=True, strict=False, max_collection_size=0)
+        writer = Fory(xlang=False, ref=True, strict=False)
+        reader = Fory(xlang=False, ref=True, strict=False, max_collection_size=0)
 
         with pytest.raises(ValueError, match="local class base size 1 exceeds"):
             reader.deserialize(writer.serialize(make_local_class()))
@@ -144,8 +149,8 @@ def test_local_function_defaults_exceed_limit(self):
         def local_function(value=1):
             return value
 
-        writer = Fory(ref=True, strict=False)
-        reader = Fory(ref=True, strict=False, max_collection_size=0)
+        writer = Fory(xlang=False, ref=True, strict=False)
+        reader = Fory(xlang=False, ref=True, strict=False, max_collection_size=0)
 
         with pytest.raises(ValueError, match="function default size 1 exceeds"):
             reader.deserialize(writer.serialize(local_function))
@@ -153,8 +158,8 @@ def local_function(value=1):
     def test_object_ndarray_length_exceeds_limit(self):
         np = pytest.importorskip("numpy")
         arr = np.array([object(), object()], dtype=object)
-        writer = Fory(ref=True, strict=False)
-        reader = Fory(ref=True, strict=False, max_collection_size=1)
+        writer = Fory(xlang=False, ref=True, strict=False)
+        reader = Fory(xlang=False, ref=True, strict=False, max_collection_size=1)
 
         with pytest.raises(ValueError, match="ndarray object size 2 exceeds"):
             reader.deserialize(writer.serialize(arr))
@@ -164,7 +169,12 @@ class TestBinarySizeLimit:
     """Binary reads are guarded by max_binary_size on the Buffer."""
 
     def test_default_limit_is_64mib(self):
-        assert Fory().max_binary_size == 64 * 1024 * 1024
+        assert (
+            Fory(
+                xlang=False,
+            ).max_binary_size
+            == 64 * 1024 * 1024
+        )
 
     @pytest.mark.parametrize("xlang", [False, True])
     def test_within_limit_succeeds(self, xlang):
@@ -185,17 +195,19 @@ def test_string_exceeds_limit_fails(self, xlang):
     def test_from_stream_respects_limit(self):
         import io
 
-        payload = Fory().serialize(b"x" * 200)
+        payload = Fory(
+            xlang=False,
+        ).serialize(b"x" * 200)
         buf = Buffer.from_stream(io.BytesIO(payload), max_binary_size=100)
         with pytest.raises(ValueError, match="exceeds the configured limit"):
-            Fory(max_binary_size=100).deserialize(buf)
+            Fory(xlang=False, max_binary_size=100).deserialize(buf)
 
     def test_in_band_buffer_object_respects_limit(self):
         payload = b"x" * 200
-        data = Fory(ref=True).serialize(payload, buffer_callback=lambda _buffer: True)
+        data = Fory(xlang=False, ref=True).serialize(payload, buffer_callback=lambda _buffer: True)
 
         with pytest.raises(ValueError, match="exceeds the configured limit"):
-            Fory(ref=True, max_binary_size=100).deserialize(data, buffers=[])
+            Fory(xlang=False, ref=True, max_binary_size=100).deserialize(data, buffers=[])
 
     def test_malformed_metastring_ref_raises_value_error(self):
         payload = bytes([1, 255, TypeId.NAMED_STRUCT, 3])
diff --git a/python/pyfory/tests/test_thread_safe.py b/python/pyfory/tests/test_thread_safe.py
index 1894fd25af..f9ec5a7418 100644
--- a/python/pyfory/tests/test_thread_safe.py
+++ b/python/pyfory/tests/test_thread_safe.py
@@ -35,7 +35,9 @@ class Address:
 
 
 def test_thread_safe_fory_basic_serialization():
-    fory = ThreadSafeFory()
+    fory = ThreadSafeFory(
+        xlang=False,
+    )
     fory.register(Person)
 
     person = Person(name="Alice", age=30)
@@ -47,7 +49,9 @@ def test_thread_safe_fory_basic_serialization():
 
 
 def test_thread_safe_fory_multiple_threads():
-    fory = ThreadSafeFory()
+    fory = ThreadSafeFory(
+        xlang=False,
+    )
     fory.register(Person)
 
     results = []
@@ -80,7 +84,9 @@ def serialize_deserialize(thread_id):
 
 
 def test_thread_safe_fory_registration():
-    fory = ThreadSafeFory()
+    fory = ThreadSafeFory(
+        xlang=False,
+    )
     fory.register(Person, type_id=100)
     fory.register(Address, namespace="test", typename="Address")
 
@@ -108,7 +114,9 @@ def test_thread_safe_fory_xlang_mode():
 
 
 def test_thread_safe_fory_dumps_loads():
-    fory = ThreadSafeFory()
+    fory = ThreadSafeFory(
+        xlang=False,
+    )
     fory.register(Person)
 
     person = Person(name="Dave", age=40)
@@ -120,7 +128,7 @@ def test_thread_safe_fory_dumps_loads():
 
 
 def test_thread_safe_fory_ref_tracking():
-    fory = ThreadSafeFory(ref=True)
+    fory = ThreadSafeFory(xlang=False, ref=True)
     fory.register(Person)
 
     person = Person(name="Eve", age=28)
@@ -134,7 +142,9 @@ def test_thread_safe_fory_ref_tracking():
 
 
 def test_thread_safe_fory_cross_thread_registration():
-    fory = ThreadSafeFory()
+    fory = ThreadSafeFory(
+        xlang=False,
+    )
     fory.register(Person)
     fory.register(Address)
 
@@ -164,7 +174,9 @@ def serialize_data(thread_id):
 
 
 def test_thread_safe_fory_register_after_use():
-    fory = ThreadSafeFory()
+    fory = ThreadSafeFory(
+        xlang=False,
+    )
     fory.register(Person)
 
     person = Person(name="Alice", age=30)
diff --git a/python/pyfory/tests/test_union.py b/python/pyfory/tests/test_union.py
index f56cb17637..273e099dfb 100644
--- a/python/pyfory/tests/test_union.py
+++ b/python/pyfory/tests/test_union.py
@@ -23,7 +23,9 @@
 
 def test_union_basic_types():
     """Test Union with basic types like int and str"""
-    fory = Fory()
+    fory = Fory(
+        xlang=False,
+    )
 
     # Test with int value
     value_int: Union[int, str] = 42
@@ -42,7 +44,9 @@ def test_union_basic_types():
 
 def test_union_multiple_types():
     """Test Union with more than two types"""
-    fory = Fory()
+    fory = Fory(
+        xlang=False,
+    )
 
     # Test with int
     value1: Union[int, str, float] = 123
@@ -68,7 +72,9 @@ def test_union_multiple_types():
 
 def test_union_with_collections():
     """Test Union with collection types"""
-    fory = Fory()
+    fory = Fory(
+        xlang=False,
+    )
 
     # Test with list
     value_list: Union[list, dict] = [1, 2, 3]
@@ -87,7 +93,7 @@ def test_union_with_collections():
 
 def test_union_with_optional():
     """Test Union with Optional (Union[T, None])"""
-    fory = Fory(ref=True)
+    fory = Fory(xlang=False, ref=True)
 
     # Test with non-None value
     value: Union[int, None] = 42
@@ -115,7 +121,9 @@ class Company:
         name: str
         employees: int
 
-    fory = Fory()
+    fory = Fory(
+        xlang=False,
+    )
     fory.register(Person)
     fory.register(Company)
 
@@ -144,7 +152,9 @@ class Container:
         value: Union[int, str]
         name: str
 
-    fory = Fory()
+    fory = Fory(
+        xlang=False,
+    )
     fory.register(Container)
 
     # Test with int value
@@ -166,7 +176,9 @@ class Container:
 
 def test_union_with_bytes():
     """Test Union with bytes type"""
-    fory = Fory()
+    fory = Fory(
+        xlang=False,
+    )
 
     # Test with bytes
     value_bytes: Union[bytes, str] = b"hello"
diff --git a/rust/README.md b/rust/README.md
index 7714e22a24..0d35246b22 100644
--- a/rust/README.md
+++ b/rust/README.md
@@ -6,20 +6,20 @@
 
 **Apache Fory™** is a blazing fast multi-language serialization framework powered by **JIT compilation** and **zero-copy** techniques, providing up to **ultra-fast performance** while maintaining ease of use and safety.
 
-The Rust implementation provides versatile and high-performance serialization with automatic memory management and compile-time type safety.
+The Rust implementation provides versatile and high-performance serialization with automatic memory management and compile-time type safety. It defaults to xlang mode for cross-language payloads; use native mode with `.xlang(false)` for Rust-only traffic when you need Rust-specific object features such as trait objects and shared-reference patterns.
 
-## 🚀 Why Apache Fory™ Rust?
+## Why Apache Fory™ Rust?
 
-- **🔥 Blazingly Fast**: Zero-copy deserialization and optimized binary protocols
-- **🌍 Cross-Language**: Seamlessly serialize/deserialize data across Java, Python, C++, Go, JavaScript, and Rust
-- **🎯 Type-Safe**: Compile-time type checking with derive macros
-- **🔄 Circular References**: Automatic tracking of shared and circular references with `Rc`/`Arc` and weak pointers
-- **🧬 Polymorphic**: Serialize trait objects with `Box`, `Rc`, and `Arc`
-- **📦 Schema Evolution**: Compatible mode for independent schema changes
-- **🔢 Reduced-Precision Types**: `Float16` and `BFloat16` scalars with `Vec` / `Vec` arrays
-- **⚡ Two Formats**: Object graph serialization and zero-copy row-based format
+- **Blazingly Fast**: Zero-copy deserialization and optimized binary protocols
+- **Cross-Language**: Seamlessly serialize/deserialize data across Java, Python, C++, Go, JavaScript, and Rust
+- **Type-Safe**: Compile-time type checking with derive macros
+- **Circular References**: Automatic tracking of shared and circular references with `Rc`/`Arc` and weak pointers
+- **Polymorphic**: Serialize trait objects with `Box`, `Rc`, and `Arc`
+- **Schema Evolution**: Compatible mode for independent schema changes
+- **Reduced-Precision Types**: `Float16` and `BFloat16` scalars with `Vec` / `Vec` arrays
+- **Two Formats**: Object graph serialization and zero-copy row-based format
 
-## 📦 Crates
+## Crates
 
 | Crate                                                                       | Description                       | Version                                                                                               |
 | --------------------------------------------------------------------------- | --------------------------------- | ----------------------------------------------------------------------------------------------------- |
@@ -27,7 +27,7 @@ The Rust implementation provides versatile and high-performance serialization wi
 | [`fory-core`](https://github.com/apache/fory/blob/main/rust/fory-core/)     | Core serialization engine         | [![crates.io](https://img.shields.io/crates/v/fory-core.svg)](https://crates.io/crates/fory-core)     |
 | [`fory-derive`](https://github.com/apache/fory/blob/main/rust/fory-derive/) | Procedural macros                 | [![crates.io](https://img.shields.io/crates/v/fory-derive.svg)](https://crates.io/crates/fory-derive) |
 
-## 🏃 Quick Start
+## Quick Start
 
 Add Apache Fory™ to your `Cargo.toml`:
 
@@ -50,7 +50,7 @@ struct User {
 }
 
 fn main() -> Result<(), Error> {
-    let mut fory = Fory::default();
+    let mut fory = Fory::builder().xlang(true).build();
     fory.register::(1)?;
 
     let user = User {
@@ -76,7 +76,7 @@ fn main() -> Result<(), Error> {
 }
 ```
 
-## 📚 Core Features
+## Core Features
 
 ### 1. Object Graph Serialization
 
@@ -111,9 +111,9 @@ struct Address {
     country: String,
 }
 
-let mut fory = Fory::default();
-fory.register::
(100); -fory.register::(200); +let mut fory = Fory::builder().xlang(true).build(); +fory.register_by_name::
("example", "Address").unwrap(); +fory.register_by_name::("example", "Person").unwrap(); let person = Person { name: "John Doe".to_string(), @@ -129,12 +129,12 @@ let person = Person { ]), }; -let bytes = fory.serialize(&person); +let bytes = fory.serialize(&person).unwrap(); let decoded: Person = fory.deserialize(&bytes)?; assert_eq!(person, decoded); ``` -### 2. Shared and Circular References +### 2. Native-Mode Shared and Circular References Apache Fory™ automatically tracks and preserves reference identity for shared objects using `Rc` and `Arc`. When the same object is referenced multiple times, Fory serializes it only once and uses reference IDs for subsequent occurrences. This ensures: @@ -142,13 +142,17 @@ Apache Fory™ automatically tracks and preserves reference identity for shared - **Reference identity preservation**: Deserialized objects maintain the same sharing relationships - **Circular reference support**: Use `RcWeak` and `ArcWeak` to break cycles +The examples in this section use native mode because `Rc`, `Arc`, and weak-pointer identity are +Rust object-graph features. Native mode stays on Rust's native type system instead of limiting the +payload to portable xlang mappings. + #### Shared References with Rc/Arc ```rust use fory::Fory; use std::rc::Rc; -let fory = Fory::default(); +let fory = Fory::builder().xlang(false).build(); // Create a shared value let shared = Rc::new(String::from("shared_value")); @@ -157,7 +161,7 @@ let shared = Rc::new(String::from("shared_value")); let data = vec![shared.clone(), shared.clone(), shared.clone()]; // The shared value is serialized only once -let bytes = fory.serialize(&data); +let bytes = fory.serialize(&data)?; let decoded: Vec> = fory.deserialize(&bytes)?; // Verify reference identity is preserved @@ -196,8 +200,8 @@ struct Node { children: Vec>>, } -let mut fory = Fory::default(); -fory.register::(2000); +let mut fory = Fory::builder().xlang(false).build(); +fory.register::(2000)?; // Build a parent-child tree let parent = Rc::new(RefCell::new(Node { @@ -222,7 +226,7 @@ parent.borrow_mut().children.push(child1.clone()); parent.borrow_mut().children.push(child2.clone()); // Serialize and deserialize the circular structure -let bytes = fory.serialize(&parent); +let bytes = fory.serialize(&parent)?; let decoded: Rc> = fory.deserialize(&bytes)?; // Verify the circular relationship @@ -233,10 +237,12 @@ for child in &decoded.borrow().children { } ``` -### 3. Trait Object Serialization +### 3. Native-Mode Trait Object Serialization Apache Fory™ supports polymorphic serialization through trait objects, enabling dynamic dispatch and type flexibility. This is essential for plugin systems, heterogeneous collections, and extensible architectures. +The examples in this section use native mode because Rust trait objects and `dyn Any` dispatch are Rust runtime features. + **Supported trait object types:** - `Box` - Owned trait objects @@ -281,10 +287,10 @@ struct Zoo { star_animal: Box, } -let mut fory = Fory::builder().compatible(true).build(); -fory.register::(100); -fory.register::(101); -fory.register::(102); +let mut fory = Fory::builder().xlang(false).compatible(true).build(); +fory.register::(100)?; +fory.register::(101)?; +fory.register::(102)?; let zoo = Zoo { star_animal: Box::new(Dog { @@ -293,7 +299,7 @@ let zoo = Zoo { }), }; -let bytes = fory.serialize(&zoo); +let bytes = fory.serialize(&zoo)?; let decoded: Zoo = fory.deserialize(&bytes)?; assert_eq!(decoded.star_animal.name(), "Buddy"); @@ -302,7 +308,7 @@ assert_eq!(decoded.star_animal.speak(), "Woof!"); ### 4. Schema Evolution -Apache Fory™ supports schema evolution in **Compatible mode**, allowing serialization and deserialization peers to have different type definitions. This enables independent evolution of services in distributed systems without breaking compatibility. +Apache Fory™ supports schema evolution in **Compatible mode**, allowing serialization and deserialization peers to have different type definitions. Xlang mode uses compatible schema evolution by default. In native mode, add `.compatible(true)` when Rust-only payloads need independent schema evolution. **Features:** @@ -340,11 +346,11 @@ struct PersonV2 { metadata: HashMap, } -let mut fory1 = Fory::builder().compatible(true).build(); -fory1.register::(1); +let mut fory1 = Fory::builder().xlang(true).compatible(true).build(); +fory1.register_by_name::("example", "Person").unwrap(); -let mut fory2 = Fory::builder().compatible(true).build(); -fory2.register::(1); +let mut fory2 = Fory::builder().xlang(true).compatible(true).build(); +fory2.register_by_name::("example", "Person").unwrap(); let person_v1 = PersonV1 { name: "Alice".to_string(), @@ -353,7 +359,7 @@ let person_v1 = PersonV1 { }; // Serialize with V1 -let bytes = fory1.serialize(&person_v1); +let bytes = fory1.serialize(&person_v1).unwrap(); // Deserialize with V2 - missing fields get default values let person_v2: PersonV2 = fory2.deserialize(&bytes)?; @@ -362,7 +368,7 @@ assert_eq!(person_v2.age, 30); assert_eq!(person_v2.phone, None); ``` -### 5. Enum Support +### 5. Native-Mode Enum Support Apache Fory™ supports three types of enum variants with full schema evolution in Compatible mode: @@ -392,7 +398,7 @@ enum Value { Object { name: String, value: i32 }, } -let mut fory = Fory::default(); +let mut fory = Fory::builder().xlang(false).build(); fory.register::(1)?; let value = Value::Object { name: "score".to_string(), value: 100 }; @@ -414,9 +420,9 @@ assert_eq!(value, decoded); - Named variants provide better evolution than unnamed - Use compatible mode for cross-version communication -### 6. Tuple Support +### 6. Native-Mode Tuple Support -Apache Fory™ supports tuples up to 22 elements out of the box with efficient serialization in both compatible and non-compatible modes. +Apache Fory™ supports tuples up to 22 elements out of the box with efficient serialization in both compatible and schema-consistent modes. **Features:** @@ -424,15 +430,15 @@ Apache Fory™ supports tuples up to 22 elements out of the box with efficient s - Heterogeneous type support (each element can be a different type) - Schema evolution in Compatible mode (handles missing/extra elements) -**Serialization modes:** +**Schema modes:** -1. **Non-compatible mode**: Serializes elements sequentially without collection headers for minimal overhead +1. **Schema-consistent mode**: Serializes elements sequentially without collection headers for minimal overhead 2. **Compatible mode**: Uses collection protocol with type metadata for schema evolution ```rust use fory::{Fory, Error}; -let mut fory = Fory::default(); +let mut fory = Fory::builder().xlang(false).build(); // Tuple with heterogeneous types let data: (i32, String, bool, Vec) = ( @@ -447,13 +453,13 @@ let decoded: (i32, String, bool, Vec) = fory.deserialize(&bytes)?; assert_eq!(data, decoded); ``` -### 7. Custom Serializers +### 7. Native-Mode Custom Serializers For types that don't support `#[derive(ForyStruct)]`, implement the `Serializer` trait manually. This is useful for: - External types from other crates - Types with special serialization requirements -- Legacy data format compatibility +- Existing data format compatibility - Performance-critical custom encoding ```rust @@ -495,14 +501,14 @@ impl ForyDefault for CustomType { } } -let mut fory = Fory::default(); -fory.register_serializer::(100); +let mut fory = Fory::builder().xlang(false).build(); +fory.register_serializer::(100)?; let custom = CustomType { value: 42, name: "test".to_string(), }; -let bytes = fory.serialize(&custom); +let bytes = fory.serialize(&custom)?; let decoded: CustomType = fory.deserialize(&bytes)?; assert_eq!(custom, decoded); ``` @@ -562,7 +568,7 @@ let profile = UserProfile { }; // Serialize to row format -let row_data = to_row(&profile); +let row_data = to_row(&profile).unwrap(); // Zero-copy deserialization - no object allocation! let row = from_row::(&row_data); @@ -576,13 +582,13 @@ assert_eq!(row.is_active(), true); // Access collections efficiently let scores = row.scores(); assert_eq!(scores.size(), 4); -assert_eq!(scores.get(0), 95); -assert_eq!(scores.get(1), 87); +assert_eq!(scores.get(0).unwrap(), 95); +assert_eq!(scores.get(1).unwrap(), 87); let prefs = row.preferences(); assert_eq!(prefs.keys().size(), 2); -assert_eq!(prefs.keys().get(0), "language"); -assert_eq!(prefs.values().get(0), "en"); +assert_eq!(prefs.keys().get(0).unwrap(), "language"); +assert_eq!(prefs.values().get(0).unwrap(), "en"); ``` **Performance comparison:** @@ -594,28 +600,26 @@ assert_eq!(prefs.values().get(0), "en"); | Memory usage | Full object graph in memory | Only accessed fields in memory | | Suitable for | Small objects, full access | Large objects, selective access | -## 🌍 Cross-Language Serialization +## Cross-Language Serialization Apache Fory™ supports seamless data exchange across multiple languages: ```rust use fory::Fory; -// Enable cross-language mode -let mut fory = Fory::builder() - .compatible(true) - .xlang(true).build(); +// Use xlang mode, the Rust default. +let mut fory = Fory::builder().xlang(true).build(); // Register types with consistent IDs across languages -fory.register::(100); +fory.register::(100)?; // Or use name-based registration -fory.register_by_name::("com.example", "MyStruct"); +fory.register_by_name::("com.example", "MyStruct")?; ``` See [xlang_type_mapping.md](https://fory.apache.org/docs/specification/xlang_type_mapping) for type mapping across languages. -## ⚡ Performance +## Performance Apache Fory™ Rust is designed for maximum performance: @@ -632,15 +636,15 @@ cd benchmarks/rust cargo bench ``` -## 📖 Documentation +## Documentation -- **[User Guide](https://fory.apache.org/docs/next/docs/guide/rust)** - Comprehensive User documents +- **[User Guide](https://fory.apache.org/docs/guide/rust/)** - Comprehensive user documentation - **[API Documentation](https://docs.rs/fory)** - Complete API reference -- **[Protocol Specification](https://fory.apache.org/docs/specification/fory_xlang_serialization_spec)** - Serialization protocol details +- **[Protocol Specification](https://fory.apache.org/docs/specification/xlang_serialization_spec)** - Serialization protocol details - **[Type Mapping](https://fory.apache.org/docs/specification/xlang_type_mapping)** - Cross-language type mappings - **[Source](https://github.com/apache/fory/tree/main/docs/guide/rust)** - Source code for doc -## 🎯 Use Cases +## Use Cases ### Object Serialization @@ -658,7 +662,7 @@ cargo bench - Real-time data streaming applications - Zero-copy scenarios -## 🛠️ Development +## Development ### Building @@ -690,15 +694,15 @@ cargo fmt --check cargo clippy --all-targets --all-features -- -D warnings ``` -## 📄 License +## License Licensed under the Apache License, Version 2.0. See [LICENSE](https://github.com/apache/fory/blob/main/LICENSE) for details. -## 🤝 Contributing +## Contributing We welcome contributions! Please see our [Contributing Guide](https://github.com/apache/fory/blob/main/CONTRIBUTING.md) for details. -## 📞 Support +## Support - **Documentation**: [docs.rs/fory](https://docs.rs/fory) - **Issues**: [GitHub Issues](https://github.com/apache/fory/issues) diff --git a/rust/fory-core/src/config.rs b/rust/fory-core/src/config.rs index c0f4a4328d..b46588576d 100644 --- a/rust/fory-core/src/config.rs +++ b/rust/fory-core/src/config.rs @@ -24,7 +24,7 @@ pub struct Config { /// Whether compatible mode is enabled for schema evolution support. pub compatible: bool, - /// Whether cross-language serialization is enabled. + /// Whether xlang mode is enabled. pub xlang: bool, /// Whether metadata sharing is enabled. pub share_meta: bool, @@ -52,7 +52,7 @@ impl Default for Config { fn default() -> Self { Config { compatible: false, - xlang: false, + xlang: true, share_meta: false, compress_string: false, check_string_read: true, @@ -77,7 +77,7 @@ impl Config { self.compatible } - /// Check if cross-language mode is enabled. + /// Check if xlang mode is enabled. #[inline(always)] pub fn is_xlang(&self) -> bool { self.xlang diff --git a/rust/fory-core/src/context.rs b/rust/fory-core/src/context.rs index 889d279659..ce2db2c135 100644 --- a/rust/fory-core/src/context.rs +++ b/rust/fory-core/src/context.rs @@ -197,7 +197,7 @@ impl<'a> WriteContext<'a> { self.compress_string } - /// Check if cross-language mode is enabled + /// Check if xlang mode is enabled #[inline(always)] pub fn is_xlang(&self) -> bool { self.xlang @@ -416,7 +416,7 @@ impl<'a> ReadContext<'a> { self.share_meta } - /// Check if cross-language mode is enabled + /// Check if xlang mode is enabled #[inline(always)] pub fn is_xlang(&self) -> bool { self.xlang diff --git a/rust/fory-core/src/error.rs b/rust/fory-core/src/error.rs index 07eb44e688..c12a9f7517 100644 --- a/rust/fory-core/src/error.rs +++ b/rust/fory-core/src/error.rs @@ -75,12 +75,12 @@ pub const fn should_panic_on_error() -> bool { /// ```rust /// use fory_core::error::Error; /// -/// // ✅ CORRECT: Use static functions +/// // CORRECT: Use static functions /// let err = Error::type_error("Expected string type"); /// let err = Error::invalid_data(format!("Invalid value: {}", 42)); /// let err = Error::type_mismatch(1, 2); /// -/// // ❌ WRONG: Do not construct directly +/// // WRONG: Do not construct directly /// // let err = Error::TypeError("Expected string type".into()); /// // let err = Error::InvalidData(format!("Invalid value: {}", 42).into()); /// ``` diff --git a/rust/fory-core/src/fory.rs b/rust/fory-core/src/fory.rs index 6ff698d59a..2b9b7f6214 100644 --- a/rust/fory-core/src/fory.rs +++ b/rust/fory-core/src/fory.rs @@ -53,7 +53,7 @@ thread_local! { /// use fory_core::Fory; /// /// let fory = Fory::builder() -/// .compatible(true) +/// .xlang(true) /// .compress_string(true) /// .max_dyn_depth(10) /// .build(); @@ -72,7 +72,7 @@ impl ForyBuilder { /// * `compatible` - The serialization compatible mode to use. Options are: /// - `false`: Schema must be consistent between serialization and deserialization. /// No metadata is shared. This is the fastest mode. - /// - true`: Supports schema evolution and type metadata sharing for better + /// - `true`: Supports schema evolution and type metadata sharing for better /// cross-version compatibility. /// /// # Returns @@ -83,14 +83,14 @@ impl ForyBuilder { /// /// Setting the compatible mode also automatically configures the `share_meta` flag: /// - `false` → `share_meta = false` - /// - true` → `share_meta = true` + /// - `true` → `share_meta = true` /// /// # Examples /// /// ```rust /// use fory_core::Fory; /// - /// let fory = Fory::builder().compatible(true).build(); + /// let fory = Fory::builder().xlang(true).compatible(true).build(); /// ``` pub fn compatible(mut self, compatible: bool) -> Self { self.compatible_set = true; @@ -105,13 +105,12 @@ impl ForyBuilder { self } - /// Enables or disables cross-language serialization protocol. + /// Enables or disables xlang mode. /// /// # Arguments /// - /// * `xlang` - If `true`, uses the cross-language serialization format compatible with - /// other Fory implementations (Java, Python, C++, etc.). If `false`, uses a Rust-only - /// optimized format. + /// * `xlang` - If `true`, uses the xlang wire format compatible with other Fory + /// implementations (Java, Python, C++, etc.). If `false`, uses Rust native mode. /// /// # Returns /// @@ -119,18 +118,17 @@ impl ForyBuilder { /// /// # Default /// - /// The default value is `false`. + /// The default value is `true`. /// /// # Examples /// /// ```rust /// use fory_core::Fory; /// - /// // For cross-language use (default) - /// let fory = Fory::builder().xlang(true).compatible(true).build(); + /// // Xlang mode, the default cross-language wire format + /// let fory = Fory::builder().xlang(true).build(); /// - /// // For Rust-only optimization, this mode is faster and more compact since it avoids - /// // cross-language metadata and type system costs. + /// // Native mode for Rust-only traffic /// let fory = Fory::builder().xlang(false).build(); /// ``` pub fn xlang(mut self, xlang: bool) -> Self { @@ -173,7 +171,7 @@ impl ForyBuilder { /// ```rust /// use fory_core::Fory; /// - /// let fory = Fory::builder().compress_string(true).build(); + /// let fory = Fory::builder().xlang(true).compress_string(true).build(); /// ``` pub fn compress_string(mut self, compress_string: bool) -> Self { self.config.compress_string = compress_string; @@ -221,7 +219,7 @@ impl ForyBuilder { /// ```rust /// use fory_core::Fory; /// - /// let fory = Fory::builder() + /// let fory = Fory::builder().xlang(false) /// .compatible(false) /// .check_struct_version(true) /// .build(); @@ -256,7 +254,7 @@ impl ForyBuilder { /// ```rust /// use fory_core::Fory; /// - /// let fory = Fory::builder().track_ref(true).build(); + /// let fory = Fory::builder().xlang(true).track_ref(true).build(); /// ``` pub fn track_ref(mut self, track_ref: bool) -> Self { self.config.track_ref = track_ref; @@ -290,10 +288,10 @@ impl ForyBuilder { /// use fory_core::Fory; /// /// // Allow deeper nesting for complex object graphs - /// let fory = Fory::builder().max_dyn_depth(10).build(); + /// let fory = Fory::builder().xlang(true).max_dyn_depth(10).build(); /// /// // Restrict nesting for safer deserialization - /// let fory = Fory::builder().max_dyn_depth(3).build(); + /// let fory = Fory::builder().xlang(true).max_dyn_depth(3).build(); /// ``` pub fn max_dyn_depth(mut self, max_dyn_depth: u32) -> Self { self.config.max_dyn_depth = max_dyn_depth; @@ -322,7 +320,7 @@ impl ForyBuilder { /// use fory_core::Fory; /// /// // Limit binary payloads to 1 MB - /// let fory = Fory::builder().max_binary_size(1024 * 1024).build(); + /// let fory = Fory::builder().xlang(true).max_binary_size(1024 * 1024).build(); /// ``` pub fn max_binary_size(mut self, max_binary_size: u32) -> Self { self.config.max_binary_size = max_binary_size; @@ -352,7 +350,7 @@ impl ForyBuilder { /// use fory_core::Fory; /// /// // Limit collections to 10000 elements - /// let fory = Fory::builder().max_collection_size(10000).build(); + /// let fory = Fory::builder().xlang(true).max_collection_size(10000).build(); /// ``` pub fn max_collection_size(mut self, max_collection_size: u32) -> Self { self.config.max_collection_size = max_collection_size; @@ -361,19 +359,26 @@ impl ForyBuilder { /// Builds a [`Fory`] runtime with the current builder configuration. pub fn build(self) -> Fory { - Fory::from_config(self.config) + let mut config = self.config; + if config.xlang && !self.compatible_set { + config.share_meta = true; + config.compatible = true; + config.check_struct_version = false; + } + Fory::from_config(config) } } /// The main Fory serialization framework instance. /// -/// `Fory` provides high-performance cross-language serialization and deserialization -/// capabilities with support for multiple modes, reference tracking, and trait object serialization. +/// `Fory` provides high-performance serialization and deserialization with xlang mode, +/// native mode, reference tracking, and trait object serialization. /// /// # Features /// -/// - **Cross-language serialization**: Serialize data in Rust and deserialize in other languages -/// - **Multiple modes**: Schema-consistent and compatible serialization modes +/// - **Xlang mode**: Default wire format for cross-language payloads +/// - **Native mode**: Rust-only wire format selected with `.xlang(false)` +/// - **Schema evolution**: Compatible and schema-consistent payload choices /// - **Reference tracking**: Handles shared and circular references /// - **Trait object serialization**: Supports serializing polymorphic trait objects /// - **Dynamic depth limiting**: Configurable limit for nested dynamic object serialization @@ -392,9 +397,10 @@ impl ForyBuilder { /// age: u32, /// } /// -/// let fory = Fory::default(); +/// let mut fory = Fory::builder().xlang(true).build(); +/// fory.register_by_name::("example", "User").unwrap(); /// let user = User { name: "Alice".to_string(), age: 30 }; -/// let bytes = fory.serialize(&user); +/// let bytes = fory.serialize(&user).unwrap(); /// let deserialized: User = fory.deserialize(&bytes).unwrap(); /// ``` /// @@ -404,7 +410,7 @@ impl ForyBuilder { /// use fory_core::Fory; /// /// let fory = Fory::builder() -/// .compatible(true) +/// .xlang(true) /// .compress_string(true) /// .max_dyn_depth(10) /// .build(); @@ -443,16 +449,16 @@ impl Fory { } } - /// Returns whether cross-language serialization is enabled. + /// Returns whether xlang mode is enabled. pub fn is_xlang(&self) -> bool { self.config.xlang } - /// Returns the current serialization mode. + /// Returns whether compatible schema evolution is enabled. /// /// # Returns /// - /// `true` if the serialization mode is compatible, `false` otherwise`. + /// `true` if compatible schema evolution is enabled, `false` otherwise. pub fn is_compatible(&self) -> bool { self.config.compatible } @@ -475,7 +481,7 @@ impl Fory { /// /// # Returns /// - /// `true` if metadata sharing is enabled (automatically set based on mode), `false` otherwise. + /// `true` if metadata sharing is enabled, `false` otherwise. pub fn is_share_meta(&self) -> bool { self.config.share_meta } @@ -553,9 +559,10 @@ impl Fory { /// #[derive(ForyStruct)] /// struct Point { x: i32, y: i32 } /// - /// let fory = Fory::default(); + /// let mut fory = Fory::builder().xlang(true).build(); + /// fory.register_by_name::("example", "Point").unwrap(); /// let point = Point { x: 10, y: 20 }; - /// let bytes = fory.serialize(&point); + /// let bytes = fory.serialize(&point).unwrap(); /// ``` pub fn serialize(&self, record: &T) -> Result, Error> { self.with_write_context( @@ -610,7 +617,8 @@ impl Fory { /// y: i32, /// } /// - /// let fory = Fory::default(); + /// let mut fory = Fory::builder().xlang(true).build(); + /// fory.register_by_name::("example", "Point").unwrap(); /// let point = Point { x: 1, y: 2 }; /// /// let mut buf = Vec::new(); @@ -630,7 +638,8 @@ impl Fory { /// y: i32, /// } /// - /// let fory = Fory::default(); + /// let mut fory = Fory::builder().xlang(true).build(); + /// fory.register_by_name::("example", "Point").unwrap(); /// let p1 = Point { x: 1, y: 2 }; /// let p2 = Point { x: -3, y: 4 }; /// @@ -671,7 +680,8 @@ impl Fory { /// y: i32, /// } /// - /// let fory = Fory::default(); + /// let mut fory = Fory::builder().xlang(true).build(); + /// fory.register_by_name::("example", "Point").unwrap(); /// let point = Point { x: 1, y: 2 }; /// /// let mut buf = Vec::with_capacity(1024); @@ -805,8 +815,8 @@ impl Fory { /// #[derive(ForyStruct)] /// struct User { name: String, age: u32 } /// - /// let mut fory = Fory::default(); - /// fory.register::(100); + /// let mut fory = Fory::builder().xlang(true).build(); + /// fory.register::(100).unwrap(); /// ``` pub fn register( &mut self, @@ -827,7 +837,7 @@ impl Fory { self.type_resolver.register_union::(id) } - /// Registers a struct type with a namespace and type name for cross-language serialization. + /// Registers a struct type with a namespace and type name for xlang serialization. /// /// # Type Parameters /// @@ -841,12 +851,15 @@ impl Fory { /// /// # Notes /// - /// This registration method is preferred for cross-language serialization as it uses + /// This registration method is preferred for xlang serialization because it uses /// human-readable type identifiers instead of numeric IDs, which improves compatibility /// across different language implementations. /// /// # Examples /// + /// The example uses xlang mode because name-based registration is the preferred + /// registration style for cross-language payloads. + /// /// ```rust, ignore /// use fory::Fory; /// use fory::{ForyEnum, ForyStruct, ForyUnion}; @@ -854,8 +867,8 @@ impl Fory { /// #[derive(ForyStruct)] /// struct User { name: String, age: u32 } /// - /// let mut fory = Fory::default(); - /// fory.register_by_name::("com.example", "User"); + /// let mut fory = Fory::builder().xlang(true).build(); + /// fory.register_by_name::("com.example", "User").unwrap(); /// ``` pub fn register_by_name( &mut self, @@ -904,8 +917,8 @@ impl Fory { /// ```rust, ignore /// use fory_core::Fory; /// - /// let mut fory = Fory::default(); - /// fory.register_serializer::(200); + /// let mut fory = Fory::builder().xlang(false).build(); + /// fory.register_serializer::(200).unwrap(); /// ``` pub fn register_serializer( &mut self, @@ -929,7 +942,7 @@ impl Fory { /// # Notes /// /// This is the named equivalent of `register_serializer()`, preferred for - /// cross-language serialization scenarios. + /// xlang serialization scenarios. /// pub fn register_serializer_by_name( &mut self, @@ -993,9 +1006,10 @@ impl Fory { /// #[derive(ForyStruct)] /// struct Point { x: i32, y: i32 } /// - /// let fory = Fory::default(); + /// let mut fory = Fory::builder().xlang(true).build(); + /// fory.register_by_name::("example", "Point").unwrap(); /// let point = Point { x: 10, y: 20 }; - /// let bytes = fory.serialize(&point); + /// let bytes = fory.serialize(&point).unwrap(); /// let deserialized: Point = fory.deserialize(&bytes).unwrap(); /// ``` pub fn deserialize(&self, bf: &[u8]) -> Result { @@ -1046,11 +1060,12 @@ impl Fory { /// #[derive(ForyStruct)] /// struct Point { x: i32, y: i32 } /// - /// let fory = Fory::default(); + /// let mut fory = Fory::builder().xlang(true).build(); + /// fory.register_by_name::("example", "Point").unwrap(); /// let point = Point { x: 10, y: 20 }; /// /// let mut buf = Vec::new(); - /// fory.serialize_to(&point, &mut buf).unwrap(); + /// fory.serialize_to(&mut buf, &point).unwrap(); /// /// let mut reader = Reader::new(&buf); /// let deserialized: Point = fory.deserialize_from(&mut reader).unwrap(); diff --git a/rust/fory-core/src/lib.rs b/rust/fory-core/src/lib.rs index 65716af4a7..eff2ed3d43 100644 --- a/rust/fory-core/src/lib.rs +++ b/rust/fory-core/src/lib.rs @@ -39,12 +39,16 @@ //! //! ## Key Concepts //! -//! ### Serialization Modes +//! ### Wire Modes And Schema Evolution //! -//! Fory supports two serialization modes: +//! Fory supports two wire modes: //! -//! - **SchemaConsistent**: Requires exact type matching between peers -//! - **Compatible**: Allows schema evolution with field additions/deletions +//! - **Xlang mode**: the default cross-language wire format, selected with +//! `.xlang(true)` and compatible schema evolution when `compatible` is +//! omitted. +//! - **Native mode**: the Rust-only wire format, selected with `.xlang(false)` +//! and schema-consistent payloads when `compatible` is omitted. Add +//! `.compatible(true)` only when Rust-only deployments need schema evolution. //! //! ### Type System //! @@ -101,16 +105,16 @@ //! } //! //! # fn main() { -//! let mut fory = Fory::builder().compatible(true).build(); -//! fory.register::(100); -//! fory.register::(101); -//! fory.register::(102); +//! let mut fory = Fory::builder().xlang(false).compatible(true).build(); +//! fory.register::(100).unwrap(); +//! fory.register::(101).unwrap(); +//! fory.register::(102).unwrap(); //! //! let zoo = Zoo { //! star_animal: Box::new(Dog { name: "Buddy".to_string() }), //! }; //! -//! let bytes = fory.serialize(&zoo); +//! let bytes = fory.serialize(&zoo).unwrap(); //! let decoded: Zoo = fory.deserialize(&bytes).unwrap(); //! assert_eq!(decoded.star_animal.speak(), "Woof!"); //! # } @@ -149,7 +153,7 @@ //! use std::collections::HashMap; //! //! // Create a Fory instance -//! let mut fory = Fory::builder().compatible(true).build(); +//! let mut fory = Fory::builder().xlang(false).compatible(true).build(); //! //! // Serialize String //! let text = String::from("Hello, Fory!"); diff --git a/rust/fory-core/src/serializer/core.rs b/rust/fory-core/src/serializer/core.rs index b91bc70b98..da4d91010f 100644 --- a/rust/fory-core/src/serializer/core.rs +++ b/rust/fory-core/src/serializer/core.rs @@ -1242,7 +1242,7 @@ pub trait Serializer: 'static { /// including field metadata, schema evolution, and compatibility features. This trait is used /// internally by Fory and automatically implemented by the derive macro. /// -/// # ⚠️ Important: Do NOT Implement This for Custom User Types +/// # Important: Do NOT Implement This for Custom User Types /// /// **User types with custom serialization should NOT implement this trait.** This trait is: /// @@ -1356,7 +1356,7 @@ pub trait StructSerializer: Serializer + 'static { /// * `type_id` - The base type ID /// * `register_by_name` - Whether type was registered by name (vs by hash) /// * `compatible` - Whether compatibility mode is enabled - /// * `xlang` - Whether cross-language mode is enabled + /// * `xlang` - Whether xlang mode is enabled /// /// # Returns /// diff --git a/rust/fory-core/src/serializer/datetime.rs b/rust/fory-core/src/serializer/datetime.rs index efa7572450..dec6eaddba 100644 --- a/rust/fory-core/src/serializer/datetime.rs +++ b/rust/fory-core/src/serializer/datetime.rs @@ -388,7 +388,7 @@ mod tests { #[test] fn test_temporal_carrier_serialization() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let timestamps = [ Timestamp::UNIX_EPOCH, @@ -438,7 +438,7 @@ mod tests { fn test_chrono_temporal_feature_serialization() { use chrono::{DateTime, Duration as ChronoDuration, NaiveDate, NaiveDateTime}; - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let date = NaiveDate::from_ymd_opt(2024, 2, 3).unwrap(); let timestamp = DateTime::from_timestamp(100, 1).unwrap().naive_utc(); let duration = ChronoDuration::nanoseconds(-1); diff --git a/rust/fory-core/src/serializer/trait_object.rs b/rust/fory-core/src/serializer/trait_object.rs index bcba476752..4eb634d4fb 100644 --- a/rust/fory-core/src/serializer/trait_object.rs +++ b/rust/fory-core/src/serializer/trait_object.rs @@ -90,12 +90,12 @@ macro_rules! downcast_and_serialize { /// register_trait_type!(Animal, Dog, Cat); /// /// # fn main() { -/// let mut fory = Fory::builder().compatible(true).build(); -/// fory.register::(100); -/// fory.register::(101); +/// let mut fory = Fory::builder().xlang(false).compatible(true).build(); +/// fory.register::(100).unwrap(); +/// fory.register::(101).unwrap(); /// /// let dog: Box = Box::new(Dog { name: "Rex".to_string() }); -/// let bytes = fory.serialize(&dog); +/// let bytes = fory.serialize(&dog).unwrap(); /// let decoded: Box = fory.deserialize(&bytes).unwrap(); /// assert_eq!(decoded.name(), "Rex"); /// # } diff --git a/rust/fory-derive/src/lib.rs b/rust/fory-derive/src/lib.rs index 15ff08df77..b92dc98ec4 100644 --- a/rust/fory-derive/src/lib.rs +++ b/rust/fory-derive/src/lib.rs @@ -157,8 +157,8 @@ //! } //! //! fn main() -> Result<(), Error> { -//! let mut fory = Fory::default(); -//! fory.register::(100); +//! let mut fory = Fory::builder().xlang(true).build(); +//! fory.register_by_name::("example", "MyData")?; //! //! let data = MyData { //! value: 42, diff --git a/rust/fory/src/lib.rs b/rust/fory/src/lib.rs index b8714853ce..3929442f09 100644 --- a/rust/fory/src/lib.rs +++ b/rust/fory/src/lib.rs @@ -35,13 +35,13 @@ //! //! **Key differentiators:** //! -//! - **🔥 Blazingly Fast**: Zero-copy deserialization and optimized binary protocols -//! - **🌍 Cross-Language**: Seamlessly serialize/deserialize data across Java, Python, C++, Go, JavaScript, and Rust -//! - **🎯 Type-Safe**: Compile-time type checking with derive macros -//! - **🔄 Circular References**: Automatic tracking of shared and circular references with `Rc`/`Arc` and weak pointers -//! - **🧬 Polymorphic**: Serialize trait objects with `Box`, `Rc`, and `Arc` -//! - **📦 Schema Evolution**: Compatible mode for independent schema changes -//! - **⚡ Two Formats**: Object graph serialization and zero-copy row-based format +//! - **Fast**: Zero-copy deserialization and optimized binary protocols +//! - **Cross-Language**: Seamlessly serialize/deserialize data across Java, Python, C++, Go, JavaScript, and Rust +//! - **Type-Safe**: Compile-time type checking with derive macros +//! - **Circular References**: Automatic tracking of shared and circular references with `Rc`/`Arc` and weak pointers +//! - **Polymorphic**: Serialize trait objects with `Box`, `Rc`, and `Arc` +//! - **Schema Evolution**: Compatible mode for independent schema changes +//! - **Two Formats**: Object graph serialization and zero-copy row-based format //! //! ## Quick Start //! @@ -67,8 +67,8 @@ //! } //! //! # fn main() -> Result<(), Error> { -//! let mut fory = Fory::default(); -//! fory.register::(1)?; +//! let mut fory = Fory::builder().xlang(true).build(); +//! fory.register_by_name::("example", "User")?; //! //! let user = User { //! name: "Alice".to_string(), @@ -139,9 +139,9 @@ //! } //! //! # fn main() -> Result<(), Error> { -//! let mut fory = Fory::default(); -//! fory.register::
(100); -//! fory.register::(200); +//! let mut fory = Fory::builder().xlang(true).build(); +//! fory.register_by_name::
("example", "Address")?; +//! fory.register_by_name::("example", "Person")?; //! //! let person = Person { //! name: "John Doe".to_string(), @@ -164,10 +164,12 @@ //! # } //! ``` //! -//! ### 2. Shared and Circular References +//! ### 2. Native-Mode Shared and Circular References //! //! **What it does:** Automatically tracks and preserves reference identity for shared //! objects using `Rc` and `Arc`, and handles circular references using weak pointers. +//! The examples in this section use native mode because `Rc`, `Arc`, and weak-pointer +//! identity are Rust object-graph features. //! //! **Why it matters:** Graph-like data structures (trees, linked lists, object-relational //! models) are common in real applications but notoriously difficult to serialize. Most @@ -193,7 +195,7 @@ //! use std::rc::Rc; //! //! # fn main() -> Result<(), Error> { -//! let fory = Fory::default(); +//! let fory = Fory::builder().xlang(false).build(); //! //! let shared = Rc::new(String::from("shared_value")); //! let data = vec![shared.clone(), shared.clone(), shared.clone()]; @@ -216,7 +218,7 @@ //! use std::sync::Arc; //! //! # fn main() -> Result<(), Error> { -//! let fory = Fory::default(); +//! let fory = Fory::builder().xlang(false).build(); //! let shared = Arc::new(String::from("shared_value")); //! let data = vec![shared.clone(), shared.clone()]; //! @@ -251,8 +253,8 @@ //! } //! //! # fn main() -> Result<(), Error> { -//! let mut fory = Fory::builder().track_ref(true).build(); -//! fory.register::(2000); +//! let mut fory = Fory::builder().xlang(false).track_ref(true).build(); +//! fory.register::(2000)?; //! //! let parent = Rc::new(RefCell::new(Node { //! value: 1, @@ -293,8 +295,8 @@ //! } //! //! # fn main() -> Result<(), Error> { -//! let mut fory = Fory::builder().track_ref(true).build(); -//! fory.register::(6000); +//! let mut fory = Fory::builder().xlang(false).track_ref(true).build(); +//! fory.register::(6000)?; //! //! let parent = Arc::new(Mutex::new(Node { //! val: 10, @@ -318,10 +320,12 @@ //! # } //! ``` //! -//! ### 3. Trait Object Serialization +//! ### 3. Native-Mode Trait Object Serialization //! //! **What it does:** Enables polymorphic serialization through trait objects, supporting //! dynamic dispatch and type flexibility. +//! The examples in this section use native mode because Rust trait objects and `dyn Any` +//! dispatch are Rust runtime features. //! //! **Why it matters:** Rust's trait system is powerful for abstraction, but serializing //! `Box` is notoriously difficult. This feature is essential for plugin systems, @@ -374,10 +378,10 @@ //! } //! //! # fn main() -> Result<(), Error> { -//! let mut fory = Fory::builder().compatible(true).build(); -//! fory.register::(100); -//! fory.register::(101); -//! fory.register::(102); +//! let mut fory = Fory::builder().xlang(false).compatible(true).build(); +//! fory.register::(100)?; +//! fory.register::(101)?; +//! fory.register::(102)?; //! //! let zoo = Zoo { //! star_animal: Box::new(Dog { @@ -420,8 +424,8 @@ //! struct Dog { name: String } //! //! # fn main() -> Result<(), Error> { -//! let mut fory = Fory::default(); -//! fory.register::(100); +//! let mut fory = Fory::builder().xlang(false).build(); +//! fory.register::(100)?; //! //! let dog: Rc = Rc::new(Dog { //! name: "Rex".to_string() @@ -449,8 +453,8 @@ //! struct Cat { name: String } //! //! # fn main() -> Result<(), Error> { -//! let mut fory = Fory::default(); -//! fory.register::(101); +//! let mut fory = Fory::builder().xlang(false).build(); +//! fory.register::(101)?; //! //! let cat: Arc = Arc::new(Cat { //! name: "Whiskers".to_string() @@ -501,10 +505,10 @@ //! } //! //! # fn main() -> Result<(), Error> { -//! let mut fory = Fory::builder().compatible(true).build(); -//! fory.register::(100); -//! fory.register::(101); -//! fory.register::(102); +//! let mut fory = Fory::builder().xlang(false).compatible(true).build(); +//! fory.register::(100)?; +//! fory.register::(101)?; +//! fory.register::(102)?; //! //! let shelter = AnimalShelter { //! animals_rc: vec![ @@ -555,8 +559,8 @@ //! register_trait_type!(Animal, Dog); //! //! # fn main() -> Result<(), Error> { -//! let mut fory = Fory::builder().compatible(true).build(); -//! fory.register::(100); +//! let mut fory = Fory::builder().xlang(false).compatible(true).build(); +//! fory.register::(100)?; //! //! // For Rc //! let dog_rc: Rc = Rc::new(Dog { name: "Rex".to_string() }); @@ -630,11 +634,11 @@ //! } //! //! # fn main() -> Result<(), Error> { -//! let mut fory1 = Fory::builder().compatible(true).build(); -//! fory1.register::(1); +//! let mut fory1 = Fory::builder().xlang(true).compatible(true).build(); +//! fory1.register_by_name::("example", "Person")?; //! -//! let mut fory2 = Fory::builder().compatible(true).build(); -//! fory2.register::(1); +//! let mut fory2 = Fory::builder().xlang(true).compatible(true).build(); +//! fory2.register_by_name::("example", "Person")?; //! //! let person_v1 = PersonV1 { //! name: "Alice".to_string(), @@ -652,7 +656,7 @@ //! # } //! ``` //! -//! ### 5. Enum Support +//! ### 5. Native-Mode Enum Support //! //! **What it does:** Comprehensive enum support with three variant types (unit, unnamed, named) //! and full schema evolution in Compatible mode. @@ -694,7 +698,7 @@ //! } //! //! # fn main() -> Result<(), Error> { -//! let mut fory = Fory::default(); +//! let mut fory = Fory::builder().xlang(false).build(); //! fory.register::(1)?; //! //! let value = Value::Object { name: "score".to_string(), value: 100 }; @@ -727,10 +731,10 @@ //! } //! //! # fn main() -> Result<(), Error> { -//! let mut fory_old = Fory::builder().compatible(true).build(); +//! let mut fory_old = Fory::builder().xlang(false).compatible(true).build(); //! fory_old.register::(5)?; //! -//! let mut fory_new = Fory::builder().compatible(true).build(); +//! let mut fory_new = Fory::builder().xlang(false).compatible(true).build(); //! fory_new.register::(5)?; //! //! // Serialize with old schema (2 fields) @@ -756,7 +760,7 @@ //! - Unnamed variant elements: add/remove elements (extras skipped, missing use defaults) //! - Variant type mismatches automatically use default value of current variant //! -//! ### 6. Tuple Support +//! ### 6. Native-Mode Tuple Support //! //! **What it does:** Supports tuples up to 22 elements with automatic heterogeneous type //! handling and schema evolution in compatible mode. @@ -765,7 +769,7 @@ //! useful for temporary groupings, function return values, and ad-hoc data structures. //! //! **Technical approach:** Each tuple size (1-22) has a specialized `Serializer` implementation. -//! In non-compatible mode, elements are serialized sequentially without overhead. In compatible +//! In schema-consistent mode, elements are serialized sequentially without overhead. In compatible //! mode, the tuple is serialized as a heterogeneous collection with type metadata for each element. //! //! **Features:** @@ -780,7 +784,7 @@ //! use fory::Error; //! //! # fn main() -> Result<(), Error> { -//! let mut fory = Fory::default(); +//! let mut fory = Fory::builder().xlang(false).build(); //! //! // Tuple with heterogeneous types //! let data: (i32, String, bool, Vec) = ( @@ -797,7 +801,7 @@ //! # } //! ``` //! -//! ### 7. Custom Serializers +//! ### 7. Native-Mode Custom Serializers //! //! **What it does:** Allows manual implementation of the `Serializer` trait for types //! that don't support `#[derive(ForyStruct)]`. @@ -855,8 +859,8 @@ //! } //! //! # fn main() -> Result<(), Error> { -//! let mut fory = Fory::default(); -//! fory.register_serializer::(100); +//! let mut fory = Fory::builder().xlang(false).build(); +//! fory.register_serializer::(100)?; //! //! let custom = CustomType { //! value: 42, @@ -1001,42 +1005,29 @@ //! - `Rc` - Runtime type dispatch without custom traits //! - `Arc` - Thread-safe runtime type dispatch //! -//! ## Serialization Modes +//! ## Wire Modes And Schema Evolution //! -//! Apache Fory™ supports two serialization modes to balance between performance -//! and flexibility: +//! Apache Fory™ Rust supports two wire modes: //! -//! ### SchemaConsistent Mode (Default) -//! -//! **When to use:** Maximum performance when schemas are guaranteed to match. -//! -//! **Characteristics:** -//! - Type declarations must match exactly between serialization and deserialization -//! - Smaller payload size (no field names or metadata) -//! - Faster serialization and deserialization -//! - Suitable for monolithic applications or tightly coupled services +//! - **Xlang mode** is selected with `.xlang(true)`. It is the default wire +//! mode and is used for cross-language payloads. When `compatible` is omitted, +//! xlang mode uses compatible schema evolution so independently deployed +//! peers can add, remove, or reorder fields. +//! - **Native mode** is selected with `.xlang(false)`. Use it for Rust-only +//! payloads. When `compatible` is omitted, native mode uses +//! schema-consistent payloads for the smaller same-schema format. //! //! ```rust //! use fory::Fory; //! -//! let fory = Fory::default(); -//! ``` -//! -//! ### Compatible Mode +//! // Xlang mode with compatible schema evolution. +//! let xlang = Fory::builder().xlang(true).build(); //! -//! **When to use:** Schema evolution in distributed systems or microservices. -//! -//! **Characteristics:** -//! - Type declarations can differ between peers -//! - Allows field additions, deletions, and reordering -//! - Larger payload size (includes field names and metadata) -//! - Slightly slower due to metadata processing -//! - Essential for zero-downtime deployments -//! -//! ```rust -//! use fory::Fory; +//! // Native mode with schema-consistent payloads. +//! let native = Fory::builder().xlang(false).build(); //! -//! let fory = Fory::builder().compatible(true).build(); +//! // Native mode with compatible schema evolution. +//! let native_compatible = Fory::builder().xlang(false).compatible(true).build(); //! ``` //! //! ## Cross-Language Serialization @@ -1053,9 +1044,7 @@ //! use fory::Fory; //! use fory::{ForyEnum, ForyStruct, ForyUnion}; //! -//! let mut fory = Fory::builder() -//! .compatible(true) -//! .xlang(true).build(); +//! let mut fory = Fory::builder().xlang(true).build(); //! //! #[derive(ForyStruct)] //! struct MyStruct { @@ -1063,7 +1052,7 @@ //! field2: String, //! } //! -//! fory.register_by_name::("com.example", "MyStruct"); +//! fory.register_by_name::("com.example", "MyStruct").unwrap(); //! ``` //! //! **Type registration strategies:** @@ -1110,8 +1099,8 @@ //! } //! //! fn process_data(bytes: &[u8]) -> Result { -//! let mut fory = Fory::default(); -//! fory.register::(100); +//! let mut fory = Fory::builder().xlang(true).build(); +//! fory.register_by_name::("example", "Data")?; //! //! let data: Data = fory.deserialize(bytes)?; //! Ok(data) @@ -1136,8 +1125,8 @@ //! value: i32, //! } //! -//! let mut fory = Fory::default(); -//! fory.register::(1000).unwrap(); +//! let mut fory = Fory::builder().xlang(true).build(); +//! fory.register_by_name::("example", "Item").unwrap(); //! let fory = Arc::new(fory); //! let handles: Vec<_> = (0..8) //! .map(|i| { @@ -1196,9 +1185,9 @@ //! //! ## Documentation //! -//! - **[Protocol Specification](https://fory.apache.org/docs/specification/fory_xlang_serialization_spec)** - Binary protocol details -//! - **[Row Format Specification](https://fory.apache.org/docs/specification/fory_row_format_spec)** - Row format internals -//! - **[Type Mapping](https://fory.apache.org/docs/guide/xlang_type_mapping)** - Cross-language type mappings +//! - **[Protocol Specification](https://fory.apache.org/docs/specification/xlang_serialization_spec)** - Binary protocol details +//! - **[Row Format Specification](https://fory.apache.org/docs/specification/row_format_spec)** - Row format internals +//! - **[Type Mapping](https://fory.apache.org/docs/specification/xlang_type_mapping)** - Cross-language type mappings //! - **[API Documentation](https://docs.rs/fory)** - Complete API reference //! - **[GitHub Repository](https://github.com/apache/fory)** - Source code and issue tracking diff --git a/rust/tests/tests/compatible/test_basic_type.rs b/rust/tests/tests/compatible/test_basic_type.rs index 5ebbe33a18..3b663af99e 100644 --- a/rust/tests/tests/compatible/test_basic_type.rs +++ b/rust/tests/tests/compatible/test_basic_type.rs @@ -415,7 +415,7 @@ fn deserialize_nullable(fory: &Fory, bins: Vec, auto_conv: bool) { // non-null <-> non-null #[test] fn basic() { - let fory = Fory::builder().compatible(true).build(); + let fory = Fory::builder().xlang(false).compatible(true).build(); // serialize let bins = serialize_non_null(&fory); // deserialize @@ -425,7 +425,7 @@ fn basic() { // nullable <-> nullable #[test] fn basic_nullable() { - let fory = Fory::builder().compatible(true).build(); + let fory = Fory::builder().xlang(false).compatible(true).build(); // serialize let bins = serialize_nullable(&fory); // deserialize @@ -435,7 +435,7 @@ fn basic_nullable() { // non-null -> nullable -> non-null #[test] fn auto_conv() { - let fory = Fory::builder().compatible(true).build(); + let fory = Fory::builder().xlang(false).compatible(true).build(); // serialize_non-null let bins = serialize_non_null(&fory); deserialize_nullable(&fory, bins, true); diff --git a/rust/tests/tests/compatible/test_container.rs b/rust/tests/tests/compatible/test_container.rs index d9ff1139c3..dd99f098ca 100644 --- a/rust/tests/tests/compatible/test_container.rs +++ b/rust/tests/tests/compatible/test_container.rs @@ -219,7 +219,7 @@ fn complex_container2() -> Vec, Vec>> { #[test] fn container_outer_auto_conv() { - let fory = Fory::builder().compatible(true).build(); + let fory = Fory::builder().xlang(false).compatible(true).build(); // serialize_outer_non-null let bytes = fory.serialize(&basic_list()).unwrap(); assert_eq!( @@ -285,9 +285,9 @@ fn container_outer_auto_conv() { #[test] fn collection_inner() { - let mut fory1 = Fory::builder().compatible(true).build(); + let mut fory1 = Fory::builder().xlang(false).compatible(true).build(); fory1.register::(101).unwrap(); - let mut fory2 = Fory::builder().compatible(true).build(); + let mut fory2 = Fory::builder().xlang(false).compatible(true).build(); fory2.register_by_name::("", "item").unwrap(); for fory in [fory1, fory2] { // serialize @@ -348,9 +348,9 @@ fn collection_inner() { #[test] fn collection_inner_auto_conv() { - let mut fory1 = Fory::builder().compatible(true).build(); + let mut fory1 = Fory::builder().xlang(false).compatible(true).build(); fory1.register::(101).unwrap(); - let mut fory2 = Fory::builder().compatible(true).build(); + let mut fory2 = Fory::builder().xlang(false).compatible(true).build(); fory2.register_by_name::("", "item").unwrap(); for fory in [fory1, fory2] { // serialize_non_null @@ -416,9 +416,9 @@ fn collection_inner_auto_conv() { #[test] fn map_inner() { - let mut fory1 = Fory::builder().compatible(true).build(); + let mut fory1 = Fory::builder().xlang(false).compatible(true).build(); fory1.register::(101).unwrap(); - let mut fory2 = Fory::builder().compatible(true).build(); + let mut fory2 = Fory::builder().xlang(false).compatible(true).build(); fory2.register_by_name::("", "item").unwrap(); for fory in [fory1, fory2] { // serialize @@ -453,9 +453,9 @@ fn map_inner() { #[test] fn map_inner_auto_conv() { - let mut fory1 = Fory::builder().compatible(true).build(); + let mut fory1 = Fory::builder().xlang(false).compatible(true).build(); fory1.register::(101).unwrap(); - let mut fory2 = Fory::builder().compatible(true).build(); + let mut fory2 = Fory::builder().xlang(false).compatible(true).build(); fory2.register_by_name::("", "item").unwrap(); for fory in [fory1, fory2] { // serialize_non_null @@ -493,9 +493,9 @@ fn map_inner_auto_conv() { #[test] fn complex() { - let mut fory1 = Fory::builder().compatible(true).build(); + let mut fory1 = Fory::builder().xlang(false).compatible(true).build(); fory1.register::(101).unwrap(); - let mut fory2 = Fory::builder().compatible(true).build(); + let mut fory2 = Fory::builder().xlang(false).compatible(true).build(); fory2.register_by_name::("", "item").unwrap(); for fory in [fory1, fory2] { let mut bins = vec![ diff --git a/rust/tests/tests/compatible/test_struct.rs b/rust/tests/tests/compatible/test_struct.rs index 38a7f38560..2787ef915e 100644 --- a/rust/tests/tests/compatible/test_struct.rs +++ b/rust/tests/tests/compatible/test_struct.rs @@ -45,8 +45,8 @@ fn simple() { f7: i16, last: i8, } - let mut fory1 = Fory::builder().compatible(true).build(); - let mut fory2 = Fory::builder().compatible(true).build(); + let mut fory1 = Fory::builder().xlang(false).compatible(true).build(); + let mut fory2 = Fory::builder().xlang(false).compatible(true).build(); fory1.register::(999).unwrap(); fory2.register::(999).unwrap(); let animal: Animal1 = Animal1 { @@ -99,8 +99,8 @@ fn compatible_list_array_field_pairs() { payload: Vec>, } - let mut writer = Fory::builder().compatible(true).build(); - let mut reader = Fory::builder().compatible(true).build(); + let mut writer = Fory::builder().xlang(false).compatible(true).build(); + let mut reader = Fory::builder().xlang(false).compatible(true).build(); writer.register::(991).unwrap(); reader.register::(991).unwrap(); let bytes = writer @@ -111,8 +111,8 @@ fn compatible_list_array_field_pairs() { let decoded: ArrayPayload = reader.deserialize(&bytes).unwrap(); assert_eq!(decoded.payload, vec![1, 2, 3]); - let mut writer = Fory::builder().compatible(true).build(); - let mut reader = Fory::builder().compatible(true).build(); + let mut writer = Fory::builder().xlang(false).compatible(true).build(); + let mut reader = Fory::builder().xlang(false).compatible(true).build(); writer.register::(992).unwrap(); reader.register::(992).unwrap(); let bytes = writer @@ -123,8 +123,8 @@ fn compatible_list_array_field_pairs() { let decoded: ListPayload = reader.deserialize(&bytes).unwrap(); assert_eq!(decoded.payload, vec![1, 2, 3]); - let mut writer = Fory::builder().compatible(true).build(); - let mut reader = Fory::builder().compatible(true).build(); + let mut writer = Fory::builder().xlang(false).compatible(true).build(); + let mut reader = Fory::builder().xlang(false).compatible(true).build(); writer.register::(993).unwrap(); reader.register::(993).unwrap(); let bytes = writer @@ -149,8 +149,8 @@ fn compatible_list_array_field_pairs() { "{err}" ); - let mut writer = Fory::builder().compatible(true).build(); - let mut reader = Fory::builder().compatible(true).build(); + let mut writer = Fory::builder().xlang(false).compatible(true).build(); + let mut reader = Fory::builder().xlang(false).compatible(true).build(); writer.register::(994).unwrap(); reader.register::(994).unwrap(); let bytes = writer @@ -177,8 +177,8 @@ fn skip_option() { f2: i8, last: i64, } - let mut fory1 = Fory::builder().compatible(true).build(); - let mut fory2 = Fory::builder().compatible(true).build(); + let mut fory1 = Fory::builder().xlang(false).compatible(true).build(); + let mut fory2 = Fory::builder().xlang(false).compatible(true).build(); fory1.register::(999).unwrap(); fory2.register::(999).unwrap(); let item1 = Item1 { @@ -216,8 +216,8 @@ fn nonexistent_struct() { f3: i64, last: String, } - let mut fory1 = Fory::builder().compatible(true).build(); - let mut fory2 = Fory::builder().compatible(true).build(); + let mut fory1 = Fory::builder().xlang(false).compatible(true).build(); + let mut fory2 = Fory::builder().xlang(false).compatible(true).build(); fory1.register::(899).unwrap(); fory1.register::(999).unwrap(); fory2.register::(799).unwrap(); @@ -248,7 +248,7 @@ fn option() { f5: Vec>>>, last: i64, } - let mut fory = Fory::builder().compatible(true).build(); + let mut fory = Fory::builder().xlang(false).compatible(true).build(); fory.register::(999).unwrap(); let animal: Animal = Animal { f1: Some(String::from("f1")), @@ -292,8 +292,8 @@ fn nullable() { last: i64, } - let mut fory1 = Fory::builder().compatible(true).build(); - let mut fory2 = Fory::builder().compatible(true).build(); + let mut fory1 = Fory::builder().xlang(false).compatible(true).build(); + let mut fory2 = Fory::builder().xlang(false).compatible(true).build(); fory1.register::(999).unwrap(); fory2.register::(999).unwrap(); @@ -346,8 +346,8 @@ fn nullable_container() { last: i64, } - let mut fory1 = Fory::builder().compatible(true).build(); - let mut fory2 = Fory::builder().compatible(true).build(); + let mut fory1 = Fory::builder().xlang(false).compatible(true).build(); + let mut fory2 = Fory::builder().xlang(false).compatible(true).build(); fory1.register::(999).unwrap(); fory2.register::(999).unwrap(); @@ -398,8 +398,8 @@ fn inner_nullable() { f3: HashMap, last: i64, } - let mut fory1 = Fory::builder().compatible(true).build(); - let mut fory2 = Fory::builder().compatible(true).build(); + let mut fory1 = Fory::builder().xlang(false).compatible(true).build(); + let mut fory2 = Fory::builder().xlang(false).compatible(true).build(); fory1.register::(999).unwrap(); fory2.register::(999).unwrap(); @@ -445,8 +445,8 @@ fn nullable_struct() { f3: Item, last: i64, } - let mut fory1 = Fory::builder().compatible(true).build(); - let mut fory2 = Fory::builder().compatible(true).build(); + let mut fory1 = Fory::builder().xlang(false).compatible(true).build(); + let mut fory2 = Fory::builder().xlang(false).compatible(true).build(); fory1.register::(199).unwrap(); fory1.register::(200).unwrap(); fory2.register::(199).unwrap(); @@ -760,8 +760,8 @@ fn test_struct_with_generic() { } } - let mut fory1 = Fory::builder().compatible(true).build(); - let mut fory2 = Fory::default(); // Without compatible it works fine. + let mut fory1 = Fory::builder().xlang(false).compatible(true).build(); + let mut fory2 = Fory::builder().xlang(false).build(); // Without compatible it works fine. let mut fory3 = Fory::builder().xlang(true).compatible(false).build(); fn inner_test(fory: &mut Fory) -> Result<(), Error> { diff --git a/rust/tests/tests/compatible/test_struct_enum.rs b/rust/tests/tests/compatible/test_struct_enum.rs index 36b75d7fef..74f18b85ea 100644 --- a/rust/tests/tests/compatible/test_struct_enum.rs +++ b/rust/tests/tests/compatible/test_struct_enum.rs @@ -44,8 +44,8 @@ fn simple() { f7: i16, last: i8, } - let mut fory1 = Fory::builder().compatible(true).build(); - let mut fory2 = Fory::builder().compatible(true).build(); + let mut fory1 = Fory::builder().xlang(false).compatible(true).build(); + let mut fory2 = Fory::builder().xlang(false).compatible(true).build(); fory1.register::(999).unwrap(); fory2.register::(999).unwrap(); let animal: Animal1 = Animal1 { @@ -81,7 +81,7 @@ fn simple_write_continuous() { last: i8, } - let mut fory = Fory::builder().compatible(true).build(); + let mut fory = Fory::builder().xlang(false).compatible(true).build(); fory.register::(999).unwrap(); let animal: Animal1 = Animal1 { f1: HashMap::from([(1, vec![2])]), @@ -128,8 +128,8 @@ fn skip_option() { f2: i8, last: i64, } - let mut fory1 = Fory::builder().compatible(true).build(); - let mut fory2 = Fory::builder().compatible(true).build(); + let mut fory1 = Fory::builder().xlang(false).compatible(true).build(); + let mut fory2 = Fory::builder().xlang(false).compatible(true).build(); fory1.register::(999).unwrap(); fory2.register::(999).unwrap(); let item1 = Item1 { @@ -167,8 +167,8 @@ fn nonexistent_struct() { f3: i64, last: String, } - let mut fory1 = Fory::builder().compatible(true).build(); - let mut fory2 = Fory::builder().compatible(true).build(); + let mut fory1 = Fory::builder().xlang(false).compatible(true).build(); + let mut fory2 = Fory::builder().xlang(false).compatible(true).build(); fory1.register::(899).unwrap(); fory1.register::(999).unwrap(); fory2.register::(799).unwrap(); @@ -199,7 +199,7 @@ fn option() { f5: Vec>>>, last: i64, } - let mut fory = Fory::builder().compatible(true).build(); + let mut fory = Fory::builder().xlang(false).compatible(true).build(); fory.register::(999).unwrap(); let animal: Animal = Animal { f1: Some(String::from("f1")), @@ -243,8 +243,8 @@ fn nullable() { last: i64, } - let mut fory1 = Fory::builder().compatible(true).build(); - let mut fory2 = Fory::builder().compatible(true).build(); + let mut fory1 = Fory::builder().xlang(false).compatible(true).build(); + let mut fory2 = Fory::builder().xlang(false).compatible(true).build(); fory1.register::(999).unwrap(); fory2.register::(999).unwrap(); @@ -297,8 +297,8 @@ fn nullable_container() { last: i64, } - let mut fory1 = Fory::builder().compatible(true).build(); - let mut fory2 = Fory::builder().compatible(true).build(); + let mut fory1 = Fory::builder().xlang(false).compatible(true).build(); + let mut fory2 = Fory::builder().xlang(false).compatible(true).build(); fory1.register::(999).unwrap(); fory2.register::(999).unwrap(); @@ -349,8 +349,8 @@ fn inner_nullable() { f3: HashMap, last: i64, } - let mut fory1 = Fory::builder().compatible(true).build(); - let mut fory2 = Fory::builder().compatible(true).build(); + let mut fory1 = Fory::builder().xlang(false).compatible(true).build(); + let mut fory2 = Fory::builder().xlang(false).compatible(true).build(); fory1.register::(999).unwrap(); fory2.register::(999).unwrap(); @@ -396,8 +396,8 @@ fn nullable_struct() { f3: Item, last: i64, } - let mut fory1 = Fory::builder().compatible(true).build(); - let mut fory2 = Fory::builder().compatible(true).build(); + let mut fory1 = Fory::builder().xlang(false).compatible(true).build(); + let mut fory2 = Fory::builder().xlang(false).compatible(true).build(); fory1.register::(199).unwrap(); fory1.register::(200).unwrap(); fory2.register::(199).unwrap(); diff --git a/rust/tests/tests/test_any.rs b/rust/tests/tests/test_any.rs index ecca768fcb..df49db8af7 100644 --- a/rust/tests/tests/test_any.rs +++ b/rust/tests/tests/test_any.rs @@ -25,7 +25,7 @@ use std::vec; #[test] fn test_box_dyn_any() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let value: Box = Box::new("hello".to_string()); let bytes = fory.serialize(&value).unwrap(); @@ -53,7 +53,7 @@ fn test_box_dyn_any() { #[test] fn test_rc_dyn_any() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let value: Rc = Rc::new("world".to_string()); let bytes = fory.serialize(&value).unwrap(); let deserialized: Rc = fory.deserialize(&bytes).unwrap(); @@ -75,7 +75,7 @@ fn test_rc_dyn_any() { #[test] fn test_arc_dyn_any() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let value: Arc = Arc::new("arc test".to_string()); let bytes = fory.serialize(&value).unwrap(); @@ -101,7 +101,7 @@ fn test_arc_dyn_any() { #[test] fn test_rc_dyn_any_shared_reference() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let shared_str: Rc = Rc::new("shared".to_string()); @@ -120,7 +120,7 @@ fn test_rc_dyn_any_shared_reference() { #[test] fn test_arc_dyn_any_shared_reference() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let shared_vec: Arc = Arc::new(vec![1, 2, 3]); @@ -146,7 +146,7 @@ fn test_any_registered_by_name() { age: i32, } - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register_by_name::("test", "Person").unwrap(); let person = Person { @@ -173,7 +173,7 @@ fn test_mixed_any_types() { value: String, } - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register_by_name::("", "Item").unwrap(); let item = Item { @@ -221,10 +221,10 @@ struct AnyMapFixedKey { #[test] fn test_hashmap_fixed_key_rc_any_field_compatible() { - let mut writer = Fory::builder().compatible(true).build(); + let mut writer = Fory::builder().xlang(false).compatible(true).build(); writer.register::(700).unwrap(); - let mut reader = Fory::builder().compatible(true).build(); + let mut reader = Fory::builder().xlang(false).compatible(true).build(); reader.register::(700).unwrap(); let original = AnyMapFixedKey { @@ -249,7 +249,7 @@ fn test_hashmap_fixed_key_rc_any_field_compatible() { #[test] fn test_arc_by_name() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register_by_name::("", "Container").unwrap(); let container = Container { @@ -280,7 +280,7 @@ fn test_arc_by_name() { #[test] fn test_rc_by_name() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register_by_name::("", "Container").unwrap(); let container = Container { @@ -330,7 +330,7 @@ struct StructB { /// Previously this caused non-deterministic failures. Now it returns a clear error message. #[test] fn test_vec_of_different_struct_types_in_box_any_returns_error() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); // Register both struct types fory.register_by_name::("", "StructA").unwrap(); fory.register_generic_trait::>().unwrap(); diff --git a/rust/tests/tests/test_array.rs b/rust/tests/tests/test_array.rs index fb2e55e2bb..09c96f3e51 100644 --- a/rust/tests/tests/test_array.rs +++ b/rust/tests/tests/test_array.rs @@ -23,7 +23,7 @@ use std::rc::Rc; #[test] fn test_array_i32() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let arr = [1, 2, 3, 4, 5]; let bin = fory.serialize(&arr).unwrap(); let obj: [i32; 5] = fory.deserialize(&bin).expect("deserialize"); @@ -32,7 +32,7 @@ fn test_array_i32() { #[test] fn test_array_i64() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let arr = [100i64, 200, 300]; let bin = fory.serialize(&arr).unwrap(); let obj: [i64; 3] = fory.deserialize(&bin).expect("deserialize"); @@ -41,7 +41,7 @@ fn test_array_i64() { #[test] fn test_array_f64() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let arr = [1.5, 2.5, 3.5, 4.5]; let bin = fory.serialize(&arr).unwrap(); let obj: [f64; 4] = fory.deserialize(&bin).expect("deserialize"); @@ -50,7 +50,7 @@ fn test_array_f64() { #[test] fn test_array_f32() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let arr = [1.1f32, 2.2, 3.3]; let bin = fory.serialize(&arr).unwrap(); let obj: [f32; 3] = fory.deserialize(&bin).expect("deserialize"); @@ -59,7 +59,7 @@ fn test_array_f32() { #[test] fn test_array_bool() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let arr = [true, false, true, false]; let bin = fory.serialize(&arr).unwrap(); let obj: [bool; 4] = fory.deserialize(&bin).expect("deserialize"); @@ -68,7 +68,7 @@ fn test_array_bool() { #[test] fn test_array_i8() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let arr = [1i8, 2, 3, 4, 5, 6, 7, 8]; let bin = fory.serialize(&arr).unwrap(); let obj: [i8; 8] = fory.deserialize(&bin).expect("deserialize"); @@ -77,7 +77,7 @@ fn test_array_i8() { #[test] fn test_array_i16() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let arr = [100i16, 200, 300, 400]; let bin = fory.serialize(&arr).unwrap(); let obj: [i16; 4] = fory.deserialize(&bin).expect("deserialize"); @@ -86,7 +86,7 @@ fn test_array_i16() { #[test] fn test_array_string() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let arr = ["hello".to_string(), "world".to_string(), "fory".to_string()]; let bin = fory.serialize(&arr).unwrap(); let obj: [String; 3] = fory.deserialize(&bin).expect("deserialize"); @@ -95,7 +95,7 @@ fn test_array_string() { #[test] fn test_array_empty() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let arr: [i32; 0] = []; let bin = fory.serialize(&arr).unwrap(); let obj: [i32; 0] = fory.deserialize(&bin).expect("deserialize"); @@ -104,7 +104,7 @@ fn test_array_empty() { #[test] fn test_array_single_element() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let arr = [42]; let bin = fory.serialize(&arr).unwrap(); let obj: [i32; 1] = fory.deserialize(&bin).expect("deserialize"); @@ -113,7 +113,7 @@ fn test_array_single_element() { #[test] fn test_array_large() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let arr = [1; 100]; let bin = fory.serialize(&arr).unwrap(); let obj: [i32; 100] = fory.deserialize(&bin).expect("deserialize"); @@ -128,7 +128,7 @@ struct Point { #[test] fn test_array_struct() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register_by_name::("", "Point").unwrap(); let arr = [ @@ -150,7 +150,7 @@ struct ArrayStruct { #[test] fn test_struct_with_arrays() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register_by_name::("", "ArrayStruct") .unwrap(); @@ -167,7 +167,7 @@ fn test_struct_with_arrays() { #[test] fn test_array_nested() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let arr = [[1, 2], [3, 4], [5, 6]]; let bin = fory.serialize(&arr).unwrap(); let obj: [[i32; 2]; 3] = fory.deserialize(&bin).expect("deserialize"); @@ -176,7 +176,7 @@ fn test_array_nested() { #[test] fn test_array_option() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let arr = [Some(1), None, Some(3)]; let bin = fory.serialize(&arr).unwrap(); let obj: [Option; 3] = fory.deserialize(&bin).expect("deserialize"); @@ -186,7 +186,7 @@ fn test_array_option() { #[test] fn test_array_vec_compatibility() { // Test that an array can be serialized and deserialized as a Vec - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let arr = [1, 2, 3, 4, 5]; let bin = fory.serialize(&arr).unwrap(); // Deserialize as Vec should work since they use the same type ID @@ -197,7 +197,7 @@ fn test_array_vec_compatibility() { #[test] fn test_vec_array_compatibility() { // Test that a Vec can be serialized and deserialized as an array - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let vec = vec![1, 2, 3]; let bin = fory.serialize(&vec).unwrap(); // Deserialize as array should work if the size matches @@ -208,7 +208,7 @@ fn test_vec_array_compatibility() { #[test] fn test_array_size_mismatch() { // Test that deserializing with wrong size fails gracefully - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let arr = [1, 2, 3, 4, 5]; let bin = fory.serialize(&arr).unwrap(); // Try to deserialize as array with wrong size @@ -256,7 +256,7 @@ register_trait_type!(Shape, Circle, Rectangle); #[test] fn test_array_box_trait_objects() { - let mut fory = Fory::builder().compatible(true).build(); + let mut fory = Fory::builder().xlang(false).compatible(true).build(); fory.register::(9001).unwrap(); fory.register::(9002).unwrap(); @@ -288,7 +288,7 @@ fn test_array_box_trait_objects() { #[test] fn test_vec_of_arrays() { // Test from GitHub issue: Vec<[f32;4]> should work with ForyStruct - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); // Test Vec of primitive arrays let points: Vec<[f32; 4]> = vec![ @@ -315,7 +315,7 @@ fn test_struct_with_vec_of_arrays() { points: Vec<[f32; 4]>, } - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register_by_name::("", "PointCloud") .unwrap(); @@ -335,7 +335,7 @@ fn test_struct_with_vec_of_arrays() { #[test] fn test_array_rc_trait_objects() { - let mut fory = Fory::builder().compatible(true).build(); + let mut fory = Fory::builder().xlang(false).compatible(true).build(); fory.register::(9001).unwrap(); fory.register::(9002).unwrap(); @@ -375,7 +375,7 @@ fn test_array_rc_trait_objects() { #[test] fn test_array_float16() { use fory_core::types::float16::float16; - let fory = fory_core::fory::Fory::default(); + let fory = fory_core::fory::Fory::builder().xlang(false).build(); let arr = [ float16::from_f32(1.0), float16::from_f32(2.5), @@ -392,7 +392,7 @@ fn test_array_float16() { #[test] fn test_array_float16_special_values() { use fory_core::types::float16::float16; - let fory = fory_core::fory::Fory::default(); + let fory = fory_core::fory::Fory::builder().xlang(false).build(); let arr = [ float16::INFINITY, float16::NEG_INFINITY, @@ -413,7 +413,7 @@ fn test_array_float16_special_values() { #[test] fn test_array_bfloat16() { use fory_core::types::bfloat16::bfloat16; - let fory = fory_core::fory::Fory::default(); + let fory = fory_core::fory::Fory::builder().xlang(false).build(); let arr = [ bfloat16::from_f32(1.0), bfloat16::from_f32(2.5), @@ -430,7 +430,7 @@ fn test_array_bfloat16() { #[test] fn test_array_bfloat16_special_values() { use fory_core::types::bfloat16::bfloat16; - let fory = fory_core::fory::Fory::default(); + let fory = fory_core::fory::Fory::builder().xlang(false).build(); let arr = [ bfloat16::INFINITY, bfloat16::NEG_INFINITY, diff --git a/rust/tests/tests/test_associated_types.rs b/rust/tests/tests/test_associated_types.rs index ef787baece..5ee2987b9f 100644 --- a/rust/tests/tests/test_associated_types.rs +++ b/rust/tests/tests/test_associated_types.rs @@ -67,7 +67,7 @@ where #[test] fn test_leader_id_with_associated_types() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::>(100).unwrap(); let leader_id: LeaderId = LeaderId { @@ -83,7 +83,7 @@ fn test_leader_id_with_associated_types() { #[test] fn test_leader_id_default_values() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::>(100).unwrap(); let leader_id: LeaderId = LeaderId { @@ -99,7 +99,7 @@ fn test_leader_id_default_values() { #[test] fn test_vec_of_leader_ids() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::>(100).unwrap(); let leader_ids: Vec> = vec![ @@ -125,7 +125,7 @@ fn test_vec_of_leader_ids() { #[test] fn test_option_leader_id() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::>(100).unwrap(); // Test with Some value diff --git a/rust/tests/tests/test_box.rs b/rust/tests/tests/test_box.rs index 66dba99db0..595af20b17 100644 --- a/rust/tests/tests/test_box.rs +++ b/rust/tests/tests/test_box.rs @@ -21,7 +21,7 @@ use std::collections::HashMap; #[test] fn test_box_primitive() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); // Test Box let value = Box::new(42i32); @@ -52,7 +52,7 @@ fn test_box_struct() { age: i32, } - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(999).unwrap(); let person = Person { @@ -75,7 +75,7 @@ fn test_box_struct_separate() { age: i32, } - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(999).unwrap(); // Test serializing the Box directly, not as a field @@ -93,7 +93,7 @@ fn test_box_struct_separate() { #[test] fn test_box_collection() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); // Test Box> let value = Box::new(vec![1, 2, 3, 4, 5]); @@ -117,7 +117,7 @@ fn test_box_collection() { #[test] fn test_box_option() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); // Test Box> with Some value let value = Box::new(Some("Hello".to_string())); @@ -135,7 +135,7 @@ fn test_box_option() { #[test] fn test_nested_box() { // Test Box> - though unusual, should work - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let value = Box::new(Box::new(42i32)); let bin = fory.serialize(&value).unwrap(); diff --git a/rust/tests/tests/test_buffer.rs b/rust/tests/tests/test_buffer.rs index 2ef944f1c5..f67cdcdf0f 100644 --- a/rust/tests/tests/test_buffer.rs +++ b/rust/tests/tests/test_buffer.rs @@ -131,7 +131,7 @@ fn test_utf8_string_read_rejects_invalid_payload() { #[test] fn test_fory_rejects_invalid_utf8_string_by_default() { - let fory = Fory::builder().build(); + let fory = Fory::builder().xlang(false).build(); assert!(fory.is_check_string_read()); let mut bytes = fory.serialize(&"a".to_string()).unwrap(); *bytes.last_mut().unwrap() = 0xff; @@ -145,7 +145,10 @@ fn test_fory_rejects_invalid_utf8_string_by_default() { #[test] fn test_fory_can_disable_checked_string_read_for_trusted_data() { - let fory = Fory::builder().check_string_read(false).build(); + let fory = Fory::builder() + .xlang(false) + .check_string_read(false) + .build(); assert!(!fory.is_check_string_read()); let bytes = fory.serialize(&"valid".to_string()).unwrap(); diff --git a/rust/tests/tests/test_collection.rs b/rust/tests/tests/test_collection.rs index b883d9202c..9271e39ae4 100644 --- a/rust/tests/tests/test_collection.rs +++ b/rust/tests/tests/test_collection.rs @@ -21,7 +21,7 @@ use std::collections::{BTreeSet, BinaryHeap, HashSet}; #[test] fn test_btreeset_roundtrip() { - let mut fory: Fory = Fory::default(); + let mut fory: Fory = Fory::builder().xlang(false).build(); fory.register_generic_trait::>().unwrap(); let mut original = BTreeSet::new(); @@ -42,7 +42,7 @@ fn test_btreeset_roundtrip() { #[test] fn test_binaryheap_roundtrip() { - let fory: Fory = Fory::default(); + let fory: Fory = Fory::builder().xlang(false).build(); let mut original = BinaryHeap::new(); original.push(10); @@ -64,7 +64,7 @@ struct SetContainer { #[test] fn test_set_container() { - let mut fory: Fory = Fory::default(); + let mut fory: Fory = Fory::builder().xlang(false).build(); fory.register::(100).unwrap(); let mut btree = BTreeSet::new(); @@ -103,7 +103,7 @@ struct HeapContainer { #[test] fn test_heap_container() { - let mut fory: Fory = Fory::default(); + let mut fory: Fory = Fory::builder().xlang(false).build(); fory.register::(100).unwrap(); let mut binary_heap = BinaryHeap::new(); @@ -123,7 +123,7 @@ fn test_heap_container() { #[test] fn test_hashset_max_collection_size_guardrail() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let original = HashSet::from([ "apple".to_string(), "banana".to_string(), @@ -131,7 +131,7 @@ fn test_hashset_max_collection_size_guardrail() { ]); let serialized = fory.serialize(&original).unwrap(); - let limited_fory = Fory::builder().max_collection_size(2).build(); + let limited_fory = Fory::builder().xlang(false).max_collection_size(2).build(); let err = limited_fory .deserialize::>(&serialized) .expect_err("expected collection size guardrail to reject the payload"); diff --git a/rust/tests/tests/test_complex_refs.rs b/rust/tests/tests/test_complex_refs.rs index d19da70bc2..0407d0aa8c 100644 --- a/rust/tests/tests/test_complex_refs.rs +++ b/rust/tests/tests/test_complex_refs.rs @@ -23,7 +23,7 @@ use std::sync::Arc; #[test] fn test_rc_shared_in_nested_vec() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let shared1 = Rc::new(String::from("shared_1")); let shared2 = Rc::new(String::from("shared_2")); @@ -55,7 +55,7 @@ fn test_rc_shared_in_nested_vec() { #[test] fn test_arc_shared_in_nested_vec() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let shared1 = Arc::new(String::from("shared_1")); let shared2 = Arc::new(String::from("shared_2")); @@ -87,7 +87,7 @@ fn test_arc_shared_in_nested_vec() { #[test] fn test_mixed_rc_arc_sharing() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); // Test both Rc and Arc sharing within the same structure let shared_rc = Rc::new(42i32); @@ -114,7 +114,7 @@ fn test_mixed_rc_arc_sharing() { #[test] fn test_deep_sharing_stress_test() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); // Create a stress test with deep nesting and many shared references let shared = Rc::new(String::from("deep_shared")); diff --git a/rust/tests/tests/test_complex_struct.rs b/rust/tests/tests/test_complex_struct.rs index 4e5fe5cd6e..9944b25418 100644 --- a/rust/tests/tests/test_complex_struct.rs +++ b/rust/tests/tests/test_complex_struct.rs @@ -40,7 +40,7 @@ use std::collections::HashMap; // }), // }; // -// let mut fory = Fory::default(); +// let mut fory = Fory::builder().xlang(false).build(); // fory.register::(999); // fory.register::(1000); // let bin = fory.serialize(&person); @@ -57,7 +57,7 @@ fn enum_without_payload() { Red, Blue, } - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(999).unwrap(); let color = Color::Red; let bin = fory.serialize(&color).unwrap(); @@ -108,13 +108,13 @@ fn complex_struct() { c5: 2.0, c6: 4.0, }; - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(899).unwrap(); fory.register::(999).unwrap(); let bin: Vec = fory.serialize(&person).unwrap(); let obj: Person = fory.deserialize(&bin).expect("should success"); assert_eq!(person, obj); - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register_by_name::("", "animal").unwrap(); fory.register_by_name::("", "person").unwrap(); let bin: Vec = fory.serialize(&person).unwrap(); @@ -141,7 +141,7 @@ fn encode_to_obin() { f8: f64, f10: HashMap, } - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(999).unwrap(); fory.register::(899).unwrap(); let bin: Vec = fory diff --git a/rust/tests/tests/test_debug.rs b/rust/tests/tests/test_debug.rs index 2908d0769b..9d3aee957c 100644 --- a/rust/tests/tests/test_debug.rs +++ b/rust/tests/tests/test_debug.rs @@ -107,7 +107,7 @@ fn debug_hooks_trigger_for_struct() { set_before_read_field_func(before_read); set_after_read_field_func(after_read); - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(4001).unwrap(); let sample = DebugSample { a: 7, @@ -150,7 +150,7 @@ fn debug_hooks_trigger_for_struct() { event_log().lock().unwrap().clear(); - let mut fory_compat = Fory::builder().compatible(true).build(); + let mut fory_compat = Fory::builder().xlang(false).compatible(true).build(); fory_compat.register::(4001).unwrap(); let compat_bytes = fory_compat.serialize(&sample).unwrap(); let _: DebugSample = fory_compat.deserialize(compat_bytes.as_slice()).unwrap(); diff --git a/rust/tests/tests/test_field_meta.rs b/rust/tests/tests/test_field_meta.rs index d09d586ac1..ee7c2981ea 100644 --- a/rust/tests/tests/test_field_meta.rs +++ b/rust/tests/tests/test_field_meta.rs @@ -38,7 +38,7 @@ struct StructWithSkip { #[test] fn test_skip_field() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(1).unwrap(); let original = StructWithSkip { @@ -66,7 +66,7 @@ struct StructWithNullable { #[test] fn test_nullable_attribute() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(2).unwrap(); // Test with Some value @@ -104,7 +104,7 @@ struct StructWithRefTracking { #[test] fn test_ref_tracking_disabled() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(3).unwrap(); fory.register::(4).unwrap(); @@ -125,7 +125,7 @@ struct StructWithExplicitNotNull { #[test] fn test_explicit_not_nullable() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(5).unwrap(); let original = StructWithExplicitNotNull { @@ -144,7 +144,7 @@ struct StructWithArc { #[test] fn test_arc_default_ref_tracking() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(6).unwrap(); fory.register::(7).unwrap(); @@ -168,7 +168,7 @@ struct StructWithCombinedAttrs { #[test] fn test_combined_attributes() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(8).unwrap(); let original = StructWithCombinedAttrs { @@ -195,7 +195,7 @@ struct StructWithPrimitives { #[test] fn test_primitive_defaults() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(9).unwrap(); let original = StructWithPrimitives { @@ -612,7 +612,7 @@ fn serializer_backed_container_fields_write_declared_generic_payloads() { #[test] fn test_nested_codec_annotations_roundtrip() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(10).unwrap(); let original = NestedFixedEncoding { @@ -628,10 +628,10 @@ fn test_nested_codec_annotations_roundtrip() { #[test] fn test_compatible_nested_integer_encoding_mismatch() { - let mut writer = Fory::builder().compatible(true).build(); + let mut writer = Fory::builder().xlang(false).compatible(true).build(); writer.register::(11).unwrap(); - let mut reader = Fory::builder().compatible(true).build(); + let mut reader = Fory::builder().xlang(false).compatible(true).build(); reader.register::(11).unwrap(); let original = NestedVarEncoding { @@ -660,7 +660,7 @@ struct StructWithFieldIds { #[test] fn test_field_id_attribute() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(10).unwrap(); let original = StructWithFieldIds { @@ -686,7 +686,7 @@ struct StructWithMixedIds { #[test] fn test_mixed_field_ids() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(11).unwrap(); let original = StructWithMixedIds { @@ -715,7 +715,7 @@ struct StructWithCombinedFieldAttrs { #[test] fn test_field_id_with_other_attrs() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(12).unwrap(); let original = StructWithCombinedFieldAttrs { @@ -769,10 +769,10 @@ mod compatible_v2 { #[test] fn test_compatible_mode_v1_to_v2() { // Serialize with V1, deserialize with V2 (forward compatibility) - let mut fory_v1 = Fory::builder().compatible(true).build(); + let mut fory_v1 = Fory::builder().xlang(false).compatible(true).build(); fory_v1.register::(100).unwrap(); - let mut fory_v2 = Fory::builder().compatible(true).build(); + let mut fory_v2 = Fory::builder().xlang(false).compatible(true).build(); fory_v2.register::(100).unwrap(); let user_v1 = compatible_v1::UserV1 { @@ -794,10 +794,10 @@ fn test_compatible_mode_v1_to_v2() { #[test] fn test_compatible_mode_v2_to_v1() { // Serialize with V2, deserialize with V1 (backward compatibility) - let mut fory_v1 = Fory::builder().compatible(true).build(); + let mut fory_v1 = Fory::builder().xlang(false).compatible(true).build(); fory_v1.register::(100).unwrap(); - let mut fory_v2 = Fory::builder().compatible(true).build(); + let mut fory_v2 = Fory::builder().xlang(false).compatible(true).build(); fory_v2.register::(100).unwrap(); let user_v2 = compatible_v2::UserV2 { @@ -850,12 +850,12 @@ mod compatible_reorder_v2 { #[test] fn test_compatible_mode_field_reorder() { // Test that field IDs allow fields to be reordered between versions - let mut fory_v1 = Fory::builder().compatible(true).build(); + let mut fory_v1 = Fory::builder().xlang(false).compatible(true).build(); fory_v1 .register::(200) .unwrap(); - let mut fory_v2 = Fory::builder().compatible(true).build(); + let mut fory_v2 = Fory::builder().xlang(false).compatible(true).build(); fory_v2 .register::(200) .unwrap(); @@ -909,12 +909,12 @@ mod compatible_remove_field_v2 { #[test] fn test_compatible_mode_field_removed() { // Test that removed fields are handled in compatible mode - let mut fory_v1 = Fory::builder().compatible(true).build(); + let mut fory_v1 = Fory::builder().xlang(false).compatible(true).build(); fory_v1 .register::(300) .unwrap(); - let mut fory_v2 = Fory::builder().compatible(true).build(); + let mut fory_v2 = Fory::builder().xlang(false).compatible(true).build(); fory_v2 .register::(300) .unwrap(); @@ -948,7 +948,7 @@ struct StructWithSkipAndId { #[test] fn test_skip_with_field_id() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(350).unwrap(); let original = StructWithSkipAndId { @@ -968,7 +968,7 @@ fn test_skip_with_field_id() { #[test] fn test_compatible_mode_roundtrip() { // Test full roundtrip with compatible mode and field IDs - let mut fory = Fory::builder().compatible(true).build(); + let mut fory = Fory::builder().xlang(false).compatible(true).build(); fory.register::(400).unwrap(); let original = compatible_v2::UserV2 { @@ -1025,12 +1025,12 @@ fn test_field_id_payload_compatible_mode() { // Test that structs with field IDs produce smaller payloads in compatible mode. // Field IDs are encoded as compact 1-2 byte integers instead of full field names, // following the xlang serialization spec (TAG_ID encoding with 2-bit marker 0b11). - let mut fory_compact = Fory::builder().compatible(true).build(); + let mut fory_compact = Fory::builder().xlang(false).compatible(true).build(); fory_compact .register::(500) .unwrap(); - let mut fory_verbose = Fory::builder().compatible(true).build(); + let mut fory_verbose = Fory::builder().xlang(false).compatible(true).build(); fory_verbose .register::(501) .unwrap(); diff --git a/rust/tests/tests/test_fory.rs b/rust/tests/tests/test_fory.rs index 596ac11883..e218f19528 100644 --- a/rust/tests/tests/test_fory.rs +++ b/rust/tests/tests/test_fory.rs @@ -33,7 +33,7 @@ fn test_nested_struct_register_order() { value: i32, } - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); // outer struct registered first. building fields info should be executed lazily, // otherwise the inner struct won't be found. fory.register::(100).unwrap(); @@ -55,7 +55,7 @@ fn test_serialize_to_appends_bytes() { y: i32, } - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(100).unwrap(); let p1 = Point { x: 1, y: 2 }; let p2 = Point { x: -3, y: 4 }; @@ -90,7 +90,7 @@ fn test_serialize_to_detailed() { name: String, } - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(100).unwrap(); fory.register::(101).unwrap(); @@ -265,7 +265,7 @@ fn test_unregistered_type_error_message() { inner: Inner, } - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); // Register only the outer type; inner type is intentionally not registered fory.register::(200).unwrap(); let obj = Outer { @@ -331,11 +331,12 @@ fn test_type_mismatch_error_shows_type_name() { #[test] fn test_size_guardrail_configuration_accessors() { - let default_fory = Fory::default(); + let default_fory = Fory::builder().xlang(false).build(); assert_eq!(default_fory.get_max_binary_size(), 64 * 1024 * 1024); assert_eq!(default_fory.get_max_collection_size(), 1024 * 1024); let configured_fory = Fory::builder() + .xlang(false) .max_binary_size(4096) .max_collection_size(128) .build(); @@ -345,11 +346,11 @@ fn test_size_guardrail_configuration_accessors() { #[test] fn test_max_binary_size_does_not_limit_string_reads() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let original = "this string should not be treated as binary".repeat(4); let serialized = fory.serialize(&original).unwrap(); - let limited_fory = Fory::builder().max_binary_size(4).build(); + let limited_fory = Fory::builder().xlang(false).max_binary_size(4).build(); let deserialized: String = limited_fory.deserialize(&serialized).unwrap(); assert_eq!(deserialized, original); diff --git a/rust/tests/tests/test_generate_default.rs b/rust/tests/tests/test_generate_default.rs index 9e26ef7e34..727b881ffe 100644 --- a/rust/tests/tests/test_generate_default.rs +++ b/rust/tests/tests/test_generate_default.rs @@ -41,7 +41,7 @@ impl Default for NodeWithCustomDefault { #[test] fn test_no_default_conflict() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(1).unwrap(); let node = NodeWithCustomDefault { @@ -80,7 +80,7 @@ impl Default for StatusWithCustomDefault { #[test] fn test_enum_no_default_conflict() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(2).unwrap(); let status = StatusWithCustomDefault::Active; @@ -105,7 +105,7 @@ struct StructWithGeneratedDefault { #[test] fn test_generate_default_struct() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(3).unwrap(); let data = StructWithGeneratedDefault { value: 42 }; @@ -129,7 +129,7 @@ enum EnumWithGeneratedDefault { #[test] fn test_generate_default_enum() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(4).unwrap(); let data = EnumWithGeneratedDefault::Second; @@ -157,7 +157,7 @@ impl Default for StructWithoutGeneratedDefault { #[test] fn test_generate_default_false() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(5).unwrap(); let data = StructWithoutGeneratedDefault { value: 42 }; diff --git a/rust/tests/tests/test_lifecycle_guard.rs b/rust/tests/tests/test_lifecycle_guard.rs index a12dd63e87..56e8eb64bf 100644 --- a/rust/tests/tests/test_lifecycle_guard.rs +++ b/rust/tests/tests/test_lifecycle_guard.rs @@ -41,7 +41,7 @@ struct Color { // Postive tests #[test] fn test_register_before_serialize_succeeds() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); // registration before any serialize/deserialize should succeed. assert!(fory.register::(100).is_ok()); @@ -53,7 +53,7 @@ fn test_register_before_serialize_succeeds() { #[test] fn test_multiple_registrations_before_serialize_succeed() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); assert!(fory.register::(100).is_ok()); assert!(fory.register::(101).is_ok()); @@ -65,7 +65,7 @@ fn test_multiple_registrations_before_serialize_succeed() { #[test] fn test_register_by_name_requires_type_name() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); let err = fory .register_by_name::("com.example", "") .unwrap_err(); @@ -74,7 +74,7 @@ fn test_register_by_name_requires_type_name() { #[test] fn test_register_by_name_rejects_duplicate_identity() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register_by_name::("com.example", "Point") .unwrap(); let err = fory @@ -88,7 +88,7 @@ fn test_register_by_name_rejects_duplicate_identity() { /// ensures `register()` is forbidden after `serialize()` triggers snapshot init. #[test] fn test_register_after_serialize_fails() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(100).unwrap(); // first serialize, this initializes the final_type_resolver snapshot. @@ -115,7 +115,7 @@ fn test_register_after_serialize_fails() { /// Ensures `register()` is forbidden after `deserialize()` triggers snapshot init. #[test] fn test_register_after_deserialize_fails() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(100).unwrap(); let point = Point { x: 5, y: 10 }; @@ -133,7 +133,7 @@ fn test_register_after_deserialize_fails() { /// Ensures `register_by_name()` is forbidden after snapshot init. #[test] fn test_register_by_name_after_serialize_fails() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(100).unwrap(); let _bytes = fory.serialize(&Point { x: 0, y: 0 }).unwrap(); @@ -146,7 +146,7 @@ fn test_register_by_name_after_serialize_fails() { /// Ensures `register_by_name()` with a non-empty namespace is forbidden after snapshot init. #[test] fn test_register_by_name_with_namespace_after_serialize_fails() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(100).unwrap(); let _bytes = fory.serialize(&Point { x: 0, y: 0 }).unwrap(); @@ -159,7 +159,7 @@ fn test_register_by_name_with_namespace_after_serialize_fails() { /// Ensures `register_serializer()` is forbidden after snapshot init. #[test] fn test_register_serializer_after_serialize_fails() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(100).unwrap(); let _bytes = fory.serialize(&Point { x: 0, y: 0 }).unwrap(); @@ -172,7 +172,7 @@ fn test_register_serializer_after_serialize_fails() { /// Ensures `register_serializer_by_name()` is forbidden after snapshot init. #[test] fn test_register_serializer_by_name_after_serialize_fails() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(100).unwrap(); let _bytes = fory.serialize(&Point { x: 0, y: 0 }).unwrap(); @@ -185,7 +185,7 @@ fn test_register_serializer_by_name_after_serialize_fails() { /// Ensures `register_serializer_by_name()` with a non-empty namespace is forbidden after snapshot init. #[test] fn test_register_serializer_by_name_with_namespace_after_serialize_fails() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(100).unwrap(); let _bytes = fory.serialize(&Point { x: 0, y: 0 }).unwrap(); @@ -198,7 +198,7 @@ fn test_register_serializer_by_name_with_namespace_after_serialize_fails() { /// Ensures `register_generic_trait()` is forbidden after snapshot init. #[test] fn test_register_generic_trait_after_serialize_fails() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(100).unwrap(); let _bytes = fory.serialize(&Point { x: 0, y: 0 }).unwrap(); @@ -211,7 +211,7 @@ fn test_register_generic_trait_after_serialize_fails() { /// Ensures `register_union()` is forbidden after snapshot init. #[test] fn test_register_union_after_serialize_fails() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(100).unwrap(); let _bytes = fory.serialize(&Point { x: 0, y: 0 }).unwrap(); @@ -224,7 +224,7 @@ fn test_register_union_after_serialize_fails() { /// Ensures `register_union_by_name()` is forbidden after snapshot init. #[test] fn test_register_union_by_name_after_serialize_fails() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(100).unwrap(); let _bytes = fory.serialize(&Point { x: 0, y: 0 }).unwrap(); @@ -237,7 +237,7 @@ fn test_register_union_by_name_after_serialize_fails() { /// Ensures `register_union_by_name()` with a non-empty namespace is forbidden after snapshot init. #[test] fn test_register_union_by_name_with_namespace_after_serialize_fails() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(100).unwrap(); let _bytes = fory.serialize(&Point { x: 0, y: 0 }).unwrap(); @@ -250,7 +250,7 @@ fn test_register_union_by_name_with_namespace_after_serialize_fails() { // Edge-case #[test] fn test_late_registration_error_message_is_descriptive() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(100).unwrap(); let _bytes = fory.serialize(&Point { x: 0, y: 0 }).unwrap(); @@ -277,7 +277,7 @@ fn test_late_registration_error_message_is_descriptive() { #[test] fn test_serialize_multiple_times_after_registration_succeeds() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(100).unwrap(); let p1 = Point { x: 1, y: 2 }; diff --git a/rust/tests/tests/test_list.rs b/rust/tests/tests/test_list.rs index 744204860e..6b37f109be 100644 --- a/rust/tests/tests/test_list.rs +++ b/rust/tests/tests/test_list.rs @@ -21,7 +21,7 @@ use std::collections::{LinkedList, VecDeque}; #[test] fn test_vecdeque_i32() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let mut deque = VecDeque::new(); deque.push_back(1); deque.push_back(2); @@ -33,7 +33,7 @@ fn test_vecdeque_i32() { #[test] fn test_vecdeque_empty() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let deque: VecDeque = VecDeque::new(); let bin = fory.serialize(&deque).unwrap(); let obj: VecDeque = fory.deserialize(&bin).expect("deserialize"); @@ -42,7 +42,7 @@ fn test_vecdeque_empty() { #[test] fn test_vecdeque_string() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let mut deque = VecDeque::new(); deque.push_back("hello".to_string()); deque.push_back("world".to_string()); @@ -53,7 +53,7 @@ fn test_vecdeque_string() { #[test] fn test_vecdeque_f64() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let mut deque = VecDeque::new(); deque.push_back(1.5); deque.push_back(2.5); @@ -65,7 +65,7 @@ fn test_vecdeque_f64() { #[test] fn test_linkedlist_i32() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let mut list = LinkedList::new(); list.push_back(1); list.push_back(2); @@ -77,7 +77,7 @@ fn test_linkedlist_i32() { #[test] fn test_linkedlist_empty() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let list: LinkedList = LinkedList::new(); let bin = fory.serialize(&list).unwrap(); let obj: LinkedList = fory.deserialize(&bin).expect("deserialize"); @@ -86,7 +86,7 @@ fn test_linkedlist_empty() { #[test] fn test_linkedlist_string() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let mut list = LinkedList::new(); list.push_back("foo".to_string()); list.push_back("bar".to_string()); @@ -97,7 +97,7 @@ fn test_linkedlist_string() { #[test] fn test_linkedlist_bool() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let mut list = LinkedList::new(); list.push_back(true); list.push_back(false); @@ -116,7 +116,7 @@ struct CollectionStruct { #[test] fn test_struct_with_collections() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register_by_name::("", "CollectionStruct") .unwrap(); @@ -142,7 +142,7 @@ fn test_struct_with_collections() { #[test] fn test_vec_float16_basic() { use fory_core::types::float16::float16; - let fory = fory_core::fory::Fory::default(); + let fory = fory_core::fory::Fory::builder().xlang(false).build(); let vec: Vec = vec![ float16::from_f32(1.0), float16::from_f32(2.5), @@ -160,7 +160,7 @@ fn test_vec_float16_basic() { #[test] fn test_vec_float16_special_values() { use fory_core::types::float16::float16; - let fory = fory_core::fory::Fory::default(); + let fory = fory_core::fory::Fory::builder().xlang(false).build(); let vec: Vec = vec![ float16::INFINITY, float16::NEG_INFINITY, @@ -182,7 +182,7 @@ fn test_vec_float16_special_values() { #[test] fn test_vec_float16_empty() { use fory_core::types::float16::float16; - let fory = fory_core::fory::Fory::default(); + let fory = fory_core::fory::Fory::builder().xlang(false).build(); let vec: Vec = vec![]; let bin = fory.serialize(&vec).unwrap(); let obj: Vec = fory @@ -194,7 +194,7 @@ fn test_vec_float16_empty() { #[test] fn test_vec_bfloat16_basic() { use fory_core::types::bfloat16::bfloat16; - let fory = fory_core::fory::Fory::default(); + let fory = fory_core::fory::Fory::builder().xlang(false).build(); let vec: Vec = vec![ bfloat16::from_f32(1.0), bfloat16::from_f32(2.5), @@ -212,7 +212,7 @@ fn test_vec_bfloat16_basic() { #[test] fn test_vec_bfloat16_special_values() { use fory_core::types::bfloat16::bfloat16; - let fory = fory_core::fory::Fory::default(); + let fory = fory_core::fory::Fory::builder().xlang(false).build(); let vec: Vec = vec![ bfloat16::INFINITY, bfloat16::NEG_INFINITY, @@ -235,11 +235,11 @@ fn test_vec_bfloat16_special_values() { #[test] fn test_vec_max_collection_size_guardrail() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let original = vec!["alpha".to_string(), "beta".to_string(), "gamma".to_string()]; let serialized = fory.serialize(&original).unwrap(); - let limited_fory = Fory::builder().max_collection_size(2).build(); + let limited_fory = Fory::builder().xlang(false).max_collection_size(2).build(); let err = limited_fory .deserialize::>(&serialized) .expect_err("expected vec deserialization to fail on max_collection_size"); diff --git a/rust/tests/tests/test_map.rs b/rust/tests/tests/test_map.rs index 62436c6015..bfecda5038 100644 --- a/rust/tests/tests/test_map.rs +++ b/rust/tests/tests/test_map.rs @@ -21,7 +21,7 @@ use std::collections::{BTreeMap, HashMap}; #[test] fn test_hashmap_string() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let mut map = HashMap::new(); map.insert("key1".to_string(), "value1".to_string()); map.insert("key2".to_string(), "value2".to_string()); @@ -32,7 +32,7 @@ fn test_hashmap_string() { #[test] fn test_btreemap_string() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let mut map = BTreeMap::new(); map.insert("key1".to_string(), "value1".to_string()); map.insert("key2".to_string(), "value2".to_string()); @@ -49,7 +49,7 @@ struct MapContainer { #[test] fn test_struct_with_maps() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register_by_name::("", "MapContainer") .unwrap(); let mut hash_map = HashMap::new(); @@ -70,7 +70,7 @@ fn test_struct_with_maps() { #[test] fn test_hashmap_max_collection_size_guardrail() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let map = HashMap::from([ ("key1".to_string(), 1_i32), ("key2".to_string(), 2_i32), @@ -78,7 +78,7 @@ fn test_hashmap_max_collection_size_guardrail() { ]); let serialized = fory.serialize(&map).unwrap(); - let limited_fory = Fory::builder().max_collection_size(2).build(); + let limited_fory = Fory::builder().xlang(false).max_collection_size(2).build(); let err = limited_fory .deserialize::>(&serialized) .expect_err("expected hashmap deserialization to fail on max_collection_size"); @@ -95,7 +95,7 @@ fn test_hashmap_max_collection_size_guardrail() { #[test] fn test_btreemap_max_collection_size_guardrail() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let map = BTreeMap::from([ ("key1".to_string(), 1_i32), ("key2".to_string(), 2_i32), @@ -103,7 +103,7 @@ fn test_btreemap_max_collection_size_guardrail() { ]); let serialized = fory.serialize(&map).unwrap(); - let limited_fory = Fory::builder().max_collection_size(2).build(); + let limited_fory = Fory::builder().xlang(false).max_collection_size(2).build(); let err = limited_fory .deserialize::>(&serialized) .expect_err("expected btreemap deserialization to fail on max_collection_size"); diff --git a/rust/tests/tests/test_marker.rs b/rust/tests/tests/test_marker.rs index 48401593f0..90e2ade353 100644 --- a/rust/tests/tests/test_marker.rs +++ b/rust/tests/tests/test_marker.rs @@ -39,7 +39,7 @@ struct StructWithPhantom { #[test] fn test_struct_with_phantom_data() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(100).unwrap(); let value = StructWithPhantom { @@ -63,7 +63,7 @@ struct StructWithMultiplePhantom { #[test] fn test_struct_with_multiple_phantom_data() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(101).unwrap(); let value = StructWithMultiplePhantom { @@ -93,7 +93,7 @@ struct OuterWithPhantom { #[test] fn test_nested_struct_with_phantom_data() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(102).unwrap(); fory.register::(103).unwrap(); diff --git a/rust/tests/tests/test_max_dyn_depth.rs b/rust/tests/tests/test_max_dyn_depth.rs index 1e2eff8cef..36634f55aa 100644 --- a/rust/tests/tests/test_max_dyn_depth.rs +++ b/rust/tests/tests/test_max_dyn_depth.rs @@ -33,6 +33,7 @@ fn test_max_dyn_depth_exceeded_box_dyn_any() { } for compatible in [false, true] { let mut fory = Fory::builder() + .xlang(false) .max_dyn_depth(2) .compatible(compatible) .build(); @@ -69,7 +70,7 @@ fn test_max_dyn_depth_within_limit_box_dyn_any() { if fory_core::error::should_panic_on_error() { return; } - let mut fory = Fory::builder().max_dyn_depth(3).build(); + let mut fory = Fory::builder().xlang(false).max_dyn_depth(3).build(); fory.register::(100).unwrap(); let level3 = Container { @@ -96,7 +97,7 @@ fn test_max_dyn_depth_default_exceeded() { if fory_core::error::should_panic_on_error() { return; } - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(100).unwrap(); let mut current = Container { @@ -127,7 +128,7 @@ fn test_max_dyn_depth_default_within_limit() { if fory_core::error::should_panic_on_error() { return; } - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(100).unwrap(); let mut current = Container { diff --git a/rust/tests/tests/test_multi_thread.rs b/rust/tests/tests/test_multi_thread.rs index e02c7a9076..9af71b4fcb 100644 --- a/rust/tests/tests/test_multi_thread.rs +++ b/rust/tests/tests/test_multi_thread.rs @@ -23,7 +23,7 @@ use std::thread; #[test] fn test_simple_multi_thread() { - let fory = Arc::new(Fory::default()); + let fory = Arc::new(Fory::builder().xlang(false).build()); let src: HashSet<_> = [41, 42, 43, 45, 46, 47].into_iter().collect(); // serialize let mut handles = vec![]; @@ -60,7 +60,7 @@ fn test_struct_multi_thread() { struct Item1 { f1: i32, } - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(101).unwrap(); let fory = Arc::new(fory); let src: HashSet<_> = [ @@ -129,7 +129,7 @@ fn test_multiple_threads_shared_fory() { updated_at: u64, } - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(2) .expect("register UserSessionMetrics"); let shared_fory = Arc::new(fory); diff --git a/rust/tests/tests/test_mutex.rs b/rust/tests/tests/test_mutex.rs index e22c68ba0c..ffe041549c 100644 --- a/rust/tests/tests/test_mutex.rs +++ b/rust/tests/tests/test_mutex.rs @@ -20,7 +20,7 @@ use std::sync::{Arc, Mutex}; #[test] fn test_mutex_basic_serialization() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let m = Mutex::new(42i32); let serialized = fory.serialize(&m).unwrap(); let deserialized: Mutex = fory.deserialize(&serialized).unwrap(); @@ -29,7 +29,7 @@ fn test_mutex_basic_serialization() { #[test] fn test_arc_mutex_serialization() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let arc_mutex = Arc::new(Mutex::new(String::from("hello"))); let serialized = fory.serialize(&arc_mutex).unwrap(); let deserialized: Arc> = fory.deserialize(&serialized).unwrap(); @@ -38,7 +38,7 @@ fn test_arc_mutex_serialization() { #[test] fn test_arc_mutex_sharing_preserved() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let data = Arc::new(Mutex::new(123i32)); let list = vec![data.clone(), data.clone()]; diff --git a/rust/tests/tests/test_one_struct.rs b/rust/tests/tests/test_one_struct.rs index a4bfd9f5d9..c8d9686e89 100644 --- a/rust/tests/tests/test_one_struct.rs +++ b/rust/tests/tests/test_one_struct.rs @@ -46,8 +46,8 @@ fn test_simple() { f7: i16, last: i8, } - let mut fory1 = Fory::builder().compatible(true).build(); - let mut fory2 = Fory::builder().compatible(true).build(); + let mut fory1 = Fory::builder().xlang(false).compatible(true).build(); + let mut fory2 = Fory::builder().xlang(false).compatible(true).build(); fory1.register::(999).unwrap(); fory2.register::(999).unwrap(); let animal: Animal1 = Animal1 { diff --git a/rust/tests/tests/test_rc_arc.rs b/rust/tests/tests/test_rc_arc.rs index 1cc93f821d..c2ec6d7515 100644 --- a/rust/tests/tests/test_rc_arc.rs +++ b/rust/tests/tests/test_rc_arc.rs @@ -31,7 +31,7 @@ struct NestedData { #[test] fn test_rc_string_serialization() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let data = String::from("Hello, Rc!"); let rc_data = Rc::new(data); @@ -45,7 +45,7 @@ fn test_rc_string_serialization() { #[test] fn test_arc_string_serialization() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let data = String::from("Hello, Arc!"); let arc_data = Arc::new(data); @@ -59,7 +59,7 @@ fn test_arc_string_serialization() { #[test] fn test_rc_number_serialization() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let rc_number = Rc::new(42i32); @@ -72,7 +72,7 @@ fn test_rc_number_serialization() { #[test] fn test_arc_number_serialization() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let arc_number = Arc::new(100i64); @@ -85,7 +85,7 @@ fn test_arc_number_serialization() { #[test] fn test_rc_in_collections() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let string1 = Rc::new(String::from("First")); let string2 = Rc::new(String::from("Second")); @@ -106,7 +106,7 @@ fn test_rc_in_collections() { #[test] fn test_arc_in_collections() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let number1 = Arc::new(123i32); let number2 = Arc::new(456i32); @@ -127,7 +127,7 @@ fn test_arc_in_collections() { #[test] fn test_rc_vec_serialization() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let data = vec![1, 2, 3, 4, 5]; let rc_data = Rc::new(data); @@ -141,7 +141,7 @@ fn test_rc_vec_serialization() { #[test] fn test_arc_vec_serialization() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let data = vec![String::from("a"), String::from("b"), String::from("c")]; let arc_data = Arc::new(data); @@ -155,7 +155,7 @@ fn test_arc_vec_serialization() { #[test] fn test_mixed_rc_arc_serialization() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); // Test basic types wrapped in Rc/Arc let rc_number = Rc::new(42i32); @@ -173,7 +173,7 @@ fn test_mixed_rc_arc_serialization() { #[test] fn test_nested_rc_arc() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(100).unwrap(); // Test Rc containing Arc with allowed struct type @@ -190,7 +190,7 @@ fn test_nested_rc_arc() { #[test] fn test_rc_arc_with_hashmaps() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let string_data = Arc::new(String::from("shared")); @@ -212,7 +212,7 @@ fn test_rc_arc_with_hashmaps() { #[test] fn test_arc_serialization_basic() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let arc = Arc::new(42i32); let serialized = fory.serialize(&arc).unwrap(); @@ -223,7 +223,7 @@ fn test_arc_serialization_basic() { #[test] fn test_arc_shared_reference() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let arc1 = Arc::new(String::from("shared")); let serialized = fory.serialize(&arc1).unwrap(); @@ -234,7 +234,7 @@ fn test_arc_shared_reference() { #[test] fn test_arc_shared_reference_in_vec() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let shared = Arc::new(String::from("shared_value")); let vec = vec![shared.clone(), shared.clone(), shared.clone()]; @@ -254,7 +254,7 @@ fn test_arc_shared_reference_in_vec() { #[test] fn test_arc_multiple_shared_references() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let shared1 = Arc::new(42i32); let shared2 = Arc::new(100i32); @@ -287,14 +287,14 @@ fn test_arc_multiple_shared_references() { fn test_arc_thread_safety() { use std::thread; - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let arc = Arc::new(vec![1, 2, 3, 4, 5]); let serialized = fory.serialize(&arc).unwrap(); // Test that Arc can be sent across threads let handle = thread::spawn(move || { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let deserialized: Arc> = fory.deserialize(&serialized).unwrap(); assert_eq!(*deserialized, vec![1, 2, 3, 4, 5]); }); @@ -304,7 +304,7 @@ fn test_arc_thread_safety() { #[test] fn test_rc_serialization_basic() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let rc = Rc::new(42i32); let serialized = fory.serialize(&rc).unwrap(); @@ -315,7 +315,7 @@ fn test_rc_serialization_basic() { #[test] fn test_rc_shared_reference() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let rc1 = Rc::new(String::from("shared")); let serialized = fory.serialize(&rc1).unwrap(); @@ -326,7 +326,7 @@ fn test_rc_shared_reference() { #[test] fn test_rc_shared_reference_in_vec() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let shared = Rc::new(String::from("shared_value")); let vec = vec![shared.clone(), shared.clone(), shared.clone()]; @@ -346,7 +346,7 @@ fn test_rc_shared_reference_in_vec() { #[test] fn test_rc_multiple_shared_references() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let shared1 = Rc::new(42i32); let shared2 = Rc::new(100i32); diff --git a/rust/tests/tests/test_rc_arc_trait_object.rs b/rust/tests/tests/test_rc_arc_trait_object.rs index 3fff222d49..9699444215 100644 --- a/rust/tests/tests/test_rc_arc_trait_object.rs +++ b/rust/tests/tests/test_rc_arc_trait_object.rs @@ -25,7 +25,7 @@ use std::rc::Rc; use std::sync::Arc; fn fory_compatible() -> Fory { - Fory::builder().compatible(true).build() + Fory::builder().xlang(false).compatible(true).build() } trait Animal: Serializer + Send + Sync { diff --git a/rust/tests/tests/test_refcell.rs b/rust/tests/tests/test_refcell.rs index dae3abf76c..8a714d8728 100644 --- a/rust/tests/tests/test_refcell.rs +++ b/rust/tests/tests/test_refcell.rs @@ -27,7 +27,7 @@ struct Simple { #[test] fn test_rc_refcell_simple() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(3000).unwrap(); let node = Rc::new(RefCell::new(Simple { value: 42 })); @@ -44,7 +44,7 @@ struct Parent { #[test] fn test_rc_refcell_in_struct() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(3001).unwrap(); fory.register::(3002).unwrap(); diff --git a/rust/tests/tests/test_simple_struct.rs b/rust/tests/tests/test_simple_struct.rs index dc3cc5dd95..1587b810a7 100644 --- a/rust/tests/tests/test_simple_struct.rs +++ b/rust/tests/tests/test_simple_struct.rs @@ -29,7 +29,7 @@ fn test_one_field_primitive_non_compatible() { value: i32, } - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(100).unwrap(); let data = Data { value: 42 }; let bytes = fory.serialize(&data).unwrap(); @@ -45,7 +45,7 @@ fn test_one_field_string_non_compatible() { name: String, } - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(101).unwrap(); let data = Data { name: String::from("hello"), @@ -68,8 +68,8 @@ fn test_compatible_field_type_change() { value: Option, } - let mut fory1 = Fory::builder().compatible(true).build(); - let mut fory2 = Fory::builder().compatible(true).build(); + let mut fory1 = Fory::builder().xlang(false).compatible(true).build(); + let mut fory2 = Fory::builder().xlang(false).compatible(true).build(); fory1.register::(100).unwrap(); fory2.register::(100).unwrap(); @@ -130,8 +130,8 @@ fn test_compatible_to_empty_struct() { #[derive(ForyStruct, Debug)] struct EmptyData {} - let mut fory1 = Fory::builder().compatible(true).build(); - let mut fory2 = Fory::builder().compatible(true).build(); + let mut fory1 = Fory::builder().xlang(false).compatible(true).build(); + let mut fory2 = Fory::builder().xlang(false).compatible(true).build(); fory1.register::(101).unwrap(); fory2.register::(101).unwrap(); @@ -156,8 +156,8 @@ fn test_compatible_from_empty_struct() { name: String, } - let mut fory1 = Fory::builder().compatible(true).build(); - let mut fory2 = Fory::builder().compatible(true).build(); + let mut fory1 = Fory::builder().xlang(false).compatible(true).build(); + let mut fory2 = Fory::builder().xlang(false).compatible(true).build(); fory1.register::(102).unwrap(); fory2.register::(102).unwrap(); @@ -179,8 +179,8 @@ fn test_compatible_vec_to_empty_struct() { #[derive(ForyStruct, Debug)] struct EmptyData {} - let mut fory1 = Fory::builder().compatible(true).build(); - let mut fory2 = Fory::builder().compatible(true).build(); + let mut fory1 = Fory::builder().xlang(false).compatible(true).build(); + let mut fory2 = Fory::builder().xlang(false).compatible(true).build(); fory1.register::(101).unwrap(); fory2.register::(101).unwrap(); @@ -204,8 +204,8 @@ fn test_compatible_map_to_empty_struct() { #[derive(ForyStruct, Debug)] struct EmptyData {} - let mut fory1 = Fory::builder().compatible(true).build(); - let mut fory2 = Fory::builder().compatible(true).build(); + let mut fory1 = Fory::builder().xlang(false).compatible(true).build(); + let mut fory2 = Fory::builder().xlang(false).compatible(true).build(); fory1.register::(101).unwrap(); fory2.register::(101).unwrap(); @@ -229,7 +229,7 @@ fn test_struct_with_float16_fields() { arr_field: [float16; 3], } - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(200).unwrap(); let obj = Float16Data { @@ -271,7 +271,7 @@ fn test_struct_with_bfloat16_fields() { arr_field: [bfloat16; 3], } - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(201).unwrap(); let obj = BFloat16Data { diff --git a/rust/tests/tests/test_skip_fields.rs b/rust/tests/tests/test_skip_fields.rs index 51bc326520..25b00991cb 100644 --- a/rust/tests/tests/test_skip_fields.rs +++ b/rust/tests/tests/test_skip_fields.rs @@ -82,7 +82,7 @@ enum TestEnumSkip { #[test] fn test_basic_skip_functionality() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(1).unwrap(); let original = TestSkipFields { @@ -108,7 +108,7 @@ fn test_basic_skip_functionality() { #[test] fn test_nested_skip_functionality() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(2).unwrap(); fory.register::(3).unwrap(); @@ -129,7 +129,7 @@ fn test_nested_skip_functionality() { #[test] fn test_multiple_skip_fields() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(3).unwrap(); let original = MultipleSkipFields { @@ -152,7 +152,7 @@ fn test_multiple_skip_fields() { #[test] fn test_all_fields_skipped() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(4).unwrap(); let original = AllFieldsSkipped { @@ -171,7 +171,7 @@ fn test_all_fields_skipped() { #[test] fn test_complex_nested_skip() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(5).unwrap(); fory.register::(6).unwrap(); @@ -206,7 +206,7 @@ fn test_complex_nested_skip() { #[test] fn test_enum_skip() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(6).unwrap(); let original_v1 = TestEnumSkip::Pending; @@ -224,7 +224,7 @@ fn test_enum_skip() { #[test] fn test_skip_serialization_size() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(10).unwrap(); let with_skip = TestSkipFields { @@ -274,7 +274,7 @@ fn test_skip_with_different_types() { field4: i64, } - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(12).unwrap(); let original = MultiTypeSkip { @@ -357,7 +357,7 @@ fn test_trait_object_serialization() { skipped_animal: Box, } - let mut fory = Fory::builder().compatible(true).build(); + let mut fory = Fory::builder().xlang(false).compatible(true).build(); fory.register::(100).unwrap(); fory.register::(101).unwrap(); fory.register::(102).unwrap(); @@ -397,7 +397,7 @@ fn test_trait_object_serialization() { normal_field: String, } - let mut fory = Fory::builder().compatible(true).build(); + let mut fory = Fory::builder().xlang(false).compatible(true).build(); fory.register::(106).unwrap(); let complex = ComplexSkipExample { diff --git a/rust/tests/tests/test_trait_object.rs b/rust/tests/tests/test_trait_object.rs index c06c0171b5..9544c4bdb9 100644 --- a/rust/tests/tests/test_trait_object.rs +++ b/rust/tests/tests/test_trait_object.rs @@ -22,7 +22,7 @@ use fory_derive::ForyStruct; use std::collections::{HashMap, HashSet}; fn fory_compatible() -> Fory { - Fory::builder().compatible(true).build() + Fory::builder().xlang(false).compatible(true).build() } #[test] diff --git a/rust/tests/tests/test_tuple.rs b/rust/tests/tests/test_tuple.rs index 60663a7b23..0b2657c630 100644 --- a/rust/tests/tests/test_tuple.rs +++ b/rust/tests/tests/test_tuple.rs @@ -26,7 +26,7 @@ type ComplexNestedTuple = ((Vec, Option), (Rc, (i32, f64))); // Test homogeneous tuples with primitive types #[test] fn test_homogeneous_tuple_i32() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let tuple = (1i32, 2i32, 3i32); let bin = fory.serialize(&tuple).unwrap(); let obj: (i32, i32, i32) = fory.deserialize(&bin).expect("deserialize"); @@ -35,7 +35,7 @@ fn test_homogeneous_tuple_i32() { #[test] fn test_homogeneous_tuple_f64() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let tuple = (1.5f64, 2.5f64, 3.5f64, 4.5f64); let bin = fory.serialize(&tuple).unwrap(); let obj: (f64, f64, f64, f64) = fory.deserialize(&bin).expect("deserialize"); @@ -44,7 +44,7 @@ fn test_homogeneous_tuple_f64() { #[test] fn test_homogeneous_tuple_string() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let tuple = ("hello".to_string(), "world".to_string(), "fory".to_string()); let bin = fory.serialize(&tuple).unwrap(); let obj: (String, String, String) = fory.deserialize(&bin).expect("deserialize"); @@ -54,7 +54,7 @@ fn test_homogeneous_tuple_string() { // Test heterogeneous tuples with different types #[test] fn test_heterogeneous_tuple_simple() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let tuple = (42i32, "hello".to_string()); let bin = fory.serialize(&tuple).unwrap(); let obj: (i32, String) = fory.deserialize(&bin).expect("deserialize"); @@ -63,7 +63,7 @@ fn test_heterogeneous_tuple_simple() { #[test] fn test_heterogeneous_tuple_complex() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let tuple = (42i32, "hello".to_string(), PI_F64, true, vec![1, 2, 3]); let bin = fory.serialize(&tuple).unwrap(); let obj: (i32, String, f64, bool, Vec) = fory.deserialize(&bin).expect("deserialize"); @@ -73,7 +73,7 @@ fn test_heterogeneous_tuple_complex() { // Test single element tuple #[test] fn test_single_element_tuple() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let tuple = (42i32,); let bin = fory.serialize(&tuple).unwrap(); let obj: (i32,) = fory.deserialize(&bin).expect("deserialize"); @@ -83,7 +83,7 @@ fn test_single_element_tuple() { // Test tuples with Option types #[test] fn test_tuple_with_options() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let tuple = (Some(42i32), None::, Some(100i32)); let bin = fory.serialize(&tuple).unwrap(); let obj: (Option, Option, Option) = fory.deserialize(&bin).expect("deserialize"); @@ -92,7 +92,7 @@ fn test_tuple_with_options() { #[test] fn test_heterogeneous_tuple_with_options() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let tuple = (Some(42i32), "hello".to_string(), None::); let bin = fory.serialize(&tuple).unwrap(); let obj: (Option, String, Option) = fory.deserialize(&bin).expect("deserialize"); @@ -102,7 +102,7 @@ fn test_heterogeneous_tuple_with_options() { // Test tuples with collections #[test] fn test_tuple_with_vectors() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let tuple = (vec![1, 2, 3], vec![4, 5, 6]); let bin = fory.serialize(&tuple).unwrap(); let obj: (Vec, Vec) = fory.deserialize(&bin).expect("deserialize"); @@ -111,7 +111,7 @@ fn test_tuple_with_vectors() { #[test] fn test_tuple_with_mixed_collections() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let tuple = (vec![1, 2, 3], vec!["a".to_string(), "b".to_string()]); let bin = fory.serialize(&tuple).unwrap(); let obj: (Vec, Vec) = fory.deserialize(&bin).expect("deserialize"); @@ -121,7 +121,7 @@ fn test_tuple_with_mixed_collections() { // Test nested tuples #[test] fn test_nested_tuples() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let tuple = ((1i32, 2i32), (3i32, 4i32)); let bin = fory.serialize(&tuple).unwrap(); let obj: ((i32, i32), (i32, i32)) = fory.deserialize(&bin).expect("deserialize"); @@ -130,7 +130,7 @@ fn test_nested_tuples() { #[test] fn test_deeply_nested_tuples() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let tuple = (1i32, (2i32, (3i32, 4i32))); let bin = fory.serialize(&tuple).unwrap(); let obj: (i32, (i32, (i32, i32))) = fory.deserialize(&bin).expect("deserialize"); @@ -140,7 +140,7 @@ fn test_deeply_nested_tuples() { // Test large tuples #[test] fn test_large_homogeneous_tuple() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let tuple = ( 1i32, 2i32, 3i32, 4i32, 5i32, 6i32, 7i32, 8i32, 9i32, 10i32, 11i32, 12i32, ); @@ -152,7 +152,7 @@ fn test_large_homogeneous_tuple() { #[test] fn test_large_heterogeneous_tuple() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let tuple = ( 1i32, 2i64, @@ -172,7 +172,7 @@ fn test_large_heterogeneous_tuple() { // Test tuples with Rc/Arc (shared references) #[test] fn test_tuple_with_rc() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let value = Rc::new(42i32); let tuple = (Rc::clone(&value), Rc::clone(&value)); let bin = fory.serialize(&tuple).unwrap(); @@ -185,7 +185,7 @@ fn test_tuple_with_rc() { // Test tuples with bool #[test] fn test_homogeneous_tuple_bool() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let tuple = (true, false, true, false); let bin = fory.serialize(&tuple).unwrap(); let obj: (bool, bool, bool, bool) = fory.deserialize(&bin).expect("deserialize"); @@ -195,7 +195,7 @@ fn test_homogeneous_tuple_bool() { // Test tuples with u8, u16, u32, u64 #[test] fn test_homogeneous_tuple_unsigned() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let tuple_u8 = (1u8, 2u8, 3u8); let bin = fory.serialize(&tuple_u8).unwrap(); let obj: (u8, u8, u8) = fory.deserialize(&bin).expect("deserialize"); @@ -348,7 +348,7 @@ fn test_struct_with_complex_tuple_fields_xlang() { // Test unit type () - the empty tuple / 0-element tuple #[test] fn test_tuple_with_unit() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let value: (i32, (), String) = (42, (), "hello".to_string()); let bytes = fory.serialize(&value).unwrap(); @@ -358,7 +358,7 @@ fn test_tuple_with_unit() { #[test] fn test_tuple_with_multiple_units() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let value: ((), i32, (), String, ()) = ((), 42, (), "hello".to_string(), ()); let bytes = fory.serialize(&value).unwrap(); @@ -375,7 +375,7 @@ struct StructWithUnit { #[test] fn test_struct_with_unit_field() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(200).unwrap(); let value = StructWithUnit { diff --git a/rust/tests/tests/test_tuple_compatible.rs b/rust/tests/tests/test_tuple_compatible.rs index 7811d0a21f..74a113bcba 100644 --- a/rust/tests/tests/test_tuple_compatible.rs +++ b/rust/tests/tests/test_tuple_compatible.rs @@ -34,7 +34,7 @@ const PI_F64: f64 = std::f64::consts::PI; /// Test 1: Direct tuple size mismatch - bidirectional serialization #[test] fn test_tuple_size_mismatch() { - let fory = Fory::builder().compatible(true).build(); + let fory = Fory::builder().xlang(false).compatible(true).build(); // Test 1a: Long tuple serialized, short tuple deserialized let long = (42i32, "hello".to_string(), PI_F64, true); @@ -57,7 +57,7 @@ fn test_tuple_size_mismatch() { /// Test 2: Tuples containing list/set/map elements #[test] fn test_tuple_with_collections_compatible() { - let fory = Fory::builder().compatible(true).build(); + let fory = Fory::builder().xlang(false).compatible(true).build(); // Tuple with Vec let tuple_vec = (vec![1, 2, 3], vec!["a".to_string(), "b".to_string()]); let bin = fory.serialize(&tuple_vec).unwrap(); @@ -104,7 +104,7 @@ fn test_tuple_with_collections_compatible() { /// Test 2b: Tuple with collections - length mismatch #[test] fn test_tuple_collections_size_mismatch() { - let fory = Fory::builder().compatible(true).build(); + let fory = Fory::builder().xlang(false).compatible(true).build(); // Serialize tuple with 3 collections let tuple_long = (vec![1, 2, 3], vec!["a".to_string()], vec![1.0, 2.0]); @@ -129,7 +129,7 @@ fn test_tuple_collections_size_mismatch() { /// Test 3: Nested tuples #[test] fn test_nested_tuples() { - let fory = Fory::builder().compatible(true).build(); + let fory = Fory::builder().xlang(false).compatible(true).build(); let obj = ((42i32, "hello".to_string()), (PI_F64, true)); let bin = fory.serialize(&obj).unwrap(); @@ -140,7 +140,7 @@ fn test_nested_tuples() { /// Test 3b: Nested tuple size mismatch #[test] fn test_nested_tuple_size_mismatch() { - let fory = Fory::builder().compatible(true).build(); + let fory = Fory::builder().xlang(false).compatible(true).build(); // Long to short let long = ((42i32, "test".to_string(), PI_F64), (true, 100i32)); @@ -164,7 +164,7 @@ fn test_nested_tuple_size_mismatch() { /// Test 3c: Deeply nested tuples with size mismatch #[test] fn test_deeply_nested_tuple_size_mismatch() { - let fory = Fory::builder().compatible(true).build(); + let fory = Fory::builder().xlang(false).compatible(true).build(); // Serialize deeply nested tuple let deep = (1i32, (2i32, (3i32, 4i32, 5i32))); @@ -191,7 +191,7 @@ fn test_deeply_nested_tuple_size_mismatch() { /// Test 4: Tuples with Option/Arc elements #[test] fn test_tuple_with_option_arc_compatible() { - let fory = Fory::builder().compatible(true).build(); + let fory = Fory::builder().xlang(false).compatible(true).build(); // Tuple with Options let tuple_opt = (Some(42i32), None::, Some(PI_F64)); @@ -225,7 +225,7 @@ fn test_tuple_with_option_arc_compatible() { /// Test 4b: Tuple with Option size mismatch #[test] fn test_tuple_option_size_mismatch() { - let fory = Fory::builder().compatible(true).build(); + let fory = Fory::builder().xlang(false).compatible(true).build(); // Serialize longer tuple let long = ( @@ -257,7 +257,7 @@ fn test_tuple_option_size_mismatch() { /// Test 4c: Tuple with Arc size mismatch #[test] fn test_tuple_arc_size_mismatch() { - let fory = Fory::builder().compatible(true).build(); + let fory = Fory::builder().xlang(false).compatible(true).build(); // Serialize longer tuple let long = (Arc::new(1i32), Arc::new(2i32), Arc::new(3i32)); @@ -282,7 +282,7 @@ fn test_tuple_arc_size_mismatch() { /// Test 5: Schema evolution from homogeneous to heterogeneous tuple #[test] fn test_tuple_homogeneous_to_heterogeneous() { - let fory = Fory::builder().compatible(true).build(); + let fory = Fory::builder().xlang(false).compatible(true).build(); // Serialize as homogeneous (all i32) let homogeneous = (1i32, 2i32, 3i32); @@ -306,7 +306,7 @@ fn test_tuple_homogeneous_to_heterogeneous() { /// Test 6: Schema evolution with different element counts #[test] fn test_tuple_element_count_evolution() { - let fory = Fory::builder().compatible(true).build(); + let fory = Fory::builder().xlang(false).compatible(true).build(); // Test growing from 2 to 5 elements let small = (42i32, "hello".to_string()); @@ -343,7 +343,7 @@ fn test_tuple_element_count_evolution() { /// Test 6b: Complex element count evolution #[test] fn test_tuple_element_count_evolution_complex() { - let fory = Fory::builder().compatible(true).build(); + let fory = Fory::builder().xlang(false).compatible(true).build(); // v1: simple 2-element tuple let v1 = (42i32, "hello".to_string()); @@ -369,7 +369,7 @@ fn test_tuple_element_count_evolution_complex() { /// Test 7: Edge case - empty tuple behavior #[test] fn test_empty_to_non_empty_tuple() { - let fory = Fory::builder().compatible(true).build(); + let fory = Fory::builder().xlang(false).compatible(true).build(); // Simulate deserializing to tuple when data is missing // This is tested implicitly through struct field defaults @@ -382,7 +382,7 @@ fn test_empty_to_non_empty_tuple() { /// Test 8: Very large tuple with size mismatch #[test] fn test_large_tuple_size_mismatch() { - let fory = Fory::builder().compatible(true).build(); + let fory = Fory::builder().xlang(false).compatible(true).build(); // Serialize a large tuple (10 elements) let large = (1i32, 2i32, 3i32, 4i32, 5i32, 6i32, 7i32, 8i32, 9i32, 10i32); @@ -414,7 +414,7 @@ fn test_large_tuple_size_mismatch() { /// Test 9: Mixed complex types with size mismatch #[test] fn test_mixed_complex_types_size_mismatch() { - let fory = Fory::builder().compatible(true).build(); + let fory = Fory::builder().xlang(false).compatible(true).build(); // Complex tuple with many different types let complex = ( @@ -445,7 +445,7 @@ fn test_mixed_complex_types_size_mismatch() { /// Test compatible mode with tuples #[test] fn test_tuple_xlang_compatible_mode() { - let fory = Fory::builder().compatible(true).build(); + let fory = Fory::builder().xlang(false).compatible(true).build(); // Test basic tuple let basic = (42i32, "hello".to_string(), vec![1, 2, 3]); let bin = fory.serialize(&basic).unwrap(); @@ -1035,10 +1035,10 @@ fn test_tuple_alias() { } // Use separate Fory instances with the same type ID - let mut fory1 = Fory::builder().compatible(true).build(); + let mut fory1 = Fory::builder().xlang(false).compatible(true).build(); fory1.register::(100).unwrap(); - let mut fory2 = Fory::builder().compatible(true).build(); + let mut fory2 = Fory::builder().xlang(false).compatible(true).build(); fory2.register::(100).unwrap(); // Test record1 serialized by fory1, deserialized by fory2 diff --git a/rust/tests/tests/test_tuple_struct.rs b/rust/tests/tests/test_tuple_struct.rs index c01cc4c685..42fe09c799 100644 --- a/rust/tests/tests/test_tuple_struct.rs +++ b/rust/tests/tests/test_tuple_struct.rs @@ -42,7 +42,7 @@ struct Single(i32); #[test] fn test_basic_tuple_struct() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(100).unwrap(); let point = Point(3.15, 2.72); @@ -53,7 +53,7 @@ fn test_basic_tuple_struct() { #[test] fn test_single_field_tuple_struct() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(101).unwrap(); let single = Single(42); @@ -64,7 +64,7 @@ fn test_single_field_tuple_struct() { #[test] fn test_string_wrapper_tuple_struct() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(102).unwrap(); let wrapper = Wrapper("hello world".to_string()); @@ -75,7 +75,7 @@ fn test_string_wrapper_tuple_struct() { #[test] fn test_triple_tuple_struct() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(103).unwrap(); let triple = Triple(1, 2, 3); @@ -97,7 +97,7 @@ struct WithMap(HashMap); #[test] fn test_tuple_struct_with_vec() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(104).unwrap(); let data = WithVec(vec![1, 2, 3, 4, 5], "test".to_string()); @@ -108,7 +108,7 @@ fn test_tuple_struct_with_vec() { #[test] fn test_tuple_struct_with_option() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(105).unwrap(); // Test with Some values @@ -132,7 +132,7 @@ fn test_tuple_struct_with_option() { #[test] fn test_tuple_struct_with_map() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(106).unwrap(); let mut map = HashMap::new(); @@ -156,7 +156,7 @@ struct Outer(Inner, Vec); #[test] fn test_nested_tuple_structs() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(107).unwrap(); fory.register::(108).unwrap(); @@ -177,7 +177,7 @@ struct WithRc(Rc, Rc); #[test] fn test_tuple_struct_with_rc() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(109).unwrap(); let data = WithRc(Rc::new("shared".to_string()), Rc::new(42)); @@ -198,7 +198,7 @@ struct NamedWithTupleStruct { #[test] fn test_named_struct_with_tuple_struct_fields() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(100).unwrap(); fory.register::(102).unwrap(); fory.register::(110).unwrap(); @@ -221,7 +221,7 @@ struct TupleStructWithTuple(i32, (String, f64)); #[test] fn test_tuple_struct_with_tuple_field() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(111).unwrap(); let data = TupleStructWithTuple(42, ("hello".to_string(), 3.15)); @@ -262,7 +262,7 @@ struct EmptyVecTuple(Vec); #[test] fn test_tuple_struct_with_empty_vec() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(112).unwrap(); let data = EmptyVecTuple(vec![]); @@ -276,7 +276,7 @@ struct LargeTupleStruct(i8, i16, i32, i64, u8, u16, u32, u64, f32, f64, bool, St #[test] fn test_large_tuple_struct() { - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(113).unwrap(); let data = LargeTupleStruct( @@ -328,13 +328,13 @@ mod remote_v3 { /// Test schema evolution: remote has fewer fields than local. #[test] fn test_tuple_struct_schema_evolution_add_field() { - let mut fory_writer = Fory::builder().compatible(true).build(); + let mut fory_writer = Fory::builder().xlang(false).compatible(true).build(); fory_writer.register::(100).unwrap(); let remote_data = remote_v1::Point(1.0, 2.0); let bytes = fory_writer.serialize(&remote_data).unwrap(); - let mut fory_reader = Fory::builder().compatible(true).build(); + let mut fory_reader = Fory::builder().xlang(false).compatible(true).build(); fory_reader.register::(100).unwrap(); let local_data: local_v2::Point = fory_reader.deserialize(&bytes).unwrap(); @@ -347,13 +347,13 @@ fn test_tuple_struct_schema_evolution_add_field() { /// Test schema evolution: remote has more fields than local. #[test] fn test_tuple_struct_schema_evolution_remove_field() { - let mut fory_writer = Fory::builder().compatible(true).build(); + let mut fory_writer = Fory::builder().xlang(false).compatible(true).build(); fory_writer.register::(100).unwrap(); let remote_data = remote_v3::Point(1.0, 2.0, 3.0, 4.0); let bytes = fory_writer.serialize(&remote_data).unwrap(); - let mut fory_reader = Fory::builder().compatible(true).build(); + let mut fory_reader = Fory::builder().xlang(false).compatible(true).build(); fory_reader.register::(100).unwrap(); let local_data: remote_v1::Point = fory_reader.deserialize(&bytes).unwrap(); @@ -388,7 +388,7 @@ mod local_mixed_v3 { /// Test that adding an i64 field doesn't break schema evolution #[test] fn test_tuple_struct_schema_evolution_add_i64() { - let mut fory_writer = Fory::builder().compatible(true).build(); + let mut fory_writer = Fory::builder().xlang(false).compatible(true).build(); fory_writer .register::(100) .unwrap(); @@ -396,7 +396,7 @@ fn test_tuple_struct_schema_evolution_add_i64() { let remote_data = remote_mixed_v1::MixedPoint(1.0, 2.0); let bytes = fory_writer.serialize(&remote_data).unwrap(); - let mut fory_reader = Fory::builder().compatible(true).build(); + let mut fory_reader = Fory::builder().xlang(false).compatible(true).build(); fory_reader .register::(100) .unwrap(); @@ -411,7 +411,7 @@ fn test_tuple_struct_schema_evolution_add_i64() { /// Test that adding u8 (smaller size) doesn't break schema evolution #[test] fn test_tuple_struct_schema_evolution_add_u8() { - let mut fory_writer = Fory::builder().compatible(true).build(); + let mut fory_writer = Fory::builder().xlang(false).compatible(true).build(); fory_writer .register::(100) .unwrap(); @@ -419,7 +419,7 @@ fn test_tuple_struct_schema_evolution_add_u8() { let remote_data = remote_mixed_v1::MixedPoint(1.0, 2.0); let bytes = fory_writer.serialize(&remote_data).unwrap(); - let mut fory_reader = Fory::builder().compatible(true).build(); + let mut fory_reader = Fory::builder().xlang(false).compatible(true).build(); fory_reader .register::(100) .unwrap(); diff --git a/rust/tests/tests/test_unsigned.rs b/rust/tests/tests/test_unsigned.rs index 6dcb714926..a7085215bb 100644 --- a/rust/tests/tests/test_unsigned.rs +++ b/rust/tests/tests/test_unsigned.rs @@ -23,7 +23,7 @@ use test_helpers::{test_arc_any, test_box_any, test_rc_any, test_roundtrip}; #[test] fn test_unsigned_numbers() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); test_roundtrip(&fory, u8::MAX); test_roundtrip(&fory, u16::MAX); test_roundtrip(&fory, u32::MAX); @@ -34,7 +34,7 @@ fn test_unsigned_numbers() { #[test] fn test_unsigned_arrays() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); test_roundtrip(&fory, vec![0u8, 1, 2, u8::MAX]); test_roundtrip(&fory, vec![0u16, 100, 1000, u16::MAX]); test_roundtrip(&fory, vec![0u32, 1000, 1000000, u32::MAX]); @@ -74,11 +74,11 @@ fn test_binary_when_xlang() { #[test] fn test_binary_max_size_guardrail_for_vec_u8() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let original = vec![1_u8, 2, 3, 4, 5]; let serialized = fory.serialize(&original).unwrap(); - let limited_fory = Fory::builder().max_binary_size(4).build(); + let limited_fory = Fory::builder().xlang(false).max_binary_size(4).build(); let err = limited_fory .deserialize::>(&serialized) .expect_err("expected binary size guardrail to reject the payload"); @@ -95,11 +95,11 @@ fn test_binary_max_size_guardrail_for_vec_u8() { #[test] fn test_binary_max_size_guardrail_for_vec_u32() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); let original = vec![10_u32, 20, 30]; let serialized = fory.serialize(&original).unwrap(); - let limited_fory = Fory::builder().max_binary_size(8).build(); + let limited_fory = Fory::builder().xlang(false).max_binary_size(8).build(); let err = limited_fory .deserialize::>(&serialized) .expect_err("expected primitive array size guardrail to reject the payload"); @@ -132,7 +132,7 @@ fn test_unsigned_struct_non_compatible() { vec_u128: Vec, } - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(100).unwrap(); let data = UnsignedData { @@ -173,7 +173,7 @@ fn test_unsigned_struct_compatible() { vec_u128: Vec, } - let mut fory = Fory::builder().compatible(true).build(); + let mut fory = Fory::builder().xlang(false).compatible(true).build(); fory.register::(100).unwrap(); let data = UnsignedData { @@ -211,8 +211,8 @@ fn test_unsigned_struct_compatible_add_field() { c: u32, } - let mut fory1 = Fory::builder().compatible(true).build(); - let mut fory2 = Fory::builder().compatible(true).build(); + let mut fory1 = Fory::builder().xlang(false).compatible(true).build(); + let mut fory2 = Fory::builder().xlang(false).compatible(true).build(); fory1.register::(101).unwrap(); fory2.register::(101).unwrap(); @@ -239,8 +239,8 @@ fn test_unsigned_struct_compatible_remove_field() { b: u16, } - let mut fory1 = Fory::builder().compatible(true).build(); - let mut fory2 = Fory::builder().compatible(true).build(); + let mut fory1 = Fory::builder().xlang(false).compatible(true).build(); + let mut fory2 = Fory::builder().xlang(false).compatible(true).build(); fory1.register::(102).unwrap(); fory2.register::(102).unwrap(); @@ -258,7 +258,7 @@ fn test_unsigned_struct_compatible_remove_field() { #[test] fn test_unsigned_edge_cases() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); // Test minimum values test_roundtrip(&fory, 0u8); @@ -297,7 +297,7 @@ fn test_unsigned_with_option_non_compatible() { opt_u128: Option, } - let mut fory = Fory::default(); + let mut fory = Fory::builder().xlang(false).build(); fory.register::(103).unwrap(); // Test with Some values @@ -341,7 +341,7 @@ fn test_unsigned_with_option_compatible() { opt_u128: Option, } - let mut fory = Fory::builder().compatible(true).build(); + let mut fory = Fory::builder().xlang(false).compatible(true).build(); fory.register::(104).unwrap(); // Test with Some values @@ -393,8 +393,8 @@ fn test_unsigned_mixed_fields_compatible() { new_u128: u128, } - let mut fory1 = Fory::builder().compatible(true).build(); - let mut fory2 = Fory::builder().compatible(true).build(); + let mut fory1 = Fory::builder().xlang(false).compatible(true).build(); + let mut fory2 = Fory::builder().xlang(false).compatible(true).build(); fory1.register::(105).unwrap(); fory2.register::(105).unwrap(); @@ -417,7 +417,7 @@ fn test_unsigned_mixed_fields_compatible() { #[test] fn test_unsigned_with_smart_pointers() { - let fory = Fory::default(); + let fory = Fory::builder().xlang(false).build(); // Test Box with unsigned types test_box_any(&fory, u8::MAX); diff --git a/rust/tests/tests/test_weak.rs b/rust/tests/tests/test_weak.rs index 9949427c96..d14eb16d97 100644 --- a/rust/tests/tests/test_weak.rs +++ b/rust/tests/tests/test_weak.rs @@ -25,7 +25,7 @@ use std::sync::Mutex; #[test] fn test_rc_weak_null_serialization() { - let fory = Fory::builder().track_ref(true).build(); + let fory = Fory::builder().xlang(false).track_ref(true).build(); let weak: RcWeak = RcWeak::new(); @@ -37,7 +37,7 @@ fn test_rc_weak_null_serialization() { #[test] fn test_arc_weak_null_serialization() { - let fory = Fory::builder().track_ref(true).build(); + let fory = Fory::builder().xlang(false).track_ref(true).build(); let weak: ArcWeak = ArcWeak::new(); @@ -47,9 +47,35 @@ fn test_arc_weak_null_serialization() { assert!(deserialized.upgrade().is_none()); } +#[test] +fn test_rc_weak_requires_track_ref_message() { + let fory = Fory::builder().track_ref(false).build(); + let weak: RcWeak = RcWeak::new(); + + let err = fory.serialize(&weak).unwrap_err().to_string(); + assert_eq!( + err, + "RcWeak requires track_ref to be enabled. Use Fory::builder().track_ref(true).build()" + ); + assert!(!err.contains("xlang(false)")); +} + +#[test] +fn test_arc_weak_requires_track_ref_message() { + let fory = Fory::builder().track_ref(false).build(); + let weak: ArcWeak = ArcWeak::new(); + + let err = fory.serialize(&weak).unwrap_err().to_string(); + assert_eq!( + err, + "ArcWeak requires track_ref to be enabled. Use Fory::builder().track_ref(true).build()" + ); + assert!(!err.contains("xlang(false)")); +} + #[test] fn test_rc_weak_dead_pointer_serializes_as_null() { - let fory = Fory::builder().track_ref(true).build(); + let fory = Fory::builder().xlang(false).track_ref(true).build(); let weak = { let rc = Rc::new(42i32); @@ -69,7 +95,7 @@ fn test_rc_weak_dead_pointer_serializes_as_null() { #[test] fn test_arc_weak_dead_pointer_serializes_as_null() { - let fory = Fory::builder().track_ref(true).build(); + let fory = Fory::builder().xlang(false).track_ref(true).build(); let weak = { let arc = Arc::new(String::from("test")); @@ -89,7 +115,7 @@ fn test_arc_weak_dead_pointer_serializes_as_null() { #[test] fn test_rc_weak_in_vec_circular_reference() { - let fory = Fory::builder().track_ref(true).build(); + let fory = Fory::builder().xlang(false).track_ref(true).build(); let data1 = Rc::new(42i32); let data2 = Rc::new(100i32); @@ -107,7 +133,7 @@ fn test_rc_weak_in_vec_circular_reference() { #[test] fn test_arc_weak_in_vec_circular_reference() { - let fory = Fory::builder().track_ref(true).build(); + let fory = Fory::builder().xlang(false).track_ref(true).build(); let data1 = Arc::new(String::from("hello")); let data2 = Arc::new(String::from("world")); @@ -133,7 +159,7 @@ fn test_rc_weak_field_in_struct() { weak_ref: RcWeak, } - let mut fory = Fory::builder().track_ref(true).build(); + let mut fory = Fory::builder().xlang(false).track_ref(true).build(); fory.register::(1000).unwrap(); let data = Rc::new(42i32); @@ -160,7 +186,7 @@ struct Node { #[test] fn test_node_circular_reference_with_parent_children() { // Register the Node type with Fory - let mut fory = Fory::builder().track_ref(true).build(); + let mut fory = Fory::builder().xlang(false).track_ref(true).build(); fory.register::(2000).unwrap(); // Create parent @@ -218,7 +244,7 @@ fn test_arc_mutex_circular_reference() { children: Vec>>, } - let mut fory = Fory::builder().track_ref(true).build(); + let mut fory = Fory::builder().xlang(false).track_ref(true).build(); fory.register::(6000).unwrap(); let parent = Arc::new(Mutex::new(Node { diff --git a/scala/README.md b/scala/README.md index 609060876b..4a696da242 100644 --- a/scala/README.md +++ b/scala/README.md @@ -4,6 +4,11 @@ Apache Fory™ Scala provides optimized serializers for Scala types, built on to Both Scala 2 and Scala 3 are supported. +Scala uses xlang mode for cross-language payloads by default. Use native mode +with `.withXlang(false)` for Scala/JVM-only traffic when you want the JVM native +object serialization path plus Scala-specific types such as case classes, +collections, tuples, options, and enumerations. + ## Features ### Supported Types @@ -36,6 +41,7 @@ case class Point(x: Int, y: Int, z: Int) object ScalaExample { val fory: Fory = ForyScala.builder() + .withXlang(true) .build() fory.register(classOf[Person]) @@ -57,7 +63,7 @@ Apache Fory™ Scala provides support for Scala class default values during dese When a Scala class has default parameters, the Scala compiler generates methods in the companion object (for case classes) or in the class itself that return default values. Fory detects these methods and uses them when deserializing objects where certain fields are missing from the serialized data. -### Example Usage +### Native-Mode Example ```scala import org.apache.fory.Fory @@ -70,7 +76,7 @@ case class User(name: String, age: Int) case class UserV2(name: String, age: Int, email: String = "unknown", active: Boolean = true) object DefaultValueExample { - val fory: Fory = ForyScala.builder() + val fory: Fory = ForyScala.builder().withXlang(false) .withCompatible(true) .build() @@ -104,6 +110,7 @@ import org.apache.fory.scala.ForyScala object ForyHolder { val fory: ThreadSafeFory = ForyScala.builder() + .withXlang(true) .buildThreadSafeFory() fory.register(classOf[Person]) @@ -116,16 +123,25 @@ val result = ForyHolder.fory.deserialize(bytes) ## Configuration -Fory Scala is built on Fory Java, so all Java configuration options are available: +Fory Scala is built on Fory Java, so all Java configuration options are available. The default +builder uses xlang mode with compatible schema evolution: + +```scala +val fory = ForyScala.builder() + .withXlang(true) + .build() +``` + +Native-mode payloads can opt into JVM schema evolution explicitly: ```scala import org.apache.fory.Fory import org.apache.fory.scala.ForyScala -val fory = ForyScala.builder() +val fory = ForyScala.builder().withXlang(false) // Enable reference tracking for circular references .withRefTracking(true) - // Enable schema evolution support + // Enable schema evolution support for native-mode payloads .withCompatible(true) // Enable async compilation for better startup performance .withAsyncCompilation(true) diff --git a/scala/src/test/scala/org/apache/fory/serializer/scala/CompatibleSingleObjectSerializerTest.scala b/scala/src/test/scala/org/apache/fory/serializer/scala/CompatibleSingleObjectSerializerTest.scala index 63abb7a8ec..1be9fc9705 100644 --- a/scala/src/test/scala/org/apache/fory/serializer/scala/CompatibleSingleObjectSerializerTest.scala +++ b/scala/src/test/scala/org/apache/fory/serializer/scala/CompatibleSingleObjectSerializerTest.scala @@ -40,6 +40,7 @@ class CompatibleSingleObjectSerializerTest extends AnyWordSpec with Matchers { def fory: Fory = { org.apache.fory.Fory .builder() + .withXlang(false) .requireClassRegistration(false) .withRefTracking(true) .withCompatible(true) diff --git a/scala/src/test/scala/org/apache/fory/serializer/scala/ScalaTest.scala b/scala/src/test/scala/org/apache/fory/serializer/scala/ScalaTest.scala index ab4ef2c33b..fccc21e8e1 100644 --- a/scala/src/test/scala/org/apache/fory/serializer/scala/ScalaTest.scala +++ b/scala/src/test/scala/org/apache/fory/serializer/scala/ScalaTest.scala @@ -85,6 +85,7 @@ object PkgObjectMain extends App { val fory = Fory .builder() + .withXlang(false) .requireClassRegistration(false) .withRefTracking(true).suppressClassRegistrationWarnings(false) .build() @@ -103,6 +104,7 @@ object PkgObjectMain extends App { object PkgObjectMain2 extends App { val fory = Fory .builder() + .withXlang(false) .requireClassRegistration(false) .withRefTracking(true) .suppressClassRegistrationWarnings(false) diff --git a/swift/README.md b/swift/README.md index f66a227da9..630143ae12 100644 --- a/swift/README.md +++ b/swift/README.md @@ -7,16 +7,16 @@ The Swift implementation provides high-performance object graph serialization with macro-based code generation, schema evolution support, and xlang interoperability. -## 🚀 Why Apache Fory™ Swift? +## Why Apache Fory™ Swift? -- **🔥 Fast Binary Serialization**: Efficient encoding for Swift value and reference types -- **🧩 Macro-Driven Models**: Use `@ForyStruct`, `@ForyEnum`, and `@ForyUnion` to generate serializers -- **🌍 Cross-Language**: Exchange payloads with Java, Rust, Go, Python, and other Fory runtimes via xlang -- **🔄 Shared/Circular References**: Preserve object identity with `trackRef` for reference graphs -- **🧬 Dynamic Values**: Serialize `Any`, `AnyObject`, `any Serializer`, `AnyHashable`, and dynamic containers -- **📦 Schema Evolution**: Enable compatible mode for add/remove/reorder field evolution +- **Fast Binary Serialization**: Efficient encoding for Swift value and reference types +- **Macro-Driven Models**: Use `@ForyStruct`, `@ForyEnum`, and `@ForyUnion` to generate serializers +- **Cross-Language**: Exchange payloads with Java, Rust, Go, Python, and other Fory runtimes via xlang +- **Shared/Circular References**: Preserve object identity with `trackRef` for reference graphs +- **Dynamic Values**: Serialize `Any`, `AnyObject`, `any Serializer`, `AnyHashable`, and dynamic containers +- **Schema Evolution**: Enable compatible mode for add/remove/reorder field evolution -## 📦 Package Layout +## Package Layout | Target | Description | | ---------------- | -------------------------------------------------------- | @@ -25,7 +25,7 @@ The Swift implementation provides high-performance object graph serialization wi | `ForyXlangTests` | Executable used by Java-driven xlang integration tests | | `ForyTests` | Swift unit tests | -## 🏃 Quick Start +## Quick Start ### 1. Add dependency @@ -87,7 +87,7 @@ assert(output2 == input) `Fory` is the fastest option for single-threaded reuse. Keep one instance per thread. -## 📚 Core Features +## Core Features ### 1. Object Graph Serialization @@ -137,7 +137,7 @@ assert(decoded == person) Enable reference tracking for class/reference graphs: ```swift -let fory = Fory(xlang: true, ref: true, compatible: false) +let fory = Fory(ref: true, compatible: false) ``` Shared reference identity is preserved: @@ -169,7 +169,7 @@ final class AnimalPair { } } -let fory = Fory(xlang: true, ref: true, compatible: true) +let fory = Fory(ref: true) fory.register(Animal.self, id: 200) fory.register(AnimalPair.self, id: 201) @@ -257,10 +257,10 @@ struct PersonV2 { var phone: String? = nil } -let writer = Fory(xlang: true, compatible: true) +let writer = Fory() writer.register(PersonV1.self, id: 1) -let reader = Fory(xlang: true, compatible: true) +let reader = Fory() reader.register(PersonV2.self, id: 1) let v1 = PersonV1(name: "alice", age: 30, address: "main st") @@ -351,7 +351,7 @@ enum StringOrLong: Equatable { case number(Int64) } -let fory = Fory(xlang: true, compatible: false) +let fory = Fory(compatible: false) fory.register(Color.self, id: 300) fory.register(StringOrLong.self, id: 301) @@ -370,12 +370,12 @@ assert(value == .text("hello")) For types that should not use Fory model macros, implement `Serializer` manually and register the type. See `../docs/guide/swift/custom-serializers.md` for a complete example. -## 🌍 Cross-Language Serialization +## Cross-Language Serialization -Recommended xlang preset: +Recommended preset: ```swift -let fory = Fory(xlang: true, ref: false, compatible: true) +let fory = Fory() ``` Type registration can be ID-based or name-based: @@ -391,18 +391,13 @@ Cross-language rules: - Use compatible mode for independently evolving schemas - Register all user-defined concrete types used inside dynamic payloads -## ⚠️ Row Format Status - -Swift runtime currently exposes object graph serialization APIs (`Fory.serialize` / `Fory.deserialize`). -Row-format APIs are not exposed yet in Swift. - -## ⚡ Performance Notes +## Performance Notes - Prefer `trackRef=false` for value-only payloads to avoid reference-table overhead - Reuse the same `Fory` instance and register types once per process/service lifecycle - Use schema-consistent mode (`compatible=false`) when strict schema parity is guaranteed -## 🛠️ Development +## Development Run Swift tests: @@ -418,7 +413,7 @@ cd java/fory-core ENABLE_FORY_DEBUG_OUTPUT=1 FORY_SWIFT_JAVA_CI=1 mvn -T16 test -Dtest=org.apache.fory.xlang.SwiftXlangTest ``` -## 📖 Documentation +## Documentation - [Swift Guide](../docs/guide/swift/index.md) - [Configuration](../docs/guide/swift/configuration.md) @@ -428,10 +423,10 @@ ENABLE_FORY_DEBUG_OUTPUT=1 FORY_SWIFT_JAVA_CI=1 mvn -T16 test -Dtest=org.apache. - [Xlang Specification](../docs/specification/xlang_serialization_spec.md) - [Xlang Type Mapping](../docs/specification/xlang_type_mapping.md) -## 📄 License +## License Licensed under the Apache License, Version 2.0. See [LICENSE](https://github.com/apache/fory/blob/main/LICENSE). -## 🤝 Contributing +## Contributing Contributions are welcome. See [CONTRIBUTING.md](https://github.com/apache/fory/blob/main/CONTRIBUTING.md). diff --git a/swift/Sources/Fory/DateTimeSerializers.swift b/swift/Sources/Fory/DateTimeSerializers.swift index 3c0fe45338..798b876fd7 100644 --- a/swift/Sources/Fory/DateTimeSerializers.swift +++ b/swift/Sources/Fory/DateTimeSerializers.swift @@ -258,11 +258,7 @@ public extension WriteContext { @inline(__always) func writeLocalDate(_ value: LocalDate) throws { - if xlang { - buffer.writeVarInt64(Int64(value.epochDay)) - } else { - buffer.writeInt32(value.epochDay) - } + buffer.writeVarInt64(Int64(value.epochDay)) } @inline(__always) @@ -304,13 +300,10 @@ public extension ReadContext { @inline(__always) func readLocalDate() throws -> LocalDate { - if xlang { - guard let epochDay = Int32(exactly: try buffer.readVarInt64()) else { - throw ForyError.invalidData("date epochDay is out of Int32 range") - } - return .init(epochDay: epochDay) + guard let epochDay = Int32(exactly: try buffer.readVarInt64()) else { + throw ForyError.invalidData("date epochDay is out of Int32 range") } - return .init(epochDay: try buffer.readInt32()) + return .init(epochDay: epochDay) } @inline(__always) diff --git a/swift/Sources/Fory/Fory.swift b/swift/Sources/Fory/Fory.swift index 83c3ea8329..6ec1c42260 100644 --- a/swift/Sources/Fory/Fory.swift +++ b/swift/Sources/Fory/Fory.swift @@ -18,16 +18,14 @@ import Foundation public struct Config { - public var xlang: Bool - public var trackRef: Bool - public var compatible: Bool - public var checkClassVersion: Bool - public var maxCollectionSize: Int - public var maxBinarySize: Int - public var maxDepth: Int + public let trackRef: Bool + public let compatible: Bool + public let checkClassVersion: Bool + public let maxCollectionSize: Int + public let maxBinarySize: Int + public let maxDepth: Int public init( - xlang: Bool = true, trackRef: Bool = false, compatible: Bool? = nil, checkClassVersion: Bool? = nil, @@ -35,9 +33,8 @@ public struct Config { maxBinarySize: Int = 64 * 1024 * 1024, maxDepth: Int = 5 ) { - let effectiveCompatible = compatible ?? xlang - let effectiveCheckClassVersion = checkClassVersion ?? (xlang && !effectiveCompatible) - self.xlang = xlang + let effectiveCompatible = compatible ?? true + let effectiveCheckClassVersion = checkClassVersion ?? !effectiveCompatible self.trackRef = trackRef self.compatible = effectiveCompatible self.checkClassVersion = effectiveCheckClassVersion @@ -59,7 +56,6 @@ public final class Fory { private let readContext: ReadContext public convenience init( - xlang: Bool = true, ref: Bool = false, compatible: Bool? = nil, checkClassVersion: Bool? = nil, @@ -69,7 +65,6 @@ public final class Fory { ) { self.init( config: Config( - xlang: xlang, trackRef: ref, compatible: compatible, checkClassVersion: checkClassVersion, @@ -85,7 +80,6 @@ public final class Fory { self.writeContext = WriteContext( buffer: ByteBuffer(), typeResolver: typeResolver, - xlang: self.config.xlang, trackRef: self.config.trackRef, compatible: self.config.compatible, checkClassVersion: self.config.checkClassVersion, @@ -95,7 +89,6 @@ public final class Fory { self.readContext = ReadContext( buffer: ByteBuffer(), typeResolver: typeResolver, - xlang: self.config.xlang, trackRef: self.config.trackRef, compatible: self.config.compatible, checkClassVersion: self.config.checkClassVersion, @@ -408,14 +401,14 @@ public final class Fory { @inlinable @inline(__always) func writeHead(buffer: ByteBuffer) { - buffer.writeUInt8(config.xlang ? ForyHeaderFlag.isXlang : 0) + buffer.writeUInt8(ForyHeaderFlag.isXlang) } @inlinable @inline(__always) func readHead(buffer: ByteBuffer) throws { let bitmap = try buffer.readUInt8() - let expected = config.xlang ? ForyHeaderFlag.isXlang : 0 + let expected = ForyHeaderFlag.isXlang if bitmap != expected { try readHeadSlow(bitmap: bitmap, expected: expected) } @@ -441,11 +434,10 @@ public final class Fory { _ value: T, context: WriteContext ) throws { - let writeTypeInfo = config.xlang || config.compatible try value.foryWrite( context, - refMode: config.trackRef ? .tracking : (writeTypeInfo ? .nullOnly : .none), - writeTypeInfo: writeTypeInfo, + refMode: refMode, + writeTypeInfo: true, hasGenerics: false ) } @@ -454,11 +446,10 @@ public final class Fory { private func readRootTypedValue( context: ReadContext ) throws -> T { - let readTypeInfo = config.xlang || config.compatible return try T.foryRead( context, - refMode: config.trackRef ? .tracking : (readTypeInfo ? .nullOnly : .none), - readTypeInfo: readTypeInfo + refMode: refMode, + readTypeInfo: true ) } diff --git a/swift/Sources/Fory/ReadContext.swift b/swift/Sources/Fory/ReadContext.swift index 6d1967d59f..04a7e73bd6 100644 --- a/swift/Sources/Fory/ReadContext.swift +++ b/swift/Sources/Fory/ReadContext.swift @@ -22,7 +22,6 @@ private let typeMetaSizeMask = 0xFF public final class ReadContext { public let buffer: ByteBuffer let typeResolver: TypeResolver - public let xlang: Bool public let trackRef: Bool public let compatible: Bool public let checkClassVersion: Bool @@ -41,7 +40,6 @@ public final class ReadContext { init( buffer: ByteBuffer, typeResolver: TypeResolver, - xlang: Bool = false, trackRef: Bool, compatible: Bool = false, checkClassVersion: Bool = true, @@ -51,7 +49,6 @@ public final class ReadContext { ) { self.buffer = buffer self.typeResolver = typeResolver - self.xlang = xlang self.trackRef = trackRef self.compatible = compatible self.checkClassVersion = checkClassVersion diff --git a/swift/Sources/Fory/WriteContext.swift b/swift/Sources/Fory/WriteContext.swift index 466f710b12..1fcf8ae7f4 100644 --- a/swift/Sources/Fory/WriteContext.swift +++ b/swift/Sources/Fory/WriteContext.swift @@ -56,7 +56,6 @@ final class MetaStringWriteState { public final class WriteContext { public let buffer: ByteBuffer let typeResolver: TypeResolver - public let xlang: Bool public let trackRef: Bool public let compatible: Bool public let checkClassVersion: Bool @@ -72,7 +71,6 @@ public final class WriteContext { convenience init( buffer: ByteBuffer, typeResolver: TypeResolver, - xlang: Bool = false, trackRef: Bool, compatible: Bool = false, checkClassVersion: Bool = true, @@ -81,7 +79,6 @@ public final class WriteContext { self.init( buffer: buffer, typeResolver: typeResolver, - xlang: xlang, trackRef: trackRef, compatible: compatible, checkClassVersion: checkClassVersion, @@ -93,7 +90,6 @@ public final class WriteContext { init( buffer: ByteBuffer, typeResolver: TypeResolver, - xlang: Bool, trackRef: Bool, compatible: Bool, checkClassVersion: Bool, @@ -102,7 +98,6 @@ public final class WriteContext { ) { self.buffer = buffer self.typeResolver = typeResolver - self.xlang = xlang self.trackRef = trackRef self.compatible = compatible self.checkClassVersion = checkClassVersion diff --git a/swift/Tests/ForyTests/AnyTests.swift b/swift/Tests/ForyTests/AnyTests.swift index aada8fd3ab..cffb6c92b8 100644 --- a/swift/Tests/ForyTests/AnyTests.swift +++ b/swift/Tests/ForyTests/AnyTests.swift @@ -240,7 +240,7 @@ func macroAnyHashableSetFieldsRoundTrip() throws { @Test func macroCoreAnyFieldsRoundTrip() throws { - let fory = Fory(config: .init(xlang: true, trackRef: true, compatible: false)) + let fory = Fory(config: .init(trackRef: true, compatible: false)) fory.register(AnyHashableDynamicValue.self, id: 425) fory.register(AnyObjectDynamicNode.self, id: 426) fory.register(AnyCoreFieldHolder.self, id: 427) @@ -281,7 +281,7 @@ func macroCoreAnyFieldsRoundTrip() throws { @Test func macroAnyHashableValueFieldRoundTrip() throws { - let fory = Fory(config: .init(xlang: true, trackRef: true, compatible: false)) + let fory = Fory(config: .init(trackRef: true, compatible: false)) fory.register(AnyHashableDynamicKey.self, id: 428) fory.register(AnyHashableValueHolder.self, id: 429) @@ -321,7 +321,7 @@ func dynamicAnyMapNormalizationForAnyHashableKeys() throws { @Test func topLevelAllSupportedAnyTypesRoundTrip() throws { - let fory = Fory(config: .init(xlang: true, trackRef: true, compatible: false)) + let fory = Fory(config: .init(trackRef: true, compatible: false)) fory.register(AnyHashableDynamicKey.self, id: 500) fory.register(AnyHashableDynamicValue.self, id: 501) fory.register(AnyObjectDynamicNode.self, id: 502) @@ -429,7 +429,7 @@ func topLevelAnyHomogeneousListAndMapRoundTrip() throws { @Test func dynamicAnyListTracksRefs() throws { - let fory = Fory(config: .init(xlang: true, trackRef: true, compatible: false)) + let fory = Fory(config: .init(trackRef: true, compatible: false)) fory.register(AnyObjectDynamicGraphNode.self, id: 503) let shared = AnyObjectDynamicGraphNode(value: 17) @@ -446,7 +446,7 @@ func dynamicAnyListTracksRefs() throws { @Test func dynamicAnyObjectTracksCycle() throws { - let fory = Fory(config: .init(xlang: true, trackRef: true, compatible: false)) + let fory = Fory(config: .init(trackRef: true, compatible: false)) fory.register(AnyObjectDynamicGraphNode.self, id: 504) let node = AnyObjectDynamicGraphNode(value: 21) diff --git a/swift/Tests/ForyTests/CollectionSerializerTests.swift b/swift/Tests/ForyTests/CollectionSerializerTests.swift index 81adfecd0d..7f8e4b8f7b 100644 --- a/swift/Tests/ForyTests/CollectionSerializerTests.swift +++ b/swift/Tests/ForyTests/CollectionSerializerTests.swift @@ -224,11 +224,11 @@ func floatingPointArraysPreserveBits() throws { func plainUInt8ArrayUsesListWireType() throws { let payload: [UInt8] = [0x00, 0x01, 0x7F, 0xFF] - let schemaConsistent = Fory(config: .init(xlang: true, trackRef: false, compatible: false)) + let schemaConsistent = Fory(config: .init(trackRef: false, compatible: false)) let schemaBytes = try schemaConsistent.serialize(payload) #expect(Array(schemaBytes)[2] == UInt8(TypeId.list.rawValue)) - let compatible = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let compatible = Fory(config: .init(trackRef: false, compatible: true)) let compatibleBytes = try compatible.serialize(payload) #expect(Array(compatibleBytes)[2] == UInt8(TypeId.list.rawValue)) @@ -238,7 +238,7 @@ func plainUInt8ArrayUsesListWireType() throws { @Test func nestedCollectionsAndNullabilityRoundTrip() throws { - let fory = Fory(config: .init(xlang: true, trackRef: true, compatible: true)) + let fory = Fory(config: .init(trackRef: true, compatible: true)) let nested: [[String?]] = [ ["alpha", nil], @@ -263,7 +263,7 @@ func nestedCollectionsAndNullabilityRoundTrip() throws { @Test func annotatedNestedFieldCodecsRoundTrip() throws { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let fory = Fory(config: .init(trackRef: false, compatible: true)) fory.register(AnnotatedFieldCodecHolder.self, id: 9601) fory.register(DeepAnnotatedFieldCodecHolder.self, id: 9602) fory.register(AliasAnnotatedFieldCodecHolder.self, id: 9603) @@ -382,7 +382,7 @@ func annotatedNestedFieldCodecsEmitRecursiveMetadata() { @Test func mapRefKeysTrackIdentity() throws { - let fory = Fory(config: .init(xlang: true, trackRef: true, compatible: true)) + let fory = Fory(config: .init(trackRef: true, compatible: true)) fory.register(RefKeyNode.self, id: 9501) fory.register(RefKeyHolder.self, id: 9502) @@ -401,7 +401,7 @@ func mapRefKeysTrackIdentity() throws { @Test func mapRefKeyAndValueShareIdentity() throws { - let fory = Fory(config: .init(xlang: true, trackRef: true, compatible: true)) + let fory = Fory(config: .init(trackRef: true, compatible: true)) fory.register(RefKeyNode.self, id: 9501) fory.register(RefKeyValueHolder.self, id: 9503) @@ -419,7 +419,7 @@ func mapRefKeyAndValueShareIdentity() throws { @Test func mapRefKeysChunkAcross255Entries() throws { - let fory = Fory(config: .init(xlang: true, trackRef: true, compatible: true)) + let fory = Fory(config: .init(trackRef: true, compatible: true)) fory.register(RefKeyNode.self, id: 9501) fory.register(RefKeyChunkHolder.self, id: 9504) diff --git a/swift/Tests/ForyTests/CompatibilityTests.swift b/swift/Tests/ForyTests/CompatibilityTests.swift index 2afa3f255c..17b2a30455 100644 --- a/swift/Tests/ForyTests/CompatibilityTests.swift +++ b/swift/Tests/ForyTests/CompatibilityTests.swift @@ -215,10 +215,10 @@ private final class CompatibleGraphContainer { @Test func compatibleModeSupportsAddedAndRemovedFields() throws { - let writerV1 = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let writerV1 = Fory(config: .init(trackRef: false, compatible: true)) writerV1.register(CompatibleProfileV1.self, id: 9901) - let readerV2 = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let readerV2 = Fory(config: .init(trackRef: false, compatible: true)) readerV2.register(CompatibleProfileV2.self, id: 9901) let sourceV1 = CompatibleProfileV1(id: 7, name: "swift") @@ -229,10 +229,10 @@ func compatibleModeSupportsAddedAndRemovedFields() throws { #expect(decodedAsV2.nickname == "") #expect(decodedAsV2.scores.isEmpty) - let writerV2 = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let writerV2 = Fory(config: .init(trackRef: false, compatible: true)) writerV2.register(CompatibleProfileV2.self, id: 9901) - let readerV1 = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let readerV1 = Fory(config: .init(trackRef: false, compatible: true)) readerV1.register(CompatibleProfileV1.self, id: 9901) let sourceV2 = CompatibleProfileV2(id: 9, name: "fory", nickname: "macro", scores: [1, 2, 3]) @@ -243,10 +243,10 @@ func compatibleModeSupportsAddedAndRemovedFields() throws { @Test func schemaConsistentModeRejectsVersionHashMismatch() throws { - let writer = Fory(config: .init(xlang: true, trackRef: false, compatible: false, checkClassVersion: true)) + let writer = Fory(config: .init(trackRef: false, compatible: false, checkClassVersion: true)) writer.register(SchemaVersionV1.self, id: 9902) - let reader = Fory(config: .init(xlang: true, trackRef: false, compatible: false, checkClassVersion: true)) + let reader = Fory(config: .init(trackRef: false, compatible: false, checkClassVersion: true)) reader.register(SchemaVersionV2.self, id: 9902) let bytes = try writer.serialize(SchemaVersionV1(id: 1, name: "shape")) @@ -260,7 +260,7 @@ func schemaConsistentModeRejectsVersionHashMismatch() throws { @Test func compatibleModePreservesSharedAndCircularReferencesForMacroObjects() throws { - let fory = Fory(config: .init(xlang: true, trackRef: true, compatible: true)) + let fory = Fory(config: .init(trackRef: true, compatible: true)) fory.register(CompatibleGraphNode.self, id: 9903) fory.register(CompatibleGraphContainer.self, id: 9904) @@ -297,7 +297,7 @@ func schemaHashMatchesJavaFingerprintForTaggedUnsignedFields() { @Test func schemaHashUsesNestedAnnotatedContainerTypeIDs() throws { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: false, checkClassVersion: true)) + let fory = Fory(config: .init(trackRef: false, compatible: false, checkClassVersion: true)) fory.register(SchemaHashNestedAnnotatedContainer.self, id: 9912) let bytes = try fory.serialize( @@ -322,11 +322,11 @@ func schemaHashUsesNestedAnnotatedContainerTypeIDs() throws { @Test func compatibleNestedArrayEvolves() throws { - let writerV1 = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let writerV1 = Fory(config: .init(trackRef: false, compatible: true)) writerV1.register(CompatibleNestedProfileV1.self, id: 9910) writerV1.register(CompatibleNestedArrayV1.self, id: 9911) - let readerV2 = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let readerV2 = Fory(config: .init(trackRef: false, compatible: true)) readerV2.register(CompatibleNestedProfileV2.self, id: 9910) readerV2.register(CompatibleNestedArrayV2.self, id: 9911) @@ -342,11 +342,11 @@ func compatibleNestedArrayEvolves() throws { #expect(decodedAsV2.items.allSatisfy { $0.alias.isEmpty }) #expect(decodedAsV2.items.allSatisfy { $0.scores.isEmpty }) - let writerV2 = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let writerV2 = Fory(config: .init(trackRef: false, compatible: true)) writerV2.register(CompatibleNestedProfileV2.self, id: 9910) writerV2.register(CompatibleNestedArrayV2.self, id: 9911) - let readerV1 = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let readerV1 = Fory(config: .init(trackRef: false, compatible: true)) readerV1.register(CompatibleNestedProfileV1.self, id: 9910) readerV1.register(CompatibleNestedArrayV1.self, id: 9911) @@ -365,10 +365,10 @@ func compatibleNestedArrayEvolves() throws { @Test func compatibleSkipUsesRemoteMetadataForFixedIntegerMismatch() throws { - let writer = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let writer = Fory(config: .init(trackRef: false, compatible: true)) writer.register(RemoteFixedUInt32V1.self, id: 9920) - let reader = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let reader = Fory(config: .init(trackRef: false, compatible: true)) reader.register(LocalVarUInt32V2.self, id: 9920) let source = RemoteFixedUInt32V1(id: UInt32.max, keep: 42) @@ -379,10 +379,10 @@ func compatibleSkipUsesRemoteMetadataForFixedIntegerMismatch() throws { @Test func compatibleSkipUsesRemoteMetadataForNestedMapListSetFields() throws { - let writer = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let writer = Fory(config: .init(trackRef: false, compatible: true)) writer.register(RemoteNestedFixedMapV1.self, id: 9921) - let reader = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let reader = Fory(config: .init(trackRef: false, compatible: true)) reader.register(LocalNestedVarintMapV2.self, id: 9921) let source = RemoteNestedFixedMapV1( @@ -401,10 +401,10 @@ func compatibleSkipUsesRemoteMetadataForNestedMapListSetFields() throws { @Test func compatibleReadAdaptsImmediateListAndArrayFieldPair() throws { - let writer = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let writer = Fory(config: .init(trackRef: false, compatible: true)) writer.register(CompatibleListFieldV1.self, id: 9922) - let reader = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let reader = Fory(config: .init(trackRef: false, compatible: true)) reader.register(CompatibleArrayFieldV2.self, id: 9922) let decoded: CompatibleArrayFieldV2 = try reader.deserialize( @@ -415,10 +415,10 @@ func compatibleReadAdaptsImmediateListAndArrayFieldPair() throws { @Test func compatibleReadAdaptsDefaultVarintListAndArrayFieldPair() throws { - let writer = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let writer = Fory(config: .init(trackRef: false, compatible: true)) writer.register(CompatibleVarintListFieldV1.self, id: 9924) - let reader = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let reader = Fory(config: .init(trackRef: false, compatible: true)) reader.register(CompatibleArrayFieldV2.self, id: 9924) let decoded: CompatibleArrayFieldV2 = try reader.deserialize( @@ -429,10 +429,10 @@ func compatibleReadAdaptsDefaultVarintListAndArrayFieldPair() throws { @Test func compatibleReadAdaptsArrayFieldToDefaultVarintListField() throws { - let writer = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let writer = Fory(config: .init(trackRef: false, compatible: true)) writer.register(CompatibleArrayFieldV2.self, id: 9925) - let reader = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let reader = Fory(config: .init(trackRef: false, compatible: true)) reader.register(CompatibleVarintListFieldV1.self, id: 9925) let decoded: CompatibleVarintListFieldV1 = try reader.deserialize( @@ -443,10 +443,10 @@ func compatibleReadAdaptsArrayFieldToDefaultVarintListField() throws { @Test func compatibleReadRejectsNullableListElementsForArrayField() throws { - let writer = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let writer = Fory(config: .init(trackRef: false, compatible: true)) writer.register(CompatibleNullableListFieldV1.self, id: 9923) - let reader = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let reader = Fory(config: .init(trackRef: false, compatible: true)) reader.register(CompatibleArrayFieldV2.self, id: 9923) let bytes = try writer.serialize(CompatibleNullableListFieldV1(values: [1, 2, 3], extra: 9)) @@ -461,10 +461,10 @@ func compatibleReadRejectsNullableListElementsForArrayField() throws { @Test func compatibleReadSkipsNestedListArrayFieldPair() throws { - let writer = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let writer = Fory(config: .init(trackRef: false, compatible: true)) writer.register(CompatibleNestedListArrayFieldV1.self, id: 9926) - let reader = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let reader = Fory(config: .init(trackRef: false, compatible: true)) reader.register(CompatibleNestedArrayListFieldV2.self, id: 9926) let decoded: CompatibleNestedArrayListFieldV2 = try reader.deserialize( @@ -476,11 +476,11 @@ func compatibleReadSkipsNestedListArrayFieldPair() throws { @Test func compatibleNestedMapEvolves() throws { - let writerV1 = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let writerV1 = Fory(config: .init(trackRef: false, compatible: true)) writerV1.register(CompatibleNestedProfileV1.self, id: 9910) writerV1.register(CompatibleNestedMapV1.self, id: 9912) - let readerV2 = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let readerV2 = Fory(config: .init(trackRef: false, compatible: true)) readerV2.register(CompatibleNestedProfileV2.self, id: 9910) readerV2.register(CompatibleNestedMapV2.self, id: 9912) @@ -503,11 +503,11 @@ func compatibleNestedMapEvolves() throws { @Test func compatibleNestedReadsReuseTypeMeta() throws { - let writerV1 = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let writerV1 = Fory(config: .init(trackRef: false, compatible: true)) writerV1.register(CompatibleNestedProfileV1.self, id: 9910) writerV1.register(CompatibleNestedArrayV1.self, id: 9911) - let readerV2 = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let readerV2 = Fory(config: .init(trackRef: false, compatible: true)) readerV2.register(CompatibleNestedProfileV2.self, id: 9910) readerV2.register(CompatibleNestedArrayV2.self, id: 9911) diff --git a/swift/Tests/ForyTests/DateTimeTests.swift b/swift/Tests/ForyTests/DateTimeTests.swift index 43d2fc1e9e..7cc493fde1 100644 --- a/swift/Tests/ForyTests/DateTimeTests.swift +++ b/swift/Tests/ForyTests/DateTimeTests.swift @@ -46,7 +46,7 @@ func dateAndTimestampTypeIds() { @Test func dateAndTimestampRoundTrip() throws { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let fory = Fory(config: .init(trackRef: false, compatible: true)) let day = localDate(18_745) let dayData = try fory.serialize(day) @@ -90,7 +90,6 @@ func dateAndTimestampContextHelpersUseExpectedWireProtocols() throws { let xlangWriteContext = WriteContext( buffer: xlangWriteBuffer, typeResolver: xlangTypeResolver, - xlang: true, trackRef: false, compatible: true, checkClassVersion: true, @@ -110,7 +109,6 @@ func dateAndTimestampContextHelpersUseExpectedWireProtocols() throws { let xlangReadContext = ReadContext( buffer: ByteBuffer(data: xlangWriteBuffer.copyToData()), typeResolver: xlangTypeResolver, - xlang: true, trackRef: false, compatible: true, checkClassVersion: true, @@ -121,51 +119,10 @@ func dateAndTimestampContextHelpersUseExpectedWireProtocols() throws { let xlangLocalDateDecoded = try xlangReadContext.readLocalDate(refMode: RefMode.nullOnly, readTypeInfo: true) #expect(xlangLocalDateDecoded == xlangLocalDate) - let writeBuffer = ByteBuffer() - let typeResolver = TypeResolver(trackRef: false) - let writeContext = WriteContext( - buffer: writeBuffer, - typeResolver: typeResolver, - xlang: false, - trackRef: false, - compatible: true, - checkClassVersion: true, - maxDepth: 5 - ) - - let localDate = localDate(-1) - - try writeContext.writeLocalDate(localDate, refMode: .nullOnly, writeTypeInfo: true) - - let readContext = ReadContext( - buffer: ByteBuffer(data: writeBuffer.copyToData()), - typeResolver: typeResolver, - xlang: false, - trackRef: false, - compatible: true, - checkClassVersion: true, - maxCollectionSize: 1_000_000, - maxBinarySize: 64 * 1024 * 1024, - maxDepth: 5 - ) - - let localDateDecoded = try readContext.readLocalDate(refMode: RefMode.nullOnly, readTypeInfo: true) - - #expect(localDateDecoded == localDate) - #expect(Array(writeBuffer.copyToData()) == [ - UInt8(bitPattern: RefFlag.notNullValue.rawValue), - UInt8(LocalDate.staticTypeId.rawValue), - 0xFF, - 0xFF, - 0xFF, - 0xFF - ]) - let timestampBuffer = ByteBuffer() let timestampWriteContext = WriteContext( buffer: timestampBuffer, - typeResolver: typeResolver, - xlang: false, + typeResolver: xlangTypeResolver, trackRef: false, compatible: true, checkClassVersion: true, @@ -176,8 +133,7 @@ func dateAndTimestampContextHelpersUseExpectedWireProtocols() throws { let timestampReadContext = ReadContext( buffer: ByteBuffer(data: timestampBuffer.copyToData()), - typeResolver: typeResolver, - xlang: false, + typeResolver: xlangTypeResolver, trackRef: false, compatible: true, checkClassVersion: true, @@ -191,7 +147,7 @@ func dateAndTimestampContextHelpersUseExpectedWireProtocols() throws { @Test func dateAndTimestampMacroFieldRoundTrip() throws { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let fory = Fory(config: .init(trackRef: false, compatible: true)) fory.register(DateMacroHolder.self, id: 901) let value = DateMacroHolder( diff --git a/swift/Tests/ForyTests/EnumTests.swift b/swift/Tests/ForyTests/EnumTests.swift index ed2d5960bd..7948f997e0 100644 --- a/swift/Tests/ForyTests/EnumTests.swift +++ b/swift/Tests/ForyTests/EnumTests.swift @@ -72,7 +72,7 @@ func enumTypeIdClassification() { @Test func structWithEnumFieldRoundTrip() throws { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: false)) + let fory = Fory(config: .init(trackRef: false, compatible: false)) fory.register(Color.self, id: 100) fory.register(StructWithEnum.self, id: 101) @@ -84,7 +84,7 @@ func structWithEnumFieldRoundTrip() throws { @Test func taggedUnionXlangRoundTrip() throws { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: false)) + let fory = Fory(config: .init(trackRef: false, compatible: false)) fory.register(StringOrLong.self, id: 300) fory.register(StructWithUnion.self, id: 301) @@ -103,7 +103,7 @@ func taggedUnionXlangRoundTrip() throws { @Test func taggedUnionPayloadFieldCodecsRoundTrip() throws { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: false)) + let fory = Fory(config: .init(trackRef: false, compatible: false)) fory.register(FixedPayloadEvent.self, id: 302) let values: [FixedPayloadEvent] = [ @@ -118,7 +118,7 @@ func taggedUnionPayloadFieldCodecsRoundTrip() throws { @Test func mixedEnumShapesRoundTrip() throws { - let fory = Fory(config: .init(xlang: false, trackRef: true, compatible: false)) + let fory = Fory(config: .init(trackRef: true, compatible: false)) fory.register(Token.self, id: 1000) let nestedMap: [String: Token] = [ diff --git a/swift/Tests/ForyTests/ForySwiftTests.swift b/swift/Tests/ForyTests/ForySwiftTests.swift index 0ff7a73d31..85276cd482 100644 --- a/swift/Tests/ForyTests/ForySwiftTests.swift +++ b/swift/Tests/ForyTests/ForySwiftTests.swift @@ -347,30 +347,27 @@ func floatingSpecialsRoundTrip() throws { @Test func namedInitializerBuildsConfig() { let defaultConfig = Fory() - #expect(defaultConfig.config.xlang == true) #expect(defaultConfig.config.trackRef == false) #expect(defaultConfig.config.compatible == true) #expect(defaultConfig.config.checkClassVersion == false) #expect(defaultConfig.config.maxDepth == 5) - let explicitConfig = Fory(xlang: false, ref: true, compatible: true, maxDepth: 7) - #expect(explicitConfig.config.xlang == false) + let explicitConfig = Fory(ref: true, compatible: true, maxDepth: 7) #expect(explicitConfig.config.trackRef == true) #expect(explicitConfig.config.compatible == true) #expect(explicitConfig.config.checkClassVersion == false) #expect(explicitConfig.config.maxDepth == 7) - let configInit = Fory(config: .init(xlang: false, trackRef: false, compatible: true, maxDepth: 9)) - #expect(configInit.config.xlang == false) + let configInit = Fory(config: .init(trackRef: false, compatible: true, maxDepth: 9)) #expect(configInit.config.trackRef == false) #expect(configInit.config.compatible == true) #expect(configInit.config.checkClassVersion == false) #expect(configInit.config.maxDepth == 9) - let nativeDirect = Fory(xlang: false, ref: true, compatible: false) - let nativeViaConfig = Fory(config: Config(xlang: false, trackRef: true, compatible: false)) - #expect(nativeDirect.config.checkClassVersion == false) - #expect(nativeViaConfig.config.checkClassVersion == false) + let schemaConsistentDirect = Fory(ref: true, compatible: false) + let schemaConsistentViaConfig = Fory(config: Config(trackRef: true, compatible: false)) + #expect(schemaConsistentDirect.config.checkClassVersion == true) + #expect(schemaConsistentViaConfig.config.checkClassVersion == true) } @Test @@ -551,7 +548,7 @@ func macroStructRoundTrip() throws { @Test func macroClassRefTracking() throws { - let fory = Fory(config: .init(xlang: true, trackRef: true, compatible: false)) + let fory = Fory(config: .init(trackRef: true, compatible: false)) fory.register(Node.self, id: 200) let node = Node(value: 7) @@ -566,7 +563,7 @@ func macroClassRefTracking() throws { @Test func macroClassWeakRefTracking() throws { - let fory = Fory(config: .init(xlang: true, trackRef: true, compatible: false)) + let fory = Fory(config: .init(trackRef: true, compatible: false)) fory.register(WeakNode.self, id: 201) let node = WeakNode(value: 13) @@ -691,7 +688,7 @@ func rootBufferHonorsCursor() throws { @Test func topLevelAnyObjectRoundTrip() throws { - let fory = Fory(config: .init(xlang: true, trackRef: true, compatible: false)) + let fory = Fory(config: .init(trackRef: true, compatible: false)) fory.register(Node.self, id: 210) let value: AnyObject = Node(value: 123) @@ -728,7 +725,7 @@ func topLevelAnySerializerRoundTrip() throws { @Test func macroDynamicAnyObjectAndAnySerializerFieldsRoundTrip() throws { - let fory = Fory(config: .init(xlang: true, trackRef: true, compatible: false)) + let fory = Fory(config: .init(trackRef: true, compatible: false)) fory.register(Node.self, id: 220) fory.register(Address.self, id: 221) fory.register(AnyObjectHolder.self, id: 222) @@ -769,7 +766,7 @@ func macroDynamicAnyObjectAndAnySerializerFieldsRoundTrip() throws { @Test func dynamicAnySerializerTracksRefs() throws { - let fory = Fory(config: .init(xlang: true, trackRef: true, compatible: false)) + let fory = Fory(config: .init(trackRef: true, compatible: false)) fory.register(Node.self, id: 226) fory.register(AnySerializerHolder.self, id: 227) @@ -837,7 +834,7 @@ func macroAnyFieldsRoundTrip() throws { @Test func collectionAndMapRefTracking() throws { - let fory = Fory(config: .init(xlang: true, trackRef: true, compatible: false)) + let fory = Fory(config: .init(trackRef: true, compatible: false)) fory.register(Node.self, id: 200) let shared = Node(value: 11) @@ -959,7 +956,7 @@ func macroFieldEncodingOverridesForUnsignedTypes() throws { @Test func macroEnumUsesExplicitIntegerRawValue() throws { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: false)) + let fory = Fory(config: .init(trackRef: false, compatible: false)) fory.register(SparseStatus.self, id: 302) let data = try fory.serialize(SparseStatus.ok) @@ -1019,10 +1016,10 @@ func macroFieldIDsPopulateCompatibleTypeMeta() { @Test func macroFieldIDsDriveCompatibleStructDecodeAcrossRenames() throws { - let writer = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let writer = Fory(config: .init(trackRef: false, compatible: true)) writer.register(FieldIdSource.self, id: 9101) - let reader = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let reader = Fory(config: .init(trackRef: false, compatible: true)) reader.register(FieldIdTarget.self, id: 9101) let source = FieldIdSource(value: 42, label: "alpha") @@ -1039,10 +1036,10 @@ func macroFieldIDsDriveCompatibleStructDecodeAcrossRenames() throws { @Test func macroFieldIDsDriveTaggedUnionDecodeAcrossRenames() throws { - let writer = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let writer = Fory(config: .init(trackRef: false, compatible: true)) writer.register(FieldIdUnionSource.self, id: 9102) - let reader = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let reader = Fory(config: .init(trackRef: false, compatible: true)) reader.register(FieldIdUnionTarget.self, id: 9102) let source = FieldIdUnionSource.number(123) @@ -1059,11 +1056,11 @@ func macroFieldIDsDriveTaggedUnionDecodeAcrossRenames() throws { @Test func compatibleNestedStructArrayRoundTrip() throws { - let writer = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let writer = Fory(config: .init(trackRef: false, compatible: true)) writer.register(CompatibleNestedItem.self, id: 9103) writer.register(CompatibleNestedArrayHolder.self, id: 9104) - let reader = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let reader = Fory(config: .init(trackRef: false, compatible: true)) reader.register(CompatibleNestedItem.self, id: 9103) reader.register(CompatibleNestedArrayHolder.self, id: 9104) @@ -1080,11 +1077,11 @@ func compatibleNestedStructArrayRoundTrip() throws { @Test func compatibleNestedStructOptionalArrayRoundTrip() throws { - let writer = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let writer = Fory(config: .init(trackRef: false, compatible: true)) writer.register(CompatibleNestedItem.self, id: 9103) writer.register(CompatibleNestedOptionalArrayHolder.self, id: 9105) - let reader = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let reader = Fory(config: .init(trackRef: false, compatible: true)) reader.register(CompatibleNestedItem.self, id: 9103) reader.register(CompatibleNestedOptionalArrayHolder.self, id: 9105) @@ -1102,11 +1099,11 @@ func compatibleNestedStructOptionalArrayRoundTrip() throws { @Test func compatibleNestedStructMapRoundTrip() throws { - let writer = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let writer = Fory(config: .init(trackRef: false, compatible: true)) writer.register(CompatibleNestedItem.self, id: 9103) writer.register(CompatibleNestedMapHolder.self, id: 9106) - let reader = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let reader = Fory(config: .init(trackRef: false, compatible: true)) reader.register(CompatibleNestedItem.self, id: 9103) reader.register(CompatibleNestedMapHolder.self, id: 9106) diff --git a/swift/Tests/ForyTests/StringSerializerTests.swift b/swift/Tests/ForyTests/StringSerializerTests.swift index a0e0cef0d2..47f08f6fcc 100644 --- a/swift/Tests/ForyTests/StringSerializerTests.swift +++ b/swift/Tests/ForyTests/StringSerializerTests.swift @@ -57,7 +57,7 @@ private func stringPayloadBytes(for value: String) throws -> [UInt8] { @Test func stringSerializerRoundTripsUnicodeAndLengthBoundaries() throws { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let fory = Fory(config: .init(trackRef: false, compatible: true)) let values = [ "", "ascii", diff --git a/swift/Tests/ForyTests/UnsignedTests.swift b/swift/Tests/ForyTests/UnsignedTests.swift index b5ba223855..67941645ef 100644 --- a/swift/Tests/ForyTests/UnsignedTests.swift +++ b/swift/Tests/ForyTests/UnsignedTests.swift @@ -179,10 +179,10 @@ func unsignedMacroFieldsRoundTripAcrossSchemaModes() throws { ) ] - let schemaConsistent = Fory(config: .init(xlang: true, trackRef: false, compatible: false)) + let schemaConsistent = Fory(config: .init(trackRef: false, compatible: false)) schemaConsistent.register(UnsignedFieldBundle.self, id: 9801) - let compatible = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let compatible = Fory(config: .init(trackRef: false, compatible: true)) compatible.register(UnsignedFieldBundle.self, id: 9801) for value in cases { diff --git a/swift/Tests/ForyXlangTests/main.swift b/swift/Tests/ForyXlangTests/main.swift index f96fd9c98a..565c0487e3 100644 --- a/swift/Tests/ForyXlangTests/main.swift +++ b/swift/Tests/ForyXlangTests/main.swift @@ -601,7 +601,7 @@ private func handleMurmurHash(_ bytes: [UInt8]) throws -> [UInt8] { } private func handleStringSerializer(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let fory = Fory(config: .init(trackRef: false, compatible: true)) return try roundTripStream(bytes) { buffer, out in for _ in 0..<7 { let value: String = try fory.deserialize(from: buffer) @@ -611,7 +611,7 @@ private func handleStringSerializer(_ bytes: [UInt8]) throws -> [UInt8] { } private func handleCrossLanguageSerializer(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let fory = Fory(config: .init(trackRef: false, compatible: true)) fory.register(PeerColor.self, id: 101) return try roundTripStream(bytes) { buffer, out in @@ -674,7 +674,7 @@ private func handleCrossLanguageSerializer(_ bytes: [UInt8]) throws -> [UInt8] { } private func handleSimpleStruct(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let fory = Fory(config: .init(trackRef: false, compatible: true)) fory.register(PeerColor.self, id: 101) fory.register(Item.self, id: 102) fory.register(SimpleStruct.self, id: 103) @@ -682,7 +682,7 @@ private func handleSimpleStruct(_ bytes: [UInt8]) throws -> [UInt8] { } private func handleNamedSimpleStruct(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let fory = Fory(config: .init(trackRef: false, compatible: true)) try fory.register(PeerColor.self, namespace: "demo", name: "color") try fory.register(Item.self, namespace: "demo", name: "item") try fory.register(SimpleStruct.self, namespace: "demo", name: "simple_struct") @@ -702,7 +702,7 @@ private func handleStructEvolvingOverride(_ bytes: [UInt8]) throws -> [UInt8] { } private func handleList(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let fory = Fory(config: .init(trackRef: false, compatible: true)) fory.register(Item.self, id: 102) return try roundTripStream(bytes) { buffer, out in let v1: [String?] = try fory.deserialize(from: buffer) @@ -717,7 +717,7 @@ private func handleList(_ bytes: [UInt8]) throws -> [UInt8] { } private func handleMap(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let fory = Fory(config: .init(trackRef: false, compatible: true)) fory.register(Item.self, id: 102) return try roundTripStream(bytes) { buffer, out in let v1: [String?: String?] = try fory.deserialize(from: buffer) @@ -728,7 +728,7 @@ private func handleMap(_ bytes: [UInt8]) throws -> [UInt8] { } private func handleInteger(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let fory = Fory(config: .init(trackRef: false, compatible: true)) fory.register(Item1.self, id: 101) return try roundTripStream(bytes) { buffer, out in let item: Item1 = try fory.deserialize(from: buffer) @@ -749,7 +749,7 @@ private func handleInteger(_ bytes: [UInt8]) throws -> [UInt8] { } private func handleItem(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let fory = Fory(config: .init(trackRef: false, compatible: true)) fory.register(Item.self, id: 102) return try roundTripStream(bytes) { buffer, out in let i1: Item = try fory.deserialize(from: buffer) @@ -762,7 +762,7 @@ private func handleItem(_ bytes: [UInt8]) throws -> [UInt8] { } private func handleColor(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let fory = Fory(config: .init(trackRef: false, compatible: true)) fory.register(PeerColor.self, id: 101) return try roundTripStream(bytes) { buffer, out in for _ in 0..<4 { @@ -774,7 +774,7 @@ private func handleColor(_ bytes: [UInt8]) throws -> [UInt8] { private func handleDecimal(_ bytes: [UInt8]) throws -> [UInt8] { let expectedValues = try decimalValues() - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let fory = Fory(config: .init(trackRef: false, compatible: true)) let rewritten = try roundTripStream(bytes) { buffer, out in for expected in expectedValues { let value: Decimal = try fory.deserialize(from: buffer) @@ -791,7 +791,7 @@ private func handleDecimal(_ bytes: [UInt8]) throws -> [UInt8] { } private func handleStructWithList(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let fory = Fory(config: .init(trackRef: false, compatible: true)) fory.register(StructWithList.self, id: 201) return try roundTripStream(bytes) { buffer, out in let v1: StructWithList = try fory.deserialize(from: buffer) @@ -802,7 +802,7 @@ private func handleStructWithList(_ bytes: [UInt8]) throws -> [UInt8] { } private func handleStructWithMap(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let fory = Fory(config: .init(trackRef: false, compatible: true)) fory.register(StructWithMap.self, id: 202) return try roundTripStream(bytes) { buffer, out in let v1: StructWithMap = try fory.deserialize(from: buffer) @@ -813,19 +813,19 @@ private func handleStructWithMap(_ bytes: [UInt8]) throws -> [UInt8] { } private func handleNestedAnnotatedContainerSchemaConsistent(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: false)) + let fory = Fory(config: .init(trackRef: false, compatible: false)) fory.register(NestedAnnotatedContainerSchemaConsistent.self, id: 801) return try roundTripSingle(bytes, fory: fory, as: NestedAnnotatedContainerSchemaConsistent.self) } private func handleNestedAnnotatedContainerCompatible(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let fory = Fory(config: .init(trackRef: false, compatible: true)) fory.register(NestedAnnotatedContainerCompatible.self, id: 802) return try roundTripSingle(bytes, fory: fory, as: NestedAnnotatedContainerCompatible.self) } private func handleSkipIDCustom(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let fory = Fory(config: .init(trackRef: false, compatible: true)) fory.register(PeerColor.self, id: 101) fory.register(MyStruct.self, id: 102) fory.register(MyExt.self, id: 103) @@ -834,7 +834,7 @@ private func handleSkipIDCustom(_ bytes: [UInt8]) throws -> [UInt8] { } private func handleSkipNameCustom(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let fory = Fory(config: .init(trackRef: false, compatible: true)) try fory.register(PeerColor.self, name: "color") try fory.register(MyStruct.self, name: "my_struct") try fory.register(MyExt.self, name: "my_ext") @@ -843,7 +843,7 @@ private func handleSkipNameCustom(_ bytes: [UInt8]) throws -> [UInt8] { } private func handleConsistentNamed(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: false)) + let fory = Fory(config: .init(trackRef: false, compatible: false)) try fory.register(PeerColor.self, name: "color") try fory.register(MyStruct.self, name: "my_struct") try fory.register(MyExt.self, name: "my_ext") @@ -864,7 +864,7 @@ private func handleConsistentNamed(_ bytes: [UInt8]) throws -> [UInt8] { } private func handleStructVersionCheck(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: false)) + let fory = Fory(config: .init(trackRef: false, compatible: false)) fory.register(VersionCheckStruct.self, id: 201) return try roundTripSingle(bytes, fory: fory, as: VersionCheckStruct.self) } @@ -877,7 +877,7 @@ private func registerPolymorphicTypes(_ fory: Fory) { } private func handlePolymorphicList(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let fory = Fory(config: .init(trackRef: false, compatible: true)) registerPolymorphicTypes(fory) return try roundTripStream(bytes) { buffer, out in let animals: [Any] = try fory.deserialize(from: buffer) @@ -888,7 +888,7 @@ private func handlePolymorphicList(_ bytes: [UInt8]) throws -> [UInt8] { } private func handlePolymorphicMap(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let fory = Fory(config: .init(trackRef: false, compatible: true)) registerPolymorphicTypes(fory) return try roundTripStream(bytes) { buffer, out in let animalMap: [String: Any] = try fory.deserialize(from: buffer) @@ -899,7 +899,7 @@ private func handlePolymorphicMap(_ bytes: [UInt8]) throws -> [UInt8] { } private func handleUnionXlang(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let fory = Fory(config: .init(trackRef: false, compatible: true)) fory.register(StructWithUnion2.self, id: 301) return try roundTripStream(bytes) { buffer, out in let v1: StructWithUnion2 = try fory.deserialize(from: buffer) @@ -910,98 +910,98 @@ private func handleUnionXlang(_ bytes: [UInt8]) throws -> [UInt8] { } private func handleOneStringFieldSchema(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: false)) + let fory = Fory(config: .init(trackRef: false, compatible: false)) fory.register(OneStringFieldStruct.self, id: 200) return try roundTripSingle(bytes, fory: fory, as: OneStringFieldStruct.self) } private func handleOneStringFieldCompatible(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let fory = Fory(config: .init(trackRef: false, compatible: true)) fory.register(OneStringFieldStruct.self, id: 200) return try roundTripSingle(bytes, fory: fory, as: OneStringFieldStruct.self) } private func handleTwoStringFieldCompatible(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let fory = Fory(config: .init(trackRef: false, compatible: true)) fory.register(TwoStringFieldStruct.self, id: 201) return try roundTripSingle(bytes, fory: fory, as: TwoStringFieldStruct.self) } private func handleSchemaEvolutionCompatible(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let fory = Fory(config: .init(trackRef: false, compatible: true)) fory.register(EmptyStructEvolution.self, id: 200) return try roundTripSingle(bytes, fory: fory, as: EmptyStructEvolution.self) } private func handleSchemaEvolutionCompatibleReverse(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let fory = Fory(config: .init(trackRef: false, compatible: true)) fory.register(TwoStringFieldStruct.self, id: 200) return try roundTripSingle(bytes, fory: fory, as: TwoStringFieldStruct.self) } private func handleOneEnumFieldSchema(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: false)) + let fory = Fory(config: .init(trackRef: false, compatible: false)) fory.register(PeerTestEnum.self, id: 210) fory.register(OneEnumFieldStruct.self, id: 211) return try roundTripSingle(bytes, fory: fory, as: OneEnumFieldStruct.self) } private func handleOneEnumFieldCompatible(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let fory = Fory(config: .init(trackRef: false, compatible: true)) fory.register(PeerTestEnum.self, id: 210) fory.register(OneEnumFieldStruct.self, id: 211) return try roundTripSingle(bytes, fory: fory, as: OneEnumFieldStruct.self) } private func handleTwoEnumFieldCompatible(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let fory = Fory(config: .init(trackRef: false, compatible: true)) fory.register(PeerTestEnum.self, id: 210) fory.register(TwoEnumFieldStruct.self, id: 212) return try roundTripSingle(bytes, fory: fory, as: TwoEnumFieldStruct.self) } private func handleEnumSchemaEvolutionCompatible(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let fory = Fory(config: .init(trackRef: false, compatible: true)) fory.register(PeerTestEnum.self, id: 210) fory.register(EmptyStructEvolution.self, id: 211) return try roundTripSingle(bytes, fory: fory, as: EmptyStructEvolution.self) } private func handleEnumSchemaEvolutionCompatibleReverse(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let fory = Fory(config: .init(trackRef: false, compatible: true)) fory.register(PeerTestEnum.self, id: 210) fory.register(TwoEnumFieldStruct.self, id: 211) return try roundTripSingle(bytes, fory: fory, as: TwoEnumFieldStruct.self) } private func handleNullableFieldSchemaConsistent(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: false)) + let fory = Fory(config: .init(trackRef: false, compatible: false)) fory.register(NullableComprehensiveSchemaConsistent.self, id: 401) return try roundTripSingle(bytes, fory: fory, as: NullableComprehensiveSchemaConsistent.self) } private func handleNullableFieldCompatible(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let fory = Fory(config: .init(trackRef: false, compatible: true)) fory.register(NullableComprehensiveCompatibleSwift.self, id: 402) return try roundTripSingle(bytes, fory: fory, as: NullableComprehensiveCompatibleSwift.self) } private func handleRefSchemaConsistent(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: true, compatible: false)) + let fory = Fory(config: .init(trackRef: true, compatible: false)) fory.register(RefInnerSchemaConsistent.self, id: 501) fory.register(RefOuterSchemaConsistent.self, id: 502) return try roundTripSingle(bytes, fory: fory, as: RefOuterSchemaConsistent.self) } private func handleRefCompatible(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: true, compatible: true)) + let fory = Fory(config: .init(trackRef: true, compatible: true)) fory.register(RefInnerCompatible.self, id: 503) fory.register(RefOuterCompatible.self, id: 504) return try roundTripSingle(bytes, fory: fory, as: RefOuterCompatible.self) } private func handleCollectionElementRefOverride(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: true, compatible: false)) + let fory = Fory(config: .init(trackRef: true, compatible: false)) fory.register(RefOverrideElement.self, id: 701) fory.register(RefOverrideContainer.self, id: 702) let container: RefOverrideContainer = try fory.deserialize(Data(bytes)) @@ -1036,7 +1036,7 @@ private func handleCollectionElementRefOverride(_ bytes: [UInt8]) throws -> [UIn private func handleCollectionElementRefRemoteTracking(_ bytes: [UInt8]) throws -> [UInt8] { _ = bytes - let fory = Fory(config: .init(xlang: true, trackRef: true, compatible: false)) + let fory = Fory(config: .init(trackRef: true, compatible: false)) fory.register(RefOverrideElement.self, id: 701) fory.register(RefOverrideContainer.self, id: 702) @@ -1059,61 +1059,61 @@ private func handleCollectionElementRefRemoteTracking(_ bytes: [UInt8]) throws - } private func handleCircularRefSchemaConsistent(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: true, compatible: false)) + let fory = Fory(config: .init(trackRef: true, compatible: false)) fory.register(CircularRefStruct.self, id: 601) return try roundTripSingle(bytes, fory: fory, as: CircularRefStruct.self) } private func handleCircularRefCompatible(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: true, compatible: true)) + let fory = Fory(config: .init(trackRef: true, compatible: true)) fory.register(CircularRefStruct.self, id: 602) return try roundTripSingle(bytes, fory: fory, as: CircularRefStruct.self) } private func handleUnsignedSchemaConsistentSimple(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: false)) + let fory = Fory(config: .init(trackRef: false, compatible: false)) fory.register(UnsignedSchemaConsistentSimple.self, id: 1) return try roundTripSingle(bytes, fory: fory, as: UnsignedSchemaConsistentSimple.self) } private func handleUnsignedSchemaConsistent(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: false)) + let fory = Fory(config: .init(trackRef: false, compatible: false)) fory.register(UnsignedSchemaConsistent.self, id: 501) return try roundTripSingle(bytes, fory: fory, as: UnsignedSchemaConsistent.self) } private func handleUnsignedSchemaCompatible(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let fory = Fory(config: .init(trackRef: false, compatible: true)) fory.register(UnsignedSchemaCompatible.self, id: 502) return try roundTripSingle(bytes, fory: fory, as: UnsignedSchemaCompatible.self) } private func handleReducedPrecisionFloatStruct(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: false)) + let fory = Fory(config: .init(trackRef: false, compatible: false)) fory.register(ReducedPrecisionFloatStruct.self, id: 213) return try roundTripSingle(bytes, fory: fory, as: ReducedPrecisionFloatStruct.self) } private func handleReducedPrecisionFloatStructCompatibleSkip(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let fory = Fory(config: .init(trackRef: false, compatible: true)) fory.register(EmptyStructEvolution.self, id: 213) return try roundTripSingle(bytes, fory: fory, as: EmptyStructEvolution.self) } private func handleListArrayCompatibleListToArray(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let fory = Fory(config: .init(trackRef: false, compatible: true)) fory.register(CompatibleInt32ArrayField.self, id: 901) return try roundTripSingle(bytes, fory: fory, as: CompatibleInt32ArrayField.self) } private func handleListArrayCompatibleArrayToList(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let fory = Fory(config: .init(trackRef: false, compatible: true)) fory.register(CompatibleInt32ListField.self, id: 901) return try roundTripSingle(bytes, fory: fory, as: CompatibleInt32ListField.self) } private func handleListArrayCompatibleNullableListToArrayError(_ bytes: [UInt8]) throws -> [UInt8] { - let fory = Fory(config: .init(xlang: true, trackRef: false, compatible: true)) + let fory = Fory(config: .init(trackRef: false, compatible: true)) fory.register(CompatibleInt32ArrayField.self, id: 901) do { let _: CompatibleInt32ArrayField = try fory.deserialize(Data(bytes))