This is an automated email from the ASF dual-hosted git repository. chaokunyang pushed a commit to branch remvoe_parts_of_emojis in repository https://gitbox.apache.org/repos/asf/fory-site.git
commit 287f3b12f5e8ac0dbdbf28ec261f45bd6a9746d5 Author: chaokunyang <[email protected]> AuthorDate: Tue Mar 24 13:37:20 2026 +0800 update docs --- docs/community/how_to_release.md | 1 + docs/start/install.md | 2 +- .../current/start/install.md | 2 +- .../version-0.16/community/how_to_release.md | 16 +- .../version-0.16/introduction/benchmark.md | 101 +++- .../version-0.16/start/install.md | 118 +++- .../version-0.16/start/usage.md | 643 ++++++++++++++++----- .../version-0.16/community/how_to_release.md | 14 +- .../version-0.16/introduction/benchmark.md | 77 ++- versioned_docs/version-0.16/start/install.md | 86 ++- versioned_docs/version-0.16/start/usage.md | 374 ++++++++++-- 11 files changed, 1146 insertions(+), 288 deletions(-) diff --git a/docs/community/how_to_release.md b/docs/community/how_to_release.md index 767e3e6a8a..a993cf809a 100644 --- a/docs/community/how_to_release.md +++ b/docs/community/how_to_release.md @@ -215,6 +215,7 @@ If the discussion goes positive, you will need to prepare the release artifiacts - If the Go module under `go/fory` is part of this release, create and push the Go submodule tag as well. For example, for the final `0.16.0` release: ```bash +git remote add apache [email protected]:apache/fory.git git tag go/fory/v0.16.0 git push apache go/fory/v0.16.0 ``` diff --git a/docs/start/install.md b/docs/start/install.md index efb91da732..3c93af80db 100644 --- a/docs/start/install.md +++ b/docs/start/install.md @@ -114,7 +114,7 @@ cargo add [email protected] ## JavaScript -The JavaScript packages are defined in the Apache Fory source tree as `@apache-fory/core` and `@apache-fory/hps`, but they are not published to npm yet. +The JavaScript packages are not published to npm yet. Install and build them from source for now: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/start/install.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/start/install.md index ecb8b83e43..2adeffddb8 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/start/install.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/start/install.md @@ -114,7 +114,7 @@ cargo add [email protected] ## JavaScript -Apache Fory 的 JavaScript 包在源码仓库中定义为 `@apache-fory/core` 和 `@apache-fory/hps`,但目前还没有发布到 npm。 +Apache Fory 的 JavaScript 包目前还没有发布到 npm。 目前请先从源码安装并构建: diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.16/community/how_to_release.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.16/community/how_to_release.md index 73a113b8a9..8a711b8e66 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.16/community/how_to_release.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.16/community/how_to_release.md @@ -19,7 +19,7 @@ id: how_to_release 此发布过程在 Ubuntu 系统中运行,需要以下几个环境依赖: - JDK 1.8+ -- Apache Maven 3.x+ 版本 +- Apache Maven 3.x+ - Python 3.8 - GnuPG 2.x - Git @@ -202,10 +202,17 @@ ${name} ### Github 分支和标签 -- 创建一个名为 `releases-0.12.0` +- 创建一个名为 `releases-0.16.0` - 通过执行命令将版本 `$version` 升级到 `python ci/release.py bump_version -l all -version $version` - 执行 git commit 并将分支推送到 `[email protected]:apache/fory.git` -- 通过 `git tag v0.12.0-rc1` 创建一个新标签,然后将其推送到 `[email protected]:apache/fory.git` +- 通过 `git tag v0.16.0-rc1` 创建一个 release 标签,然后将其推送到 `[email protected]:apache/fory.git` +- 如果本次发布包含 `go/fory` 这个 Go 子模块,还需要额外创建并推送 Go 子模块标签。例如,对于最终版 `0.16.0`,执行: + +```bash +git remote add apache [email protected]:apache/fory.git +git tag go/fory/v0.16.0 +git push apache go/fory/v0.16.0 +``` ### 构建 artifacts 并上传到 SVN dist/dev 仓库 @@ -284,6 +291,9 @@ https://repository.apache.org/content/repositories/orgapachefory-1003 Git tag for the release: https://github.com/apache/fory/releases/tag/v0.12.0-rc1 +如果本次发布还包含 Go 模块,请同时附上 Go 子模块标签: +https://github.com/apache/fory/releases/tag/go/fory/v0.16.0 + Git commit for the release: https://github.com/apache/fory/commit/fae06330edd049bb960536e978a45b97bca66faf diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.16/introduction/benchmark.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.16/introduction/benchmark.md index 1145623351..568bd14fc0 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.16/introduction/benchmark.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.16/introduction/benchmark.md @@ -13,21 +13,82 @@ sidebar_position: 2 ## Java 性能测试 -### Java 序列化 +Java 部分已切换为 `docs/benchmarks/java` 中的最新性能测试图。图表现在按以下维度分组: -<img width="33%" alt="" src="/img/benchmarks/serialization/bench_serialize_compatible_STRUCT_to_directBuffer_tps.png" /> -<img width="33%" alt="" src="/img/benchmarks/serialization/bench_serialize_compatible_MEDIA_CONTENT_to_array_tps.png" /> -<img width="33%" alt="" src="/img/benchmarks/serialization/bench_serialize_MEDIA_CONTENT_to_array_tps.png" /> -<img width="33%" alt="" src="/img/benchmarks/serialization/bench_serialize_SAMPLE_to_array_tps.png" /> +- **Heap**:序列化到堆上 `byte[]` 缓冲区 +- **Off-heap**:序列化到 direct/off-heap 缓冲区 +- **Compatible**:启用前向/后向兼容的 Schema 演进模式 +- **Consistent**:要求读写两端 Schema 完全一致的模式 -### Java 反序列化 +**测试数据类型**: -<img width="33%" alt="" src="/img/benchmarks/deserialization/bench_deserialize_compatible_STRUCT_from_directBuffer_tps.png" /> -<img width="33%" alt="" src="/img/benchmarks/deserialization/bench_deserialize_compatible_MEDIA_CONTENT_from_array_tps.png" /> -<img width="33%" alt="" src="/img/benchmarks/deserialization/bench_deserialize_MEDIA_CONTENT_from_array_tps.png" /> -<img width="33%" alt="" src="/img/benchmarks/deserialization/bench_deserialize_SAMPLE_from_array_tps.png" /> +- `Struct`:包含 [100 个基础类型字段](https://github.com/apache/fory/tree/main/docs/benchmarks#Struct) 的类 +- `MediaContent`:来自 [jvm-serializers](https://github.com/eishay/jvm-serializers/blob/master/tpc/src/data/media/MediaContent.java) 的类 +- `Sample`:来自 [Kryo benchmark](https://github.com/EsotericSoftware/kryo/blob/master/benchmarks/src/main/java/com/esotericsoftware/kryo/benchmarks/data/Sample.java) 的类 -更多关于类型前向/后向兼容性、堆外支持、零拷贝序列化的性能测试,请参见 [benchmarks](https://github.com/apache/fory/tree/main/docs/benchmarks)。 +### Heap 序列化 + +Compatible 模式: + +<img width="90%" alt="" src="/img/benchmarks/serialization/bench_serialize_compatible_MEDIA_CONTENT_to_array_tps.png" /> + +Consistent 模式: + +<img width="90%" alt="" src="/img/benchmarks/serialization/bench_serialize_MEDIA_CONTENT_to_array_tps.png" /> + +### Off-heap 序列化 + +Compatible 模式: + +<img width="90%" alt="" src="/img/benchmarks/serialization/bench_serialize_compatible_STRUCT_to_directBuffer_tps.png" /> + +Consistent 模式: + +<img width="90%" alt="" src="/img/benchmarks/serialization/bench_serialize_STRUCT_to_directBuffer_tps.png" /> + +### Heap 反序列化 + +Compatible 模式: + +<img width="90%" alt="" src="/img/benchmarks/deserialization/bench_deserialize_compatible_MEDIA_CONTENT_from_array_tps.png" /> + +Consistent 模式: + +<img width="90%" alt="" src="/img/benchmarks/deserialization/bench_deserialize_MEDIA_CONTENT_from_array_tps.png" /> + +### Off-heap 反序列化 + +Compatible 模式: + +<img width="90%" alt="" src="/img/benchmarks/deserialization/bench_deserialize_compatible_STRUCT_from_directBuffer_tps.png" /> + +Consistent 模式: + +<img width="90%" alt="" src="/img/benchmarks/deserialization/bench_deserialize_STRUCT_from_directBuffer_tps.png" /> + +### 仓库基准汇总 + +序列化吞吐: + +<img width="90%" alt="" src="/img/benchmarks/serialization/bench_serialize_SAMPLE_to_array_tps.png" /> + +反序列化吞吐: + +<img width="90%" alt="" src="/img/benchmarks/deserialization/bench_deserialize_SAMPLE_from_array_tps.png" /> + +### 零拷贝性能测试 + +序列化: + +<img width="90%" alt="" src="/img/benchmarks/zerocopy/zero_copy_bench_serialize_BUFFER_to_array_tps.png" /> + +反序列化: + +<img width="90%" alt="" src="/img/benchmarks/zerocopy/zero_copy_bench_deserialize_BUFFER_from_array_tps.png" /> + +注意:Apache Fory™ 依赖运行时代码生成,进行性能测试前必须充分预热。 + +更多说明、原始数据和完整 Java benchmark README 请参见 [Java Benchmarks](https://github.com/apache/fory/tree/main/docs/benchmarks/java)。 ## Rust 性能测试 @@ -51,23 +112,15 @@ Fory Go 在单对象和列表两类工作负载下,相较 Protobuf 与 Msgpack <img src="/img/blog/fory_0_16_0_release/go_benchmark_combined.png" width="90%"/> -注意:结果取决于硬件、数据集和实现版本。详细信息请参见 Go benchmark 报告: https://fory.apache.org/docs/benchmarks/go/ - -## C# 性能测试 - -Fory C# 在强类型对象的序列化和反序列化场景下,相较 Protobuf 与 Msgpack 展现出较强的性能表现。 - -<img src="/img/blog/fory_0_16_0_release/csharp_benchmark_combined.png" width="90%"/> - -注意:结果取决于硬件和运行时版本。详细信息请参见 C# benchmark 报告: https://fory.apache.org/docs/benchmarks/csharp/ +注意:结果取决于硬件、数据集和实现版本。详细信息请参见 Go 性能测试报告:https://fory.apache.org/docs/benchmarks/go/ -## Swift 性能测试 +## Python 性能测试 -Fory Swift 在标量对象和列表两类工作负载下,相较 Protobuf 与 Msgpack 展现出较强的性能表现。 +Fory Python 在单对象和列表两类工作负载下,相较 `pickle` 与 Protobuf 展现出较强的性能表现。 -<img src="/img/blog/fory_0_16_0_release/swift_benchmark_combined.png" width="90%"/> +<img src="/img/benchmarks/python/throughput.png" width="90%"/> -注意:结果取决于硬件和运行时版本。详细信息请参见 Swift benchmark 报告: https://fory.apache.org/docs/benchmarks/swift/ +注意:结果取决于硬件、数据集、Python 运行时和实现版本。详细信息请参见 Python 性能测试报告:https://fory.apache.org/docs/benchmarks/python/ ## JavaScript 性能测试 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.16/start/install.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.16/start/install.md index 39dbd11a0c..2adeffddb8 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.16/start/install.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.16/start/install.md @@ -4,98 +4,164 @@ title: 安装 Apache Fory™ sidebar_position: 0 --- -Apache Fory™ 源码下载请参见 Apache Fory™ [download](https://github.com/apache/fory/releases)页面。 +Apache Fory™ 同时提供源码发布物和各语言对应的软件包。 -## 安装 Apache Fory™ Java +源码下载请参见 Apache Fory™ [download](https://fory.apache.org/download) 页面。 -要使用 Maven 添加对 Apache Fory™ 的依赖,请使用以下配置: +## Java + +使用 Maven 添加 Apache Fory™: ```xml <dependency> <groupId>org.apache.fory</groupId> <artifactId>fory-core</artifactId> - <version>0.14.1</version> + <version>0.16.0</version> </dependency> -<!-- Optional row format support --> +<!-- 可选:row format 支持 --> <!-- <dependency> <groupId>org.apache.fory</groupId> <artifactId>fory-format</artifactId> - <version>0.13.1</version> + <version>0.16.0</version> </dependency> --> -<!-- SIMD acceleration for array compression (Java 16+) --> +<!-- 可选:数组压缩 SIMD 加速(Java 16+) --> <!-- <dependency> <groupId>org.apache.fory</groupId> <artifactId>fory-simd</artifactId> - <version>0.13.1</version> + <version>0.16.0</version> </dependency> --> ``` -## 安装 Apache Fory™ Scala +## Scala -要使用 Maven 添加 scala 2.13 的 Fory scala 依赖,请使用以下配置: +Scala 2.13 的 Maven 依赖: ```xml <dependency> <groupId>org.apache.fory</groupId> <artifactId>fory-scala_2.13</artifactId> - <version>0.14.1</version> + <version>0.16.0</version> </dependency> ``` -要使用 Maven 添加 scala 3 的 Fory scala 依赖,请使用以下配置: +Scala 3 的 Maven 依赖: ```xml <dependency> <groupId>org.apache.fory</groupId> <artifactId>fory-scala_3</artifactId> - <version>0.14.1</version> + <version>0.16.0</version> </dependency> ``` -要使用 sbt 添加 scala 2.13 的 Fory scala 依赖,请使用以下配置: +Scala 2.13 的 sbt 依赖: ```sbt -libraryDependencies += "org.apache.fory" % "fory-scala_2.13" % "0.14.1" +libraryDependencies += "org.apache.fory" % "fory-scala_2.13" % "0.16.0" ``` -要使用 sbt 添加 scala 3 的 Fory scala 依赖,请使用以下配置: +Scala 3 的 sbt 依赖: ```sbt -libraryDependencies += "org.apache.fory" % "fory-scala_3" % "0.14.1" +libraryDependencies += "org.apache.fory" % "fory-scala_3" % "0.16.0" ``` -## 安装 Apache Fory™ Kotlin +## Kotlin -要使用 Maven 添加 Apache Fory™ Kotlin 依赖,请使用以下配置: +使用 Maven 添加 Apache Fory™ Kotlin: ```xml <dependency> <groupId>org.apache.fory</groupId> <artifactId>fory-kotlin</artifactId> - <version>0.14.1</version> + <version>0.16.0</version> </dependency> ``` -## 安装 Apache Fory™ Python +## Python ```bash python -m pip install --upgrade pip -pip install pyfory==0.14.1 +pip install pyfory==0.16.0 ``` -## 安装 Apache Fory™ Rust +## Go + +请使用完整的 Go 模块路径 `github.com/apache/fory/go/fory`: + +```bash +go get github.com/apache/fory/go/[email protected] +``` + +如果你的 Go proxy 还没有同步到新的子模块 tag,可以稍后重试,或者临时使用 `GOPROXY=direct`。 + +## Rust ```toml [dependencies] -fory = "0.14" +fory = "0.16.0" +``` + +或者使用 `cargo add`: + +```bash +cargo add [email protected] +``` + +## JavaScript + +Apache Fory 的 JavaScript 包目前还没有发布到 npm。 + +目前请先从源码安装并构建: + +```bash +git clone https://github.com/apache/fory.git +cd fory/javascript +npm install +npm run build +``` + +完成源码构建后,再在你的项目或 workspace 中使用 `@apache-fory/core`,并按需启用 `@apache-fory/hps`。 + +可选的原生加速模块需要 Node.js 20+: + +```bash +cd packages/hps +npm run build ``` -或者直接执行以下命令: +## C\# + +安装 `Apache.Fory` NuGet 包。它同时包含运行时和 `[ForyObject]` 类型所需的源代码生成器。 ```bash -cargo add [email protected] +dotnet add package Apache.Fory --version 0.16.0 +``` + +```xml +<ItemGroup> + <PackageReference Include="Apache.Fory" Version="0.16.0" /> +</ItemGroup> +``` + +## Swift + +使用 Swift Package Manager 从 GitHub 仓库引入 Apache Fory™: + +```swift +dependencies: [ + .package(url: "https://github.com/apache/fory.git", exact: "0.16.0") +], +targets: [ + .target( + name: "MyApp", + dependencies: [ + .product(name: "Fory", package: "fory") + ] + ) +] ``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.16/start/usage.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.16/start/usage.md index 427b649d61..f1a27c31db 100644 --- a/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.16/start/usage.md +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.16/start/usage.md @@ -4,239 +4,592 @@ title: Apache Fory™ 使用 sidebar_position: 1 --- -本章节演示如何在不同编程语言中使用 Apache Fory™ 进行序列化。 +本章节提供 Apache Fory™ 的快速入门示例。 -## Java 序列化 +## 原生序列化 + +**当你只在单一语言内使用时,请始终选择原生模式。** 原生模式不需要为跨语言兼容写入额外类型元信息,因此性能最好。 + +xlang 模式会引入额外的元信息编码开销,并且只允许序列化所有受支持语言都能映射的类型。语言特有类型在 xlang 模式下会被拒绝。 + +### Java 序列化 + +如果不需要跨语言支持,请使用 Java 模式以获得最佳性能。 + +下面的示例展示了最基本的 Java 原生用法:创建可复用的 Java 模式运行时、注册用户类型,然后完成一次序列化和反序列化往返。实际项目中不要为每个对象重新创建 `Fory` 实例,而应长期复用同一个实例。 ```java -import java.util.List; -import java.util.Arrays; -import io.fory.*; +import org.apache.fory.*; +import org.apache.fory.config.*; public class Example { + public static class Person { + String name; + int age; + } + public static void main(String[] args) { - SomeClass object = new SomeClass(); - // 注意:Fory 实例应该被复用,而不是每次序列化前都重新创建 - Fory fory = Fory.builder().withLanguage(Language.JAVA) - // 允许反序列化未注册类型,灵活性更高,但安全性更低 - // .requireClassRegistration(false) + // 创建一次 Fory 实例并重复复用。 + BaseFory fory = Fory.builder() + .withLanguage(Language.JAVA) + .requireClassRegistration(true) + // 如果需要线程安全用法,请将 `build` 替换为 `buildThreadSafeFory`。 .build(); - // 注册类型可以减少类名序列化开销,但不是强制要求 - // 如果开启安全模式,所有自定义类型都必须注册 - fory.register(SomeClass.class); - byte[] bytes = fory.serialize(object); - System.out.println(fory.deserialize(bytes)); + fory.register(Person.class); + + Person person = new Person(); + person.name = "chaokunyang"; + person.age = 28; + + byte[] bytes = fory.serialize(person); + Person result = (Person) fory.deserialize(bytes); + System.out.println(result.name + " " + result.age); + } +} +``` + +关于兼容模式、压缩和更多高级特性,请参见 [Java 序列化指南](../guide/java/index.md)。 + +### Python 序列化 + +Python 原生模式可以作为 `pickle` 和 `cloudpickle` 的高性能替代方案。 + +这个示例使用 dataclass 和显式整数类型注解,让 Fory 能以更清晰的 Schema 进行高效序列化。与其他语言一样,推荐只创建一次 `Fory` 实例、只注册一次类型,然后在后续调用中重复使用。 + +```python +from dataclasses import dataclass +import pyfory + +@dataclass +class Person: + name: str + age: pyfory.int32 + +fory = pyfory.Fory() +fory.register_type(Person) + +person = Person(name="chaokunyang", age=28) +data = fory.serialize(person) +result = fory.deserialize(data) +print(result.name, result.age) +``` + +关于类型注解、兼容模式和更多高级特性,请参见 [Python 指南](../guide/python/index.md)。 + +### Go 序列化 + +Go 原生模式默认启用。注册一次结构体后,重复复用同一个 `Fory` 实例即可。 + +Go 运行时天然适配导出的 struct 字段和显式类型注册。下面的代码演示了最常见的流程:创建 `Fory`、注册结构体类型、序列化一个值,再反序列化到目标结构体中。 + +```go +package main + +import ( + "fmt" + + "github.com/apache/fory/go/fory" +) + +type Person struct { + Name string + Age int32 +} + +func main() { + f := fory.New() + if err := f.RegisterStruct(Person{}, 1); err != nil { + panic(err) + } + + person := &Person{Name: "chaokunyang", Age: 28} + data, err := f.Serialize(person) + if err != nil { + panic(err) + } + + var result Person + if err := f.Deserialize(data, &result); err != nil { + panic(err) + } + + fmt.Printf("%s %d\n", result.Name, result.Age) +} +``` + +关于配置、struct tag 和 Schema 演进,请参见 [Go 指南](../guide/go/index.md)。 + +### C# 序列化 + +C# 原生序列化使用 `Apache.Fory` 运行时和 `[ForyObject]` 模型类型。 + +在 C# 中,常见模式是先用 `[ForyObject]` 标记模型,再创建一次运行时并在使用前注册类型。示例展示的是强类型的 `Serialize` / `Deserialize<T>` API,这也是 .NET 应用中最直接的用法。 + +```csharp +using Apache.Fory; + +[ForyObject] +public sealed class Person +{ + public string Name { get; set; } = string.Empty; + public int Age { get; set; } +} + +Fory fory = Fory.Builder().Build(); +fory.Register<Person>(1); + +Person person = new() { Name = "chaokunyang", Age = 28 }; +byte[] data = fory.Serialize(person); +Person result = fory.Deserialize<Person>(data); + +Console.WriteLine($"{result.Name} {result.Age}"); +``` + +关于源代码生成器、引用跟踪和 Schema 演进,请参见 [C# 指南](../guide/csharp/index.md)。 + +### Swift 序列化 + +Swift 原生序列化直接使用 `@ForyObject` 模型和 `Fory` 运行时。 + +Swift 通过宏定义模型类型,因此示例先使用 `@ForyObject` 标记类型,再注册类型 ID 并完成一次强类型往返。这是 Swift 应用侧最推荐的入门方式。 + +```swift +import Fory + +@ForyObject +struct Person: Equatable { + var name: String = "" + var age: Int32 = 0 +} + +let fory = Fory() +fory.register(Person.self, id: 1) + +let person = Person(name: "chaokunyang", age: 28) +let data = try fory.serialize(person) +let result: Person = try fory.deserialize(data) + +print("\(result.name) \(result.age)") +``` + +关于多态、Schema 演进和常见问题排查,请参见 [Swift 指南](../guide/swift/index.md)。 + +### Rust 序列化 + +Rust 原生模式使用 `Fory::default()` 和 derive 宏来实现编译期类型安全的序列化。常见模式是先为类型派生 `ForyObject`,注册一次类型,再重复复用已经配置好的运行时。 + +```rust +use fory::{Error, Fory, ForyObject}; + +#[derive(ForyObject, Debug, PartialEq)] +struct Person { + name: String, + age: i32, +} + +fn main() -> Result<(), Error> { + let mut fory = Fory::default(); + fory.register::<Person>(1)?; + + let person = Person { + name: "chaokunyang".to_string(), + age: 28, + }; + + let bytes = fory.serialize(&person)?; + let result: Person = fory.deserialize(&bytes)?; + assert_eq!(person, result); + Ok(()) +} +``` + +关于引用、多态和 row format 支持,请参见 [Rust 指南](../guide/rust/index.md)。 + +### C++ 序列化 + +C++ 原生模式使用 `FORY_STRUCT` 宏描述可序列化字段,再通过配置好的 `Fory` 运行时对值进行编码和解码。对于单语言 C++ 场景,建议显式设置 `xlang(false)`,让运行时保持在原生模式。 + +```cpp +#include "fory/serialization/fory.h" + +using namespace fory::serialization; + +struct Person { + std::string name; + int32_t age; + + bool operator==(const Person &other) const { + return name == other.name && age == other.age; } + + FORY_STRUCT(Person, name, age); +}; + +int main() { + auto fory = Fory::builder().xlang(false).build(); + fory.register_struct<Person>(1); + + Person person{"chaokunyang", 28}; + + auto bytes = fory.serialize(person); + auto result = fory.deserialize<Person>(bytes.value()); + assert(result.ok()); + assert(person == result.value()); + return 0; } ``` -## Scala 序列化 +关于 `FORY_STRUCT`、线程安全和 Schema 演进,请参见 [C++ 指南](../guide/cpp/index.md)。 + +### Scala 序列化 + +Scala 原生模式对 case class、集合和 `Option` 等 Scala 特有类型提供了优化支持。 + +在 Scala 项目中,应先注册 Scala 专用序列化器,让 Fory 正确理解 Scala 特有的数据结构。完成这一步后,就可以像 Java 运行时一样注册 case class 并执行序列化。 ```scala import org.apache.fory.Fory +import org.apache.fory.config.Language import org.apache.fory.serializer.scala.ScalaSerializers -case class Person(name: String, id: Long, github: String) -case class Point(x: Int, y: Int, z: Int) - -object ScalaExample { - val fory: Fory = Fory.builder().withScalaOptimizationEnabled(true).build() - // 注册 Scala 优化序列化器 - ScalaSerializers.registerSerializers(fory) - fory.register(classOf[Person]) - fory.register(classOf[Point]) +case class Person(name: String, age: Int) +object Example { def main(args: Array[String]): Unit = { - val p = Person("Shawn Yang", 1, "https://github.com/chaokunyang") - println(fory.deserialize(fory.serialize(p))) - println(fory.deserialize(fory.serialize(Point(1, 2, 3)))) + val fory = Fory.builder() + .withLanguage(Language.JAVA) + .requireClassRegistration(true) + .build() + ScalaSerializers.registerSerializers(fory) + fory.register(classOf[Person]) + + val bytes = fory.serialize(Person("chaokunyang", 28)) + val result = fory.deserialize(bytes).asInstanceOf[Person] + println(s"${result.name} ${result.age}") } } ``` -## Kotlin 序列化 +关于集合序列化和集成模式,请参见 [Scala 指南](../guide/scala/index.md)。 + +### Kotlin 序列化 + +Kotlin 原生模式对 data class、可空类型和 Kotlin 集合提供了优化支持。 + +Kotlin 的整体流程与 Java 类似,只是额外需要注册 Kotlin 专用序列化器。下面的示例使用 data class,展示了进行高效原生序列化所需的最小配置。 ```kotlin import org.apache.fory.Fory -import org.apache.fory.ThreadSafeFory +import org.apache.fory.config.Language import org.apache.fory.serializer.kotlin.KotlinSerializers -data class Person(val name: String, val id: Long, val github: String) -data class Point(val x: Int, val y: Int, val z: Int) +data class Person(val name: String, val age: Int) -fun main(args: Array<String>) { - // 注意:下面的 Fory 初始化代码应该只执行一次,而不是每次序列化前都运行 - val fory: ThreadSafeFory = Fory.builder().requireClassRegistration(true).buildThreadSafeFory() +fun main() { + val fory = Fory.builder() + .withLanguage(Language.JAVA) + .requireClassRegistration(true) + .build() KotlinSerializers.registerSerializers(fory) fory.register(Person::class.java) - fory.register(Point::class.java) - val p = Person("Shawn Yang", 1, "https://github.com/chaokunyang") - println(fory.deserialize(fory.serialize(p))) - println(fory.deserialize(fory.serialize(Point(1, 2, 3)))) + val bytes = fory.serialize(Person("chaokunyang", 28)) + val result = fory.deserialize(bytes) as Person + println("${result.name} ${result.age}") } ``` +关于空安全和默认值支持,请参见 [kotlin/README.md](https://github.com/apache/fory/blob/main/kotlin/README.md)。 + ## 跨语言序列化 +**只有在确实需要跨语言数据交换时才使用 xlang 模式。** xlang 模式会为跨语言兼容增加类型元信息开销,并且只支持能够在所有语言之间映射的类型。 + +下面的示例在多个运行时中使用同一个 `Person` Schema。无论使用哪种语言,都需要启用 xlang 模式,并用相同的 ID 或相同的全限定名称注册类型。 + ### Java +Java 的 xlang 用法可以看作 JVM 服务中的基准模式。启用 `Language.XLANG` 后,用稳定的 ID 或名称注册类型,并确保所有对端语言都使用相同的映射关系。 + ```java -import com.google.common.collect.ImmutableMap; -import io.fory.*; +import org.apache.fory.*; +import org.apache.fory.config.*; -import java.util.Map; +public class XlangExample { + public record Person(String name, int age) {} -public class ReferenceExample { - public static class SomeClass { - SomeClass f1; - Map<String, String> f2; - Map<String, String> f3; - } + public static void main(String[] args) { + Fory fory = Fory.builder() + .withLanguage(Language.XLANG) + .build(); - public static Object createObject() { - SomeClass obj = new SomeClass(); - obj.f1 = obj; - obj.f2 = ImmutableMap.of("k1", "v1", "k2", "v2"); - obj.f3 = obj.f2; - return obj; - } + fory.register(Person.class, 1); + // fory.register(Person.class, "example.Person"); - // mvn exec:java -Dexec.mainClass="io.fory.examples.ReferenceExample" - public static void main(String[] args) { - Fory fory = Fory.builder().withLanguage(Language.XLANG) - .withRefTracking(true).build(); - fory.register(SomeClass.class, "example.SomeClass"); - byte[] bytes = fory.serialize(createObject()); - // bytes 可以被其他语言生成或消费 - System.out.println(fory.deserialize(bytes)); + Person person = new Person("chaokunyang", 28); + byte[] bytes = fory.serialize(person); + Person result = (Person) fory.deserialize(bytes); + System.out.println(result.name() + " " + result.age()); } } ``` -### Python - -```python -from typing import Dict -import pyfory - -class SomeClass: - f1: "SomeClass" - f2: Dict[str, str] - f3: Dict[str, str] - -fory = pyfory.Fory(ref_tracking=True) -fory.register_class(SomeClass, "example.SomeClass") -obj = SomeClass() -obj.f2 = {"k1": "v1", "k2": "v2"} -obj.f1, obj.f3 = obj, obj.f2 -data = fory.serialize(obj) -# bytes 可以被其他语言生成或消费 -print(fory.deserialize(data)) -``` - ### Go +Go 通过 `WithXlang(true)` 启用跨语言模式。真正关键的不是 Go 语法本身,而是保证注册的类型身份与其他读写同一载荷的语言完全一致。 + ```go package main import ( "fmt" - forygo "github.com/apache/fory/go/fory" + + "github.com/apache/fory/go/fory" ) -func main() { - type SomeClass struct { - F1 *SomeClass - F2 map[string]string - F3 map[string]string - } +type Person struct { + Name string + Age int32 +} - fory := forygo.NewFory(true) - if err := fory.RegisterTagType("example.SomeClass", SomeClass{}); err != nil { +func main() { + f := fory.New(fory.WithXlang(true)) + if err := f.RegisterStruct(Person{}, 1); err != nil { panic(err) } - value := &SomeClass{F2: map[string]string{"k1": "v1", "k2": "v2"}} - value.F3 = value.F2 - value.F1 = value - - bytes, err := fory.Marshal(value) + person := &Person{Name: "chaokunyang", Age: 28} + data, err := f.Serialize(person) if err != nil { panic(err) } - var newValue interface{} - // bytes 可以被其他语言生成或消费 - if err := fory.Unmarshal(bytes, &newValue); err != nil { + var result Person + if err := f.Deserialize(data, &result); err != nil { panic(err) } - fmt.Println(newValue) + + fmt.Printf("%s %d\n", result.Name, result.Age) +} +``` + +### Rust + +Rust 同样遵循这套跨语言约定,只是通过派生 trait 和在 `Fory` 实例上显式注册来表达。只要类型 ID 与其他运行时一致,载荷就可以安全地跨语言流转。 + +```rust +use fory::{Fory, ForyObject}; +use std::error::Error; + +#[derive(ForyObject, Debug)] +struct Person { + name: String, + age: i32, +} + +fn main() -> Result<(), Box<dyn Error>> { + let mut fory = Fory::default().xlang(true); + fory.register::<Person>(1)?; + // fory.register_by_name::<Person>("example.Person")?; + + let person = Person { + name: "chaokunyang".to_string(), + age: 28, + }; + let bytes = fory.serialize(&person); + let result: Person = fory.deserialize(&bytes)?; + println!("{} {}", result.name, result.age); + Ok(()) } ``` ### JavaScript -```typescript -import Fory, { Type } from "@apache-fory/fory"; +JavaScript 的跨语言支持是基于 Schema 描述的。它不是注册类,而是通过 `Type.object(...)` 描述载荷结构,再使用返回的序列化器来编码和解码数据。 + +这些包目前还没有发布到 npm。因此请先从 Apache Fory 仓库完成构建,再按下面的 API 方式使用。 + +```javascript +import Fory, { Type } from "@apache-fory/core"; /** - * @apache-fory/hps 使用 v8 的 fast-calls-api,可被 JIT 直接调用, - * 因此要求 Node 版本至少为 20。 - * 该特性仍是实验性的,当前不保证一定能安装成功。 - * 如果模块无法安装,可替换为 `const hps = null;` + * `@apache-fory/hps` 会通过 JIT 直接使用 V8 fast calls。 + * 启用时请使用 Node.js 20+。 + * 如果安装失败,请将它替换为 `const hps = null;`。 */ import hps from "@apache-fory/hps"; -// 当前使用 JSON 方式描述数据结构,未来会提供更多描述方式。 -const description = Type.object("example.foo", { - foo: Type.string(), +const description = Type.object("example.Person", { + name: Type.string(), + age: Type.int32(), }); const fory = new Fory({ hps }); const { serialize, deserialize } = fory.registerSerializer(description); -const input = serialize({ foo: "hello fory" }); -const result = deserialize(input); + +const payload = serialize({ name: "chaokunyang", age: 28 }); +const result = deserialize(payload); console.log(result); ``` -### Rust +### C\# -```rust -use fory::{from_buffer, to_buffer, Fory}; +C# 的跨语言代码看起来与原生模式很接近,但运行时需要显式启用 xlang 和 compatible 模式。与此同时,仍然必须与 Java、Go、Swift、Rust 等对端使用相同的类型 ID 或 namespace/name 映射。 -#[derive(Fory, Debug, PartialEq)] -#[tag("example.foo")] -struct Animal { - name: String, - category: String, +```csharp +using Apache.Fory; + +[ForyObject] +public sealed class Person +{ + public string Name { get; set; } = string.Empty; + public int Age { get; set; } } -#[derive(Fory, Debug, PartialEq)] -#[tag("example.bar")] -struct Person { - name: String, - age: u32, - pets: Vec<Animal>, +Fory fory = Fory.Builder() + .Xlang(true) + .Compatible(true) + .Build(); + +fory.Register<Person>(1); + +Person person = new() { Name = "chaokunyang", Age = 28 }; +byte[] payload = fory.Serialize(person); +Person result = fory.Deserialize<Person>(payload); + +Console.WriteLine($"{result.Name} {result.Age}"); +``` + +### Swift + +Swift 的跨语言序列化仍然使用与原生模式相同的 `@ForyObject` 模型风格,只是在创建运行时时要传入 `xlang: true`。要实现互操作,稳定的注册 ID 仍然是最核心的要求。 + +```swift +import Fory + +@ForyObject +struct Person: Equatable { + var name: String = "" + var age: Int32 = 0 } -fn main() { - let person = Person { - name: "hello".to_string(), - age: 12, - pets: vec![ - Animal { - name: "world1".to_string(), - category: "cat".to_string(), - }, - Animal { - name: "world2".to_string(), - category: "dog".to_string(), - }, - ], - }; - let bin = to_buffer(&person); - let obj: Person = from_buffer(&bin).expect("should success"); - assert_eq!(obj, person); +let fory = Fory(xlang: true, trackRef: false, compatible: true) +fory.register(Person.self, id: 1) + +let person = Person(name: "chaokunyang", age: 28) +let data = try fory.serialize(person) +let result: Person = try fory.deserialize(data) + +print("\(result.name) \(result.age)") +``` + +### 要点 + +- 在每个运行时中都显式启用 xlang 模式,例如 `Language.XLANG`、`WithXlang(true)`、`Xlang(true)`、`Fory(xlang: true, ...)` 等。 +- 在所有语言中使用**一致的 ID 或名称**注册类型。 +- 基于 ID 的注册更紧凑、速度更快,但需要集中协调以避免冲突。 +- 基于名称的注册更容易在团队间管理,但载荷会稍大一些。 +- 只使用具备跨语言映射的类型,详见 [Type Mapping](../specification/xlang_type_mapping.md)。 + +关于跨语言场景中的循环引用、共享引用和多态示例,请参见: + +- [跨语言序列化指南](../guide/xlang/index.md) +- [Go 指南 - 跨语言](../guide/go/cross-language.md) +- [C# 指南 - 跨语言](../guide/csharp/cross-language.md) +- [Swift 指南 - 跨语言](../guide/swift/cross-language.md) + +## Row Format 编码 + +Row format 提供零拷贝随机访问能力,非常适合分析型负载和数据处理流水线。 + +### Java + +```java +import org.apache.fory.format.*; +import java.util.*; +import java.util.stream.*; + +public class Bar { + String f1; + List<Long> f2; } + +public class Foo { + int f1; + List<Integer> f2; + Map<String, Integer> f3; + List<Bar> f4; +} + +RowEncoder<Foo> encoder = Encoders.bean(Foo.class); +Foo foo = new Foo(); +foo.f1 = 10; +foo.f2 = IntStream.range(0, 1000000).boxed().collect(Collectors.toList()); +foo.f3 = IntStream.range(0, 1000000).boxed().collect(Collectors.toMap(i -> "k"+i, i -> i)); + +List<Bar> bars = new ArrayList<>(1000000); +for (int i = 0; i < 1000000; i++) { + Bar bar = new Bar(); + bar.f1 = "s" + i; + bar.f2 = LongStream.range(0, 10).boxed().collect(Collectors.toList()); + bars.add(bar); +} +foo.f4 = bars; + +// 序列化为 row format(可被 Python 以零拷贝方式读取) +BinaryRow binaryRow = encoder.toRow(foo); + +// 反序列化整个对象 +Foo newFoo = encoder.fromRow(binaryRow); + +// 不做完整反序列化,直接零拷贝访问嵌套字段 +BinaryArray binaryArray2 = binaryRow.getArray(1); // 访问 f2 字段 +BinaryArray binaryArray4 = binaryRow.getArray(3); // 访问 f4 字段 +BinaryRow barStruct = binaryArray4.getStruct(10); // 访问第 11 个 Bar 元素 +long value = barStruct.getArray(1).getInt64(5); // 访问嵌套值 + +// 部分反序列化 +RowEncoder<Bar> barEncoder = Encoders.bean(Bar.class); +Bar newBar = barEncoder.fromRow(barStruct); +Bar newBar2 = barEncoder.fromRow(binaryArray4.getStruct(20)); +``` + +### Python + +```python +from dataclasses import dataclass +from typing import List, Dict +import pyarrow as pa +import pyfory + +@dataclass +class Bar: + f1: str + f2: List[pa.int64] + +@dataclass +class Foo: + f1: pa.int32 + f2: List[pa.int32] + f3: Dict[str, pa.int32] + f4: List[Bar] + +encoder = pyfory.encoder(Foo) +foo = Foo( + f1=10, + f2=list(range(1000_000)), + f3={f"k{i}": i for i in range(1000_000)}, + f4=[Bar(f1=f"s{i}", f2=list(range(10))) for i in range(1000_000)] +) + +# 序列化为 row format +binary: bytes = encoder.to_row(foo).to_bytes() + +# 无需完整反序列化即可零拷贝随机访问 +foo_row = pyfory.RowData(encoder.schema, binary) +print(foo_row.f2[100000]) # 直接访问元素 +print(foo_row.f4[100000].f1) # 访问嵌套字段 +print(foo_row.f4[200000].f2[5]) # 访问更深层的嵌套字段 ``` + +更多 row format 细节请参见 [Java Row Format 指南](../guide/java/row-format.md) 或 [Python Row Format 指南](../guide/python/row-format.md)。 diff --git a/versioned_docs/version-0.16/community/how_to_release.md b/versioned_docs/version-0.16/community/how_to_release.md index a4abe54c23..a993cf809a 100644 --- a/versioned_docs/version-0.16/community/how_to_release.md +++ b/versioned_docs/version-0.16/community/how_to_release.md @@ -208,10 +208,17 @@ If the discussion goes positive, you will need to prepare the release artifiacts ### Github branch and tag -- Create a new branch named `releases-0.12.0` +- Create a new branch named `releases-0.16.0` - Bump version to `$version` by executing command `python ci/release.py bump_version -l all -version $version` - Make a git commit and push the branch to `[email protected]:apache/fory.git` -- Create a new tag by `git tag v0.12.0-rc1`, then push it to `[email protected]:apache/fory.git` +- Create a new release tag by `git tag v0.16.0-rc1`, then push it to `[email protected]:apache/fory.git` +- If the Go module under `go/fory` is part of this release, create and push the Go submodule tag as well. For example, for the final `0.16.0` release: + +```bash +git remote add apache [email protected]:apache/fory.git +git tag go/fory/v0.16.0 +git push apache go/fory/v0.16.0 +``` ### Build and upload artifacts to SVN dist/dev repo @@ -435,6 +442,9 @@ https://repository.apache.org/content/repositories/orgapachefory-1003 Git tag for the release: https://github.com/apache/fory/releases/tag/v0.12.0-rc1 +If this release also publishes the Go module, include the Go submodule tag too: +https://github.com/apache/fory/releases/tag/go/fory/v0.16.0 + Git commit for the release: https://github.com/apache/fory/commit/fae06330edd049bb960536e978a45b97bca66faf diff --git a/versioned_docs/version-0.16/introduction/benchmark.md b/versioned_docs/version-0.16/introduction/benchmark.md index 195418881d..2acb2c2d00 100644 --- a/versioned_docs/version-0.16/introduction/benchmark.md +++ b/versioned_docs/version-0.16/introduction/benchmark.md @@ -9,7 +9,14 @@ sidebar_position: 3 ## Java Benchmark -The following benchmarks compare Fory against popular Java serialization frameworks. Charts labeled **"compatible"** show schema evolution mode with forward/backward compatibility enabled, while others show schema consistent mode where class schemas must match. +The Java benchmark section compares Fory against popular Java serialization frameworks using the current benchmark suite from `docs/benchmarks/java`. + +The plots are grouped by: + +- **Heap**: serialization to on-heap `byte[]` buffers +- **Off-heap**: serialization to direct/off-heap buffers +- **Compatible**: schema evolution mode with forward/backward compatibility enabled +- **Consistent**: schema-consistent mode where peer schemas must match exactly **Test Classes**: @@ -17,23 +24,69 @@ The following benchmarks compare Fory against popular Java serialization framewo - `MediaContent`: Class from [jvm-serializers](https://github.com/eishay/jvm-serializers/blob/master/tpc/src/data/media/MediaContent.java) - `Sample`: Class from [Kryo benchmark](https://github.com/EsotericSoftware/kryo/blob/master/benchmarks/src/main/java/com/esotericsoftware/kryo/benchmarks/data/Sample.java) -### Java Serialization +### Heap Serialization + +Compatible mode: + +<img width="90%" alt="" src="/img/benchmarks/serialization/bench_serialize_compatible_MEDIA_CONTENT_to_array_tps.png" /> + +Consistent mode: + +<img width="90%" alt="" src="/img/benchmarks/serialization/bench_serialize_MEDIA_CONTENT_to_array_tps.png" /> + +### Off-Heap Serialization + +Compatible mode: + +<img width="90%" alt="" src="/img/benchmarks/serialization/bench_serialize_compatible_STRUCT_to_directBuffer_tps.png" /> + +Consistent mode: + +<img width="90%" alt="" src="/img/benchmarks/serialization/bench_serialize_STRUCT_to_directBuffer_tps.png" /> + +### Heap Deserialization + +Compatible mode: + +<img width="90%" alt="" src="/img/benchmarks/deserialization/bench_deserialize_compatible_MEDIA_CONTENT_from_array_tps.png" /> + +Consistent mode: + +<img width="90%" alt="" src="/img/benchmarks/deserialization/bench_deserialize_MEDIA_CONTENT_from_array_tps.png" /> + +### Off-Heap Deserialization + +Compatible mode: + +<img width="90%" alt="" src="/img/benchmarks/deserialization/bench_deserialize_compatible_STRUCT_from_directBuffer_tps.png" /> + +Consistent mode: + +<img width="90%" alt="" src="/img/benchmarks/deserialization/bench_deserialize_STRUCT_from_directBuffer_tps.png" /> + +### Repository Throughput Summary + +Serialization throughput: + +<img width="90%" alt="" src="/img/benchmarks/serialization/bench_serialize_SAMPLE_to_array_tps.png" /> + +Deserialization throughput: + +<img width="90%" alt="" src="/img/benchmarks/deserialization/bench_deserialize_SAMPLE_from_array_tps.png" /> + +### Zero-Copy Benchmark + +Serialize: -<img width="48%" alt="" src="/img/benchmarks/serialization/bench_serialize_compatible_STRUCT_to_directBuffer_tps.png" /> -<img width="48%" alt="" src="/img/benchmarks/serialization/bench_serialize_compatible_MEDIA_CONTENT_to_array_tps.png" /> -<img width="48%" alt="" src="/img/benchmarks/serialization/bench_serialize_MEDIA_CONTENT_to_array_tps.png" /> -<img width="48%" alt="" src="/img/benchmarks/serialization/bench_serialize_SAMPLE_to_array_tps.png" /> +<img width="90%" alt="" src="/img/benchmarks/zerocopy/zero_copy_bench_serialize_BUFFER_to_array_tps.png" /> -### Java Deserialization +Deserialize: -<img width="48%" alt="" src="/img/benchmarks/deserialization/bench_deserialize_compatible_STRUCT_from_directBuffer_tps.png" /> -<img width="48%" alt="" src="/img/benchmarks/deserialization/bench_deserialize_compatible_MEDIA_CONTENT_from_array_tps.png" /> -<img width="48%" alt="" src="/img/benchmarks/deserialization/bench_deserialize_MEDIA_CONTENT_from_array_tps.png" /> -<img width="48%" alt="" src="/img/benchmarks/deserialization/bench_deserialize_SAMPLE_from_array_tps.png" /> +<img width="90%" alt="" src="/img/benchmarks/zerocopy/zero_copy_bench_deserialize_BUFFER_from_array_tps.png" /> **Important**: Fory's runtime code generation requires proper warm-up for performance measurement: -For additional benchmarks covering type forward/backward compatibility, off-heap support, and zero-copy serialization, see [Java Benchmarks](https://github.com/apache/fory/tree/main/docs/benchmarks). +For additional benchmark notes, raw data, and the complete Java benchmark README, see [Java Benchmarks](https://github.com/apache/fory/tree/main/docs/benchmarks/java). ## Rust Benchmark diff --git a/versioned_docs/version-0.16/start/install.md b/versioned_docs/version-0.16/start/install.md index 9f73a9c5d2..3c93af80db 100644 --- a/versioned_docs/version-0.16/start/install.md +++ b/versioned_docs/version-0.16/start/install.md @@ -4,13 +4,13 @@ title: Install sidebar_position: 0 --- -The official Apache Fory™ releases are provided as source artifacts. +Apache Fory™ releases are available both as source artifacts and language-specific packages. -For source download, please see Apache Fory™ [download](https://fory.apache.org/download) page. +For source downloads, see the Apache Fory™ [download](https://fory.apache.org/download) page. ## Java -To add a dependency on Apache Fory™ using Maven, use the following: +Use Maven to add Apache Fory™: ```xml <dependency> @@ -23,7 +23,7 @@ To add a dependency on Apache Fory™ using Maven, use the following: <dependency> <groupId>org.apache.fory</groupId> <artifactId>fory-format</artifactId> - <version>0.13.1</version> + <version>0.16.0</version> </dependency> --> <!-- SIMD acceleration for array compression (Java 16+) --> @@ -31,14 +31,14 @@ To add a dependency on Apache Fory™ using Maven, use the following: <dependency> <groupId>org.apache.fory</groupId> <artifactId>fory-simd</artifactId> - <version>0.13.1</version> + <version>0.16.0</version> </dependency> --> ``` ## Scala -To add a dependency on Apache Fory™ scala for scala 2.13 with maven, use the following: +Scala 2.13 with Maven: ```xml <dependency> @@ -48,7 +48,7 @@ To add a dependency on Apache Fory™ scala for scala 2.13 with maven, use the f </dependency> ``` -To add a dependency on Apache Fory™ scala for scala 3 with maven, use the following: +Scala 3 with Maven: ```xml <dependency> @@ -58,13 +58,13 @@ To add a dependency on Apache Fory™ scala for scala 3 with maven, use the foll </dependency> ``` -To add a dependency on Apache Fory™ scala for scala 2.13 with sbt, use the following: +Scala 2.13 with sbt: ```sbt libraryDependencies += "org.apache.fory" % "fory-scala_2.13" % "0.16.0" ``` -To add a dependency on Apache Fory™ scala for scala 3 with sbt, use the following: +Scala 3 with sbt: ```sbt libraryDependencies += "org.apache.fory" % "fory-scala_3" % "0.16.0" @@ -72,7 +72,7 @@ libraryDependencies += "org.apache.fory" % "fory-scala_3" % "0.16.0" ## Kotlin -To add a dependency on Apache Fory™ kotlin with maven, use the following: +Add Apache Fory™ Kotlin with Maven: ```xml <dependency> @@ -89,14 +89,24 @@ python -m pip install --upgrade pip pip install pyfory==0.16.0 ``` +## Go + +Use the full Go module path `github.com/apache/fory/go/fory`: + +```bash +go get github.com/apache/fory/go/[email protected] +``` + +If your Go proxy has not picked up the new submodule tag yet, retry later or use `GOPROXY=direct` temporarily. + ## Rust ```toml [dependencies] -fory = "0.14" +fory = "0.16.0" ``` -or just execute command: +Or use `cargo add`: ```bash cargo add [email protected] @@ -104,4 +114,54 @@ cargo add [email protected] ## JavaScript -Apache Fory™ is NOT yet available on npm, please install from source code now. +The JavaScript packages are not published to npm yet. + +Install and build them from source for now: + +```bash +git clone https://github.com/apache/fory.git +cd fory/javascript +npm install +npm run build +``` + +After building from source, use `@apache-fory/core` and optionally `@apache-fory/hps` in your project or workspace setup. + +Optional native acceleration requires Node.js 20+: + +```bash +cd packages/hps +npm run build +``` + +## C\# + +Install the `Apache.Fory` NuGet package. It includes both the runtime and the source generator for `[ForyObject]` types. + +```bash +dotnet add package Apache.Fory --version 0.16.0 +``` + +```xml +<ItemGroup> + <PackageReference Include="Apache.Fory" Version="0.16.0" /> +</ItemGroup> +``` + +## Swift + +Add Apache Fory™ from the GitHub repository with Swift Package Manager: + +```swift +dependencies: [ + .package(url: "https://github.com/apache/fory.git", exact: "0.16.0") +], +targets: [ + .target( + name: "MyApp", + dependencies: [ + .product(name: "Fory", package: "fory") + ] + ) +] +``` diff --git a/versioned_docs/version-0.16/start/usage.md b/versioned_docs/version-0.16/start/usage.md index fe52197804..4dc169da0b 100644 --- a/versioned_docs/version-0.16/start/usage.md +++ b/versioned_docs/version-0.16/start/usage.md @@ -10,11 +10,13 @@ This section provides quick examples for getting started with Apache Fory™. **Always use native mode when working with a single language.** Native mode delivers optimal performance by avoiding the type metadata overhead required for cross-language compatibility. -Xlang mode introduces additional metadata encoding costs and restricts serialization to types that are common across all supported languages. Language-specific types will be rejected during serialization in xlang-mode. +Xlang mode introduces additional metadata encoding costs and restricts serialization to types that are common across all supported languages. Language-specific types will be rejected during serialization in xlang mode. ### Java Serialization -When you don't need cross-language support, use Java mode for optimal performance. +When you do not need cross-language support, use Java mode for optimal performance. + +This example creates a reusable Java-mode runtime, registers a user class, and then performs a basic serialize/deserialize round trip. In production code, keep the `Fory` instance alive and reuse it across requests instead of rebuilding it for every object. ```java import org.apache.fory.*; @@ -27,21 +29,21 @@ public class Example { } public static void main(String[] args) { - // Create Fory instance - should be reused across serializations + // Create a Fory instance once and reuse it. BaseFory fory = Fory.builder() .withLanguage(Language.JAVA) .requireClassRegistration(true) - // replace `build` with `buildThreadSafeFory` for Thread-Safe Usage + // Replace `build` with `buildThreadSafeFory` for thread-safe usage. .build(); - // Register your classes (required when class registration is enabled) fory.register(Person.class); - // Serialize + Person person = new Person(); person.name = "chaokunyang"; person.age = 28; + byte[] bytes = fory.serialize(person); Person result = (Person) fory.deserialize(bytes); - System.out.println(result.name + " " + result.age); // Output: chaokunyang 28 + System.out.println(result.name + " " + result.age); } } ``` @@ -50,7 +52,9 @@ For detailed Java usage including compatibility modes, compression, and advanced ### Python Serialization -Python native mode provides a high-performance drop-in replacement for pickle/cloudpickle with better speed and compatibility. +Python native mode provides a high-performance drop-in replacement for `pickle` and `cloudpickle`. + +The example below uses a dataclass with explicit integer typing so Fory can preserve the intended schema efficiently. As with other runtimes, create the `Fory` instance once, register your types once, and then reuse it for repeated serialization. ```python from dataclasses import dataclass @@ -61,21 +65,187 @@ class Person: name: str age: pyfory.int32 -# Create Fory instance - should be reused across serializations fory = pyfory.Fory() -# Register your classes (required when class registration is enabled) fory.register_type(Person) + person = Person(name="chaokunyang", age=28) data = fory.serialize(person) result = fory.deserialize(data) -print(result.name, result.age) # Output: chaokunyang 28 +print(result.name, result.age) ``` For detailed Python usage including type hints, compatibility modes, and advanced features, see [Python Guide](../guide/python/index.md). +### Go Serialization + +Go native mode is the default. Register your structs once, then reuse the same `Fory` instance. + +The Go runtime works naturally with exported struct fields and explicit type registration. This snippet shows the standard flow: create `Fory`, register a struct type, serialize a value, and deserialize into a destination struct. + +```go +package main + +import ( + "fmt" + + "github.com/apache/fory/go/fory" +) + +type Person struct { + Name string + Age int32 +} + +func main() { + f := fory.New() + if err := f.RegisterStruct(Person{}, 1); err != nil { + panic(err) + } + + person := &Person{Name: "chaokunyang", Age: 28} + data, err := f.Serialize(person) + if err != nil { + panic(err) + } + + var result Person + if err := f.Deserialize(data, &result); err != nil { + panic(err) + } + + fmt.Printf("%s %d\n", result.Name, result.Age) +} +``` + +For detailed Go usage including configuration, struct tags, and schema evolution, see [Go Guide](../guide/go/index.md). + +### C# Serialization + +C# native serialization uses the `Apache.Fory` runtime together with `[ForyObject]` model types. + +In C#, the usual pattern is to mark your model with `[ForyObject]`, build a runtime once, and register the type before use. The example demonstrates the strongly typed `Serialize` and `Deserialize<T>` APIs that fit normal .NET application code. + +```csharp +using Apache.Fory; + +[ForyObject] +public sealed class Person +{ + public string Name { get; set; } = string.Empty; + public int Age { get; set; } +} + +Fory fory = Fory.Builder().Build(); +fory.Register<Person>(1); + +Person person = new() { Name = "chaokunyang", Age = 28 }; +byte[] data = fory.Serialize(person); +Person result = fory.Deserialize<Person>(data); + +Console.WriteLine($"{result.Name} {result.Age}"); +``` + +For detailed C# usage including source generators, references, and schema evolution, see [C# Guide](../guide/csharp/index.md). + +### Swift Serialization + +Swift native serialization uses `@ForyObject` models and the `Fory` runtime directly. + +Swift uses macro-based model definitions, so the example starts by annotating the type with `@ForyObject`, then registers the type ID and performs a typed round trip. This is the recommended starting point for app-side Swift usage. + +```swift +import Fory + +@ForyObject +struct Person: Equatable { + var name: String = "" + var age: Int32 = 0 +} + +let fory = Fory() +fory.register(Person.self, id: 1) + +let person = Person(name: "chaokunyang", age: 28) +let data = try fory.serialize(person) +let result: Person = try fory.deserialize(data) + +print("\(result.name) \(result.age)") +``` + +For detailed Swift usage including polymorphism, schema evolution, and troubleshooting, see [Swift Guide](../guide/swift/index.md). + +### Rust Serialization + +Rust native mode uses `Fory::default()` and derive macros for compile-time type-safe serialization. The normal pattern is to derive `ForyObject`, register the type once, and then reuse the configured runtime for repeated serialization. + +```rust +use fory::{Error, Fory, ForyObject}; + +#[derive(ForyObject, Debug, PartialEq)] +struct Person { + name: String, + age: i32, +} + +fn main() -> Result<(), Error> { + let mut fory = Fory::default(); + fory.register::<Person>(1)?; + + let person = Person { + name: "chaokunyang".to_string(), + age: 28, + }; + + let bytes = fory.serialize(&person)?; + let result: Person = fory.deserialize(&bytes)?; + assert_eq!(person, result); + Ok(()) +} +``` + +For detailed Rust usage including references, polymorphism, and row format support, see [Rust Guide](../guide/rust/index.md). + +### C++ Serialization + +C++ native mode uses the `FORY_STRUCT` macro to describe serializable fields and a configured `Fory` runtime to encode and decode values. For single-language C++ usage, set `xlang(false)` explicitly so the runtime stays in native mode. + +```cpp +#include "fory/serialization/fory.h" + +using namespace fory::serialization; + +struct Person { + std::string name; + int32_t age; + + bool operator==(const Person &other) const { + return name == other.name && age == other.age; + } + + FORY_STRUCT(Person, name, age); +}; + +int main() { + auto fory = Fory::builder().xlang(false).build(); + fory.register_struct<Person>(1); + + Person person{"chaokunyang", 28}; + + auto bytes = fory.serialize(person); + auto result = fory.deserialize<Person>(bytes.value()); + assert(result.ok()); + assert(person == result.value()); + return 0; +} +``` + +For detailed C++ usage including `FORY_STRUCT`, thread safety, and schema evolution, see [C++ Guide](../guide/cpp/index.md). + ### Scala Serialization -Scala native mode provides optimized serialization for Scala-specific types including case classes, collections, and Option types. +Scala native mode provides optimized serialization for Scala-specific types including case classes, collections, and `Option`. + +For Scala projects, register the Scala serializers first so Fory understands Scala-specific data structures correctly. After that, you can register your case classes and use the same core API as the Java runtime. ```scala import org.apache.fory.Fory @@ -86,18 +256,16 @@ case class Person(name: String, age: Int) object Example { def main(args: Array[String]): Unit = { - // Create Fory instance - should be reused across serializations val fory = Fory.builder() .withLanguage(Language.JAVA) .requireClassRegistration(true) .build() - // Register Scala serializers for Scala-specific types ScalaSerializers.registerSerializers(fory) - // Register your case classes fory.register(classOf[Person]) + val bytes = fory.serialize(Person("chaokunyang", 28)) val result = fory.deserialize(bytes).asInstanceOf[Person] - println(s"${result.name} ${result.age}") // Output: chaokunyang 28 + println(s"${result.name} ${result.age}") } } ``` @@ -108,6 +276,8 @@ For detailed Scala usage including collection serialization and integration patt Kotlin native mode provides optimized serialization for Kotlin-specific types including data classes, nullable types, and Kotlin collections. +Kotlin follows the same builder flow as Java, with an extra registration step for Kotlin-specific serializers. The example uses a data class and shows the minimal setup needed for efficient native serialization. + ```kotlin import org.apache.fory.Fory import org.apache.fory.config.Language @@ -116,18 +286,16 @@ import org.apache.fory.serializer.kotlin.KotlinSerializers data class Person(val name: String, val age: Int) fun main() { - // Create Fory instance - should be reused across serializations val fory = Fory.builder() .withLanguage(Language.JAVA) .requireClassRegistration(true) .build() - // Register Kotlin serializers for Kotlin-specific types KotlinSerializers.registerSerializers(fory) - // Register your data classes fory.register(Person::class.java) + val bytes = fory.serialize(Person("chaokunyang", 28)) val result = fory.deserialize(bytes) as Person - println("${result.name} ${result.age}") // Output: chaokunyang 28 + println("${result.name} ${result.age}") } ``` @@ -135,12 +303,14 @@ For detailed Kotlin usage including null safety and default value support, see [ ## Cross-Language Serialization -**Only use xlang mode when you need cross-language data exchange.** Xlang mode adds type metadata overhead for cross-language compatibility and only supports types that can be mapped across all languages. For single-language use cases, always prefer native mode for better performance. +**Only use xlang mode when you need cross-language data exchange.** Xlang mode adds type metadata overhead for cross-language compatibility and only supports types that can be mapped across all languages. -The following examples demonstrate serializing a `Person` object across Java and Rust. For other languages (Python, Go, JavaScript, etc.), simply set the language mode to `XLANG` and follow the same pattern. +The examples below use the same `Person` schema across multiple runtimes. In every language, enable xlang mode and register the type with the same ID or the same fully qualified name. ### Java +Java xlang usage is the baseline pattern for JVM services. Enable `Language.XLANG`, register the type with a stable ID or name, and make sure every peer language uses the same mapping. + ```java import org.apache.fory.*; import org.apache.fory.config.*; @@ -149,27 +319,67 @@ public class XlangExample { public record Person(String name, int age) {} public static void main(String[] args) { - // Create Fory instance with XLANG mode Fory fory = Fory.builder() .withLanguage(Language.XLANG) .build(); - // Register with cross-language type id/name fory.register(Person.class, 1); // fory.register(Person.class, "example.Person"); + Person person = new Person("chaokunyang", 28); byte[] bytes = fory.serialize(person); - // bytes can be deserialized by Rust, Python, Go, or other languages Person result = (Person) fory.deserialize(bytes); - System.out.println(result.name + " " + result.age); // Output: chaokunyang 28 + System.out.println(result.name() + " " + result.age()); } } ``` +### Go + +Go xlang mode is enabled through `WithXlang(true)`. The important part is not the Go syntax itself, but keeping the registered type identity aligned with every other language that reads or writes the payload. + +```go +package main + +import ( + "fmt" + + "github.com/apache/fory/go/fory" +) + +type Person struct { + Name string + Age int32 +} + +func main() { + f := fory.New(fory.WithXlang(true)) + if err := f.RegisterStruct(Person{}, 1); err != nil { + panic(err) + } + + person := &Person{Name: "chaokunyang", Age: 28} + data, err := f.Serialize(person) + if err != nil { + panic(err) + } + + var result Person + if err := f.Deserialize(data, &result); err != nil { + panic(err) + } + + fmt.Printf("%s %d\n", result.Name, result.Age) +} +``` + ### Rust +Rust follows the same cross-language contract, but expresses it through derived traits and explicit registration on the `Fory` instance. Once the type ID matches the other runtimes, the payload can move across language boundaries safely. + ```rust use fory::{Fory, ForyObject}; +use std::error::Error; #[derive(ForyObject, Debug)] struct Person { @@ -177,74 +387,116 @@ struct Person { age: i32, } -fn main() -> Result<(), Error> { - let mut fory = Fory::default(); +fn main() -> Result<(), Box<dyn Error>> { + let mut fory = Fory::default().xlang(true); fory.register::<Person>(1)?; // fory.register_by_name::<Person>("example.Person")?; + let person = Person { name: "chaokunyang".to_string(), age: 28, }; let bytes = fory.serialize(&person); - // bytes can be deserialized by Java, Python, Go, or other languages let result: Person = fory.deserialize(&bytes)?; - println!("{} {}", result.name, result.age); // Output: chaokunyang 28 + println!("{} {}", result.name, result.age); + Ok(()) } ``` -### Golang +### JavaScript -```go -type Person struct { - name: string - age: i32 -} -fory := fory.NewFory(true) -fory.Register(Person{}, 1) -person := Person{"chaokunyang", 28} -bytes, err := fory.Marshal(person) -var p Person -err = fory.Unmarshal(bytes, &p) -``` +JavaScript cross-language support is schema-driven. Instead of registering a class, you describe the payload shape with `Type.object(...)`, then use the returned serializer pair to encode and decode values. -### JavaScript +These packages are not published to npm yet. Build them from the Apache Fory repository first, then use the following API shape. ```javascript -import Fory, { Type } from "@apache-fory/fory"; +import Fory, { Type } 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;` - **/ + * `@apache-fory/hps` uses V8 fast calls directly from JIT. + * Use Node.js 20+ when enabling it. + * If installation fails, replace it with `const hps = null;`. + */ import hps from "@apache-fory/hps"; -// Now we describe data structures using JSON, but in the future, we will use more ways. const description = Type.object("example.Person", { name: Type.string(), age: Type.int32(), }); + const fory = new Fory({ hps }); const { serialize, deserialize } = fory.registerSerializer(description); -const input = serialize({ name: "chaokunyang", age: 28 }); -const result = deserialize(input); + +const payload = serialize({ name: "chaokunyang", age: 28 }); +const result = deserialize(payload); console.log(result); ``` +### C\# + +C# cross-language code looks similar to native usage, but the runtime is explicitly configured for xlang and compatible mode. Use the same type ID or namespace/name mapping as your Java, Go, Swift, or Rust peers. + +```csharp +using Apache.Fory; + +[ForyObject] +public sealed class Person +{ + public string Name { get; set; } = string.Empty; + public int Age { get; set; } +} + +Fory fory = Fory.Builder() + .Xlang(true) + .Compatible(true) + .Build(); + +fory.Register<Person>(1); + +Person person = new() { Name = "chaokunyang", Age = 28 }; +byte[] payload = fory.Serialize(person); +Person result = fory.Deserialize<Person>(payload); + +Console.WriteLine($"{result.Name} {result.Age}"); +``` + +### Swift + +Swift cross-language serialization uses the same `@ForyObject` model style as native mode, but you create the runtime with `xlang: true`. Stable registration IDs are still the key requirement for interoperability. + +```swift +import Fory + +@ForyObject +struct Person: Equatable { + var name: String = "" + var age: Int32 = 0 +} + +let fory = Fory(xlang: true, trackRef: false, compatible: true) +fory.register(Person.self, id: 1) + +let person = Person(name: "chaokunyang", age: 28) +let data = try fory.serialize(person) +let result: Person = try fory.deserialize(data) + +print("\(result.name) \(result.age)") +``` + ### Key Points -- Use `Language.XLANG` mode in all languages -- Register types with **consistent IDs or names** across all languages: - - **By ID** (`fory.register(Person.class, 1)`): Faster serialization, more compact encoding, but requires coordination to avoid ID conflicts - - **By name** (`fory.register(Person.class, "example.Person")`): More flexible, less prone to conflicts, easier to manage across teams, but slightly larger encoding -- Type IDs/names must match across all languages for successful deserialization -- Only use types that have cross-language mappings (see [Type Mapping](../specification/xlang_type_mapping.md)) +- Enable xlang mode in every runtime (`Language.XLANG`, `WithXlang(true)`, `Xlang(true)`, `Fory(xlang: true, ...)`, and so on). +- Register types with **consistent IDs or names** across all languages. +- ID-based registration is more compact and faster, but it requires coordination to avoid conflicts. +- Name-based registration is easier to manage across teams, but it produces slightly larger payloads. +- Only use types that have cross-language mappings; see [Type Mapping](../specification/xlang_type_mapping.md). -For examples with **circular references**, **shared references**, and **polymorphism** across languages, see: +For examples with circular references, shared references, and polymorphism across languages, see: - [Cross-Language Serialization Guide](../guide/xlang/index.md) -- [Java Serialization Guide - Cross Language](../guide/java/cross-language.md) -- [Python Guide - Cross Language](../guide/python/cross-language.md) +- [Go Guide - Cross Language](../guide/go/cross-language.md) +- [C# Guide - Cross Language](../guide/csharp/cross-language.md) +- [Swift Guide - Cross Language](../guide/swift/cross-language.md) ## Row Format Encoding --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
