You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fory.apache.org by ch...@apache.org on 2026/08/03 17:23:12 UTC
(fory) branch main updated: docs: reorganize documentation by capability (#3916)
This is an automated email from the ASF dual-hosted git repository.
chaokunyang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fory.git
The following commit(s) were added to refs/heads/main by this push:
new b853d32af docs: reorganize documentation by capability (#3916)
b853d32af is described below
commit b853d32af0c0397e1b3785697f71773cf21d19e1
Author: Shawn Yang <sh...@gmail.com>
AuthorDate: Tue Aug 4 01:23:07 2026 +0800
docs: reorganize documentation by capability (#3916)
<!--
**Thanks for contributing to Apache Fory™.**
**If this is your first time opening a PR on fory, you can refer to
[CONTRIBUTING.md](https://github.com/apache/fory/blob/main/CONTRIBUTING.md).**
Contribution Checklist
- The **Apache Fory™** community has requirements on the naming of pr
titles. You can also find instructions in
[CONTRIBUTING.md](https://github.com/apache/fory/blob/main/CONTRIBUTING.md).
- Apache Fory™ has a strong focus on performance. If the PR you submit
will have an impact on performance, please benchmark it first and
provide the benchmark result here.
-->
## Why?
<!-- Describe the purpose of this PR. -->
## What does this PR do?
<!-- Describe the details of this PR. -->
## Related issues
<!--
Is there any related issue? If this PR closes them you say say
fix/closes:
- #xxxx0
- #xxxx1
- Fixes #xxxx2
-->
## AI Contribution Checklist
<!-- Full requirements and disclosure template:
https://github.com/apache/fory/blob/main/AI_POLICY.md#9-contributor-checklist-for-ai-assisted-prs
-->
- [ ] Substantial AI assistance was used in this PR: `yes` / `no`
- [ ] If `yes`, I included a completed [AI Contribution
Checklist](https://github.com/apache/fory/blob/main/AI_POLICY.md#9-contributor-checklist-for-ai-assisted-prs)
in this PR description and the required `AI Usage Disclosure`.
- [ ] If `yes`, my PR description includes the required `ai_review`
summary and screenshot evidence or equivalent persisted links of the
final clean AI review results from both fresh reviewers described in
`AI_POLICY.md`, the Fory-guided reviewer and the independent general
reviewer, on the current PR diff or current HEAD after the latest code
changes.
<!-- If substantial AI assistance = `yes`, paste the completed checklist
and disclosure block here, including the final ai_review summary and
screenshot evidence or equivalent persisted links from both fresh
reviewers described in AI_POLICY.md, the Fory-guided reviewer and the
independent general reviewer, on the current PR diff or current HEAD
after the latest code changes. -->
## Does this PR introduce any user-facing change?
<!--
If any user-facing interface changes, please [open an
issue](https://github.com/apache/fory/issues/new/choose) describing the
need to do so and update the document if necessary.
Delete section if not applicable.
-->
- [ ] Does this PR introduce any public API change?
- [ ] Does this PR introduce any binary protocol compatibility change?
## Benchmark
<!--
When the PR has an impact on performance (if you don't know whether the
PR will have an impact on performance, you can submit the PR first, and
if it will have impact on performance, the code reviewer will explain
it), be sure to attach a benchmark data here.
Delete section if not applicable.
-->
---
.agents/docs-and-formatting.md | 15 +-
.agents/languages/cpp.md | 2 +-
.agents/repo-reference.md | 10 +-
.../references/language-command-matrix.md | 2 +-
.agents/skills/fory-version-bump/SKILL.md | 3 +-
.github/sync.yml | 37 +-
AGENTS.md | 29 +-
CONTRIBUTING.md | 10 +-
README.md | 112 +-
SECURITY.md | 12 +-
benchmarks/cpp/README.md | 2 +-
benchmarks/cpp/run.sh | 4 +-
benchmarks/csharp/run.sh | 4 +-
benchmarks/dart/run.sh | 4 +-
benchmarks/go/README.md | 2 +-
benchmarks/go/run.sh | 4 +-
benchmarks/java/analyze.py | 22 +-
benchmarks/java/benchmark_report.py | 53 +-
benchmarks/java/run.sh | 4 +-
benchmarks/javascript/README.md | 6 +-
benchmarks/javascript/run.sh | 2 +-
benchmarks/python/README.md | 2 +-
benchmarks/python/run.sh | 4 +-
benchmarks/rust/README.md | 2 +-
benchmarks/rust/run.sh | 4 +-
benchmarks/swift/run.sh | 4 +-
ci/extract_cpp_doc_code.py | 37 +-
ci/release.py | 10 +-
ci/tasks/cpp.py | 4 +-
compiler/README.md | 4 +-
cpp/README.md | 6 +-
csharp/README.md | 8 +-
dart/README.md | 2 +-
dart/packages/fory/README.md | 4 +-
docs/README.md | 55 -
docs/{guide/rust => benchmarks}/_category_.json | 2 +-
docs/benchmarks/index.md | 133 ++
.../{guide/cpp => benchmarks/json}/_category_.json | 2 +-
docs/benchmarks/{java/json => json/java}/README.md | 0
.../{java/json => json/java}/string_throughput.png | Bin
.../json => json/java}/utf8_bytes_throughput.png | Bin
docs/benchmarks/methodology.md | 42 +
.../object-serialization}/_category_.json | 2 +-
.../object-serialization/native}/_category_.json | 2 +-
.../object-serialization/native/java-graalvm.md | 110 ++
.../native}/java/README.md | 32 +-
.../java/data/jmh-jdk-11-deserialization.csv | 0
.../native}/java/data/jmh-jdk-11-serialization.csv | 0
.../native}/java/data/jmh-jdk-11-zerocopy.csv | 0
.../java/java_heap_deserialize_compatible.png | Bin
.../java/java_heap_deserialize_consistent.png | Bin
.../java/java_heap_serialize_compatible.png | Bin
.../java/java_heap_serialize_consistent.png | Bin
.../java/java_offheap_deserialize_compatible.png | Bin
.../java/java_offheap_deserialize_consistent.png | Bin
.../java/java_offheap_serialize_compatible.png | Bin
.../java/java_offheap_serialize_consistent.png | Bin
.../java/java_repo_deserialization_throughput.png | Bin
.../java/java_repo_serialization_throughput.png | Bin
.../native}/java/java_zero_copy_deserialize.png | Bin
.../native}/java/java_zero_copy_serialize.png | Bin
.../object-serialization/xlang}/_category_.json | 2 +-
.../{ => object-serialization/xlang}/cpp/README.md | 0
.../xlang}/cpp/throughput.png | Bin
.../xlang}/csharp/README.md | 0
.../xlang}/csharp/throughput.png | Bin
.../xlang}/dart/README.md | 0
.../xlang}/dart/throughput.png | Bin
.../{ => object-serialization/xlang}/go/README.md | 0
.../xlang}/go/benchmark_results.txt | 0
.../xlang}/go/serialized_sizes.txt | 0
.../xlang}/go/throughput.png | Bin
.../object-serialization/xlang/java/README.md | 52 +
.../xlang}/java/throughput.png | Bin
.../xlang}/javascript/README.md | 0
.../xlang}/javascript/throughput.png | Bin
.../xlang}/python/README.md | 0
.../xlang}/python/throughput.png | Bin
.../xlang}/rust/README.md | 0
.../xlang}/rust/throughput.png | Bin
.../xlang}/swift/README.md | 0
.../xlang}/swift/throughput.png | Bin
docs/compiler/_category_.json | 4 +-
docs/compiler/build-integration.md | 230 +++
docs/compiler/{compiler-guide.md => cli.md} | 235 +--
docs/compiler/flatbuffers-idl.md | 4 +-
docs/compiler/generated-code.md | 1962 --------------------
.../generated-code}/_category_.json | 2 +-
docs/compiler/generated-code/cpp.md | 158 ++
docs/compiler/generated-code/csharp.md | 109 ++
docs/compiler/generated-code/dart.md | 194 ++
docs/compiler/generated-code/go.md | 148 ++
docs/compiler/generated-code/index.md | 176 ++
docs/compiler/generated-code/java.md | 150 ++
docs/compiler/generated-code/javascript.md | 94 +
docs/compiler/generated-code/kotlin.md | 161 ++
docs/compiler/generated-code/python.md | 120 ++
docs/compiler/generated-code/rust.md | 154 ++
docs/compiler/generated-code/scala.md | 164 ++
docs/compiler/generated-code/swift.md | 108 ++
docs/compiler/getting-started.md | 46 +
docs/compiler/index.md | 19 +-
docs/compiler/protobuf-idl.md | 6 +-
docs/compiler/schema-idl.md | 6 +-
docs/{guide/dart => development}/_category_.json | 2 +-
docs/{DEVELOPMENT.md => development/building.md} | 2 +-
.../{cpp_debug.md => development/cpp-debugging.md} | 10 +-
docs/development/index.md | 32 +
docs/{guide/swift => grpc}/_category_.json | 2 +-
docs/grpc/architecture.md | 41 +
docs/{guide/cpp/grpc-support.md => grpc/cpp.md} | 6 +-
.../csharp/grpc-support.md => grpc/csharp.md} | 6 +-
docs/{guide/dart/grpc-support.md => grpc/dart.md} | 6 +-
docs/{guide/go/grpc-support.md => grpc/go.md} | 6 +-
docs/grpc/index.md | 73 +
docs/grpc/interoperability.md | 41 +
docs/{guide/java/grpc-support.md => grpc/java.md} | 10 +-
.../grpc-support.md => grpc/javascript.md} | 6 +-
.../kotlin/grpc-support.md => grpc/kotlin.md} | 4 +-
.../python/grpc-support.md => grpc/python.md} | 6 +-
docs/{guide/rust/grpc-support.md => grpc/rust.md} | 6 +-
.../{guide/scala/grpc-support.md => grpc/scala.md} | 6 +-
docs/grpc/troubleshooting.md | 37 +
docs/guide/_category_.json | 4 -
docs/guide/java/json-support.md | 1354 --------------
docs/guide/xlang/_category_.json | 6 -
docs/guide/xlang/getting-started.md | 465 -----
docs/guide/xlang/row_format.md | 239 ---
docs/guide/xlang/serialization.md | 596 ------
docs/index.md | 59 +
docs/{guide/java => introduction}/_category_.json | 2 +-
docs/introduction/choose-a-format.md | 61 +
docs/introduction/index.md | 125 ++
docs/introduction/product-model.md | 47 +
docs/introduction/support-matrix.md | 37 +
docs/{guide/javascript => json}/_category_.json | 2 +-
.../java/android-support.md => json/android.md} | 134 +-
docs/json/annotations.md | 768 ++++++++
docs/json/custom-codecs.md | 281 +++
docs/json/getting-started.md | 169 ++
.../java/graalvm-support.md => json/graalvm.md} | 306 +--
docs/json/index.md | 68 +
docs/json/object-mapping.md | 222 +++
docs/json/security.md | 115 ++
docs/json/troubleshooting.md | 41 +
.../cpp => object-serialization}/_category_.json | 2 +-
.../cpp/_category_.json | 2 +-
.../cpp/configuration.md | 4 +-
.../cpp/core-api.md} | 4 +-
.../cpp/custom-serializers.md | 8 +-
docs/{guide => object-serialization}/cpp/index.md | 25 +-
.../cpp/native.md} | 10 +-
.../cpp/polymorphism.md | 4 +-
.../cpp/schema-evolution.md | 4 +-
.../cpp/schema-metadata.md | 4 +-
.../cpp/supported-types.md | 8 +-
.../cpp/type-registration.md | 8 +-
.../cpp/xlang.md} | 4 +-
.../csharp/_category_.json | 2 +-
.../csharp/configuration.md | 4 +-
.../csharp/core-api.md} | 4 +-
.../csharp/custom-serializers.md | 2 +-
.../csharp/external-types.md | 6 +-
.../csharp/index.md | 34 +-
.../csharp/references.md | 4 +-
.../csharp/schema-evolution.md | 4 +-
.../csharp/schema-metadata.md | 4 +-
.../csharp/supported-types.md | 8 +-
.../csharp/thread-safety.md | 4 +-
.../csharp/troubleshooting.md | 6 +-
.../csharp/type-registration.md | 6 +-
.../csharp/xlang.md} | 4 +-
.../dart/_category_.json | 2 +-
.../dart/code-generation.md | 2 +-
.../dart/configuration.md | 6 +-
.../dart/core-api.md} | 4 +-
.../dart/custom-serializers.md | 6 +-
.../dart/external-types.md | 4 +-
docs/{guide => object-serialization}/dart/index.md | 10 +-
.../dart/inheritance.md | 2 +-
.../dart/schema-evolution.md | 6 +-
.../dart/schema-metadata.md | 6 +-
.../dart/supported-types.md | 6 +-
.../dart/troubleshooting.md | 8 +-
.../dart/type-registration.md | 8 +-
.../dart/web-platform-support.md | 2 +-
.../dart/xlang.md} | 4 +-
.../deserialization-security-model.md} | 73 +-
.../go/_category_.json | 2 +-
.../go/configuration.md | 2 +-
.../go/core-api.md} | 4 +-
.../go/custom-serializers.md | 6 +-
docs/{guide => object-serialization}/go/index.md | 36 +-
.../go/native.md} | 8 +-
.../go/references.md | 2 +-
.../go/schema-evolution.md | 6 +-
.../go/schema-metadata.md | 6 +-
.../go/supported-types.md | 8 +-
.../go/thread-safety.md | 6 +-
.../go/troubleshooting.md | 4 +-
.../go/type-registration.md | 8 +-
.../go/xlang.md} | 139 +-
docs/object-serialization/index.md | 53 +
.../java/_category_.json | 2 +-
.../java/advanced-features.md | 10 +-
.../java/android.md} | 122 +-
.../java/compression.md | 0
.../java/configuration.md | 0
.../java/core-api.md} | 12 +-
.../java/custom-serializers.md | 4 +-
docs/object-serialization/java/graalvm.md | 203 ++
docs/{guide => object-serialization}/java/index.md | 174 +-
.../object-serialization/java/jdk-serialization.md | 94 +
.../java/native.md} | 84 +-
.../java/object-copy.md | 6 +-
.../java/schema-evolution.md | 6 +-
.../java/schema-metadata.md | 6 +-
.../java/static-generated-serializers.md | 6 +-
.../java/troubleshooting.md | 4 +-
.../java/type-registration.md | 2 +-
.../java/virtual-threads.md | 4 +-
.../java/xlang.md} | 110 +-
.../javascript/_category_.json | 6 +
.../javascript/configuration.md | 4 +-
.../javascript/core-api.md} | 4 +-
.../javascript/index.md | 28 +-
.../javascript/references.md | 6 +-
.../javascript/schema-evolution.md | 8 +-
.../javascript/schema-metadata.md | 4 +-
.../javascript/supported-types.md | 8 +-
.../javascript/troubleshooting.md | 4 +-
.../javascript/type-registration.md | 10 +-
.../javascript/xlang.md} | 55 +-
.../kotlin/_category_.json | 2 +-
.../kotlin/configuration.md | 2 +-
.../kotlin/default-values.md | 2 +-
.../kotlin/index.md | 15 +-
.../kotlin/native.md} | 12 +-
.../kotlin/schema-metadata.md | 6 +-
.../kotlin/static-generated-serializers.md | 4 +-
docs/object-serialization/kotlin/xlang.md | 59 +
docs/object-serialization/native.md | 42 +
.../python/_category_.json | 2 +-
.../python/configuration.md | 4 +-
.../python/core-api.md} | 8 +-
.../python/custom-serializers.md | 6 +-
.../python/functions-classes-methods.md | 144 ++
.../python/index.md | 33 +-
.../python/native.md} | 168 +-
.../python/numpy-integration.md | 6 +-
.../python/out-of-band.md | 6 +-
.../python/schema-evolution.md | 6 +-
.../python/schema-metadata.md | 6 +-
.../python/serialization-hooks.md | 63 +
.../python/troubleshooting.md | 2 +-
.../python/type-registration.md | 2 +-
.../python/xlang.md} | 127 +-
docs/object-serialization/runtimes.md | 38 +
.../rust/_category_.json | 2 +-
.../rust/configuration.md | 4 +-
.../rust/core-api.md} | 16 +-
.../rust/custom-serializers.md | 4 +-
.../rust/external-types.md | 4 +-
docs/{guide => object-serialization}/rust/index.md | 28 +-
.../rust/native.md} | 12 +-
.../rust/polymorphism.md | 2 +-
.../rust/references.md | 4 +-
.../rust/schema-evolution.md | 6 +-
.../rust/schema-metadata.md | 8 +-
.../rust/troubleshooting.md | 2 +-
.../rust/type-registration.md | 4 +-
.../rust/xlang.md} | 89 +-
.../scala/_category_.json | 2 +-
.../scala/configuration.md | 2 +-
.../scala/default-values.md | 2 +-
.../{guide => object-serialization}/scala/index.md | 14 +-
.../scala/native.md} | 10 +-
.../scala/schema-metadata.md | 6 +-
.../scala/xlang.md} | 30 +-
docs/object-serialization/security.md | 210 +++
.../swift/_category_.json | 2 +-
.../swift/configuration.md | 2 +-
.../swift/core-api.md} | 4 +-
.../swift/custom-serializers.md | 4 +-
.../swift/external-types.md | 4 +-
.../{guide => object-serialization}/swift/index.md | 8 +-
.../swift/polymorphism.md | 2 +-
.../swift/references.md | 2 +-
.../swift/schema-evolution.md | 4 +-
.../swift/schema-metadata.md | 4 +-
.../swift/troubleshooting.md | 2 +-
.../swift/type-registration.md | 2 +-
.../swift/xlang.md} | 37 +-
.../xlang}/_category_.json | 2 +-
.../{guide => object-serialization}/xlang/index.md | 53 +-
.../xlang/nullability.md} | 10 +-
docs/object-serialization/xlang/polymorphism.md | 33 +
.../xlang/references.md} | 8 +-
.../object-serialization/xlang/schema-evolution.md | 33 +
.../xlang/troubleshooting.md | 4 +-
.../xlang/type-identity.md} | 25 +-
docs/object-serialization/xlang/type-system.md | 42 +
.../xlang/zero-copy.md | 6 +-
docs/{security => row-format}/_category_.json | 2 +-
docs/row-format/compact.md | 50 +
.../{guide/cpp/row-format.md => row-format/cpp.md} | 13 +-
docs/row-format/index.md | 45 +
.../java/row-format.md => row-format/java.md} | 101 +-
.../python/row-format.md => row-format/python.md} | 19 +-
.../rust/row-format.md => row-format/rust.md} | 15 +-
docs/row-format/standard.md | 57 +
docs/row-format/troubleshooting.md | 35 +
docs/security/index.md | 22 -
docs/security/threat-model.md | 95 -
docs/{guide/python => start}/_category_.json | 2 +-
docs/start/cpp.md | 41 +
docs/start/csharp.md | 38 +
docs/start/dart.md | 39 +
docs/start/go.md | 41 +
docs/start/index.md | 62 +
docs/start/java.md | 42 +
docs/start/javascript.md | 39 +
docs/start/kotlin.md | 41 +
docs/start/python.md | 40 +
docs/start/rust.md | 40 +
docs/start/scala.md | 40 +
docs/start/swift.md | 37 +
go/README.md | 4 +-
go/fory/README.md | 20 +-
go/fory/doc.go | 2 +-
java/README.md | 34 +-
java/fory-annotation-processor/README.md | 6 +-
java/fory-json/README.md | 1480 +--------------
javascript/README.md | 8 +-
javascript/packages/core/README.md | 8 +-
javascript/packages/core/package.json | 2 +-
javascript/packages/hps/README.md | 2 +-
javascript/packages/hps/package.json | 2 +-
kotlin/README.md | 14 +-
kotlin/fory-kotlin-ksp/README.md | 4 +-
python/README.md | 2 +-
python/pyfory/policy.py | 2 +-
rust/README.md | 8 +-
scala/README.md | 12 +-
swift/README.md | 18 +-
345 files changed, 8110 insertions(+), 8114 deletions(-)
diff --git a/.agents/docs-and-formatting.md b/.agents/docs-and-formatting.md
index 67806ccb3..1d1d6da40 100644
--- a/.agents/docs-and-formatting.md
+++ b/.agents/docs-and-formatting.md
@@ -6,8 +6,9 @@ Load this file when changing documentation, public APIs, protocol specs, benchma
- `README.md`
- `CONTRIBUTING.md`
-- `docs/DEVELOPMENT.md`
-- language guides under `docs/guide/`
+- `docs/development/building.md`
+- capability and runtime guides under `docs/object-serialization/`, `docs/row-format/`,
+ `docs/json/`, and `docs/grpc/`
- `docs/specification/**`
- `docs/compiler/**`
@@ -25,16 +26,18 @@ Load this file when changing documentation, public APIs, protocol specs, benchma
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.
-- `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.
+- The capability-first `docs/` tree is the canonical source for the website documentation,
+ including Introduction and Getting Started.
- When benchmark logic, scripts, config, or compared serializers change, rerun the relevant benchmarks and refresh the report and plots under `docs/benchmarks/**`.
- Never manually edit generated code for compiler or IDL outputs; regenerate from the source schema or IDL.
- Do not treat checked-in benchmark reports as canonical for current numbers. Run the active harness or a source-aligned size check, and match schema, config, and mode before comparing runtimes.
- Use portable repo-relative or web links in repository docs, not local filesystem paths. Package readmes rendered by external package managers should link to canonical published docs pages when that is the user-facing context.
-- Published `docs/guide/**` pages should prioritize end-user installation and usage flows unless the file is explicitly a contributor or development guide.
-- Keep `docs/guide/**` configuration text user-action focused. Do not put cache ownership,
+- Published capability guides should prioritize end-user installation and usage flows unless the
+ file is explicitly a contributor or development guide.
+- Keep user-facing configuration text action focused. Do not put cache ownership,
cold/hot path, publish ordering, exact-local schema bypass, or TypeDef/TypeMeta routing
invariants there; put implementation invariants in `docs/specification/xlang_implementation_guide.md`,
- security classifications in `docs/security/deserialization.md`, and agent-only workflow rules in
+ security classifications in `docs/object-serialization/deserialization-security-model.md`, and agent-only workflow rules in
`.agents/**`.
- Documentation examples should use normal explicit imports, avoid unused imports, and keep common Fory types unqualified where that is the idiom.
- Generated Markdown under `docs/benchmarks/**` should satisfy markdownlint blank-line rules at generation time: no repeated blank lines and no extra blank line after final content.
diff --git a/.agents/languages/cpp.md b/.agents/languages/cpp.md
index eeae2e6d3..aae2f0399 100644
--- a/.agents/languages/cpp.md
+++ b/.agents/languages/cpp.md
@@ -83,6 +83,6 @@ FORY_CPP_JAVA_CI=1 ENABLE_FORY_DEBUG_OUTPUT=1 mvn -T16 test -Dtest=org.apache.fo
## Debugging And Profiling
-- See `docs/cpp_debug.md` for C++ debugging guidance.
+- See `docs/development/cpp-debugging.md` for C++ debugging guidance.
- Generate `compile_commands.json` with `bazel run :refresh_compile_commands`.
- DTrace-based stack sampling is documented in `CONTRIBUTING.md`.
diff --git a/.agents/repo-reference.md b/.agents/repo-reference.md
index a9ed416da..14ee53320 100644
--- a/.agents/repo-reference.md
+++ b/.agents/repo-reference.md
@@ -19,8 +19,8 @@ Load this file when you need repo layout, protocol context, compiler guidance, o
- `CLAUDE.md`: compatibility shim that points back to `AGENTS.md`
- `README.md`: project overview and quick start
- `CONTRIBUTING.md`: contributor workflow and environment notes
-- `docs/DEVELOPMENT.md`: development setup and build notes
-- `docs/cpp_debug.md`: C++ debugging guidance
+- `docs/development/building.md`: development setup and build notes
+- `docs/development/cpp-debugging.md`: C++ debugging guidance
- `licenserc.toml`: license header configuration
## Protocol Overview
@@ -36,10 +36,10 @@ Apache Fory is a multi-language serialization framework with multiple wire forma
- Primary references:
- `docs/compiler/index.md`
- - `docs/compiler/compiler-guide.md`
+ - `docs/compiler/cli.md`
- `docs/compiler/schema-idl.md`
- - `docs/compiler/type-system.md`
- - `docs/compiler/generated-code.md`
+ - `docs/compiler/schema-idl.md#type-system`
+ - `docs/compiler/generated-code/index.md`
- `docs/compiler/protobuf-idl.md`
- `docs/compiler/flatbuffers-idl.md`
- Compiler location: `compiler/`
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 cf22665b8..f81728730 100644
--- a/.agents/skills/fory-performance-optimization/references/language-command-matrix.md
+++ b/.agents/skills/fory-performance-optimization/references/language-command-matrix.md
@@ -19,7 +19,7 @@ Canonical runtime-specific rules now live under `../../../languages/*.md` and `.
- Build: `bazel build //cpp/...`
- Tests: `bazel test $(bazel query //cpp/...)`
- Perf tests: `bazel test $(bazel query //cpp/fory/serialization/...)`
-- Profile: use repository-approved sampling tooling from `CONTRIBUTING.md` and `docs/cpp_debug.md`
+- Profile: use repository-approved sampling tooling from `CONTRIBUTING.md` and `docs/development/cpp-debugging.md`
## Java
diff --git a/.agents/skills/fory-version-bump/SKILL.md b/.agents/skills/fory-version-bump/SKILL.md
index f2ce914d6..547aec343 100644
--- a/.agents/skills/fory-version-bump/SKILL.md
+++ b/.agents/skills/fory-version-bump/SKILL.md
@@ -78,7 +78,8 @@ python ci/release.py bump_version -l all -version <next-dev-version>
- `README.md`
- Runtime README files such as `java/README.md`, `rust/README.md`, `scala/README.md`, `csharp/README.md`, `swift/README.md`, `dart/packages/fory/README.md`
-- `docs/guide/**`
+- `docs/start/**` and `docs/object-serialization/**`
+- `docs/row-format/**`, `docs/json/**`, and `docs/grpc/**`
- `docs/compiler/**` when compiler examples include Fory package versions
- `examples/**`
diff --git a/.github/sync.yml b/.github/sync.yml
index 16be95d8a..05c5290b3 100644
--- a/.github/sync.yml
+++ b/.github/sync.yml
@@ -16,17 +16,38 @@
# under the License.
apache/fory-site@main:
- - source: docs/DEVELOPMENT.md
- dest: docs/community/DEVELOPMENT.md
- - source: docs/guide/
- dest: docs/guide/
+ - source: docs/index.md
+ dest: docs/index.md
+ - source: docs/introduction/
+ dest: docs/introduction/
deleteOrphaned: true
- - source: docs/specification/
- dest: docs/specification/
+ - source: docs/start/
+ dest: docs/start/
+ deleteOrphaned: true
+ - source: docs/benchmarks/
+ dest: docs/benchmarks/
+ deleteOrphaned: true
+ - source: docs/object-serialization/
+ dest: docs/object-serialization/
+ deleteOrphaned: true
+ - source: docs/row-format/
+ dest: docs/row-format/
+ deleteOrphaned: true
+ - source: docs/json/
+ dest: docs/json/
deleteOrphaned: true
- source: docs/compiler/
dest: docs/compiler/
deleteOrphaned: true
- - source: docs/benchmarks/
- dest: docs/benchmarks/
+ - source: docs/grpc/
+ dest: docs/grpc/
+ deleteOrphaned: true
+ - source: docs/development/
+ dest: docs/development/
+ deleteOrphaned: true
+ - source: docs/images/
+ dest: docs/images/
+ deleteOrphaned: true
+ - source: docs/specification/
+ dest: docs/specification/
deleteOrphaned: true
diff --git a/AGENTS.md b/AGENTS.md
index 4193fd292..6f6dc1a67 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -9,10 +9,11 @@ This is the entry point for AI guidance in Apache Fory. Read this file first, th
- `.agents/docs-and-formatting.md`: documentation, specification, and markdown rules.
- `.agents/ci-and-pr.md`: code review workflow, CI triage, PR expectations, and commit conventions.
- `.agents/testing/integration-tests.md`: `integration_tests/` prerequisites, regeneration rules, and commands.
-- `docs/security/index.md`: security model index.
-- `docs/security/threat-model.md`: project-level trust boundaries, non-goals,
- and downstream responsibilities.
-- `docs/security/deserialization.md`: security boundaries for untrusted deserialization classification.
+- `docs/object-serialization/security.md`: user-facing security guidance for binary object
+ serialization.
+- `docs/json/security.md`: user-facing security guidance for Fory JSON.
+- `docs/object-serialization/deserialization-security-model.md`: implementation boundaries for
+ untrusted deserialization classification.
- `.agents/languages/java.md`
- `.agents/languages/csharp.md`
- `.agents/languages/cpp.md`
@@ -33,7 +34,7 @@ This is the entry point for AI guidance in Apache Fory. Read this file first, th
- Respect ownership. Keep logic, state, and helpers in their natural owner, and do not move serializer-local, context-local, runtime-type-local, or protocol-local problems into global utilities.
- Check the spec before implementation. For wire behavior and xlang mapping, use the specs as the source of truth and never copy one runtime's bug into another runtime just to make tests pass.
- Do not make assumptions about runtime behavior, ownership, registration, metadata construction, protocol semantics, or test coverage. Read the current code, owning docs/specs, and relevant tests before making a design judgment or implementation decision. If the evidence is incomplete, inspect more or state the uncertainty explicitly instead of filling gaps from memory or analogy with another runtime.
-- For untrusted deserialization, read `docs/security/deserialization.md` before changing allocation, stream filling, skip, reference, metadata, or policy validation behavior. Variable-length deserialization must not allocate or reserve backing/output capacity from attacker-declared lengths or counts before the byte owner has proven proportional readable bytes with `checkReadableBytes` or the runtime equivalent. Root graph memory reservation is accounting only and may happen before that b [...]
+- For untrusted deserialization, read `docs/object-serialization/deserialization-security-model.md` before changing allocation, stream filling, skip, reference, metadata, or policy validation behavior. Variable-length deserialization must not allocate or reserve backing/output capacity from attacker-declared lengths or counts before the byte owner has proven proportional readable bytes with `checkReadableBytes` or the runtime equivalent. Root graph memory reservation is accounting only a [...]
- Malformed input must surface as a controlled root-operation error and still run
root cleanup, but the exact exception type, error code, message, detection
layer, and detection point are not contracts unless a public API or
@@ -164,7 +165,8 @@ This is the entry point for AI guidance in Apache Fory. Read this file first, th
## Design Integrity Gates
-- Record all core design and decisions in the owning docs when they belong there, especially under `docs/guide/**` or `docs/specification/**`.
+- Record all core design and decisions in the owning docs when they belong there, especially under
+ `docs/object-serialization/**`, the relevant capability directory, or `docs/specification/**`.
- Do not allow implementation drift from the design document.
- Do not compromise design decisions to make implementation easier.
- Do not leave workaround code behind.
@@ -220,10 +222,12 @@ This is the entry point for AI guidance in Apache Fory. Read this file first, th
## Source of Truth
-- Primary references: `README.md`, `CONTRIBUTING.md`, `docs/DEVELOPMENT.md`, and language guides under `docs/guide/`.
+- Primary references: `README.md`, `CONTRIBUTING.md`, `docs/development/building.md`, and the
+ capability-first documentation under `docs/`.
- Protocol changes require reading and updating the relevant specs in `docs/specification/**` and aligning the relevant cross-language tests.
- If instructions conflict, follow the most specific module docs and call out the conflict.
-- `docs/DEVELOPMENT.md` plus updates under `docs/guide/` and `docs/benchmarks/` are synced to `apache/fory-site`; other website content belongs there.
+- The `docs/` tree is the canonical source for the website's Introduction, Getting Started,
+ Benchmarks, capability guides, development guides, and separate Specification surface.
- When benchmark logic, scripts, configuration, or compared serializers change, rerun the relevant benchmarks and refresh the artifacts under `docs/benchmarks/**`.
## Shared Engineering Expectations
@@ -327,8 +331,7 @@ This is the entry point for AI guidance in Apache Fory. Read this file first, th
## Security
-Security models start at `docs/security/index.md`. Read
-`docs/security/threat-model.md` for project-level trust boundaries, non-goals,
-and downstream responsibilities. For untrusted deserialization, read
-`docs/security/deserialization.md` before reporting or changing allocation,
-stream filling, skip, reference, metadata, or policy validation behavior.
+User-facing security guidance lives only under Object Serialization and Fory JSON. Read
+`docs/object-serialization/security.md` or `docs/json/security.md` for the selected product. Before
+reporting or changing allocation, stream filling, skip, reference, metadata, or policy validation
+behavior, read `docs/object-serialization/deserialization-security-model.md`.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 76753945e..1cd0c6355 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -42,7 +42,7 @@ Key points:
## Testing
-For environmental requirements, please check [DEVELOPMENT.md](./docs/DEVELOPMENT.md).
+For environmental requirements, please check [DEVELOPMENT.md](./docs/development/building.md).
### Python
@@ -182,7 +182,7 @@ cygdb build
### C++
-See the [Debugging C++](docs/cpp_debug.md) doc.
+See the [Debugging C++](docs/development/cpp-debugging.md) guide.
### Debug Crash
@@ -236,10 +236,12 @@ And due to the usage of `sun.misc.Unsafe` API, which is not visible in Java 11+,
Apache Fory™'s website consists of static pages hosted at https://github.com/apache/fory-site.
-Updates to [docs/DEVELOPMENT.md](./docs/DEVELOPMENT.md), docs under [guide](docs/guide), and docs under [benchmarks](docs/benchmarks) will be synced to the site repo automatically.
+The capability-first [docs](docs) tree is the canonical source for the website documentation,
+including Introduction, Getting Started, Benchmarks, user guides, Development, and the separate
+Specification surface.
If you want write a blog, or update other contents about the website, please submit PR to the site repo.
## Development
-For more information, please refer to [Development Guide](./docs/DEVELOPMENT.md).
+For more information, please refer to [Development Guide](./docs/development/building.md).
diff --git a/README.md b/README.md
index ffeea5983..dc5d5cd4a 100644
--- a/README.md
+++ b/README.md
@@ -55,7 +55,7 @@ Fory also provides specialized formats for other data-processing requirements:
- **Row Format**: Read fields, arrays, and nested values without rebuilding
complete objects, with zero-copy access and partial reads.
-- **Fory JSON**: A Java JSON serialization framework built for maximum
+- **[Fory JSON](java/fory-json/README.md)**: A Java JSON serialization framework built for maximum
throughput through runtime-generated codecs and optimized readers and
writers. Supports Java 8 and later on standard JDKs, GraalVM native images,
and Android, including Java 17 records.
@@ -67,72 +67,72 @@ payloads than common serialization frameworks on representative workloads. Java
has the broadest comparison set; the other charts show language-specific results
across supported languages.
-**Java** [Benchmarks](docs/benchmarks/java)
+**Java** [Benchmarks](docs/benchmarks/index.md)
In Java serialization benchmarks, Fory reaches up to **170x** the throughput of
JDK serialization on selected workloads.
<p align="center">
-<img src="docs/benchmarks/java/java_repo_serialization_throughput.png" width="95%" alt="Java serialization throughput">
+<img src="docs/benchmarks/object-serialization/native/java/java_repo_serialization_throughput.png" width="95%" alt="Java serialization throughput">
</p>
<p align="center">
-<img src="docs/benchmarks/java/java_repo_deserialization_throughput.png" width="95%" alt="Java deserialization throughput">
+<img src="docs/benchmarks/object-serialization/native/java/java_repo_deserialization_throughput.png" width="95%" alt="Java deserialization throughput">
</p>
<p align="center">
-<img src="docs/benchmarks/java/throughput.png" width="95%" alt="Java xlang throughput">
+<img src="docs/benchmarks/object-serialization/xlang/java/throughput.png" width="95%" alt="Java xlang throughput">
</p>
**Python** [Benchmarks](benchmarks/python)
<p align="center">
-<img src="docs/benchmarks/python/throughput.png" width="95%" alt="Python serialization throughput">
+<img src="docs/benchmarks/object-serialization/xlang/python/throughput.png" width="95%" alt="Python serialization throughput">
</p>
**Rust** [Benchmarks](benchmarks/rust)
<p align="center">
-<img src="docs/benchmarks/rust/throughput.png" width="95%" alt="Rust serialization throughput">
+<img src="docs/benchmarks/object-serialization/xlang/rust/throughput.png" width="95%" alt="Rust serialization throughput">
</p>
<details>
-<summary><strong>Benchmarks for <a href="benchmarks/cpp">C++</a>, <a href="benchmarks/go">Go</a>, <a href="docs/benchmarks/javascript">JavaScript/TypeScript</a>, <a href="docs/benchmarks/csharp">C#</a>, <a href="docs/benchmarks/swift">Swift</a>, and <a href="docs/benchmarks/dart">Dart</a></strong></summary>
+<summary><strong>Benchmarks for <a href="benchmarks/cpp">C++</a>, <a href="benchmarks/go">Go</a>, <a href="docs/benchmarks/object-serialization/xlang/javascript">JavaScript/TypeScript</a>, <a href="docs/benchmarks/object-serialization/xlang/csharp">C#</a>, <a href="docs/benchmarks/object-serialization/xlang/swift">Swift</a>, and <a href="docs/benchmarks/object-serialization/xlang/dart">Dart</a></strong></summary>
**C++** [Benchmarks](benchmarks/cpp)
<p align="center">
-<img src="docs/benchmarks/cpp/throughput.png" width="95%" alt="C++ serialization throughput">
+<img src="docs/benchmarks/object-serialization/xlang/cpp/throughput.png" width="95%" alt="C++ serialization throughput">
</p>
**Go** [Benchmarks](benchmarks/go)
<p align="center">
-<img src="docs/benchmarks/go/throughput.png" width="95%" alt="Go serialization throughput">
+<img src="docs/benchmarks/object-serialization/xlang/go/throughput.png" width="95%" alt="Go serialization throughput">
</p>
-**JavaScript/TypeScript** [Benchmarks](docs/benchmarks/javascript)
+**JavaScript/TypeScript** [Benchmarks](docs/benchmarks/object-serialization/xlang/javascript)
<p align="center">
-<img src="docs/benchmarks/javascript/throughput.png" width="95%" alt="JavaScript serialization throughput">
+<img src="docs/benchmarks/object-serialization/xlang/javascript/throughput.png" width="95%" alt="JavaScript serialization throughput">
</p>
-**C#** [Benchmarks](docs/benchmarks/csharp)
+**C#** [Benchmarks](docs/benchmarks/object-serialization/xlang/csharp)
<p align="center">
-<img src="docs/benchmarks/csharp/throughput.png" width="95%" alt="C# serialization throughput">
+<img src="docs/benchmarks/object-serialization/xlang/csharp/throughput.png" width="95%" alt="C# serialization throughput">
</p>
-**Swift** [Benchmarks](docs/benchmarks/swift)
+**Swift** [Benchmarks](docs/benchmarks/object-serialization/xlang/swift)
<p align="center">
-<img src="docs/benchmarks/swift/throughput.png" width="95%" alt="Swift serialization throughput">
+<img src="docs/benchmarks/object-serialization/xlang/swift/throughput.png" width="95%" alt="Swift serialization throughput">
</p>
-**Dart** [Benchmarks](docs/benchmarks/dart)
+**Dart** [Benchmarks](docs/benchmarks/object-serialization/xlang/dart)
<p align="center">
-<img src="docs/benchmarks/dart/throughput.png" width="95%" alt="Dart serialization throughput">
+<img src="docs/benchmarks/object-serialization/xlang/dart/throughput.png" width="95%" alt="Dart serialization throughput">
</p>
</details>
@@ -250,7 +250,7 @@ deps = ["@fory//cpp/fory/serialization:fory_serialization"]
When building C++ with MSVC, enable the conforming preprocessor option
`/Zc:preprocessor`; see the C++ installation guide for setup details.
-See the [C++ installation guide](https://fory.apache.org/docs/guide/cpp/#installation)
+See the [C++ installation guide](https://fory.apache.org/docs/object-serialization/cpp/#installation)
for complete CMake, Bazel, and source-build details.
**Go**
@@ -300,24 +300,24 @@ targets: [
]
```
-See the [Swift guide](https://fory.apache.org/docs/guide/swift/) for generated
+See the [Swift guide](https://fory.apache.org/docs/object-serialization/swift/) for generated
serializer setup.
**Development From Source**
-See [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md).
+See [docs/development/building.md](docs/development/building.md).
Snapshots for Java, Scala, and Kotlin are available from
`https://repository.apache.org/snapshots/` with the matching `-SNAPSHOT` version.
## Choose a Serialization Format
-| Format | Use it when | Start here |
-| ------------- | ------------------------------------------------------------- | -------------------------------------------------------- |
-| Xlang binary | Data crosses language boundaries | [Cross-language guide](docs/guide/xlang) |
-| Native binary | Producer and consumer are in the same language | Language guide |
-| Row format | You need random field access or analytics-style partial reads | [Row format spec](docs/specification/row_format_spec.md) |
-| Fory JSON | Java applications need high-performance standard JSON | [Fory JSON guide](docs/guide/java/json-support.md) |
+| Format | Use it when | Start here |
+| ------------- | ------------------------------------------------------------- | ------------------------------------------------------- |
+| Xlang binary | Data crosses language boundaries | [Xlang guide](docs/object-serialization/xlang/index.md) |
+| Native binary | Producer and consumer are in the same runtime family | [Native guide](docs/object-serialization/native.md) |
+| Row format | You need random field access or analytics-style partial reads | [Row Format](docs/row-format/index.md) |
+| Fory JSON | Java applications need high-performance standard JSON | [Fory JSON](java/fory-json/README.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
@@ -616,7 +616,7 @@ fun main() {
```
For shared/circular references, polymorphism, numeric IDs versus names, and
-type-mapping rules, see the [cross-language guide](docs/guide/xlang) and
+type-mapping rules, see the [cross-language guide](docs/object-serialization/xlang) and
[type mapping specification](docs/specification/xlang_type_mapping.md).
## Native Serialization
@@ -800,9 +800,10 @@ int secondScore = scores.getInt32(1);
```
For Java imports, nested structs, arrays/maps, Arrow integration, and partial
-deserialization, see the
-[Java row-format guide](https://fory.apache.org/docs/guide/java/row_format), the
-[Python row-format guide](docs/guide/python/row-format.md), and the
+deserialization, see the [Java row-format guide](docs/row-format/java.md),
+[Python row-format guide](docs/row-format/python.md),
+[C++ row-format guide](docs/row-format/cpp.md),
+[Rust row-format guide](docs/row-format/rust.md), and
[row-format specification](docs/specification/row_format_spec.md).
## Fory JSON
@@ -816,12 +817,12 @@ images, and Android, with Java records supported on Java 17 and later.
The benchmark compares fory-json with Jackson and Gson using String and UTF-8 byte APIs. The String
group excludes UTF-8 conversion. Gson's byte results include its required String/UTF-8 conversion.
-Higher throughput is better. See the [full benchmark report](docs/benchmarks/java/json/) for more
+Higher throughput is better. See the [full benchmark report](docs/benchmarks/json/java/) for more
details.
<p align="center">
-<img src="docs/benchmarks/java/json/string_throughput.png" width="49%" alt="Java JSON String throughput">
-<img src="docs/benchmarks/java/json/utf8_bytes_throughput.png" width="49%" alt="Java JSON UTF-8 bytes throughput">
+<img src="docs/benchmarks/json/java/string_throughput.png" width="49%" alt="Java JSON String throughput">
+<img src="docs/benchmarks/json/java/utf8_bytes_throughput.png" width="49%" alt="Java JSON UTF-8 bytes throughput">
</p>
| Representation | Operation | fory-json ops/sec | Jackson ops/sec | Gson ops/sec |
@@ -887,31 +888,32 @@ public final class JsonExample {
}
```
-See the [Fory JSON guide](docs/guide/java/json-support.md) for installation,
-configuration, supported types, custom codecs, and platform-specific setup.
+Start with the [Fory JSON module README](java/fory-json/README.md), then use the
+[Fory JSON documentation](docs/json/index.md) for the complete mapping, annotation, codec,
+security, and platform guides.
## Documentation
**User Guides**
-| Guide | Source | Website |
-| --------------------- | ---------------------------------------------------------------------------- | ------------------------------------------------------------------ |
-| Java | [docs/guide/java](docs/guide/java) | [View](https://fory.apache.org/docs/guide/java/) |
-| Python | [docs/guide/python](docs/guide/python) | [View](https://fory.apache.org/docs/guide/python/) |
-| Rust | [docs/guide/rust](docs/guide/rust) | [View](https://fory.apache.org/docs/guide/rust/) |
-| C++ | [docs/guide/cpp](docs/guide/cpp) | [View](https://fory.apache.org/docs/guide/cpp/) |
-| Go | [docs/guide/go](docs/guide/go) | [View](https://fory.apache.org/docs/guide/go/) |
-| JavaScript/TypeScript | [docs/guide/javascript](docs/guide/javascript) | [View](https://fory.apache.org/docs/guide/javascript/) |
-| C# | [docs/guide/csharp](docs/guide/csharp) | [View](https://fory.apache.org/docs/guide/csharp/) |
-| Swift | [docs/guide/swift](docs/guide/swift) | [View](https://fory.apache.org/docs/guide/swift/) |
-| Dart | [docs/guide/dart](docs/guide/dart) | [View](https://fory.apache.org/docs/guide/dart/) |
-| Scala | [docs/guide/scala](docs/guide/scala) | [View](https://fory.apache.org/docs/guide/scala/) |
-| Kotlin | [docs/guide/kotlin](docs/guide/kotlin) | [View](https://fory.apache.org/docs/guide/kotlin/) |
-| Cross-language xlang | [docs/guide/xlang](docs/guide/xlang) | [View](https://fory.apache.org/docs/guide/xlang/) |
-| Schema IDL/compiler | [docs/compiler](docs/compiler) | [View](https://fory.apache.org/docs/compiler/) |
-| GraalVM native image | [docs/guide/java/graalvm-support.md](docs/guide/java/graalvm-support.md) | [View](https://fory.apache.org/docs/guide/java/graalvm_support) |
-| Android | [docs/guide/kotlin/android-support.md](docs/guide/kotlin/android-support.md) | [View](https://fory.apache.org/docs/guide/kotlin/android_support/) |
-| Development | [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) | [View](docs/DEVELOPMENT.md) |
+| Guide | Source | Website |
+| --------------------- | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- |
+| Java | [docs/object-serialization/java](docs/object-serialization/java) | [View](https://fory.apache.org/docs/object-serialization/java/) |
+| Python | [docs/object-serialization/python](docs/object-serialization/python) | [View](https://fory.apache.org/docs/object-serialization/python/) |
+| Rust | [docs/object-serialization/rust](docs/object-serialization/rust) | [View](https://fory.apache.org/docs/object-serialization/rust/) |
+| C++ | [docs/object-serialization/cpp](docs/object-serialization/cpp) | [View](https://fory.apache.org/docs/object-serialization/cpp/) |
+| Go | [docs/object-serialization/go](docs/object-serialization/go) | [View](https://fory.apache.org/docs/object-serialization/go/) |
+| JavaScript/TypeScript | [docs/object-serialization/javascript](docs/object-serialization/javascript) | [View](https://fory.apache.org/docs/object-serialization/javascript/) |
+| C# | [docs/object-serialization/csharp](docs/object-serialization/csharp) | [View](https://fory.apache.org/docs/object-serialization/csharp/) |
+| Swift | [docs/object-serialization/swift](docs/object-serialization/swift) | [View](https://fory.apache.org/docs/object-serialization/swift/) |
+| Dart | [docs/object-serialization/dart](docs/object-serialization/dart) | [View](https://fory.apache.org/docs/object-serialization/dart/) |
+| Scala | [docs/object-serialization/scala](docs/object-serialization/scala) | [View](https://fory.apache.org/docs/object-serialization/scala/) |
+| Kotlin | [docs/object-serialization/kotlin](docs/object-serialization/kotlin) | [View](https://fory.apache.org/docs/object-serialization/kotlin/) |
+| Cross-language xlang | [docs/object-serialization/xlang](docs/object-serialization/xlang) | [View](https://fory.apache.org/docs/object-serialization/xlang/) |
+| Schema IDL/compiler | [docs/compiler](docs/compiler) | [View](https://fory.apache.org/docs/compiler/) |
+| GraalVM native image | [docs/object-serialization/java/graalvm.md](docs/object-serialization/java/graalvm.md) | [View](https://fory.apache.org/docs/object-serialization/java/graalvm) |
+| Android | [docs/object-serialization/java/android.md](docs/object-serialization/java/android.md) | [View](https://fory.apache.org/docs/object-serialization/java/android) |
+| Development | [docs/development/building.md](docs/development/building.md) | [View](docs/development/building.md) |
**Specifications**
@@ -932,7 +934,7 @@ configuration, supported types, custom codecs, and platform-specific setup.
## Contributing
Read [CONTRIBUTING.md](CONTRIBUTING.md) and
-[docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) before sending pull requests. Bug
+[docs/development/building.md](docs/development/building.md) before sending pull requests. Bug
reports, docs fixes, tests, benchmarks, and implementation improvements are welcome.
## License
diff --git a/SECURITY.md b/SECURITY.md
index 321a1673a..d83379eae 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -24,8 +24,10 @@ GitHub issues or pull requests for security reports.
## Security Models
-Apache Fory security models are documented under
-[docs/security](docs/security/index.md). Start with the
-[project threat model](docs/security/threat-model.md); for detailed untrusted
-deserialization classification rules, see the
-[deserialization security model](docs/security/deserialization.md).
+User-facing guidance is product-specific:
+
+- [Object Serialization Security](docs/object-serialization/security.md)
+- [Fory JSON Security](docs/json/security.md)
+
+For detailed implementation classification rules for untrusted deserialization, see the
+[Deserialization Security Model](docs/object-serialization/deserialization-security-model.md).
diff --git a/benchmarks/cpp/README.md b/benchmarks/cpp/README.md
index 0bca3b0fa..bb75d7b6c 100644
--- a/benchmarks/cpp/README.md
+++ b/benchmarks/cpp/README.md
@@ -25,7 +25,7 @@ Note: Protobuf is fetched automatically via CMake FetchContent, so no manual ins
### Throughput Results (ops/sec)
-
+
| Datatype | Operation | Fory TPS | Protobuf TPS | Faster |
| ------------- | ----------- | --------- | ------------ | ----------- |
diff --git a/benchmarks/cpp/run.sh b/benchmarks/cpp/run.sh
index 4237a6b3e..87f5ab065 100755
--- a/benchmarks/cpp/run.sh
+++ b/benchmarks/cpp/run.sh
@@ -20,7 +20,7 @@ set -e
export ENABLE_FORY_DEBUG_OUTPUT=0
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
-DOCS_DIR="$SCRIPT_DIR/../../docs/benchmarks/cpp"
+DOCS_DIR="$SCRIPT_DIR/../../docs/benchmarks/object-serialization/xlang/cpp"
# Colors for output
RED='\033[0;31m'
@@ -49,7 +49,7 @@ usage() {
echo " Filter benchmark by serializer"
echo " --duration <seconds> Minimum time to run each benchmark (e.g., 10, 30)"
echo " --debug Build with debug symbols and low optimization for profiling"
- echo " --no-copy-docs Skip copying report/plots into docs/benchmarks/cpp"
+ echo " --no-copy-docs Skip copying report/plots into docs/benchmarks/object-serialization/xlang/cpp"
echo " --help Show this help message"
echo ""
echo "Examples:"
diff --git a/benchmarks/csharp/run.sh b/benchmarks/csharp/run.sh
index bc953d8fe..d6b4641b0 100755
--- a/benchmarks/csharp/run.sh
+++ b/benchmarks/csharp/run.sh
@@ -21,7 +21,7 @@ export ENABLE_FORY_DEBUG_OUTPUT=0
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
-DOCS_DIR="$SCRIPT_DIR/../../docs/benchmarks/csharp"
+DOCS_DIR="$SCRIPT_DIR/../../docs/benchmarks/object-serialization/xlang/csharp"
RED='\033[0;31m'
GREEN='\033[0;32m'
@@ -52,7 +52,7 @@ Options:
--duration <seconds> Measure duration per benchmark (default: 3)
--warmup <seconds> Warmup duration per benchmark (default: 1)
--output-dir <dir> Base directory for benchmark outputs
- --no-copy-docs Skip copying report/plots into docs/benchmarks/csharp
+ --no-copy-docs Skip copying report/plots into docs/benchmarks/object-serialization/xlang/csharp
--external-equivalence Run ordinary/external Fory equivalence cases
--allocation-iterations <n> Allocation operations per equivalence case (default: 100000)
--baseline-worktree <dir> Dedicated apache/main worktree for equivalence runs
diff --git a/benchmarks/dart/run.sh b/benchmarks/dart/run.sh
index a1869a7f2..1a38c88f7 100644
--- a/benchmarks/dart/run.sh
+++ b/benchmarks/dart/run.sh
@@ -22,7 +22,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
OUTPUT_DIR="$SCRIPT_DIR/results"
-DOCS_DIR="$SCRIPT_DIR/../../docs/benchmarks/dart"
+DOCS_DIR="$SCRIPT_DIR/../../docs/benchmarks/object-serialization/xlang/dart"
BUILD_DIR="$SCRIPT_DIR/build"
RUNNER="$BUILD_DIR/benchmark_runner"
GENERATE_REPORT=true
@@ -45,7 +45,7 @@ usage() {
echo " --duration <sec> Seconds per sample (default: 1.5)."
echo " --warmup <sec> Warmup seconds per case (default: 1.0)."
echo " --no-report Skip report generation."
- echo " --no-copy-docs Skip copying report/plots into docs/benchmarks/dart."
+ echo " --no-copy-docs Skip copying report/plots into docs/benchmarks/object-serialization/xlang/dart."
echo " -h, --help Show this help."
}
diff --git a/benchmarks/go/README.md b/benchmarks/go/README.md
index 2fbfc13c5..7e7d53032 100644
--- a/benchmarks/go/README.md
+++ b/benchmarks/go/README.md
@@ -102,7 +102,7 @@ go test -bench=BenchmarkFory_NumericStruct -benchmem
Example results on Apple M1 Pro:
-
+
| Data Type | Operation | Fory (ops/s) | Protobuf (ops/s) | Msgpack (ops/s) | Fory vs PB | Fory vs MP |
| ----------------- | ----------- | ------------ | ---------------- | --------------- | ---------- | ---------- |
diff --git a/benchmarks/go/run.sh b/benchmarks/go/run.sh
index fea1f8fc3..b064666d4 100755
--- a/benchmarks/go/run.sh
+++ b/benchmarks/go/run.sh
@@ -23,7 +23,7 @@ cd "$SCRIPT_DIR"
# Output directory for results
OUTPUT_DIR="$SCRIPT_DIR/results"
-DOCS_DIR="$SCRIPT_DIR/../../docs/benchmarks/go"
+DOCS_DIR="$SCRIPT_DIR/../../docs/benchmarks/object-serialization/xlang/go"
mkdir -p "$OUTPUT_DIR"
# Default values
@@ -70,7 +70,7 @@ while [[ $# -gt 0 ]]; do
echo " --count <n> Number of benchmark runs (default: 5)"
echo " --benchtime <dur> Time for each benchmark (default: 1s)"
echo " --no-report Skip report generation"
- echo " --no-copy-docs Skip copying report/plots into docs/benchmarks/go"
+ echo " --no-copy-docs Skip copying report/plots into docs/benchmarks/object-serialization/xlang/go"
echo " -h, --help Show this help message"
echo ""
echo "Examples:"
diff --git a/benchmarks/java/analyze.py b/benchmarks/java/analyze.py
index 20cba4068..04d89a363 100644
--- a/benchmarks/java/analyze.py
+++ b/benchmarks/java/analyze.py
@@ -30,7 +30,7 @@ import subprocess
dir_path = os.path.dirname(os.path.realpath(__file__))
repo_root = Path(dir_path).parent.parent
-java_benchmark_dir = repo_root / "docs/benchmarks/java"
+java_benchmark_dir = repo_root / "docs/benchmarks/object-serialization/native/java"
java_benchmark_data_dir = java_benchmark_dir / "data"
java_benchmark_readme = java_benchmark_dir / "README.md"
@@ -156,22 +156,22 @@ java_plot_combined_groups = [
repo_plot_combined_groups = [
{
"alt": "Java Serialization Throughput",
- "combined": "docs/benchmarks/java/java_repo_serialization_throughput.png",
+ "combined": "docs/benchmarks/object-serialization/native/java/java_repo_serialization_throughput.png",
"sources": [
- "docs/benchmarks/java/compatible/bench_serialize_compatible_STRUCT_to_directBuffer_tps.png",
- "docs/benchmarks/java/compatible/bench_serialize_compatible_MEDIA_CONTENT_to_array_tps.png",
- "docs/benchmarks/java/serialization/bench_serialize_MEDIA_CONTENT_to_array_tps.png",
- "docs/benchmarks/java/serialization/bench_serialize_SAMPLE_to_array_tps.png",
+ "docs/benchmarks/object-serialization/native/java/compatible/bench_serialize_compatible_STRUCT_to_directBuffer_tps.png",
+ "docs/benchmarks/object-serialization/native/java/compatible/bench_serialize_compatible_MEDIA_CONTENT_to_array_tps.png",
+ "docs/benchmarks/object-serialization/native/java/serialization/bench_serialize_MEDIA_CONTENT_to_array_tps.png",
+ "docs/benchmarks/object-serialization/native/java/serialization/bench_serialize_SAMPLE_to_array_tps.png",
],
},
{
"alt": "Java Deserialization Throughput",
- "combined": "docs/benchmarks/java/java_repo_deserialization_throughput.png",
+ "combined": "docs/benchmarks/object-serialization/native/java/java_repo_deserialization_throughput.png",
"sources": [
- "docs/benchmarks/java/compatible/bench_deserialize_compatible_STRUCT_from_directBuffer_tps.png",
- "docs/benchmarks/java/compatible/bench_deserialize_compatible_MEDIA_CONTENT_from_array_tps.png",
- "docs/benchmarks/java/deserialization/bench_deserialize_MEDIA_CONTENT_from_array_tps.png",
- "docs/benchmarks/java/deserialization/bench_deserialize_SAMPLE_from_array_tps.png",
+ "docs/benchmarks/object-serialization/native/java/compatible/bench_deserialize_compatible_STRUCT_from_directBuffer_tps.png",
+ "docs/benchmarks/object-serialization/native/java/compatible/bench_deserialize_compatible_MEDIA_CONTENT_from_array_tps.png",
+ "docs/benchmarks/object-serialization/native/java/deserialization/bench_deserialize_MEDIA_CONTENT_from_array_tps.png",
+ "docs/benchmarks/object-serialization/native/java/deserialization/bench_deserialize_SAMPLE_from_array_tps.png",
],
},
]
diff --git a/benchmarks/java/benchmark_report.py b/benchmarks/java/benchmark_report.py
index ce6923d27..410cf0db2 100755
--- a/benchmarks/java/benchmark_report.py
+++ b/benchmarks/java/benchmark_report.py
@@ -66,8 +66,15 @@ DATATYPE_ORDER = [
"mediacontentlist",
]
OPERATIONS = ["serialize", "deserialize"]
-DOCS_HEADER = "# Java Benchmarks"
-JSON_REPORT_LINK = "See the [Java JSON benchmark report](json/) for fory-json, Jackson, and Gson results."
+DOCS_HEADER = "# Java Xlang Serialization Benchmarks"
+DOCS_JSON_REPORT_LINK = (
+ "See the [Java JSON benchmark report](../../../json/java/README.md) "
+ "for fory-json, Jackson, and Gson results."
+)
+LOCAL_JSON_REPORT_LINK = (
+ "See the [Java JSON benchmark report](../../../docs/benchmarks/json/java/README.md) "
+ "for fory-json, Jackson, and Gson results."
+)
BENCHMARK_PATTERN = re.compile(
r"(?:^|[.])BM_(?P<serializer>Fory|Protobuf|Flatbuffer)_"
r"(?P<datatype>NumericStruct|Sample|MediaContent|NumericStructList|SampleList|MediaContentList)_"
@@ -250,22 +257,9 @@ def winner_cell(values: dict) -> str:
return serializer_title(winner)
-def build_xlang_section(results: dict, image_name: str) -> str:
+def build_results_section(results: dict, image_name: str) -> str:
lines = [
- "## Xlang Benchmark\n\n",
- (
- "Run from `benchmarks/java/run.sh`. Raw JMH JSON stays under the ignored local "
- "`benchmarks/java/reports/` directory; `throughput.png` and this xlang "
- "section are synced into `docs/benchmarks/java/`.\n\n"
- ),
- "```bash\n",
- "cd benchmarks/java\n",
- "./run.sh\n",
- "```\n\n",
- (
- "JMH parameters: `-f 1 -wi 3 -i 3 -t 1 -w 3s -r 3s -bm thrpt -tu s`. "
- "Higher throughput is better.\n\n"
- ),
+ "## Results\n\n",
f"\n\n",
"| Data type | Operation | "
+ " | ".join(
@@ -299,7 +293,8 @@ def build_xlang_section(results: dict, image_name: str) -> str:
def write_local_readme(output_dir: Path, section: str) -> Path:
report_path = output_dir / "README.md"
report_path.write_text(
- "# Java Xlang Benchmark Report\n\n" + section, encoding="utf-8"
+ DOCS_HEADER + "\n\n" + LOCAL_JSON_REPORT_LINK + "\n\n" + section,
+ encoding="utf-8",
)
run_prettier(report_path)
return report_path
@@ -309,25 +304,25 @@ def update_docs_readme(docs_output_dir: Path, section: str) -> Path:
docs_readme = docs_output_dir / "README.md"
if docs_readme.exists():
content = docs_readme.read_text(encoding="utf-8").rstrip()
- if JSON_REPORT_LINK not in content:
+ if DOCS_JSON_REPORT_LINK not in content:
if content.startswith(DOCS_HEADER):
content = (
DOCS_HEADER
+ "\n\n"
- + JSON_REPORT_LINK
+ + DOCS_JSON_REPORT_LINK
+ "\n\n"
+ content[len(DOCS_HEADER) :].lstrip()
)
else:
- content = JSON_REPORT_LINK + "\n\n" + content
- marker = "\n## Xlang Benchmark\n"
- if marker in content:
- prefix = content.split(marker, 1)[0].rstrip()
- content = prefix + "\n\n" + section
- else:
- content = content + "\n\n" + section
+ content = DOCS_JSON_REPORT_LINK + "\n\n" + content
+ result_markers = ("\n## Results\n", "\n## Xlang Benchmark\n")
+ marker = next(
+ (candidate for candidate in result_markers if candidate in content), None
+ )
+ prefix = content.split(marker, 1)[0].rstrip() if marker else content
+ content = prefix + "\n\n" + section
else:
- content = DOCS_HEADER + "\n\n" + JSON_REPORT_LINK + "\n\n" + section
+ content = DOCS_HEADER + "\n\n" + DOCS_JSON_REPORT_LINK + "\n\n" + section
docs_readme.write_text(content.rstrip() + "\n", encoding="utf-8")
run_prettier(docs_readme)
return docs_readme
@@ -343,7 +338,7 @@ def main() -> None:
output_dir.mkdir(parents=True, exist_ok=True)
results = collect_results(load_json(args.json_file))
output_path = render_plot(results, str(output_dir))
- section = build_xlang_section(results, os.path.basename(output_path))
+ section = build_results_section(results, os.path.basename(output_path))
report_path = write_local_readme(output_dir, section)
print(f"Generated {report_path}")
print(f"Generated {output_path}")
diff --git a/benchmarks/java/run.sh b/benchmarks/java/run.sh
index 58e68250c..176ca4dac 100755
--- a/benchmarks/java/run.sh
+++ b/benchmarks/java/run.sh
@@ -24,7 +24,7 @@ DATA_FILTER=""
SERIALIZER_FILTER=""
DURATION_SECONDS="3"
REPORT_DIR="${SCRIPT_DIR}/reports"
-DOCS_OUTPUT_DIR="${SCRIPT_DIR}/../../docs/benchmarks/java"
+DOCS_OUTPUT_DIR="${SCRIPT_DIR}/../../docs/benchmarks/object-serialization/xlang/java"
SKIP_BUILD="false"
GENERATE_REPORT="true"
@@ -41,7 +41,7 @@ Options:
--reports-dir <dir> Local directory for benchmark_results.json and throughput.png
(default: reports)
--output-dir <dir> Docs directory for copied throughput.png and README xlang section
- --no-copy-docs Skip copying xlang report artifacts into docs/benchmarks/java
+ --no-copy-docs Skip copying xlang report artifacts into docs/benchmarks/object-serialization/xlang/java
--skip-build Reuse an existing target/benchmarks.jar
--no-report Do not generate throughput.png
--help Show this help
diff --git a/benchmarks/javascript/README.md b/benchmarks/javascript/README.md
index 3f65ec383..331f8ce5d 100644
--- a/benchmarks/javascript/README.md
+++ b/benchmarks/javascript/README.md
@@ -2,7 +2,7 @@
This benchmark compares serialization and deserialization throughput in JavaScript for Apache Fory, Protocol Buffers, and JSON.
-It mirrors the benchmark layout used by [`benchmarks/cpp`](benchmarks/cpp/README.md) and uses the shared schema in [`benchmarks/proto/bench.proto`](benchmarks/proto/bench.proto).
+It mirrors the benchmark layout used by [`benchmarks/cpp`](../cpp/README.md) and uses the shared schema in [`benchmarks/proto/bench.proto`](../proto/bench.proto).
## Coverage
@@ -57,8 +57,8 @@ widened to int64.
Running the pipeline writes:
- raw benchmark JSON to `benchmarks/javascript/benchmark_results.json`
-- throughput plot to `docs/benchmarks/javascript/throughput.png`
-- Markdown report to `docs/benchmarks/javascript/README.md`
+- throughput plot to `docs/benchmarks/object-serialization/xlang/javascript/throughput.png`
+- Markdown report to `docs/benchmarks/object-serialization/xlang/javascript/README.md`
## Notes
diff --git a/benchmarks/javascript/run.sh b/benchmarks/javascript/run.sh
index 87ba74a85..118250903 100755
--- a/benchmarks/javascript/run.sh
+++ b/benchmarks/javascript/run.sh
@@ -23,7 +23,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
JS_ROOT="${REPO_ROOT}/javascript"
OUTPUT_JSON="${SCRIPT_DIR}/benchmark_results.json"
-DOC_OUTPUT_DIR="${REPO_ROOT}/docs/benchmarks/javascript"
+DOC_OUTPUT_DIR="${REPO_ROOT}/docs/benchmarks/object-serialization/xlang/javascript"
RED='\033[0;31m'
GREEN='\033[0;32m'
diff --git a/benchmarks/python/README.md b/benchmarks/python/README.md
index a17150d25..6ffa37134 100644
--- a/benchmarks/python/README.md
+++ b/benchmarks/python/README.md
@@ -21,7 +21,7 @@ cd benchmarks/python
1. Generate Python protobuf bindings from `benchmarks/proto/bench.proto`
2. Run `benchmark.py`
3. Generate plots + markdown report via `benchmark_report.py`
-4. Copy report/plots to `docs/benchmarks/python`
+4. Copy report/plots to `docs/benchmarks/object-serialization/xlang/python`
### Common Options
diff --git a/benchmarks/python/run.sh b/benchmarks/python/run.sh
index cd1d93870..783c4bb31 100755
--- a/benchmarks/python/run.sh
+++ b/benchmarks/python/run.sh
@@ -26,7 +26,7 @@ PYTHON_BIN="${PYTHON_BIN:-python3}"
OUTPUT_DIR="$SCRIPT_DIR/results"
REPORT_DIR="$OUTPUT_DIR/report"
PROTO_DIR="$SCRIPT_DIR/proto"
-DOCS_DIR="$SCRIPT_DIR/../../docs/benchmarks/python"
+DOCS_DIR="$SCRIPT_DIR/../../docs/benchmarks/object-serialization/xlang/python"
DATA=""
SERIALIZER=""
@@ -51,7 +51,7 @@ Options:
--iterations <n> Measurement iterations (default: 15)
--repeat <n> Repeat count per iteration (default: 5)
--number <n> Inner loop call count (default: 1000)
- --no-copy-docs Skip copying report/plots into docs/benchmarks/python
+ --no-copy-docs Skip copying report/plots into docs/benchmarks/object-serialization/xlang/python
-h, --help Show this help message
Examples:
diff --git a/benchmarks/rust/README.md b/benchmarks/rust/README.md
index e8754b0ef..b042eef5d 100644
--- a/benchmarks/rust/README.md
+++ b/benchmarks/rust/README.md
@@ -37,7 +37,7 @@ Options:
Filter benchmark by serializer
--filter <regex> Custom criterion filter
--no-report Skip Python report generation
- --no-copy-docs Skip copying the report into docs/benchmarks/rust
+ --no-copy-docs Skip copying the report into docs/benchmarks/object-serialization/xlang/rust
```
Examples:
diff --git a/benchmarks/rust/run.sh b/benchmarks/rust/run.sh
index 4435f3993..c0a21fb46 100755
--- a/benchmarks/rust/run.sh
+++ b/benchmarks/rust/run.sh
@@ -23,7 +23,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
OUTPUT_DIR="$SCRIPT_DIR/results"
-DOCS_DIR="$SCRIPT_DIR/../../docs/benchmarks/rust"
+DOCS_DIR="$SCRIPT_DIR/../../docs/benchmarks/object-serialization/xlang/rust"
LOCAL_MANIFEST="$SCRIPT_DIR/local/Cargo.toml"
XLANG_MANIFEST="$SCRIPT_DIR/xlang/Cargo.toml"
LOG_FILE="$OUTPUT_DIR/cargo_bench.log"
@@ -45,7 +45,7 @@ usage() {
echo " --serializer <name> Filter by serializer: fory, protobuf, msgpack"
echo " --filter <regex> Custom criterion filter regex (overrides --data/--serializer)"
echo " --no-report Skip report generation"
- echo " --no-copy-docs Skip copying report/plots into docs/benchmarks/rust"
+ echo " --no-copy-docs Skip copying report/plots into docs/benchmarks/object-serialization/xlang/rust"
echo " -h, --help Show this help message"
echo ""
echo "Examples:"
diff --git a/benchmarks/swift/run.sh b/benchmarks/swift/run.sh
index 7eae4ca59..61afc759a 100755
--- a/benchmarks/swift/run.sh
+++ b/benchmarks/swift/run.sh
@@ -21,7 +21,7 @@ export ENABLE_FORY_DEBUG_OUTPUT=0
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
-DOCS_DIR="$SCRIPT_DIR/../../docs/benchmarks/swift"
+DOCS_DIR="$SCRIPT_DIR/../../docs/benchmarks/object-serialization/xlang/swift"
# Colors
RED='\033[0;31m'
@@ -52,7 +52,7 @@ usage() {
echo " --duration <seconds> Minimum time to run each benchmark (default: 3)"
echo " --external Also run isolated external-type benchmarks"
echo " --no-report Skip Python report generation"
- echo " --no-copy-docs Skip copying report/plots into docs/benchmarks/swift"
+ echo " --no-copy-docs Skip copying report/plots into docs/benchmarks/object-serialization/xlang/swift"
echo " --help Show this help message"
echo ""
echo "Examples:"
diff --git a/ci/extract_cpp_doc_code.py b/ci/extract_cpp_doc_code.py
index 08bb48d29..310af74b3 100644
--- a/ci/extract_cpp_doc_code.py
+++ b/ci/extract_cpp_doc_code.py
@@ -18,9 +18,9 @@
"""
Extract C++ code examples from markdown documentation and generate test files.
-This script scans markdown files in docs/guide/cpp/, extracts ```cpp code blocks,
-and generates compilable C++ test files that can be run to verify the documentation
-examples are correct.
+This script scans C++ object-serialization documentation plus explicitly selected
+C++ capability pages, extracts ```cpp code blocks, and generates compilable C++
+test files that can be run to verify the documentation examples are correct.
"""
import argparse
@@ -28,7 +28,7 @@ import logging
import re
import sys
from pathlib import Path
-from typing import List, Tuple
+from typing import List, Optional, Tuple
logging.basicConfig(
level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
@@ -181,14 +181,18 @@ int main() {{
"""
-def generate_test_file_name(doc_file: str, block_index: int) -> str:
+def generate_test_file_name(
+ doc_file: str, block_index: int, file_tag: Optional[str] = None
+) -> str:
# Generate a test file name from documentation file and block index.
- base_name = Path(doc_file).stem
+ base_name = file_tag or Path(doc_file).stem
return f"doc_test_{base_name}_{block_index}.cc"
-def process_markdown_file(md_path: Path, output_dir: Path) -> List[Path]:
+def process_markdown_file(
+ md_path: Path, output_dir: Path, file_tag: Optional[str] = None
+) -> List[Path]:
# logging.info(f"Processing {md_path}")
with open(md_path, "r", encoding="utf-8") as f:
@@ -205,7 +209,7 @@ def process_markdown_file(md_path: Path, output_dir: Path) -> List[Path]:
continue
test_content = wrap_code_as_test(code, md_path.name, i)
- test_file_name = generate_test_file_name(md_path.name, i)
+ test_file_name = generate_test_file_name(md_path.name, i, file_tag)
test_path = output_dir / test_file_name
with open(test_path, "w", encoding="utf-8") as f:
@@ -269,7 +273,7 @@ def main():
)
parser.add_argument(
"--docs-dir",
- default="docs/guide/cpp",
+ default="docs/object-serialization/cpp",
help="Directory containing markdown documentation files",
)
parser.add_argument(
@@ -277,6 +281,12 @@ def main():
default="cpp/doc_tests",
help="Output directory for generated test files",
)
+ parser.add_argument(
+ "--docs-file",
+ action="append",
+ default=[],
+ help="Additional Markdown file to scan; may be specified more than once",
+ )
parser.add_argument(
"--generate-build",
action="store_true",
@@ -311,6 +321,15 @@ def main():
test_files = process_markdown_file(md_file, output_dir)
all_test_files.extend(test_files)
+ for docs_file in args.docs_file:
+ md_file = project_root / docs_file
+ if not md_file.is_file():
+ logging.error(f"Documentation file not found: {md_file}")
+ sys.exit(1)
+ file_tag = f"{md_file.parent.name}-{md_file.stem}"
+ test_files = process_markdown_file(md_file, output_dir, file_tag)
+ all_test_files.extend(test_files)
+
logging.info(f"\nTotal: Generated {len(all_test_files)} test files")
if args.generate_build and all_test_files:
diff --git a/ci/release.py b/ci/release.py
index 4bf01202c..1bc7ef12c 100644
--- a/ci/release.py
+++ b/ci/release.py
@@ -70,8 +70,14 @@ RELEASE_DOC_ROOTS = (
"csharp/README.md",
"swift/README.md",
"dart/packages/fory/README.md",
+ "docs/introduction",
+ "docs/start",
+ "docs/object-serialization",
+ "docs/row-format",
+ "docs/json",
"docs/compiler",
- "docs/guide",
+ "docs/grpc",
+ "docs/development",
"examples",
)
RELEASE_DOC_EXTS = (".md", ".example")
@@ -756,7 +762,7 @@ def bump_csharp_version(new_version):
_update_csharp_readme_package_version,
)
_bump_version(
- "docs/guide/csharp",
+ "docs/object-serialization/csharp",
"index.md",
release_version or new_version,
_update_csharp_readme_package_version,
diff --git a/ci/tasks/cpp.py b/ci/tasks/cpp.py
index 30d5c2acf..bb151e577 100644
--- a/ci/tasks/cpp.py
+++ b/ci/tasks/cpp.py
@@ -31,7 +31,9 @@ def generate_doc_example_tests():
"python",
script_path,
"--docs-dir",
- "docs/guide/cpp",
+ "docs/object-serialization/cpp",
+ "--docs-file",
+ "docs/row-format/cpp.md",
"--output-dir",
"cpp/doc_tests",
"--generate-build",
diff --git a/compiler/README.md b/compiler/README.md
index d7eebb98e..84bdb1b32 100644
--- a/compiler/README.md
+++ b/compiler/README.md
@@ -18,8 +18,8 @@ For comprehensive documentation, see the [FDL Schema Guide](../docs/compiler/ind
- [FDL Syntax Reference](../docs/compiler/schema-idl.md) - Complete language syntax and grammar
- [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
+- [Compiler Guide](../docs/compiler/cli.md) - CLI options and build integration
+- [Generated Code](../docs/compiler/generated-code/index.md) - Output format for each target language
- [Protocol Buffers vs FDL](../docs/compiler/protobuf-idl.md) - Feature comparison and porting guide
## Installation
diff --git a/cpp/README.md b/cpp/README.md
index 0870fd0d2..44dcab23f 100644
--- a/cpp/README.md
+++ b/cpp/README.md
@@ -22,7 +22,7 @@ The C++ implementation provides high-performance serialization with compile-time
Fory C++ demonstrates competitive performance compared to protobuf c++ serialization framework.
<p align="center">
-<img src="../docs/benchmarks/cpp/throughput.png" width="95%">
+<img src="../docs/benchmarks/object-serialization/xlang/cpp/throughput.png" width="95%">
</p>
For more detailed benchmarks and methodology, see [C++ Benchmarks](../benchmarks/cpp).
@@ -413,10 +413,10 @@ bash ci/format.sh --cpp
## Documentation
-- **[User Guide](https://fory.apache.org/docs/guide/cpp)** - Comprehensive user documentation
+- **[User Guide](https://fory.apache.org/docs/object-serialization/cpp)** - Comprehensive user documentation
- **[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
+- **[Source](https://github.com/apache/fory/tree/main/docs/object-serialization/cpp)** - Documentation source
## Use Cases
diff --git a/csharp/README.md b/csharp/README.md
index 6297bfb16..1deff4912 100644
--- a/csharp/README.md
+++ b/csharp/README.md
@@ -127,7 +127,7 @@ public sealed class User : Entity
An inaccessible base member is serialized only when it has `[ForyField]`.
Unmodifiable third-party bases use an external `BaseOnly` declaration. See the
-[class inheritance guide](https://fory.apache.org/docs/guide/csharp/basic-serialization/#class-inheritance).
+[class inheritance guide](https://fory.apache.org/docs/object-serialization/csharp/core-api/#class-inheritance).
### 3. Shared and Circular References
@@ -236,7 +236,7 @@ ThirdParty.User decoded = fory.Deserialize<ThirdParty.User>(payload);
```
The target is the runtime and registration type. See the
-[external-types guide](https://fory.apache.org/docs/guide/csharp/external-types/).
+[external-types guide](https://fory.apache.org/docs/object-serialization/csharp/external-types/).
### 8. Custom Serializers
@@ -278,10 +278,10 @@ Fory fory = Fory.Builder()
fory.Register<Person>(100); // same ID on other language peers
```
-See [xlang guide](https://fory.apache.org/docs/guide/xlang/) for mapping details.
+See [xlang guide](https://fory.apache.org/docs/object-serialization/xlang/) for mapping details.
## Documentation
-- [C# guide index](https://fory.apache.org/docs/guide/csharp/)
+- [C# guide index](https://fory.apache.org/docs/object-serialization/csharp/)
- [Cross-language serialization spec](https://fory.apache.org/docs/specification/xlang_serialization_spec/)
- [Cross-language type mapping](https://fory.apache.org/docs/specification/xlang_type_mapping/)
diff --git a/dart/README.md b/dart/README.md
index 9cc8ed727..981d1db8a 100644
--- a/dart/README.md
+++ b/dart/README.md
@@ -97,7 +97,7 @@ final roundTrip = fory.deserialize<Person>(bytes);
Ordinary annotated classes serialize one flattened view of their concrete
superclass and applied-mixin storage. See
-[Struct Inheritance](../docs/guide/dart/inheritance.md) for details.
+[Struct Inheritance](../docs/object-serialization/dart/inheritance.md) for details.
## Development
diff --git a/dart/packages/fory/README.md b/dart/packages/fory/README.md
index 37a2109d3..df12f0d63 100644
--- a/dart/packages/fory/README.md
+++ b/dart/packages/fory/README.md
@@ -108,7 +108,7 @@ class TextMessage extends MessageBase {
The generated `TextMessage` schema contains both `sequence` and `text`.
Superclass and applied-mixin fields are flattened into one child serializer.
-See [Struct Inheritance](https://fory.apache.org/docs/guide/dart/inheritance)
+See [Struct Inheritance](https://fory.apache.org/docs/object-serialization/dart/inheritance)
for private fields, mixins, constructors, and
`ignoreInheritedPrivateFields`.
@@ -384,4 +384,4 @@ For the xlang wire format and type mapping details, see the
[Apache Fory specification](https://github.com/apache/fory/tree/main/docs/specification).
For the full Dart guide, see
-[https://fory.apache.org/docs/guide/dart/](https://fory.apache.org/docs/guide/dart/).
+[https://fory.apache.org/docs/object-serialization/dart/](https://fory.apache.org/docs/object-serialization/dart/).
diff --git a/docs/README.md b/docs/README.md
deleted file mode 100644
index 15d039336..000000000
--- a/docs/README.md
+++ /dev/null
@@ -1,55 +0,0 @@
-# User Guide
-
-- For xlang serialization, see the [xlang guide](guide/xlang/index.md).
-- For language-specific serialization, see the [Java](guide/java/index.md),
- [Python](guide/python/index.md), [C++](guide/cpp/index.md),
- [Go](guide/go/index.md), [Rust](guide/rust/index.md),
- [JavaScript/TypeScript](guide/javascript/index.md),
- [C#](guide/csharp/index.md), [Swift](guide/swift/index.md),
- [Dart](guide/dart/index.md), [Scala](guide/scala/index.md), and
- [Kotlin](guide/kotlin/index.md) guides.
-- For row format, see the [row format spec](specification/row_format_spec.md).
-- For using Apache Fory™ with GraalVM native image, see [graalvm support](guide/java/graalvm-support.md) doc.
-- For security models and deserialization boundaries, see the
- [security docs](security/index.md).
-
-## Fory IDL Schema
-
-Define cross-language data structures with Fory IDL and generate native code for multiple languages.
-
-- [Fory IDL Overview](compiler/index.md) - Introduction and quick start
-- [Fory Schema IDL](compiler/schema-idl.md) - Complete language syntax
-- [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 porting
-
-## Serialization Format
-
-- For Cross Language Serialization Format, see [xlang serialization spec](specification/xlang_serialization_spec.md) doc.
-- For Java Object Graph Format, see [java serialization spec](specification/java_serialization_spec.md) doc.
-- For Row Format, see [row format spec](specification/row_format_spec.md) doc.
-
-## Benchmarks
-
-- Benchmark source code:
- - Java: [Java Benchmarks Source](../benchmarks/java)
- - Python: [Python Benchmarks Source](../benchmarks/python)
- - C++: [C++ Benchmarks Source](../benchmarks/cpp)
- - Go: [Go Benchmarks Source](../benchmarks/go)
- - Rust: [Rust Benchmarks Source](../benchmarks/rust/)
-- Benchmark result:
- - Java: [Java Benchmarks](benchmarks/java/)
- - Python: [Python Benchmarks](benchmarks/python/)
- - C++: [C++ Benchmarks](benchmarks/cpp/)
- - Go: [Go Benchmarks](benchmarks/go/)
- - Rust: [Rust Benchmarks](benchmarks/rust/)
- - JavaScript/TypeScript: [JavaScript Benchmarks](benchmarks/javascript/)
- - C#: [C# Benchmarks](benchmarks/csharp/)
- - Swift: [Swift Benchmarks](benchmarks/swift/)
- - Dart: [Dart Benchmarks](benchmarks/dart/)
-
-## Development
-
-- For cpp debug, see [cpp debug](cpp_debug.md) doc.
-- For development, see [CONTRIBUTING](../CONTRIBUTING.md) doc.
diff --git a/docs/guide/rust/_category_.json b/docs/benchmarks/_category_.json
similarity index 71%
copy from docs/guide/rust/_category_.json
copy to docs/benchmarks/_category_.json
index e89108621..c87e2b8de 100644
--- a/docs/guide/rust/_category_.json
+++ b/docs/benchmarks/_category_.json
@@ -1,6 +1,6 @@
{
- "label": "Rust",
"position": 3,
+ "label": "Benchmarks",
"collapsible": true,
"collapsed": true
}
diff --git a/docs/benchmarks/index.md b/docs/benchmarks/index.md
new file mode 100644
index 000000000..46af48783
--- /dev/null
+++ b/docs/benchmarks/index.md
@@ -0,0 +1,133 @@
+---
+title: Benchmarks
+sidebar_position: 1
+id: index
+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.
+---
+
+> **Note**: Different serializers and configurations excel in different workloads. Benchmark results are for reference only.
+> For your specific use case, conduct benchmarks with appropriate configurations and workloads.
+
+## Java Benchmark
+
+The Java benchmark section compares Fory against popular Java serialization frameworks using the current benchmark suite from `docs/benchmarks/object-serialization/native/java`.
+
+**Serialization Throughput**:
+
+
+
+**Deserialization Throughput**:
+
+
+
+**JSON Throughput**:
+
+<div style={{display: 'flex', gap: '1rem'}}>
+ <img src={require('./json/java/string_throughput.png').default} alt="Java JSON String Throughput" style={{width: 'calc(50% - 0.5rem)'}} />
+ <img src={require('./json/java/utf8_bytes_throughput.png').default} alt="Java JSON UTF-8 Throughput" style={{width: 'calc(50% - 0.5rem)'}} />
+</div>
+
+**Cross-Language Throughput**:
+
+
+
+**Important**: Fory's runtime code generation requires proper warm-up for performance measurement:
+
+For additional benchmark notes, raw data, and the complete Java benchmark README, see [Java Benchmarks](object-serialization/native/java/README.md).
+
+## Python Benchmark
+
+Fory Python demonstrates strong performance compared to `pickle` and Protobuf across object and list workloads.
+
+
+
+For benchmark setup, raw results, and reproduction steps, see [Python Benchmarks](object-serialization/xlang/python/README.md).
+
+## Rust Benchmark
+
+Fory Rust demonstrates competitive performance compared to other Rust serialization frameworks.
+
+
+
+Note: Results depend on hardware, dataset, and implementation versions. See the
+[Rust benchmark report](object-serialization/xlang/rust/README.md) for the recorded environment,
+workloads, and reproduction command.
+
+## C++ Benchmark
+
+Fory C++ demonstrates competitive performance compared to Protobuf C++ serialization framework.
+
+
+
+See the [C++ benchmark report](object-serialization/xlang/cpp/README.md) for the recorded
+environment, workloads, and reproduction command.
+
+## Go Benchmark
+
+Fory Go demonstrates strong performance compared to Protobuf and Msgpack across
+single-object and list workloads.
+
+
+
+Note: Results depend on hardware, dataset, and implementation versions. See the
+[Go benchmark report](object-serialization/xlang/go/README.md) for details.
+
+## C# Benchmark
+
+Fory C# demonstrates strong performance compared to Protobuf and Msgpack across
+typed object serialization and deserialization workloads.
+
+
+
+Note: Results depend on hardware and runtime versions. See the
+[C# benchmark report](object-serialization/xlang/csharp/README.md) for details.
+
+## Swift Benchmark
+
+Fory Swift demonstrates strong performance compared to Protobuf and Msgpack
+across both scalar-object and list workloads.
+
+
+
+Note: Results depend on hardware and runtime versions. See the
+[Swift benchmark report](object-serialization/xlang/swift/README.md) for details.
+
+## JavaScript Benchmark
+
+Fory JavaScript demonstrates strong performance compared to Protocol Buffers and
+JSON across representative Node.js workloads.
+
+
+
+Note: Results depend on hardware, dataset, and runtime versions. See the
+[JavaScript benchmark report](object-serialization/xlang/javascript/README.md) for details.
+
+## Dart Benchmark
+
+Fory Dart demonstrates strong performance compared to Protocol Buffers across
+representative object and list workloads.
+
+
+
+Note: Results depend on hardware, dataset, and runtime versions. See the
+[Dart benchmark report](object-serialization/xlang/dart/README.md) for details.
+
+## Read Results Responsibly
+
+Start with [Methodology](methodology.md), then open the report whose capability, runtime, schema,
+mode, and operation match your workload. The checked-in results are historical evidence from their
+recorded environment, not a guarantee for a different application or current main branch.
diff --git a/docs/guide/cpp/_category_.json b/docs/benchmarks/json/_category_.json
similarity index 72%
copy from docs/guide/cpp/_category_.json
copy to docs/benchmarks/json/_category_.json
index 9660bafa8..d6cdf1f1b 100644
--- a/docs/guide/cpp/_category_.json
+++ b/docs/benchmarks/json/_category_.json
@@ -1,6 +1,6 @@
{
- "label": "C++",
"position": 4,
+ "label": "Fory JSON",
"collapsible": true,
"collapsed": true
}
diff --git a/docs/benchmarks/java/json/README.md b/docs/benchmarks/json/java/README.md
similarity index 100%
rename from docs/benchmarks/java/json/README.md
rename to docs/benchmarks/json/java/README.md
diff --git a/docs/benchmarks/java/json/string_throughput.png b/docs/benchmarks/json/java/string_throughput.png
similarity index 100%
rename from docs/benchmarks/java/json/string_throughput.png
rename to docs/benchmarks/json/java/string_throughput.png
diff --git a/docs/benchmarks/java/json/utf8_bytes_throughput.png b/docs/benchmarks/json/java/utf8_bytes_throughput.png
similarity index 100%
rename from docs/benchmarks/java/json/utf8_bytes_throughput.png
rename to docs/benchmarks/json/java/utf8_bytes_throughput.png
diff --git a/docs/benchmarks/methodology.md b/docs/benchmarks/methodology.md
new file mode 100644
index 000000000..4cf3f2cc3
--- /dev/null
+++ b/docs/benchmarks/methodology.md
@@ -0,0 +1,42 @@
+---
+title: Benchmark Methodology
+sidebar_position: 2
+id: methodology
+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.
+---
+
+A useful serialization benchmark records enough context to reproduce the workload and avoid
+comparing different semantics as if they were equivalent.
+
+## Required context
+
+- repository commit and dependency versions;
+- hardware, operating system, runtime version, and relevant environment variables;
+- schema and object population;
+- xlang or native mode, compatibility mode, reference tracking, and registration;
+- warm-up, forks/processes, iterations, duration, and concurrency;
+- serialized representation and operation measured;
+- exact command and raw result location.
+
+## Interpret the reports
+
+Compare one operation and equivalent data semantics. Do not compare an xlang payload with a native
+payload or a full object reconstruction with Row Format field access without naming that semantic
+difference. JIT- and codegen-based runtimes require representative warm-up.
+
+Run the active benchmark harness for decisions that depend on current code. Checked-in reports are
+evidence for the commit and environment they record.
diff --git a/docs/guide/rust/_category_.json b/docs/benchmarks/object-serialization/_category_.json
similarity index 64%
copy from docs/guide/rust/_category_.json
copy to docs/benchmarks/object-serialization/_category_.json
index e89108621..517dab897 100644
--- a/docs/guide/rust/_category_.json
+++ b/docs/benchmarks/object-serialization/_category_.json
@@ -1,6 +1,6 @@
{
- "label": "Rust",
"position": 3,
+ "label": "Object Serialization",
"collapsible": true,
"collapsed": true
}
diff --git a/docs/guide/python/_category_.json b/docs/benchmarks/object-serialization/native/_category_.json
similarity index 75%
copy from docs/guide/python/_category_.json
copy to docs/benchmarks/object-serialization/native/_category_.json
index b37a31907..1baf4a4d9 100644
--- a/docs/guide/python/_category_.json
+++ b/docs/benchmarks/object-serialization/native/_category_.json
@@ -1,6 +1,6 @@
{
- "label": "Python",
"position": 2,
+ "label": "Native",
"collapsible": true,
"collapsed": true
}
diff --git a/docs/benchmarks/object-serialization/native/java-graalvm.md b/docs/benchmarks/object-serialization/native/java-graalvm.md
new file mode 100644
index 000000000..d0f743a36
--- /dev/null
+++ b/docs/benchmarks/object-serialization/native/java-graalvm.md
@@ -0,0 +1,110 @@
+# Java GraalVM Native Image Benchmark
+
+These measurements cover Fory binary object serialization in GraalVM Native Image. Results depend on the recorded workloads and environment.
+
+## Benchmark
+
+Performance comparison between Fory and GraalVM JDK Serialization:
+
+| Type | Compression | Speed | Size |
+| ------ | ----------- | ---------- | ---- |
+| Struct | Off | 46x faster | 43% |
+| Struct | On | 24x faster | 31% |
+| Pojo | Off | 12x faster | 56% |
+| Pojo | On | 12x faster | 48% |
+
+See [Benchmark.java](https://github.com/apache/fory/blob/main/integration_tests/graalvm_tests/src/main/java/org/apache/fory/graalvm/Benchmark.java) for benchmark code.
+
+### Struct Benchmark
+
+#### Class Fields
+
+```java
+public class Struct implements Serializable {
+ public int f1;
+ public long f2;
+ public float f3;
+ public double f4;
+ public int f5;
+ public long f6;
+ public float f7;
+ public double f8;
+ public int f9;
+ public long f10;
+ public float f11;
+ public double f12;
+}
+```
+
+#### Benchmark Results
+
+No compression:
+
+```
+Benchmark repeat number: 400000
+Object type: class org.apache.fory.graalvm.Struct
+Compress number: false
+Fory size: 76.0
+JDK size: 178.0
+Fory serialization took mills: 49
+JDK serialization took mills: 2254
+Compare speed: Fory is 45.70x speed of JDK
+Compare size: Fory is 0.43x size of JDK
+```
+
+Compress number:
+
+```
+Benchmark repeat number: 400000
+Object type: class org.apache.fory.graalvm.Struct
+Compress number: true
+Fory size: 55.0
+JDK size: 178.0
+Fory serialization took mills: 130
+JDK serialization took mills: 3161
+Compare speed: Fory is 24.16x speed of JDK
+Compare size: Fory is 0.31x size of JDK
+```
+
+### Pojo Benchmark
+
+#### Class Fields
+
+```java
+public class Foo implements Serializable {
+ int f1;
+ String f2;
+ List<String> f3;
+ Map<String, Long> f4;
+}
+```
+
+#### Benchmark Results
+
+No compression:
+
+```
+Benchmark repeat number: 400000
+Object type: class org.apache.fory.graalvm.Foo
+Compress number: false
+Fory size: 541.0
+JDK size: 964.0
+Fory serialization took mills: 1663
+JDK serialization took mills: 16266
+Compare speed: Fory is 12.19x speed of JDK
+Compare size: Fory is 0.56x size of JDK
+```
+
+Compress number:
+
+```
+Benchmark repeat number: 400000
+Object type: class org.apache.fory.graalvm.Foo
+Compress number: true
+Fory size: 459.0
+JDK size: 964.0
+Fory serialization took mills: 1289
+JDK serialization took mills: 15069
+Compare speed: Fory is 12.11x speed of JDK
+Compare size: Fory is 0.48x size of JDK
+```
diff --git a/docs/benchmarks/java/README.md b/docs/benchmarks/object-serialization/native/java/README.md
similarity index 90%
rename from docs/benchmarks/java/README.md
rename to docs/benchmarks/object-serialization/native/java/README.md
index ba955ceb8..8be666c44 100644
--- a/docs/benchmarks/java/README.md
+++ b/docs/benchmarks/object-serialization/native/java/README.md
@@ -1,6 +1,6 @@
-# Java Benchmarks
+# Java Native Serialization Benchmarks
-See the [Java JSON benchmark report](json/) for fory-json, Jackson, and Gson results.
+See the [Java JSON benchmark report](../../../json/java/README.md) for fory-json, Jackson, and Gson results.
## System Environment
@@ -239,31 +239,3 @@ But if you serialize data between processes on same node and use shared-memory,
| deserialize | 5000 | array | PRIMITIVE_ARRAY | 40312.590172 | 6122.351228 | 10672.872798 |
| deserialize | 5000 | directBuffer | BUFFER | 3284441.570594 | 148614.476829 | 77950.612503 |
| deserialize | 5000 | directBuffer | PRIMITIVE_ARRAY | 40413.743717 | 21826.040410 | 8561.694533 |
-
-## Xlang Benchmark
-
-Run from `benchmarks/java/run.sh`. Raw JMH JSON stays under the ignored local `benchmarks/java/reports/` directory; `throughput.png` and this xlang section are synced into `docs/benchmarks/java/`.
-
-```bash
-cd benchmarks/java
-./run.sh
-```
-
-JMH parameters: `-f 1 -wi 3 -i 3 -t 1 -w 3s -r 3s -bm thrpt -tu s`. Higher throughput is better.
-
-
-
-| Data type | Operation | Fory ops/sec | Protobuf ops/sec | Flatbuffer ops/sec | Fastest |
-| ----------------- | ----------- | ------------ | ---------------- | ------------------ | ------- |
-| NumericStruct | Serialize | 46,787,647 | 33,024,161 | 9,612,018 | Fory |
-| NumericStruct | Deserialize | 71,683,707 | 29,837,931 | 40,514,436 | Fory |
-| Sample | Serialize | 17,406,902 | 2,071,963 | 3,153,672 | Fory |
-| Sample | Deserialize | 17,772,123 | 1,867,967 | 4,179,494 | Fory |
-| MediaContent | Serialize | 10,783,325 | 1,781,338 | 1,444,737 | Fory |
-| MediaContent | Deserialize | 7,950,203 | 2,184,597 | 3,453,985 | Fory |
-| NumericStructList | Serialize | 21,263,673 | 2,511,081 | 3,047,836 | Fory |
-| NumericStructList | Deserialize | 19,249,877 | 2,067,204 | 8,168,569 | Fory |
-| SampleList | Serialize | 4,580,165 | 401,280 | 696,268 | Fory |
-| SampleList | Deserialize | 3,811,985 | 344,945 | 773,625 | Fory |
-| MediaContentList | Serialize | 1,657,717 | 353,717 | 296,868 | Fory |
-| MediaContentList | Deserialize | 1,111,043 | 435,956 | 516,192 | Fory |
diff --git a/docs/benchmarks/java/data/jmh-jdk-11-deserialization.csv b/docs/benchmarks/object-serialization/native/java/data/jmh-jdk-11-deserialization.csv
similarity index 100%
rename from docs/benchmarks/java/data/jmh-jdk-11-deserialization.csv
rename to docs/benchmarks/object-serialization/native/java/data/jmh-jdk-11-deserialization.csv
diff --git a/docs/benchmarks/java/data/jmh-jdk-11-serialization.csv b/docs/benchmarks/object-serialization/native/java/data/jmh-jdk-11-serialization.csv
similarity index 100%
rename from docs/benchmarks/java/data/jmh-jdk-11-serialization.csv
rename to docs/benchmarks/object-serialization/native/java/data/jmh-jdk-11-serialization.csv
diff --git a/docs/benchmarks/java/data/jmh-jdk-11-zerocopy.csv b/docs/benchmarks/object-serialization/native/java/data/jmh-jdk-11-zerocopy.csv
similarity index 100%
rename from docs/benchmarks/java/data/jmh-jdk-11-zerocopy.csv
rename to docs/benchmarks/object-serialization/native/java/data/jmh-jdk-11-zerocopy.csv
diff --git a/docs/benchmarks/java/java_heap_deserialize_compatible.png b/docs/benchmarks/object-serialization/native/java/java_heap_deserialize_compatible.png
similarity index 100%
rename from docs/benchmarks/java/java_heap_deserialize_compatible.png
rename to docs/benchmarks/object-serialization/native/java/java_heap_deserialize_compatible.png
diff --git a/docs/benchmarks/java/java_heap_deserialize_consistent.png b/docs/benchmarks/object-serialization/native/java/java_heap_deserialize_consistent.png
similarity index 100%
rename from docs/benchmarks/java/java_heap_deserialize_consistent.png
rename to docs/benchmarks/object-serialization/native/java/java_heap_deserialize_consistent.png
diff --git a/docs/benchmarks/java/java_heap_serialize_compatible.png b/docs/benchmarks/object-serialization/native/java/java_heap_serialize_compatible.png
similarity index 100%
rename from docs/benchmarks/java/java_heap_serialize_compatible.png
rename to docs/benchmarks/object-serialization/native/java/java_heap_serialize_compatible.png
diff --git a/docs/benchmarks/java/java_heap_serialize_consistent.png b/docs/benchmarks/object-serialization/native/java/java_heap_serialize_consistent.png
similarity index 100%
rename from docs/benchmarks/java/java_heap_serialize_consistent.png
rename to docs/benchmarks/object-serialization/native/java/java_heap_serialize_consistent.png
diff --git a/docs/benchmarks/java/java_offheap_deserialize_compatible.png b/docs/benchmarks/object-serialization/native/java/java_offheap_deserialize_compatible.png
similarity index 100%
rename from docs/benchmarks/java/java_offheap_deserialize_compatible.png
rename to docs/benchmarks/object-serialization/native/java/java_offheap_deserialize_compatible.png
diff --git a/docs/benchmarks/java/java_offheap_deserialize_consistent.png b/docs/benchmarks/object-serialization/native/java/java_offheap_deserialize_consistent.png
similarity index 100%
rename from docs/benchmarks/java/java_offheap_deserialize_consistent.png
rename to docs/benchmarks/object-serialization/native/java/java_offheap_deserialize_consistent.png
diff --git a/docs/benchmarks/java/java_offheap_serialize_compatible.png b/docs/benchmarks/object-serialization/native/java/java_offheap_serialize_compatible.png
similarity index 100%
rename from docs/benchmarks/java/java_offheap_serialize_compatible.png
rename to docs/benchmarks/object-serialization/native/java/java_offheap_serialize_compatible.png
diff --git a/docs/benchmarks/java/java_offheap_serialize_consistent.png b/docs/benchmarks/object-serialization/native/java/java_offheap_serialize_consistent.png
similarity index 100%
rename from docs/benchmarks/java/java_offheap_serialize_consistent.png
rename to docs/benchmarks/object-serialization/native/java/java_offheap_serialize_consistent.png
diff --git a/docs/benchmarks/java/java_repo_deserialization_throughput.png b/docs/benchmarks/object-serialization/native/java/java_repo_deserialization_throughput.png
similarity index 100%
rename from docs/benchmarks/java/java_repo_deserialization_throughput.png
rename to docs/benchmarks/object-serialization/native/java/java_repo_deserialization_throughput.png
diff --git a/docs/benchmarks/java/java_repo_serialization_throughput.png b/docs/benchmarks/object-serialization/native/java/java_repo_serialization_throughput.png
similarity index 100%
rename from docs/benchmarks/java/java_repo_serialization_throughput.png
rename to docs/benchmarks/object-serialization/native/java/java_repo_serialization_throughput.png
diff --git a/docs/benchmarks/java/java_zero_copy_deserialize.png b/docs/benchmarks/object-serialization/native/java/java_zero_copy_deserialize.png
similarity index 100%
rename from docs/benchmarks/java/java_zero_copy_deserialize.png
rename to docs/benchmarks/object-serialization/native/java/java_zero_copy_deserialize.png
diff --git a/docs/benchmarks/java/java_zero_copy_serialize.png b/docs/benchmarks/object-serialization/native/java/java_zero_copy_serialize.png
similarity index 100%
rename from docs/benchmarks/java/java_zero_copy_serialize.png
rename to docs/benchmarks/object-serialization/native/java/java_zero_copy_serialize.png
diff --git a/docs/guide/java/_category_.json b/docs/benchmarks/object-serialization/xlang/_category_.json
similarity index 76%
copy from docs/guide/java/_category_.json
copy to docs/benchmarks/object-serialization/xlang/_category_.json
index 3508cf900..d2418b5e5 100644
--- a/docs/guide/java/_category_.json
+++ b/docs/benchmarks/object-serialization/xlang/_category_.json
@@ -1,6 +1,6 @@
{
- "label": "Java",
"position": 1,
+ "label": "Xlang",
"collapsible": true,
"collapsed": true
}
diff --git a/docs/benchmarks/cpp/README.md b/docs/benchmarks/object-serialization/xlang/cpp/README.md
similarity index 100%
rename from docs/benchmarks/cpp/README.md
rename to docs/benchmarks/object-serialization/xlang/cpp/README.md
diff --git a/docs/benchmarks/cpp/throughput.png b/docs/benchmarks/object-serialization/xlang/cpp/throughput.png
similarity index 100%
rename from docs/benchmarks/cpp/throughput.png
rename to docs/benchmarks/object-serialization/xlang/cpp/throughput.png
diff --git a/docs/benchmarks/csharp/README.md b/docs/benchmarks/object-serialization/xlang/csharp/README.md
similarity index 100%
rename from docs/benchmarks/csharp/README.md
rename to docs/benchmarks/object-serialization/xlang/csharp/README.md
diff --git a/docs/benchmarks/csharp/throughput.png b/docs/benchmarks/object-serialization/xlang/csharp/throughput.png
similarity index 100%
rename from docs/benchmarks/csharp/throughput.png
rename to docs/benchmarks/object-serialization/xlang/csharp/throughput.png
diff --git a/docs/benchmarks/dart/README.md b/docs/benchmarks/object-serialization/xlang/dart/README.md
similarity index 100%
rename from docs/benchmarks/dart/README.md
rename to docs/benchmarks/object-serialization/xlang/dart/README.md
diff --git a/docs/benchmarks/dart/throughput.png b/docs/benchmarks/object-serialization/xlang/dart/throughput.png
similarity index 100%
rename from docs/benchmarks/dart/throughput.png
rename to docs/benchmarks/object-serialization/xlang/dart/throughput.png
diff --git a/docs/benchmarks/go/README.md b/docs/benchmarks/object-serialization/xlang/go/README.md
similarity index 100%
rename from docs/benchmarks/go/README.md
rename to docs/benchmarks/object-serialization/xlang/go/README.md
diff --git a/docs/benchmarks/go/benchmark_results.txt b/docs/benchmarks/object-serialization/xlang/go/benchmark_results.txt
similarity index 100%
rename from docs/benchmarks/go/benchmark_results.txt
rename to docs/benchmarks/object-serialization/xlang/go/benchmark_results.txt
diff --git a/docs/benchmarks/go/serialized_sizes.txt b/docs/benchmarks/object-serialization/xlang/go/serialized_sizes.txt
similarity index 100%
rename from docs/benchmarks/go/serialized_sizes.txt
rename to docs/benchmarks/object-serialization/xlang/go/serialized_sizes.txt
diff --git a/docs/benchmarks/go/throughput.png b/docs/benchmarks/object-serialization/xlang/go/throughput.png
similarity index 100%
rename from docs/benchmarks/go/throughput.png
rename to docs/benchmarks/object-serialization/xlang/go/throughput.png
diff --git a/docs/benchmarks/object-serialization/xlang/java/README.md b/docs/benchmarks/object-serialization/xlang/java/README.md
new file mode 100644
index 000000000..1330697fd
--- /dev/null
+++ b/docs/benchmarks/object-serialization/xlang/java/README.md
@@ -0,0 +1,52 @@
+# Java Xlang Serialization Benchmarks
+
+See the [Java JSON benchmark report](../../../json/java/README.md) for fory-json, Jackson, and Gson results.
+
+This report covers the xlang benchmark groups generated by
+`benchmarks/java/run.sh`. It does not inherit the environment or native-mode
+workloads recorded in the separate [Java native report](../../native/java/README.md).
+The checked-in xlang snapshot did not record its host, Fory commit, or dependency
+versions; rerun the benchmark before using these results for a current comparison.
+
+See [Benchmark Methodology](../../../methodology.md) for the metadata required by
+new or refreshed reports.
+
+## Workloads
+
+- `NumericStruct`: primitive numeric fields.
+- `Sample`: the model used by the [Kryo benchmark suite](https://github.com/EsotericSoftware/kryo/blob/master/benchmarks/src/main/java/com/esotericsoftware/kryo/benchmarks/data/Sample.java).
+- `MediaContent`: the media model from [jvm-serializers](https://github.com/eishay/jvm-serializers/blob/master/tpc/src/data/media/MediaContent.java).
+- `*List`: a list of the corresponding model.
+
+## Run the Benchmark
+
+Run from `benchmarks/java/run.sh`. Raw JMH JSON stays under the ignored local
+`benchmarks/java/reports/` directory; `throughput.png` and this xlang report are
+published under `docs/benchmarks/object-serialization/xlang/java/`.
+
+```bash
+cd benchmarks/java
+./run.sh
+```
+
+JMH parameters: `-f 1 -wi 3 -i 3 -t 1 -w 3s -r 3s -bm thrpt -tu s`. Do not
+skip warmup. Higher throughput is better.
+
+## Results
+
+
+
+| Data type | Operation | Fory ops/sec | Protobuf ops/sec | Flatbuffer ops/sec | Fastest |
+| ----------------- | ----------- | ------------ | ---------------- | ------------------ | ------- |
+| NumericStruct | Serialize | 46,787,647 | 33,024,161 | 9,612,018 | Fory |
+| NumericStruct | Deserialize | 71,683,707 | 29,837,931 | 40,514,436 | Fory |
+| Sample | Serialize | 17,406,902 | 2,071,963 | 3,153,672 | Fory |
+| Sample | Deserialize | 17,772,123 | 1,867,967 | 4,179,494 | Fory |
+| MediaContent | Serialize | 10,783,325 | 1,781,338 | 1,444,737 | Fory |
+| MediaContent | Deserialize | 7,950,203 | 2,184,597 | 3,453,985 | Fory |
+| NumericStructList | Serialize | 21,263,673 | 2,511,081 | 3,047,836 | Fory |
+| NumericStructList | Deserialize | 19,249,877 | 2,067,204 | 8,168,569 | Fory |
+| SampleList | Serialize | 4,580,165 | 401,280 | 696,268 | Fory |
+| SampleList | Deserialize | 3,811,985 | 344,945 | 773,625 | Fory |
+| MediaContentList | Serialize | 1,657,717 | 353,717 | 296,868 | Fory |
+| MediaContentList | Deserialize | 1,111,043 | 435,956 | 516,192 | Fory |
diff --git a/docs/benchmarks/java/throughput.png b/docs/benchmarks/object-serialization/xlang/java/throughput.png
similarity index 100%
rename from docs/benchmarks/java/throughput.png
rename to docs/benchmarks/object-serialization/xlang/java/throughput.png
diff --git a/docs/benchmarks/javascript/README.md b/docs/benchmarks/object-serialization/xlang/javascript/README.md
similarity index 100%
rename from docs/benchmarks/javascript/README.md
rename to docs/benchmarks/object-serialization/xlang/javascript/README.md
diff --git a/docs/benchmarks/javascript/throughput.png b/docs/benchmarks/object-serialization/xlang/javascript/throughput.png
similarity index 100%
rename from docs/benchmarks/javascript/throughput.png
rename to docs/benchmarks/object-serialization/xlang/javascript/throughput.png
diff --git a/docs/benchmarks/python/README.md b/docs/benchmarks/object-serialization/xlang/python/README.md
similarity index 100%
rename from docs/benchmarks/python/README.md
rename to docs/benchmarks/object-serialization/xlang/python/README.md
diff --git a/docs/benchmarks/python/throughput.png b/docs/benchmarks/object-serialization/xlang/python/throughput.png
similarity index 100%
rename from docs/benchmarks/python/throughput.png
rename to docs/benchmarks/object-serialization/xlang/python/throughput.png
diff --git a/docs/benchmarks/rust/README.md b/docs/benchmarks/object-serialization/xlang/rust/README.md
similarity index 100%
rename from docs/benchmarks/rust/README.md
rename to docs/benchmarks/object-serialization/xlang/rust/README.md
diff --git a/docs/benchmarks/rust/throughput.png b/docs/benchmarks/object-serialization/xlang/rust/throughput.png
similarity index 100%
rename from docs/benchmarks/rust/throughput.png
rename to docs/benchmarks/object-serialization/xlang/rust/throughput.png
diff --git a/docs/benchmarks/swift/README.md b/docs/benchmarks/object-serialization/xlang/swift/README.md
similarity index 100%
rename from docs/benchmarks/swift/README.md
rename to docs/benchmarks/object-serialization/xlang/swift/README.md
diff --git a/docs/benchmarks/swift/throughput.png b/docs/benchmarks/object-serialization/xlang/swift/throughput.png
similarity index 100%
rename from docs/benchmarks/swift/throughput.png
rename to docs/benchmarks/object-serialization/xlang/swift/throughput.png
diff --git a/docs/compiler/_category_.json b/docs/compiler/_category_.json
index f835a1000..a26705007 100644
--- a/docs/compiler/_category_.json
+++ b/docs/compiler/_category_.json
@@ -1,6 +1,6 @@
{
- "position": 3,
- "label": "Schema IDL & Compiler",
+ "position": 7,
+ "label": "Fory IDL & Compiler",
"collapsible": true,
"collapsed": true
}
diff --git a/docs/compiler/build-integration.md b/docs/compiler/build-integration.md
new file mode 100644
index 000000000..ea89c33e5
--- /dev/null
+++ b/docs/compiler/build-integration.md
@@ -0,0 +1,230 @@
+---
+title: Build Integration
+sidebar_position: 2
+id: build-integration
+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.
+---
+
+## Maven (Java)
+
+Add to your `pom.xml`:
+
+```xml
+<build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>exec-maven-plugin</artifactId>
+ <version>3.1.0</version>
+ <executions>
+ <execution>
+ <id>generate-fory-types</id>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>exec</goal>
+ </goals>
+ <configuration>
+ <executable>foryc</executable>
+ <arguments>
+ <argument>${project.basedir}/src/main/fdl/schema.fdl</argument>
+ <argument>--java_out</argument>
+ <argument>${project.build.directory}/generated-sources/fory</argument>
+ </arguments>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+</build>
+```
+
+Add generated sources:
+
+```xml
+<build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>build-helper-maven-plugin</artifactId>
+ <version>3.4.0</version>
+ <executions>
+ <execution>
+ <phase>generate-sources</phase>
+ <goals>
+ <goal>add-source</goal>
+ </goals>
+ <configuration>
+ <sources>
+ <source>${project.build.directory}/generated-sources/fory</source>
+ </sources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+</build>
+```
+
+## Gradle (Java/Kotlin)
+
+Add to `build.gradle`:
+
+```groovy
+task generateForyTypes(type: Exec) {
+ commandLine 'foryc',
+ "${projectDir}/src/main/fdl/schema.fdl",
+ '--java_out', "${buildDir}/generated/sources/fory"
+}
+
+compileJava.dependsOn generateForyTypes
+
+sourceSets {
+ main {
+ java {
+ srcDir "${buildDir}/generated/sources/fory"
+ }
+ }
+}
+```
+
+## Python (setuptools)
+
+Add to `setup.py` or `pyproject.toml`:
+
+```python
+# setup.py
+from setuptools import setup
+from setuptools.command.build_py import build_py
+import subprocess
+
+class BuildWithForyIdl(build_py):
+ def run(self):
+ subprocess.run([
+ 'foryc',
+ 'schema.fdl',
+ '--python_out', 'src/generated'
+ ], check=True)
+ super().run()
+
+setup(
+ cmdclass={'build_py': BuildWithForyIdl},
+ # ...
+)
+```
+
+## Go (go generate)
+
+Add to your Go file:
+
+```go
+//go:generate foryc ../schema.fdl --lang go --output .
+package models
+```
+
+Run:
+
+```bash
+go generate ./...
+```
+
+## Rust (build.rs)
+
+Add to `build.rs`:
+
+```rust
+use std::process::Command;
+
+fn main() {
+ println!("cargo:rerun-if-changed=schema.fdl");
+
+ let status = Command::new("foryc")
+ .args(&["schema.fdl", "--rust_out", "src/generated"])
+ .status()
+ .expect("Failed to run foryc");
+
+ if !status.success() {
+ panic!("Fory IDL compilation failed");
+ }
+}
+```
+
+## CMake (C++)
+
+Add to `CMakeLists.txt`:
+
+```cmake
+find_program(FORY_COMPILER foryc)
+
+add_custom_command(
+ OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/generated/example.h
+ COMMAND ${FORY_COMPILER}
+ ${CMAKE_CURRENT_SOURCE_DIR}/schema.fdl
+ --cpp_out ${CMAKE_CURRENT_SOURCE_DIR}/generated
+ DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/schema.fdl
+ COMMENT "Generating Fory IDL types"
+)
+
+add_custom_target(generate_fory_idl DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/generated/example.h)
+
+add_library(mylib ...)
+add_dependencies(mylib generate_fory_idl)
+target_include_directories(mylib PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/generated)
+```
+
+## Bazel
+
+Create a rule in `BUILD`:
+
+```python
+genrule(
+ name = "generate_fdl",
+ srcs = ["schema.fdl"],
+ outs = ["generated/example.h"],
+ cmd = "$(location //:fory_compiler) $(SRCS) --cpp_out $(RULEDIR)/generated",
+ tools = ["//:fory_compiler"],
+)
+
+cc_library(
+ name = "models",
+ hdrs = [":generate_fdl"],
+ # ...
+)
+```
+
+## Dart / Flutter
+
+Add the Fory dependency to `pubspec.yaml`:
+
+```yaml
+dependencies:
+ fory: ^1.5.0
+
+dev_dependencies:
+ build_runner: ^2.4.0
+```
+
+Generate schema types with the compiler:
+
+```bash
+foryc schema.fdl --dart_out=lib/generated
+```
+
+Then run `build_runner` to generate the serializers:
+
+```bash
+dart run build_runner build
+```
diff --git a/docs/compiler/compiler-guide.md b/docs/compiler/cli.md
similarity index 82%
rename from docs/compiler/compiler-guide.md
rename to docs/compiler/cli.md
index abe4721f8..a9f6b82ce 100644
--- a/docs/compiler/compiler-guide.md
+++ b/docs/compiler/cli.md
@@ -1,7 +1,7 @@
---
-title: Compiler Guide
-sidebar_position: 3
-id: compiler_guide
+title: Compiler CLI
+sidebar_position: 4
+id: cli
license: |
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@@ -19,23 +19,6 @@ license: |
limitations under the License.
---
-This guide covers installation, usage, and integration of the Fory IDL compiler.
-
-## Installation
-
-### From Source
-
-```bash
-cd compiler
-pip install -e .
-```
-
-### Verify Installation
-
-```bash
-foryc --help
-```
-
## Command Line Interface
### Basic Usage
@@ -528,218 +511,6 @@ This runs:
The script also sets `DATA_FILE*` variables so file-based roundtrip paths are exercised.
-## Build Integration
-
-### Maven (Java)
-
-Add to your `pom.xml`:
-
-```xml
-<build>
- <plugins>
- <plugin>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>exec-maven-plugin</artifactId>
- <version>3.1.0</version>
- <executions>
- <execution>
- <id>generate-fory-types</id>
- <phase>generate-sources</phase>
- <goals>
- <goal>exec</goal>
- </goals>
- <configuration>
- <executable>foryc</executable>
- <arguments>
- <argument>${project.basedir}/src/main/fdl/schema.fdl</argument>
- <argument>--java_out</argument>
- <argument>${project.build.directory}/generated-sources/fory</argument>
- </arguments>
- </configuration>
- </execution>
- </executions>
- </plugin>
- </plugins>
-</build>
-```
-
-Add generated sources:
-
-```xml
-<build>
- <plugins>
- <plugin>
- <groupId>org.codehaus.mojo</groupId>
- <artifactId>build-helper-maven-plugin</artifactId>
- <version>3.4.0</version>
- <executions>
- <execution>
- <phase>generate-sources</phase>
- <goals>
- <goal>add-source</goal>
- </goals>
- <configuration>
- <sources>
- <source>${project.build.directory}/generated-sources/fory</source>
- </sources>
- </configuration>
- </execution>
- </executions>
- </plugin>
- </plugins>
-</build>
-```
-
-### Gradle (Java/Kotlin)
-
-Add to `build.gradle`:
-
-```groovy
-task generateForyTypes(type: Exec) {
- commandLine 'foryc',
- "${projectDir}/src/main/fdl/schema.fdl",
- '--java_out', "${buildDir}/generated/sources/fory"
-}
-
-compileJava.dependsOn generateForyTypes
-
-sourceSets {
- main {
- java {
- srcDir "${buildDir}/generated/sources/fory"
- }
- }
-}
-```
-
-### Python (setuptools)
-
-Add to `setup.py` or `pyproject.toml`:
-
-```python
-# setup.py
-from setuptools import setup
-from setuptools.command.build_py import build_py
-import subprocess
-
-class BuildWithForyIdl(build_py):
- def run(self):
- subprocess.run([
- 'foryc',
- 'schema.fdl',
- '--python_out', 'src/generated'
- ], check=True)
- super().run()
-
-setup(
- cmdclass={'build_py': BuildWithForyIdl},
- # ...
-)
-```
-
-### Go (go generate)
-
-Add to your Go file:
-
-```go
-//go:generate foryc ../schema.fdl --lang go --output .
-package models
-```
-
-Run:
-
-```bash
-go generate ./...
-```
-
-### Rust (build.rs)
-
-Add to `build.rs`:
-
-```rust
-use std::process::Command;
-
-fn main() {
- println!("cargo:rerun-if-changed=schema.fdl");
-
- let status = Command::new("foryc")
- .args(&["schema.fdl", "--rust_out", "src/generated"])
- .status()
- .expect("Failed to run foryc");
-
- if !status.success() {
- panic!("Fory IDL compilation failed");
- }
-}
-```
-
-### CMake (C++)
-
-Add to `CMakeLists.txt`:
-
-```cmake
-find_program(FORY_COMPILER foryc)
-
-add_custom_command(
- OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/generated/example.h
- COMMAND ${FORY_COMPILER}
- ${CMAKE_CURRENT_SOURCE_DIR}/schema.fdl
- --cpp_out ${CMAKE_CURRENT_SOURCE_DIR}/generated
- DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/schema.fdl
- COMMENT "Generating Fory IDL types"
-)
-
-add_custom_target(generate_fory_idl DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/generated/example.h)
-
-add_library(mylib ...)
-add_dependencies(mylib generate_fory_idl)
-target_include_directories(mylib PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/generated)
-```
-
-### Bazel
-
-Create a rule in `BUILD`:
-
-```python
-genrule(
- name = "generate_fdl",
- srcs = ["schema.fdl"],
- outs = ["generated/example.h"],
- cmd = "$(location //:fory_compiler) $(SRCS) --cpp_out $(RULEDIR)/generated",
- tools = ["//:fory_compiler"],
-)
-
-cc_library(
- name = "models",
- hdrs = [":generate_fdl"],
- # ...
-)
-```
-
-### Dart / Flutter
-
-Add the Fory dependency to `pubspec.yaml`:
-
-```yaml
-dependencies:
- fory: ^1.5.0
-
-dev_dependencies:
- build_runner: ^2.4.0
-```
-
-Generate schema types with the compiler:
-
-```bash
-foryc schema.fdl --dart_out=lib/generated
-```
-
-Then run `build_runner` to generate the serializers:
-
-```bash
-dart run build_runner build
-```
-
## Error Handling
### Syntax Errors
diff --git a/docs/compiler/flatbuffers-idl.md b/docs/compiler/flatbuffers-idl.md
index 0ad11a648..caa7f3fea 100644
--- a/docs/compiler/flatbuffers-idl.md
+++ b/docs/compiler/flatbuffers-idl.md
@@ -1,7 +1,7 @@
---
title: FlatBuffers IDL Support
-sidebar_position: 7
-id: flatbuffers_idl
+sidebar_position: 6
+id: flatbuffers-idl
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/compiler/generated-code.md b/docs/compiler/generated-code.md
deleted file mode 100644
index 6e1936945..000000000
--- a/docs/compiler/generated-code.md
+++ /dev/null
@@ -1,1962 +0,0 @@
----
-title: Generated Code
-sidebar_position: 5
-id: generated_code
-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 document explains generated code for each target language.
-
-Fory IDL generated types are idiomatic in host languages and can be used directly as domain objects. Generated types also include `to/from bytes` helpers and schema modules or registration helpers, depending on the target language.
-
-Generated schema modules are named from the schema source file, not from the
-package or namespace. In targets that expose the module directly in a language
-package or namespace, names such as `AddressbookForyModule` or
-`ComplexPbForyModule` let multiple IDL files target the same package or
-namespace without producing colliding `ForyModule` types.
-
-## Reference Schemas
-
-The examples below use two real schemas:
-
-1. `addressbook.fdl` (explicit type IDs)
-2. `auto_id.fdl` (no explicit type IDs)
-
-### `addressbook.fdl` (excerpt)
-
-```protobuf
-package addressbook;
-
-option go_package = "github.com/myorg/myrepo/gen/addressbook;addressbook";
-
-message Person [id=100] {
- string name = 1;
- int32 id = 2;
-
- enum PhoneType [id=101] {
- PHONE_TYPE_MOBILE = 0;
- PHONE_TYPE_HOME = 1;
- PHONE_TYPE_WORK = 2;
- }
-
- message PhoneNumber [id=102] {
- string number = 1;
- PhoneType phone_type = 2;
- }
-
- list<PhoneNumber> phones = 7;
- Animal pet = 8;
-}
-
-message Dog [id=104] {
- string name = 1;
- int32 bark_volume = 2;
-}
-
-message Cat [id=105] {
- string name = 1;
- int32 lives = 2;
-}
-
-union Animal [id=106] {
- Dog dog = 1;
- Cat cat = 2;
-}
-
-message AddressBook [id=103] {
- list<Person> people = 1;
- map<string, Person> people_by_name = 2;
-}
-```
-
-### `auto_id.fdl` (excerpt)
-
-```protobuf
-package auto_id;
-
-enum Status {
- UNKNOWN = 0;
- OK = 1;
-}
-
-message Envelope {
- string id = 1;
-
- message Payload {
- int32 value = 1;
- }
-
- union Detail {
- Payload payload = 1;
- string note = 2;
- }
-
- Payload payload = 2;
- Detail detail = 3;
- Status status = 4;
-}
-
-union Wrapper {
- Envelope envelope = 1;
- string raw = 2;
-}
-```
-
-## Java
-
-### Output Layout
-
-For `package addressbook`, Java output is generated under:
-
-- `<java_out>/addressbook/`
-- Type files: `AddressBook.java`, `Person.java`, `Dog.java`, `Cat.java`, `Animal.java`
-- Schema module: `AddressbookForyModule.java`
-
-For schemas without a Java package, the schema module name is derived from the
-source file stem, for example `main.fdl` generates `MainForyModule.java`.
-Java import graphs cannot mix default-package schemas with named Java packages.
-
-### Type Generation
-
-Messages generate Java classes with `@ForyField`, default constructors, getters/setters, and byte helpers:
-
-```java
-public class Person {
- public static enum PhoneType {
- MOBILE,
- HOME,
- WORK;
- }
-
- public static class PhoneNumber {
- @ForyField(id = 1)
- private String number;
-
- @ForyField(id = 2)
- private PhoneType phoneType;
-
- public byte[] toBytes() { ... }
- public static PhoneNumber fromBytes(byte[] bytes) { ... }
- }
-
- @ForyField(id = 1)
- private String name;
-
- @ForyField(id = 8)
- private Animal pet;
-
- public byte[] toBytes() { ... }
- public static Person fromBytes(byte[] bytes) { ... }
-}
-```
-
-Messages with `evolving=false` are generated with Java fixed-schema struct encoding.
-
-Unions generate classes extending `org.apache.fory.type.union.Union`:
-
-```java
-public final class Animal extends Union {
- public enum AnimalCase {
- DOG(1),
- CAT(2);
- public final int id;
- AnimalCase(int id) { this.id = id; }
- }
-
- public static Animal ofDog(Dog v) { ... }
- public AnimalCase getAnimalCase() { ... }
- public int getAnimalCaseId() { ... }
-
- public boolean hasDog() { ... }
- public Dog getDog() { ... }
- public void setDog(Dog v) { ... }
-}
-```
-
-### Schema Module
-
-Each JVM schema generates a `ForyModule`. Imported schema modules are installed
-through `fory.register(...)`, so shared imports are deduplicated by the Fory instance.
-
-```java
-public final class AddressbookForyModule implements org.apache.fory.ForyModule {
- public static final AddressbookForyModule INSTANCE = new AddressbookForyModule();
-
- static ThreadSafeFory getFory() { ... }
-
- @Override
- public void install(Fory fory) {
- org.apache.fory.resolver.TypeResolver resolver = fory.getTypeResolver();
- resolver.registerUnion(Animal.class, 106L, new org.apache.fory.serializer.UnionSerializer(resolver, Animal.class));
- resolver.register(Person.class, 100L);
- resolver.register(Person.PhoneType.class, 101L);
- resolver.register(Person.PhoneNumber.class, 102L);
- resolver.register(Dog.class, 104L);
- resolver.register(Cat.class, 105L);
- resolver.register(AddressBook.class, 103L);
- }
-}
-```
-
-For schemas without explicit `[id=...]`, generated registration uses computed numeric IDs (for example from `auto_id.fdl`):
-
-```java
-resolver.register(Status.class, 1124725126L);
-resolver.registerUnion(Wrapper.class, 1471345060L, new org.apache.fory.serializer.UnionSerializer(resolver, Wrapper.class));
-resolver.register(Envelope.class, 3022445236L);
-resolver.registerUnion(Envelope.Detail.class, 1609214087L, new org.apache.fory.serializer.UnionSerializer(resolver, Envelope.Detail.class));
-resolver.register(Envelope.Payload.class, 2862577837L);
-```
-
-If `option enable_auto_type_id = false;` is set, registration uses symbolic names:
-
-```java
-resolver.register(Config.class, "myapp.models", "Config");
-resolver.registerUnion(
- Holder.class,
- "myapp.models",
- "Holder",
- new org.apache.fory.serializer.UnionSerializer(resolver, Holder.class));
-```
-
-### Usage
-
-```java
-Person person = new Person();
-person.setName("Alice");
-person.setPet(Animal.ofDog(new Dog()));
-
-byte[] data = person.toBytes();
-Person restored = Person.fromBytes(data);
-```
-
-### gRPC Service Companions
-
-When a schema contains services and the compiler is run with `--grpc`, Java
-generation emits one `<ServiceName>Grpc.java` file per service next to the model
-types.
-
-```java
-public final class AddressBookServiceGrpc {
- public static final String SERVICE_NAME = "addressbook.AddressBookService";
-
- public static AddressBookServiceStub newStub(io.grpc.Channel channel) { ... }
- public static AddressBookServiceBlockingStub newBlockingStub(io.grpc.Channel channel) { ... }
- public static AddressBookServiceFutureStub newFutureStub(io.grpc.Channel channel) { ... }
-
- public abstract static class AddressBookServiceImplBase
- implements io.grpc.BindableService {
- public void lookup(Person request, io.grpc.stub.StreamObserver<AddressBook> responseObserver) { ... }
- }
-}
-```
-
-The generated marshaller serializes each request or response with the schema
-module's `ThreadSafeFory`. It uses grpc-java's `MethodDescriptor.Marshaller`
-API, so applications compiling these files must provide grpc-java dependencies.
-Those dependencies are not added to Fory Java artifacts.
-
-## Python
-
-### Output Layout
-
-Python output is one module per schema file, for example:
-
-- `<python_out>/addressbook.py`
-
-### Type Generation
-
-Unions generate a case enum plus a `Union` subclass with typed helpers:
-
-```python
-class AnimalCase(Enum):
- DOG = 1
- CAT = 2
-
-class Animal(Union):
- @classmethod
- def dog(cls, v: Dog) -> "Animal": ...
-
- def case(self) -> AnimalCase: ...
- def case_id(self) -> int: ...
-
- def is_dog(self) -> bool: ...
- def dog_value(self) -> Dog: ...
- def set_dog(self, v: Dog) -> None: ...
-```
-
-Messages generate `@pyfory.dataclass` types, and nested types stay nested:
-
-```python
-@pyfory.dataclass
-class Person:
- class PhoneType(IntEnum):
- MOBILE = 0
- HOME = 1
- WORK = 2
-
- @pyfory.dataclass
- class PhoneNumber:
- number: str = pyfory.field(id=1, default="")
- phone_type: Person.PhoneType = pyfory.field(id=2, default=None)
-
- name: str = pyfory.field(id=1, default="")
- phones: List[Person.PhoneNumber] = pyfory.field(id=7, default_factory=list)
- pet: Animal = pyfory.field(id=8, default=None)
-
- def to_bytes(self) -> bytes: ...
- @classmethod
- def from_bytes(cls, data: bytes) -> "Person": ...
-```
-
-### Registration
-
-Generated registration function:
-
-```python
-def register_addressbook_types(fory: pyfory.Fory):
- fory.register_union(Animal, type_id=106, serializer=AnimalSerializer(fory))
- fory.register_type(Person, type_id=100)
- fory.register_type(Person.PhoneType, type_id=101)
- fory.register_type(Person.PhoneNumber, type_id=102)
- fory.register_type(Dog, type_id=104)
- fory.register_type(Cat, type_id=105)
- fory.register_type(AddressBook, type_id=103)
-```
-
-For schemas without explicit `[id=...]`, generated registration uses computed numeric IDs:
-
-```python
-fory.register_type(Status, type_id=1124725126)
-fory.register_union(Wrapper, type_id=1471345060, serializer=WrapperSerializer(fory))
-fory.register_type(Envelope, type_id=3022445236)
-fory.register_union(Envelope.Detail, type_id=1609214087, serializer=Envelope.DetailSerializer(fory))
-fory.register_type(Envelope.Payload, type_id=2862577837)
-```
-
-If `option enable_auto_type_id = false;` is set:
-
-```python
-fory.register_type(Config, name="myapp.models.Config")
-fory.register_union(
- Holder,
- name="myapp.models.Holder",
- serializer=HolderSerializer(fory),
-)
-```
-
-### Usage
-
-```python
-person = Person(name="Alice", pet=Animal.dog(Dog(name="Rex", bark_volume=10)))
-
-data = person.to_bytes()
-restored = Person.from_bytes(data)
-```
-
-### gRPC Service Companions
-
-When a schema contains services and the compiler is run with `--grpc`, Python
-generation emits a companion module named `<module>_grpc.py`. The module name is
-derived from the Fory package by replacing dots with underscores, or `generated`
-when the schema has no package. Python gRPC output defaults to `grpc.aio`
-AsyncIO APIs.
-
-```python
-import grpc
-import grpc.aio
-
-
-class AddressBookServiceStub:
- def __init__(self, channel):
- self.lookup = channel.unary_unary(
- "/addressbook.AddressBookService/Lookup",
- request_serializer=_serialize,
- response_deserializer=_deserialize,
- )
-
-
-class AddressBookServiceServicer:
- async def lookup(self, request, context):
- await context.abort(grpc.StatusCode.UNIMPLEMENTED, "Method not implemented!")
-
-
-def add_servicer(servicer, server): ...
-```
-
-Python gRPC serializers receive and return complete `bytes` payloads, so the
-generated callbacks call the model module's `_get_fory().serialize(...)` and
-`_get_fory().deserialize(...)` directly. Applications using the generated
-companion module must install `grpcio`; `pyfory` does not add a hard gRPC
-dependency. The Python API uses snake_case method names while preserving the
-original IDL method names in the gRPC wire paths.
-
-Generate synchronous Python `grpcio` companions with
-`--grpc --grpc-python-mode=sync`. Sync mode keeps the same generated filename
-and public names, but servicer methods use regular `def` methods and sync
-`grpc.Channel` and `grpc.Server` instances.
-
-## Rust
-
-### Output Layout
-
-Rust output is one module file per schema, for example:
-
-- `<rust_out>/addressbook.rs`
-
-When `--grpc` is used and the schema contains services, Rust also emits:
-
-- `<rust_out>/addressbook_service.rs`
-- `<rust_out>/addressbook_service_grpc.rs`
-
-### Type Generation
-
-Unions map to Rust enums with `#[fory(id = ...)]` schema case attributes.
-`#[fory(unknown)] Unknown(::fory::UnknownCase)` marks the Fory-provided
-forward-compatibility carrier. The marker only selects the carrier and does not
-add an entry to the schema case table; schema cases still use the full `0..N`
-ID range. A generated typed union must have at least one non-`Unknown` case. The
-compiler marks the first declared non-`Unknown` case as `#[fory(default)]`.
-When that case's payload implements Rust's standard `Default` trait, the
-compiler also emits a standard `Default` implementation from that case:
-
-```rust
-#[derive(::fory::ForyUnion, Clone, Debug, PartialEq, Eq, Hash)]
-pub enum Animal {
- #[fory(unknown)]
- Unknown(::fory::UnknownCase),
- #[fory(id = 0, default)]
- Dog(self::Dog),
- #[fory(id = 1)]
- Cat(self::Cat),
-}
-
-impl ::std::default::Default for Animal {
- fn default() -> Self {
- Self::Dog(<self::Dog as ::std::default::Default>::default())
- }
-}
-```
-
-If the selected payload does not implement standard `Default`, such as an
-`any` payload, the generated union has no infallible `Default`
-implementation. This model-level default is independent of Fory's fallible
-deserialization default, which reconstructs the selected case through its
-codecs and active read context.
-
-Nested types generate nested modules:
-
-```rust
-pub mod person {
- #[derive(ForyEnum, Debug, Clone, PartialEq, Default)]
- #[repr(i32)]
- pub enum PhoneType {
- #[default]
- Mobile = 0,
- Home = 1,
- Work = 2,
- }
-
- #[derive(ForyStruct, Debug, Clone, PartialEq, Default)]
- pub struct PhoneNumber {
- #[fory(id = 1)]
- pub number: String,
- #[fory(id = 2)]
- pub phone_type: PhoneType,
- }
-}
-```
-
-Messages derive `ForyStruct` and include `to_bytes`/`from_bytes` helpers:
-
-```rust
-#[derive(ForyStruct, Debug, Clone, PartialEq, Default)]
-pub struct Person {
- #[fory(id = 1)]
- pub name: String,
- #[fory(id = 7)]
- pub phones: Vec<person::PhoneNumber>,
- #[fory(id = 8)]
- pub pet: Animal,
-}
-```
-
-### Registration
-
-Generated registration function:
-
-```rust
-pub fn register_types(fory: &mut Fory) -> Result<(), fory::Error> {
- fory.register_union::<Animal>(106)?;
- fory.register::<person::PhoneType>(101)?;
- fory.register::<person::PhoneNumber>(102)?;
- fory.register::<Person>(100)?;
- fory.register::<Dog>(104)?;
- fory.register::<Cat>(105)?;
- fory.register::<AddressBook>(103)?;
- Ok(())
-}
-```
-
-For schemas without explicit `[id=...]`, generated registration uses computed numeric IDs:
-
-```rust
-fory.register::<Status>(1124725126)?;
-fory.register_union::<Wrapper>(1471345060)?;
-fory.register::<Envelope>(3022445236)?;
-fory.register_union::<envelope::Detail>(1609214087)?;
-fory.register::<envelope::Payload>(2862577837)?;
-```
-
-If `option enable_auto_type_id = false;` is set:
-
-```rust
-fory.register_by_name::<Config>("myapp.models.Config")?;
-fory.register_union_by_name::<Holder>("myapp.models.Holder")?;
-```
-
-### Usage
-
-```rust
-let person = Person {
- name: "Alice".into(),
- pet: Animal::Dog(self::Dog::default()),
- ..Default::default()
-};
-
-let bytes = person.to_bytes()?;
-let restored = Person::from_bytes(&bytes)?;
-```
-
-### gRPC Service Companions
-
-When a schema contains services and the compiler is run with `--grpc`, Rust
-generation emits a service API module and a tonic binding module. For a schema
-module named `addressbook`, those files are `addressbook_service.rs` and
-`addressbook_service_grpc.rs`.
-
-The service API module contains the async trait and gRPC path constants:
-
-```rust
-#[::tonic::async_trait]
-pub trait AddressBookService: ::std::marker::Send + ::std::marker::Sync + 'static {
- async fn lookup(
- &self,
- request: ::tonic::Request<crate::addressbook::Person>,
- ) -> ::std::result::Result<
- ::tonic::Response<crate::addressbook::AddressBook>,
- ::tonic::Status,
- >;
-}
-
-pub const ADDRESS_BOOK_SERVICE_SERVICE_NAME: &str = "addressbook.AddressBookService";
-pub const ADDRESS_BOOK_SERVICE_LOOKUP_PATH: &str = "/addressbook.AddressBookService/Lookup";
-```
-
-The tonic binding module contains Fory-backed codecs, payload implementations,
-and client/server wrappers. It serializes each request or response with the
-generated model type's `to_bytes` and `from_bytes` helpers:
-
-```rust
-impl codec::ForyGrpcPayload for crate::addressbook::Person {
- fn encode_fory_payload(&self) -> ::std::result::Result<::std::vec::Vec<u8>, ::fory::Error> {
- self.to_bytes()
- }
-
- fn decode_fory_payload(payload: &[u8]) -> ::std::result::Result<Self, ::fory::Error> {
- Self::from_bytes(payload)
- }
-}
-```
-
-Applications compiling the generated Rust service files must provide `tonic` and
-`bytes` dependencies; Fory's Rust crate does not add those gRPC dependencies as
-hard dependencies.
-
-## C++
-
-### Output Layout
-
-C++ output is one header per schema file, for example:
-
-- `<cpp_out>/addressbook.h`
-
-### Type Generation
-
-Messages generate `final` classes with typed accessors and byte helpers:
-
-```cpp
-class Person final {
- public:
- class PhoneNumber final {
- public:
- const std::string& number() const;
- std::string* mutable_number();
- template <class Arg, class... Args>
- void set_number(Arg&& arg, Args&&... args);
-
- fory::Result<std::vector<uint8_t>, fory::Error> to_bytes() const;
- static fory::Result<PhoneNumber, fory::Error> from_bytes(
- const uint8_t* data, std::size_t size);
- static fory::Result<PhoneNumber, fory::Error> from_bytes(const std::vector<uint8_t>& data);
- };
-
- const std::string& name() const;
- std::string* mutable_name();
- template <class Arg, class... Args>
- void set_name(Arg&& arg, Args&&... args);
-
- const Animal& pet() const;
- Animal* mutable_pet();
-};
-```
-
-Optional message fields generate `has_xxx`, `mutable_xxx`, and `clear_xxx` APIs:
-
-```cpp
-class Envelope final {
- public:
- bool has_payload() const { return payload_ != nullptr; }
- const Envelope::Payload& payload() const { return *payload_; }
- Envelope::Payload* mutable_payload() {
- if (!payload_) {
- payload_ = std::make_unique<Envelope::Payload>();
- }
- return payload_.get();
- }
- void clear_payload() { payload_.reset(); }
-
- private:
- std::unique_ptr<Envelope::Payload> payload_;
-};
-```
-
-Unions generate `std::variant` wrappers:
-
-```cpp
-class Animal final {
- public:
- enum class AnimalCase : uint32_t {
- DOG = 1,
- CAT = 2,
- };
-
- static Animal dog(Dog v);
- static Animal cat(Cat v);
-
- AnimalCase animal_case() const noexcept;
- uint32_t animal_case_id() const noexcept;
-
- bool is_dog() const noexcept;
- const Dog* as_dog() const noexcept;
- Dog* as_dog() noexcept;
- const Dog& dog() const;
- Dog& dog();
-
- template <class Visitor>
- decltype(auto) visit(Visitor&& vis) const;
-
- private:
- std::variant<Dog, Cat> value_;
-};
-```
-
-Generated headers include `FORY_UNION`, `FORY_ENUM`, and `FORY_STRUCT` macros
-for serialization metadata. Field and payload configuration is embedded in the
-generated `FORY_STRUCT`/`FORY_UNION` entries.
-
-### Registration
-
-Generated registration function:
-
-```cpp
-inline void register_types(fory::serialization::BaseFory& fory) {
- fory.register_union<Animal>(106);
- fory.register_enum<Person::PhoneType>(101);
- fory.register_struct<Person::PhoneNumber>(102);
- fory.register_struct<Person>(100);
- fory.register_struct<Dog>(104);
- fory.register_struct<Cat>(105);
- fory.register_struct<AddressBook>(103);
-}
-```
-
-For schemas without explicit `[id=...]`, generated registration uses computed numeric IDs:
-
-```cpp
-fory.register_enum<Status>(1124725126);
-fory.register_union<Wrapper>(1471345060);
-fory.register_struct<Envelope>(3022445236);
-fory.register_union<Envelope::Detail>(1609214087);
-fory.register_struct<Envelope::Payload>(2862577837);
-```
-
-If `option enable_auto_type_id = false;` is set:
-
-```cpp
-fory.register_struct<Config>("myapp.models.Config");
-fory.register_union<Holder>("myapp.models.Holder");
-```
-
-### Usage
-
-```cpp
-addressbook::Person person;
-person.set_name("Alice");
-*person.mutable_pet() = addressbook::Animal::dog(addressbook::Dog{});
-
-auto bytes = person.to_bytes();
-auto restored = addressbook::Person::from_bytes(bytes.value());
-```
-
-### gRPC Service Companions
-
-When a schema contains services and the compiler is run with `--grpc`, C++
-generation emits a service API header file and gRPC binding files. For a schema
-header named `addressbook.h`, those files are `addressbook.service.h`,
-`addressbook.service.grpc.h`, and `addressbook.service.grpc.cc`.
-
-The service API header file contains synchronous business interfaces and gRPC path
-constants under `::<schema namespace>::service`:
-
-```cpp
-namespace addressbook::service {
-
-class AddressBookService {
- public:
- virtual ~AddressBookService() = default;
-
- virtual ::grpc::Status Lookup(
- ::grpc::ServerContext* context,
- const ::addressbook::Person* request,
- ::addressbook::AddressBook* response) = 0;
-};
-
-inline constexpr char AddressBookServiceServiceName[] =
- "addressbook.AddressBookService";
-inline constexpr char AddressBookServiceLookupPath[] =
- "/addressbook.AddressBookService/Lookup";
-
-} // namespace addressbook::service
-```
-
-The gRPC binding header file and source file contain Fory-backed codecs, generated
-`grpc::SerializationTraits` specializations, and synchronous client/server
-wrappers under `::<schema namespace>::service::grpc`. They serialize each
-request or response with the generated model type's `to_bytes` and `from_bytes`
-helpers:
-
-```cpp
-template <>
-class SerializationTraits<::addressbook::Person, void>
- : public ::fory::grpc::detail::ForyGrpcSerializationTraits<
- ::addressbook::Person> {};
-```
-
-Applications compiling the generated C++ service files must provide gRPC C++
-1.39.0 or later and link the generated `.service.grpc.cc` file with Fory
-serialization.
-
-## Go
-
-### Output Layout
-
-Go output path depends on schema options and `--go_out`.
-
-For `addressbook.fdl`, `go_package` is configured and generated output follows the configured import path/package (for example under your `--go_out` root).
-
-Without `go_package`, output uses the requested `--go_out` directory and package-derived file naming.
-
-### Type Generation
-
-Nested types use underscore naming by default (`Person_PhoneType`, `Person_PhoneNumber`):
-
-```go
-type Person_PhoneType int32
-
-const (
- Person_PhoneTypeMobile Person_PhoneType = 0
- Person_PhoneTypeHome Person_PhoneType = 1
- Person_PhoneTypeWork Person_PhoneType = 2
-)
-
-type Person_PhoneNumber struct {
- Number string `fory:"id=1"`
- PhoneType Person_PhoneType `fory:"id=2"`
-}
-```
-
-Messages generate structs with `fory` tags and byte helpers:
-
-```go
-type Person struct {
- Name string `fory:"id=1"`
- Id int32 `fory:"id=2"`
- Phones []Person_PhoneNumber `fory:"id=7,type=list"`
- Pet Animal `fory:"id=8"`
-}
-
-func (m *Person) ToBytes() ([]byte, error) { ... }
-func (m *Person) FromBytes(data []byte) error { ... }
-```
-
-Unions generate typed case structs with constructors/accessors/visitor APIs:
-
-```go
-type AnimalCase uint32
-
-type Animal struct {
- case_ AnimalCase
- value any
-}
-
-func DogAnimal(v *Dog) Animal { ... }
-func CatAnimal(v *Cat) Animal { ... }
-
-func (u Animal) Case() AnimalCase { ... }
-func (u Animal) AsDog() (*Dog, bool) { ... }
-func (u Animal) Visit(visitor AnimalVisitor) error { ... }
-```
-
-### Registration
-
-Generated registration function:
-
-```go
-func RegisterTypes(f *fory.Fory) error {
- if err := f.RegisterUnion(Animal{}, 106, fory.NewUnionSerializer(...)); err != nil {
- return err
- }
- if err := f.RegisterEnum(Person_PhoneType(0), 101); err != nil {
- return err
- }
- if err := f.RegisterStruct(Person_PhoneNumber{}, 102); err != nil {
- return err
- }
- if err := f.RegisterStruct(Person{}, 100); err != nil {
- return err
- }
- return nil
-}
-```
-
-For schemas without explicit `[id=...]`, generated registration uses computed numeric IDs:
-
-```go
-if err := f.RegisterEnum(Status(0), 1124725126); err != nil { ... }
-if err := f.RegisterUnion(Wrapper{}, 1471345060, fory.NewUnionSerializer(...)); err != nil { ... }
-if err := f.RegisterStruct(Envelope{}, 3022445236); err != nil { ... }
-if err := f.RegisterUnion(Envelope_Detail{}, 1609214087, fory.NewUnionSerializer(...)); err != nil { ... }
-if err := f.RegisterStruct(Envelope_Payload{}, 2862577837); err != nil { ... }
-```
-
-If `option enable_auto_type_id = false;` is set:
-
-```go
-if err := f.RegisterStructByName(Config{}, "myapp.models.Config"); err != nil { ... }
-if err := f.RegisterUnionByName(Holder{}, "myapp.models.Holder", fory.NewUnionSerializer(...)); err != nil { ... }
-```
-
-`go_nested_type_style` controls nested type naming:
-
-```protobuf
-option go_nested_type_style = "camelcase";
-```
-
-The CLI flag `--go_nested_type_style` overrides this schema option when both are set.
-
-### Usage
-
-```go
-person := &Person{
- Name: "Alice",
- Pet: DogAnimal(&Dog{Name: "Rex"}),
-}
-
-data, err := person.ToBytes()
-if err != nil {
- panic(err)
-}
-var restored Person
-if err := restored.FromBytes(data); err != nil {
- panic(err)
-}
-```
-
-### gRPC Service Companions
-
-When a schema contains services and the compiler is run with `--grpc`, Go
-generation emits one `<module>_grpc.go` file next to the model file. The
-companion contains grpc-go client and server interfaces plus a Fory-backed
-`CodecV2`.
-
-```go
-type AddressBookServiceClient interface {
- Lookup(ctx context.Context, in *Person, opts ...grpc.CallOption) (*AddressBook, error)
-}
-
-func NewAddressBookServiceClient(cc grpc.ClientConnInterface) AddressBookServiceClient { ... }
-
-type CodecV2 struct{}
-```
-
-The generated codec uses the same package-level thread-safe Fory runtime as the
-generated `ToBytes` and `FromBytes` helpers. Applications should pass
-`CodecV2{}` to grpc-go server options, and generated clients force the same
-codec on each call:
-
-```go
-server := grpc.NewServer(grpc.ForceServerCodecV2(addressbook.CodecV2{}))
-addressbook.RegisterAddressBookServiceServer(server, service)
-
-client := addressbook.NewAddressBookServiceClient(conn)
-```
-
-Go method names are exported as PascalCase identifiers, while the gRPC method
-path keeps the exact service and method names from the schema. Regenerate both
-peers after changing service or method names.
-
-Applications compiling these files must provide grpc-go dependencies; Fory Go
-packages do not add gRPC as a hard dependency.
-
-## C\#
-
-### Output Layout
-
-C# output is one `.cs` file per schema, for example:
-
-- `<csharp_out>/addressbook/Addressbook.cs`
-
-The C# model file name uses the normalized PascalCase source file stem. For
-example, `service.fdl` generates `Service.cs`, `order-events.fdl` generates
-`OrderEvents.cs`, and `123-schema.fdl` generates `Schema123Schema.cs`.
-
-### Type Generation
-
-Messages generate `[ForyStruct]` classes with C# properties and byte helpers:
-
-```csharp
-[ForyStruct]
-public sealed partial class Person
-{
- public string Name { get; set; } = string.Empty;
- public int Id { get; set; }
- public List<Person.PhoneNumber> Phones { get; set; } = new();
- public Animal Pet { get; set; } = null!;
-
- public byte[] ToBytes() { ... }
- public static Person FromBytes(byte[] data) { ... }
-}
-```
-
-Unions generate `[ForyUnion]` ADTs. `Unknown(UnknownCase)` is the
-Fory-provided forward-compatibility carrier marked with `[ForyUnknownCase]`.
-The marker only selects the carrier and does not add an entry to the schema case
-table. Schema-defined cases use non-negative `[ForyCase]` IDs. If a case needs
-non-default schema encoding, the generated `[ForyCase]` carries `Type`. Known
-case record names are PascalCase FDL case names; payload types are emitted as
-qualified references when needed to avoid name conflicts. A typed union must
-have at least one non-`Unknown` case.
-
-```csharp
-[ForyUnion]
-public abstract partial record Animal
-{
- private Animal() {}
-
- [ForyUnknownCase]
- public sealed partial record Unknown(UnknownCase Value) : Animal;
-
- [ForyCase(0)]
- public sealed partial record Dog(global::addressbook.Dog Value) : Animal;
-
- [ForyCase(1)]
- public sealed partial record Cat(global::addressbook.Cat Value) : Animal;
-}
-```
-
-### Module Installation
-
-Each schema generates a module class that installs imported modules first and
-then registers the local schema types:
-
-```csharp
-public static class AddressbookForyModule
-{
- public static void Install(Fory fory)
- {
- fory.Register<addressbook.Animal>((uint)106);
- fory.Register<addressbook.Person>((uint)100);
- // ...
- }
-}
-```
-
-The C# model file basename and module class both use the normalized source file
-stem. They do not use `csharp_namespace` and they do not use gRPC service names.
-For example, `service.fdl` generates `Service.cs` and `ServiceForyModule`,
-while `order-events.fdl` generates `OrderEvents.cs` and
-`OrderEventsForyModule`. A gRPC service named `Greeter` generates the service
-companion `GreeterGrpc.cs`; it does not change the schema module name. To get
-`GreeterForyModule`, name the schema file `greeter.fdl` or `Greeter.fdl`.
-
-This source-file rule lets several schemas target the same C# namespace without
-colliding. No namespace-derived or service-derived module alias is generated.
-
-When explicit type IDs are not provided, generated installation uses computed
-numeric IDs (same behavior as other targets).
-
-### gRPC Service Companions
-
-When a schema contains services and the compiler is run with `--grpc`, C#
-generation emits one `<ServiceName>Grpc.cs` file per service next to the schema
-model file.
-
-```csharp
-public static partial class AddressBookService
-{
- public abstract partial class AddressBookServiceBase
- {
- public virtual Task<AddressBook> Lookup(
- Person request,
- grpc::ServerCallContext context) { ... }
- }
-
- public partial class AddressBookServiceClient
- : grpc::ClientBase<AddressBookServiceClient>
- {
- public virtual AddressBook Lookup(Person request, grpc::CallOptions options) { ... }
- public virtual grpc::AsyncUnaryCall<AddressBook> LookupAsync(
- Person request,
- grpc::CallOptions options) { ... }
- }
-
- public static grpc::ServerServiceDefinition BindService(
- AddressBookServiceBase serviceImpl) { ... }
-
- public static void BindService(
- grpc::ServiceBinderBase serviceBinder,
- AddressBookServiceBase? serviceImpl) { ... }
-}
-```
-
-Each generated method descriptor uses a static Fory-backed
-`Grpc.Core.Marshaller<T>` that reuses the schema module's `ThreadSafeFory`.
-Deserialization copies the gRPC body through `PayloadAsNewBuffer()` and rejects
-trailing bytes after the single Fory frame. Generated service companions
-do not use protobuf parsers and do not create Fory instances per RPC call.
-
-Streaming RPCs map to standard gRPC C# APIs:
-
-| IDL shape | Server method | Client method |
-| ----------------------------------------- | ----------------------------------------------------------------------------- | ------------------------------------------- |
-| `rpc A (Req) returns (Res)` | `Task<Res> A(Req request, ServerCallContext context)` | `A(...)` and `AAsync(...)` |
-| `rpc A (Req) returns (stream Res)` | `Task A(Req request, IServerStreamWriter<Res> responseStream, ...)` | `AsyncServerStreamingCall<Res> A(...)` |
-| `rpc A (stream Req) returns (Res)` | `Task<Res> A(IAsyncStreamReader<Req> requestStream, ...)` | `AsyncClientStreamingCall<Req, Res> A(...)` |
-| `rpc A (stream Req) returns (stream Res)` | `Task A(IAsyncStreamReader<Req> requestStream, IServerStreamWriter<Res> ...)` | `AsyncDuplexStreamingCall<Req, Res> A(...)` |
-
-Applications compiling generated C# service files must provide `Grpc.Core.Api`
-and their chosen .NET gRPC hosting or client package, such as `Grpc.AspNetCore`
-or `Grpc.Net.Client`. The `Apache.Fory` package does not add gRPC dependencies
-as hard dependencies.
-
-## JavaScript/TypeScript
-
-### Output Layout
-
-JavaScript/TypeScript output is one `.ts` file per schema, for example:
-
-- `<javascript_out>/addressbook.ts`
-
-When the schema contains services, JavaScript can also emit service companions:
-
-- `<javascript_out>/addressbook_grpc.ts` with `--grpc`
-- `<javascript_out>/addressbook_grpc_web.ts` with `--grpc-web`
-
-### Type Generation
-
-Messages generate `export interface` declarations with camelCase field names:
-
-```typescript
-export interface Person {
- name: string;
- id: number;
- phones: PhoneNumber[];
- pet?: Animal | null;
-}
-```
-
-Enums generate `export enum` declarations:
-
-```typescript
-export enum PhoneType {
- MOBILE = 0,
- HOME = 1,
- WORK = 2,
-}
-```
-
-Unions generate a discriminated union with a case enum:
-
-```typescript
-export enum AnimalCase {
- DOG = 1,
- CAT = 2,
-}
-
-export type Animal =
- { case: AnimalCase.DOG; value: Dog } | { case: AnimalCase.CAT; value: Cat };
-```
-
-### Schema Helpers
-
-Each generated model file exports a registration helper for custom `Fory`
-instances and root serialization helpers. The public API looks like:
-
-```typescript
-import type Fory, { Serializer } from "@apache-fory/core";
-
-export function registerAddressbookTypes(fory: Fory): {
- person: {
- serialize: (value: Person | null) => Uint8Array;
- deserialize: (bytes: Uint8Array) => Person;
- serializer: Serializer;
- };
-};
-export const serializePerson: (value: Person | null) => Uint8Array;
-export const deserializePerson: (bytes: Uint8Array) => Person;
-```
-
-Imported schema modules are registered automatically by `registerXxxTypes(fory)`.
-Use `serializeX` and `deserializeX` for the generated default serialization
-path. Call `registerXxxTypes(fory)` when the application manages its own `Fory`
-instance. Generated gRPC companions import the generated helpers automatically.
-
-### gRPC Service Companions
-
-When a schema contains services and the compiler is run with `--grpc`,
-JavaScript generation emits a Node.js companion named `<module>_grpc.ts`.
-The file contains service descriptors, handler interfaces, a client class, and a
-server registration helper for `@grpc/grpc-js`.
-
-```typescript
-export interface GreeterHandlers extends grpc.UntypedServiceImplementation {
- sayHello: grpc.handleUnaryCall<HelloRequest, HelloReply>;
-}
-
-export function addGreeterService(
- server: grpc.Server,
- handlers: GreeterHandlers,
-): void { ... }
-
-export class GreeterClient extends grpc.Client { ... }
-```
-
-When the compiler is run with `--grpc-web`, JavaScript generation emits a
-browser companion named `<module>_grpc_web.ts`. It contains callback clients for
-`grpc-web`; services with unary RPCs also get promise clients:
-
-```typescript
-export class GreeterWebClient { ... }
-
-export class GreeterWebPromiseClient { ... }
-```
-
-Node.js service companions import `@grpc/grpc-js`; browser companions import
-`grpc-web`. Add those packages to the application that compiles or runs the
-generated files.
-
-## Swift
-
-### Output Layout
-
-Swift output is one `.swift` file per schema, for example:
-
-- `<swift_out>/addressbook/addressbook.swift`
-
-### Type Generation
-
-The generator creates Swift models with split model macros and stable field/case IDs.
-A typed union must include `@ForyUnknownCase case unknown(UnknownCase)` and at
-least one non-`unknown` case; `unknown(UnknownCase)` is only the
-Fory-provided forward-compatibility carrier. The marker only selects the carrier
-and does not add an entry to the schema case table.
-
-When package/namespace is non-empty, namespace shaping is controlled by `swift_namespace_style`:
-
-- `enum` (default): nested enum namespace wrappers.
-- `flatten`: package-derived prefix on top-level type names (for example `Demo_Foo_User`).
-
-When package/namespace is empty, no enum wrapper or flatten prefix is applied.
-
-For non-empty package with default `enum` style:
-
-```swift
-public enum Addressbook {
- @ForyUnion
- public enum Animal {
- @ForyUnknownCase
- case unknown(UnknownCase)
- @ForyCase(id: 0)
- case dog(Addressbook.Dog)
- @ForyCase(id: 1)
- case cat(Addressbook.Cat)
- }
-
- @ForyStruct
- public struct Person: Equatable {
- @ForyField(id: 1)
- public var name: String = ""
- @ForyField(id: 8)
- public var pet: Addressbook.Animal =
- Addressbook.Animal.dog(Addressbook.Dog())
- }
-}
-```
-
-For non-empty package with `flatten` style:
-
-```swift
-@ForyStruct
-public struct Addressbook_Person: Equatable { ... }
-```
-
-The CLI flag `--swift_namespace_style` overrides schema option `swift_namespace_style` when both are set.
-
-Unions are generated as tagged Swift enums with associated payload values.
-Recursive unions are emitted as `indirect` enums. The first known union case
-must have a finite recursively constructible default; the compiler rejects a
-first-case default cycle instead of emitting a non-terminating initializer.
-Messages with `ref`/`weak_ref` fields are generated as `final class` models to preserve reference semantics.
-A directly stored message cycle must mark at least one cycle edge `ref`; otherwise
-the compiler rejects the schema because Swift value types cannot represent it.
-Fixed or tagged integer encodings inside list/map fields are emitted as Swift
-field type hints, for example `@ListField(element: .encoding(.fixed))` or
-`@MapField(value: .encoding(.tagged))`.
-For non-null fixed-width integer list elements, Swift classifies the field as
-the corresponding Fory primitive packed-array type; fixed-width integer sets
-remain Fory sets.
-
-### Module Installation
-
-Each schema includes a `ForyModule` owner with transitive import installation:
-
-```swift
-public enum ForyModule {
- public static func install(_ fory: Fory) throws {
- try ComplexPb.ForyModule.install(fory)
- try fory.register(Addressbook.Person.self, id: 100)
- try fory.register(Addressbook.Animal.self, id: 106)
- }
-}
-```
-
-With non-empty package and `flatten` style, the helper is prefixed too (for example `Addressbook_ForyModule`).
-
-For schemas without explicit `[id=...]`, installation uses computed numeric IDs.
-If `option enable_auto_type_id = false;` is set, generated code uses name-based registration APIs.
-
-## Dart
-
-### Output Layout
-
-Dart output is two files per schema: a main `.dart` file with annotated types and the IDL module owner, and a `.fory.dart` part file with generated serializers and metadata.
-
-- `<dart_out>/package/package.dart`
-- `<dart_out>/package/package.fory.dart`
-
-### Type Generation
-
-Messages generate `@ForyStruct` annotated `final class` declarations with `@ForyField` on each field:
-
-```dart
-@ForyStruct()
-final class Person {
- Person();
-
- @ForyField(id: 1)
- String name = '';
-
- @ForyField(id: 2, type: Int32Type())
- int id = 0;
-
- @ForyField(id: 7)
- List<Person_PhoneNumber> phones = <Person_PhoneNumber>[];
-
- @ForyField(id: 8)
- Animal pet = Animal._empty();
-}
-```
-
-Enums generate Dart `enum` declarations with a `rawValue` getter and `fromRawValue` factory:
-
-```dart
-enum Person_PhoneType {
- mobile,
- home,
- work;
-
- int get rawValue => switch (this) {
- Person_PhoneType.mobile => 0,
- Person_PhoneType.home => 1,
- Person_PhoneType.work => 2,
- };
-
- static Person_PhoneType fromRawValue(int value) => switch (value) {
- 0 => Person_PhoneType.mobile,
- 1 => Person_PhoneType.home,
- 2 => Person_PhoneType.work,
- _ => throw StateError('Unknown Person_PhoneType raw value $value.'),
- };
-}
-```
-
-Unions generate `@ForyUnion` annotated classes with factory constructors, a case enum, and a custom serializer:
-
-```dart
-enum AnimalCase {
- dog,
- cat;
-
- int get id => switch (this) {
- AnimalCase.dog => 1,
- AnimalCase.cat => 2,
- };
-}
-
-@ForyUnion()
-final class Animal {
- final AnimalCase _case;
- final Object? _value;
-
- const Animal._(this._case, this._value);
-
- factory Animal.dog(Dog value) => Animal._(AnimalCase.dog, value);
- factory Animal.cat(Cat value) => Animal._(AnimalCase.cat, value);
-
- bool get isDog => _case == AnimalCase.dog;
- Dog get dogValue => _value as Dog;
- // ...
-}
-```
-
-Nested types use flat underscore naming (e.g., `Person_PhoneNumber`, `Person_PhoneType`).
-
-`list<T>` fields generate ordered collection carriers and use the Fory list
-protocol. `array<T>` fields generate dense one-dimensional bool or numeric
-carriers and use the specialized dense-array protocol. Generated code must not
-choose `array<T>` only because a language has an optimized list-like carrier;
-the schema kind comes from the IDL.
-
-| IDL schema | Dart generated carrier | Notes |
-| ------------------- | ---------------------- | ------------------------------------------ |
-| `list<int32>` | `List<int>` | List protocol, varint element encoding |
-| `list<fixed int32>` | `List<int>` | List protocol, fixed-width element segment |
-| `array<bool>` | `BoolList` | One byte per bool |
-| `array<int8>` | `Int8List` | Dense signed bytes |
-| `array<int16>` | `Int16List` | Dense little-endian int16 |
-| `array<int32>` | `Int32List` | Dense little-endian int32 |
-| `array<int64>` | `Int64List` | Dense little-endian int64 |
-| `array<uint8>` | `Uint8List` | Dense unsigned bytes |
-| `array<uint16>` | `Uint16List` | Dense little-endian uint16 |
-| `array<uint32>` | `Uint32List` | Dense little-endian uint32 |
-| `array<uint64>` | `Uint64List` | Dense little-endian uint64 |
-| `array<float16>` | `Float16List` | Dense binary16 storage |
-| `array<bfloat16>` | `Bfloat16List` | Dense bfloat16 storage |
-| `array<float32>` | `Float32List` | Dense little-endian float32 |
-| `array<float64>` | `Float64List` | Dense little-endian float64 |
-
-Generated Dart fields that use `ArrayType(element: BoolType())` must use
-`BoolList`; plain `List<bool>` remains the generated and handwritten carrier
-for `list<bool>`.
-
-Reference tracking on list elements or map values uses the container sugar annotations:
-
-```dart
-@ListField(element: DeclaredType(ref: true))
-@ForyField(id: 3)
-List<Node> children = <Node>[];
-
-@MapField(value: DeclaredType(ref: true))
-@ForyField(id: 2)
-Map<String, Node> byName = <String, Node>{};
-```
-
-### Module Installation
-
-Each generated Dart IDL library includes a module owner named after the input
-file, such as `AddressbookForyModule` for `addressbook.dart`. The module
-installs imported modules first and then registers every local schema type with
-its default IDL identity:
-
-```dart
-abstract final class AddressbookForyModule {
- static void install(Fory fory) {
- complex_pb.ComplexPbForyModule.install(fory);
- _registerType(fory, Person);
- _registerType(fory, Dog);
- }
-
- static Fory getFory() { ... }
-
- static void _registerType(Fory fory, Type type) {
- if (type == Person) {
- registerGeneratedStruct(fory, _personForySchema, id: 100, namespace: null, typeName: null);
- return;
- }
- // ... other types
- }
-}
-```
-
-### Usage
-
-```dart
-import 'package:fory/fory.dart';
-import 'generated/addressbook/addressbook.dart';
-
-void main() {
- final fory = Fory();
- AddressbookForyModule.install(fory);
-
- final person = Person()
- ..name = 'Alice'
- ..id = 1;
-
- final bytes = fory.serialize(person);
- final roundTrip = fory.deserialize<Person>(bytes);
-}
-```
-
-### gRPC Service Companions
-
-When a schema contains services and the compiler is run with `--grpc`, Dart
-generation emits one `<module>_grpc.dart` file per schema next to the model
-types. It targets `package:grpc`. Request and response serialization uses a Fory
-runtime the companion obtains automatically and that registers the schema's
-types on first use, so no manual registration is required; an application may
-optionally inject a custom `Fory` via the schema module's `install(...)` before
-the first call.
-
-All four RPC modes are generated: unary, server-streaming, client-streaming, and
-bidirectional. The client class extends `Client`; the service base class extends
-`Service` and self-registers each method with `$addMethod`.
-
-```dart
-class GreeterClient extends Client {
- // Single response: ResponseFuture. Streaming response: ResponseStream.
- ResponseFuture<HelloReply> sayHello(HelloRequest request, {CallOptions? options}) { ... }
- ResponseStream<HelloReply> sayHellos(HelloRequest request, {CallOptions? options}) { ... }
- ResponseFuture<HelloReply> collectHellos(Stream<HelloRequest> request, {CallOptions? options}) { ... }
- ResponseStream<HelloReply> chatHellos(Stream<HelloRequest> request, {CallOptions? options}) { ... }
-}
-
-abstract class GreeterServiceBase extends Service {
- Future<HelloReply> sayHello(ServiceCall call, HelloRequest request);
- Stream<HelloReply> sayHellos(ServiceCall call, HelloRequest request);
- Future<HelloReply> collectHellos(ServiceCall call, Stream<HelloRequest> request);
- Stream<HelloReply> chatHellos(ServiceCall call, Stream<HelloRequest> request);
-}
-```
-
-A single-response client method returns `ResponseFuture<R>` (client-streaming
-adapts the streaming call with `.single`); a streaming-response method returns
-`ResponseStream<R>`. On the server, implementations override the abstract methods,
-which receive a single request as `Q` and a client-streaming request as
-`Stream<Q>`, and return a `Future` for single responses or a `Stream` for
-streaming responses.
-Applications compiling these files must provide a `grpc` dependency; the Fory Dart
-runtime does not add one. The original IDL method names are used in the gRPC wire
-paths.
-
-## Kotlin
-
-The Kotlin target emits Kotlin source only. The compiler does not generate Java
-files.
-
-### Output Layout
-
-For source file `addressbook.fdl` with `package addressbook`, Kotlin output is
-generated under:
-
-- `<kotlin_out>/addressbook/`
-- Type files: `AddressBook.kt`, `Person.kt`, `Dog.kt`, `Cat.kt`, `Animal.kt`
-- Schema module: `AddressbookForyModule.kt`
-
-The schema module name is derived from the source file stem. Schemas in the same
-Kotlin package need distinct generated file names; duplicate generated Kotlin
-file paths are rejected before files are written.
-
-If `option kotlin_package = "...";` is present, the output path and Kotlin
-package use that option. Otherwise Kotlin uses the FDL package. A Kotlin import
-graph cannot mix default-package schemas with named Kotlin packages.
-Registration still uses the FDL package so cross-language type names stay
-stable.
-
-### Type Generation
-
-Messages generate Kotlin `data class` declarations by default:
-
-```kotlin
-@ForyStruct
-public data class Person(
- @field:ForyField(id = 1)
- public val name: String,
-
- @field:ForyField(id = 7)
- public val phones: List<PersonPhoneNumber>,
-
- @field:ForyField(id = 8)
- public val pet: Animal,
-) {
- public fun toBytes(): ByteArray = AddressbookForyModule.getFory().serialize(this)
-
- public companion object {
- public fun fromBytes(bytes: ByteArray): Person =
- AddressbookForyModule.getFory().deserialize(bytes, Person::class.java)
- }
-}
-```
-
-Messages that participate in compiler-detected construction cycles generate
-normal mutable classes so the generated serializer can publish the instance
-before reading back-references:
-
-```kotlin
-@ForyStruct
-public class Node() {
- @ForyField(id = 1)
- public var id: String = ""
-
- @Ref
- @ForyField(id = 2)
- public var parent: Node? = null
-}
-```
-
-Generated Kotlin IDL sources express nullability with Kotlin `?`, not Fory
-`@Nullable`, including mutable classes emitted for compiler-detected
-construction cycles.
-
-Enums generate Kotlin enum classes with stable Fory enum IDs. Unions generate
-sealed classes with `@ForyUnion`; the Fory-provided `Unknown(UnknownCase)`
-carrier is marked with `@ForyUnknownCase`. The marker only selects the carrier
-and does not add an entry to the schema case table. Schema-defined cases may use
-case IDs `0..N` and hold a single `value` property. A typed union must have at
-least one non-`Unknown` case.
-
-```kotlin
-package addressbook
-
-import org.apache.fory.annotation.ForyCase
-import org.apache.fory.annotation.ForyUnion
-import org.apache.fory.annotation.ForyUnknownCase
-import org.apache.fory.type.union.UnknownCase
-
-@ForyUnion
-public sealed class Animal {
- @ForyUnknownCase
- public data class Unknown(public val value: UnknownCase) : Animal()
-
- @ForyCase(id = 0)
- public data class Dog(public val value: addressbook.Dog) : Animal()
-}
-```
-
-Packaged Kotlin output keeps the schema case name and qualifies the payload
-type when both have the same simple name. If a target output mode cannot express
-a legal qualifier for a conflict, the compiler appends `Case` to the generated
-case class name.
-
-Kotlin `int32`, `int64`, `uint32`, and `uint64` fields use xlang varint
-encoding by default, so generated Kotlin does not emit `@VarInt` for the
-default case. It emits `@Fixed` or `@Tagged` only when the schema requests that
-non-default encoding. `duration` maps to `kotlin.time.Duration`, and infinite
-durations are rejected when encoded. Dense `array<float16>` and
-`array<bfloat16>` use the Java core `Float16Array` and `BFloat16Array`
-carriers. Generated Kotlin IDL uses `@ArrayType ByteArray` for `array<int8>`,
-including nested positions.
-
-### Schema Module
-
-Generated schema modules register schema types and resolve KSP-generated
-serializers from the target class name. The package-owned helper Fory instance uses
-`ForyKotlin.builder().withXlang(true)` with the schema module installed, so message
-`toBytes`/`fromBytes` helpers work without caller-managed Fory setup. For
-`addressbook.fdl`:
-
-```kotlin
-public object AddressbookForyModule : ForyModule {
- private val fory: ThreadSafeFory by lazy {
- ForyKotlin.builder()
- .withXlang(true)
- .withRefTracking(true)
- .withModule(this)
- .buildThreadSafeFory()
- }
-
- internal fun getFory(): ThreadSafeFory = fory
-
- override fun install(fory: Fory) {
- KotlinSerializers.registerType(fory, Person::class.java, 100L)
- KotlinSerializers.registerSerializer(fory, Person::class.java)
- KotlinSerializers.registerUnion(fory, Animal::class.java, 106L)
- }
-}
-```
-
-`registerUnion` discovers the generated `<Target>_ForySerializer`; callers do
-not pass a serializer instance.
-
-### gRPC Service Companions
-
-When a schema contains services and the compiler is run with `--grpc`, Kotlin
-generation emits one `<ServiceName>GrpcKt.kt` file per service next to the model
-types. The file contains a grpc-kotlin coroutine companion object, not Java
-`*Grpc.java` source.
-
-```kotlin
-public object AddressBookServiceGrpcKt {
- public const val SERVICE_NAME: String = "addressbook.AddressBookService"
-
- @JvmStatic
- public val serviceDescriptor: io.grpc.ServiceDescriptor
- get() = serviceDescriptorValue
-
- @JvmStatic
- public val lookupMethod: io.grpc.MethodDescriptor<Person, AddressBook>
- get() = lookupMethodValue
-
- public abstract class AddressBookServiceCoroutineImplBase(
- coroutineContext: kotlin.coroutines.CoroutineContext =
- kotlin.coroutines.EmptyCoroutineContext,
- ) : io.grpc.kotlin.AbstractCoroutineServerImpl(coroutineContext) {
- public open suspend fun lookup(request: Person): AddressBook =
- throw io.grpc.StatusException(
- io.grpc.Status.UNIMPLEMENTED.withDescription(
- "Method addressbook.AddressBookService/Lookup is unimplemented",
- ),
- )
- }
-
- public class AddressBookServiceCoroutineStub @JvmOverloads constructor(
- channel: io.grpc.Channel,
- callOptions: io.grpc.CallOptions = io.grpc.CallOptions.DEFAULT,
- ) : io.grpc.kotlin.AbstractCoroutineStub<AddressBookServiceCoroutineStub>(
- channel,
- callOptions,
- ) {
- public suspend fun lookup(
- request: Person,
- headers: io.grpc.Metadata = io.grpc.Metadata(),
- ): AddressBook =
- io.grpc.kotlin.ClientCalls.unaryRpc(
- channel,
- lookupMethod,
- request,
- callOptions,
- headers,
- )
- }
-}
-```
-
-Streaming RPCs use `kotlinx.coroutines.flow.Flow`:
-
-| IDL shape | Server method | Client method |
-| ----------------------------------------- | ----------------------------------------- | ----------------------------------------- |
-| `rpc A (Req) returns (Res)` | `suspend fun a(request: Req): Res` | `suspend fun a(request: Req): Res` |
-| `rpc A (Req) returns (stream Res)` | `fun a(request: Req): Flow<Res>` | `fun a(request: Req): Flow<Res>` |
-| `rpc A (stream Req) returns (Res)` | `suspend fun a(requests: Flow<Req>): Res` | `suspend fun a(requests: Flow<Req>): Res` |
-| `rpc A (stream Req) returns (stream Res)` | `fun a(requests: Flow<Req>): Flow<Res>` | `fun a(requests: Flow<Req>): Flow<Res>` |
-
-Each method descriptor uses a Fory-backed `io.grpc.MethodDescriptor.Marshaller`
-that reuses the generated schema module's `ThreadSafeFory`. Generated service
-companions do not call protobuf parsers, do not expose KSP serializer class
-names, and do not create Fory instances per call.
-
-Applications compiling the generated Kotlin service files must provide
-grpc-java, grpc-kotlin, and `kotlinx-coroutines-core` dependencies. Fory Kotlin
-artifacts do not add those gRPC dependencies as hard dependencies.
-
-## Scala
-
-The Scala target emits Scala 3 source only. The `fory-scala` artifact still
-supports Scala 2.13 and Scala 3, but generated IDL source and macro derivation
-require Scala 3.
-
-### Output Layout
-
-For `package addressbook`, Scala output is generated under:
-
-- `<scala_out>/addressbook/`
-- Type files: `AddressBook.scala`, `Person.scala`, `Dog.scala`, `Cat.scala`, `Animal.scala`
-- Schema module: `AddressbookForyModule.scala`
-
-For schemas without a Scala package, the schema module name is derived from the
-source file stem, for example `main.fdl` generates `MainForyModule.scala`.
-Scala import graphs cannot mix default-package schemas with named Scala
-packages.
-
-### Type Generation
-
-Messages outside compiler-detected construction cycles generate case classes:
-
-```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 = 3) email: Option[String],
- @ForyField(id = 7) phones: List[Person.PhoneNumber],
- @ForyField(id = 8) pet: Animal
-) derives ForySerializer {
- def toBytes(): Array[Byte] =
- AddressbookForyModule.getFory.serialize(this)
-}
-
-object Person {
- def fromBytes(bytes: Array[Byte]): Person =
- AddressbookForyModule.getFory.deserialize(bytes).asInstanceOf[Person]
-}
-```
-
-Messages in circular construction cycles generate normal classes with mutable
-serialized fields so reads can register the object before reading back-references:
-
-```scala
-import org.apache.fory.annotation.{ForyField, ForyStruct, Ref}
-import org.apache.fory.scala.ForySerializer
-
-@ForyStruct
-final class Node() derives ForySerializer {
- @ForyField(id = 1)
- var id: String = ""
-
- @Ref
- @ForyField(id = 2)
- var parent: Option[Node] = None
-}
-```
-
-Enums generate Scala 3 enums with stable Fory IDs:
-
-```scala
-import org.apache.fory.annotation.ForyEnumId
-
-enum PhoneType {
- @ForyEnumId(0)
- case Mobile
-
- @ForyEnumId(1)
- case Home
-
- @ForyEnumId(2)
- case Work
-}
-```
-
-Unions generate Scala 3 ADT enums. `Unknown(UnknownCase)` is the Fory-provided
-forward-compatibility carrier marked with `@ForyUnknownCase`. It is omitted
-from the schema case table because the marker only selects the carrier and does
-not add a schema entry. Schema-defined cases use non-negative `@ForyCase` IDs.
-A typed union must have at least one
-non-`Unknown` case.
-
-```scala
-package addressbook
-
-import org.apache.fory.annotation.{ForyCase, ForyUnion, ForyUnknownCase}
-import org.apache.fory.scala.ForySerializer
-import org.apache.fory.`type`.union.UnknownCase
-
-@ForyUnion
-enum Animal derives ForySerializer {
- @ForyUnknownCase
- case Unknown(value: UnknownCase)
-
- @ForyCase(id = 0)
- case Dog(value: _root_.addressbook.Dog)
-
- @ForyCase(id = 1)
- case Cat(value: _root_.addressbook.Cat)
-}
-```
-
-Packaged Scala output keeps the schema case name and qualifies the payload type
-when both have the same simple name. If a target output mode cannot express a
-legal qualifier for a conflict, the compiler appends `Case` to the generated
-case name.
-
-`optional T` fields generate `Option[T]`. Top-level message references use
-`@Ref` on the field or constructor parameter. Nested element/value references
-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 Fory instance uses
-`ForyScala.builder().withXlang(true)` with the schema module installed, so
-message `toBytes`/`fromBytes` helpers work without caller-managed Fory setup:
-
-```scala
-object AddressbookForyModule extends org.apache.fory.ForyModule {
- private lazy val fory: ThreadSafeFory =
- ForyScala.builder()
- .withXlang(true)
- .withRefTracking(true)
- .withModule(this)
- .buildThreadSafeFory()
-
- private[addressbook] def getFory: ThreadSafeFory = fory
-
- override def install(fory: Fory): Unit = {
- ScalaSerializers.registerEnum(fory, classOf[Person.PhoneType], 101L)
- ForySerializer.register(fory, classOf[Person.PhoneNumber], 102L)
- ForySerializer.register(fory, classOf[Person], 100L)
- ForySerializer.register(fory, classOf[Animal], 106L)
- }
-}
-```
-
-### gRPC Service Companions
-
-When a schema contains services and the compiler is run with `--grpc`, Scala
-generation emits one `<ServiceName>Grpc.scala` companion per local service
-definition. The companion lives in the same Scala package as the generated
-models and schema module.
-
-For a service such as:
-
-```protobuf
-service AddressBookService {
- rpc Lookup (Person) returns (AddressBook);
- rpc Watch (Person) returns (stream AddressBook);
- rpc Upload (stream Person) returns (AddressBook);
- rpc Chat (stream Person) returns (stream AddressBook);
-}
-```
-
-the generated companion contains:
-
-- `SERVICE_NAME` and grpc-java method descriptors
-- `AddressBookServiceImplBase` for server implementations
-- `AddressBookServiceClient` for client calls
-- Fory-backed grpc-java marshallers for request and response payloads
-
-The generated Scala client keeps grpc-java available per method while adding
-Scala-friendly convenience methods for the shapes where a direct Scala handle
-can preserve the needed lifecycle controls:
-
-| RPC shape | Scala convenience method | grpc-java-style method |
-| ------------------------------------------------------- | ----------------------------------- | ------------------------------------------------ |
-| `rpc Lookup (Person) returns (AddressBook)` | `lookup(request): RpcFuture[Resp]` | async observer, blocking, and `ListenableFuture` |
-| `rpc Watch (Person) returns (stream AddressBook)` | `watch(request): RpcIterator[Resp]` | async observer and blocking iterator |
-| `rpc Upload (stream Person) returns (AddressBook)` | None | request `StreamObserver` |
-| `rpc Chat (stream Person) returns (stream AddressBook)` | None | request and response `StreamObserver` |
-
-Unary client convenience methods return `org.apache.fory.scala.rpc.RpcFuture`:
-
-```scala
-val client = AddressBookServiceGrpc.newClient(channel)
-val call = client.lookup(person)
-call.asFuture.foreach(handleAddressBook)(scala.concurrent.ExecutionContext.global)
-```
-
-Server-streaming client convenience methods return
-`org.apache.fory.scala.rpc.RpcIterator`:
-
-```scala
-val stream = client.watch(person)
-try {
- while (stream.hasNext) {
- handleAddressBook(stream.next())
- }
-} finally {
- stream.close()
-}
-```
-
-Close or cancel the `RpcIterator` when the client stops before consuming the
-whole stream. The generated adapter cancels the underlying gRPC call so the
-server is not left writing a response stream the client no longer reads.
-
-Client-streaming and bidirectional methods use grpc-java `StreamObserver` APIs:
-
-```scala
-val requestStream = client.upload(
- new io.grpc.stub.StreamObserver[AddressBook] {
- override def onNext(value: AddressBook): Unit = handleAddressBook(value)
- override def onError(t: Throwable): Unit = handleError(t)
- override def onCompleted(): Unit = ()
- }
-)
-requestStream.onNext(person)
-requestStream.onCompleted()
-```
-
-Server implementations mirror grpc-java. Unary methods can override the direct
-request-to-response method generated by Scala, but streaming methods override
-observer-based methods and must call `onNext`, `onError`, and `onCompleted`
-according to grpc-java lifecycle rules.
-
-Applications compiling generated Scala gRPC companions must provide grpc-java
-dependencies such as `grpc-api`, `grpc-stub`, and a transport like
-`grpc-netty-shaded`. The `fory-scala` artifact does not add grpc-java as a hard
-dependency.
-
-## Cross-Language Notes
-
-### Type ID Behavior
-
-- Explicit `[id=...]` values are used directly by generated module installation or registration helpers.
-- When type IDs are omitted, generated code uses computed numeric IDs (see `auto_id.*` outputs).
-- If `option enable_auto_type_id = false;` is set, generated module installation or registration helpers use name-based APIs instead of numeric IDs.
-
-### Nested Type Shape
-
-| Language | Nested type form |
-| --------------------- | ------------------------------ |
-| Java | `Person.PhoneNumber` |
-| Python | `Person.PhoneNumber` |
-| Rust | `person::PhoneNumber` |
-| C++ | `Person::PhoneNumber` |
-| Go | `Person_PhoneNumber` (default) |
-| C# | `Person.PhoneNumber` |
-| JavaScript/TypeScript | `Person.PhoneNumber` |
-| Swift | `Person.PhoneNumber` |
-| Dart | `Person_PhoneNumber` |
-| Kotlin | `PersonPhoneNumber` |
-| Scala | `Person.PhoneNumber` |
-
-### Byte Helper Naming
-
-| Language | Helpers |
-| --------------------- | ------------------------- |
-| Java | `toBytes` / `fromBytes` |
-| Kotlin | `toBytes` / `fromBytes` |
-| Scala | `toBytes` / `fromBytes` |
-| Python | `to_bytes` / `from_bytes` |
-| Rust | `to_bytes` / `from_bytes` |
-| C++ | `to_bytes` / `from_bytes` |
-| Go | `ToBytes` / `FromBytes` |
-| C# | `ToBytes` / `FromBytes` |
-| JavaScript/TypeScript | (via `fory.serialize()`) |
-| Swift | `toBytes` / `fromBytes` |
-| Dart | (via `fory.serialize()`) |
diff --git a/docs/guide/swift/_category_.json b/docs/compiler/generated-code/_category_.json
similarity index 68%
copy from docs/guide/swift/_category_.json
copy to docs/compiler/generated-code/_category_.json
index dfdaf5996..3ad310cd3 100644
--- a/docs/guide/swift/_category_.json
+++ b/docs/compiler/generated-code/_category_.json
@@ -1,6 +1,6 @@
{
- "label": "Swift",
"position": 8,
+ "label": "Generated Code",
"collapsible": true,
"collapsed": true
}
diff --git a/docs/compiler/generated-code/cpp.md b/docs/compiler/generated-code/cpp.md
new file mode 100644
index 000000000..c2646b000
--- /dev/null
+++ b/docs/compiler/generated-code/cpp.md
@@ -0,0 +1,158 @@
+---
+title: C++
+sidebar_position: 5
+id: cpp
+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.
+---
+
+## Output Layout
+
+C++ output is one header per schema file, for example:
+
+- `<cpp_out>/addressbook.h`
+
+## Type Generation
+
+Messages generate `final` classes with typed accessors and byte helpers:
+
+```cpp
+class Person final {
+ public:
+ class PhoneNumber final {
+ public:
+ const std::string& number() const;
+ std::string* mutable_number();
+ template <class Arg, class... Args>
+ void set_number(Arg&& arg, Args&&... args);
+
+ fory::Result<std::vector<uint8_t>, fory::Error> to_bytes() const;
+ static fory::Result<PhoneNumber, fory::Error> from_bytes(
+ const uint8_t* data, std::size_t size);
+ static fory::Result<PhoneNumber, fory::Error> from_bytes(const std::vector<uint8_t>& data);
+ };
+
+ const std::string& name() const;
+ std::string* mutable_name();
+ template <class Arg, class... Args>
+ void set_name(Arg&& arg, Args&&... args);
+
+ const Animal& pet() const;
+ Animal* mutable_pet();
+};
+```
+
+Optional message fields generate `has_xxx`, `mutable_xxx`, and `clear_xxx` APIs:
+
+```cpp
+class Envelope final {
+ public:
+ bool has_payload() const { return payload_ != nullptr; }
+ const Envelope::Payload& payload() const { return *payload_; }
+ Envelope::Payload* mutable_payload() {
+ if (!payload_) {
+ payload_ = std::make_unique<Envelope::Payload>();
+ }
+ return payload_.get();
+ }
+ void clear_payload() { payload_.reset(); }
+
+ private:
+ std::unique_ptr<Envelope::Payload> payload_;
+};
+```
+
+Unions generate `std::variant` wrappers:
+
+```cpp
+class Animal final {
+ public:
+ enum class AnimalCase : uint32_t {
+ DOG = 1,
+ CAT = 2,
+ };
+
+ static Animal dog(Dog v);
+ static Animal cat(Cat v);
+
+ AnimalCase animal_case() const noexcept;
+ uint32_t animal_case_id() const noexcept;
+
+ bool is_dog() const noexcept;
+ const Dog* as_dog() const noexcept;
+ Dog* as_dog() noexcept;
+ const Dog& dog() const;
+ Dog& dog();
+
+ template <class Visitor>
+ decltype(auto) visit(Visitor&& vis) const;
+
+ private:
+ std::variant<Dog, Cat> value_;
+};
+```
+
+Generated headers include `FORY_UNION`, `FORY_ENUM`, and `FORY_STRUCT` macros
+for serialization metadata. Field and payload configuration is embedded in the
+generated `FORY_STRUCT`/`FORY_UNION` entries.
+
+## Registration
+
+Generated registration function:
+
+```cpp
+inline void register_types(fory::serialization::BaseFory& fory) {
+ fory.register_union<Animal>(106);
+ fory.register_enum<Person::PhoneType>(101);
+ fory.register_struct<Person::PhoneNumber>(102);
+ fory.register_struct<Person>(100);
+ fory.register_struct<Dog>(104);
+ fory.register_struct<Cat>(105);
+ fory.register_struct<AddressBook>(103);
+}
+```
+
+For schemas without explicit `[id=...]`, generated registration uses computed numeric IDs:
+
+```cpp
+fory.register_enum<Status>(1124725126);
+fory.register_union<Wrapper>(1471345060);
+fory.register_struct<Envelope>(3022445236);
+fory.register_union<Envelope::Detail>(1609214087);
+fory.register_struct<Envelope::Payload>(2862577837);
+```
+
+If `option enable_auto_type_id = false;` is set:
+
+```cpp
+fory.register_struct<Config>("myapp.models.Config");
+fory.register_union<Holder>("myapp.models.Holder");
+```
+
+## Usage
+
+```cpp
+addressbook::Person person;
+person.set_name("Alice");
+*person.mutable_pet() = addressbook::Animal::dog(addressbook::Dog{});
+
+auto bytes = person.to_bytes();
+auto restored = addressbook::Person::from_bytes(bytes.value());
+```
+
+## gRPC Service Companions
+
+With `--grpc`, C++ emits `<stem>.service.h`, `<stem>.service.grpc.h`, and `<stem>.service.grpc.cc`. The API header contains synchronous interfaces and route constants under `::<namespace>::service`; the binding header contains generated `grpc::SerializationTraits` plus client and server wrappers under `::<namespace>::service::grpc`. See [C++ gRPC](../../grpc/cpp.md) for build and usage guidance.
diff --git a/docs/compiler/generated-code/csharp.md b/docs/compiler/generated-code/csharp.md
new file mode 100644
index 000000000..431c8b08c
--- /dev/null
+++ b/docs/compiler/generated-code/csharp.md
@@ -0,0 +1,109 @@
+---
+title: C#
+sidebar_position: 7
+id: csharp
+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.
+---
+
+## Output Layout
+
+C# output is one `.cs` file per schema, for example:
+
+- `<csharp_out>/addressbook/Addressbook.cs`
+
+The C# model file name uses the normalized PascalCase source file stem. For
+example, `service.fdl` generates `Service.cs`, `order-events.fdl` generates
+`OrderEvents.cs`, and `123-schema.fdl` generates `Schema123Schema.cs`.
+
+## Type Generation
+
+Messages generate `[ForyStruct]` classes with C# properties and byte helpers:
+
+```csharp
+[ForyStruct]
+public sealed partial class Person
+{
+ public string Name { get; set; } = string.Empty;
+ public int Id { get; set; }
+ public List<Person.PhoneNumber> Phones { get; set; } = new();
+ public Animal Pet { get; set; } = null!;
+
+ public byte[] ToBytes() { ... }
+ public static Person FromBytes(byte[] data) { ... }
+}
+```
+
+Unions generate `[ForyUnion]` ADTs. `Unknown(UnknownCase)` is the
+Fory-provided forward-compatibility carrier marked with `[ForyUnknownCase]`.
+The marker only selects the carrier and does not add an entry to the schema case
+table. Schema-defined cases use non-negative `[ForyCase]` IDs. If a case needs
+non-default schema encoding, the generated `[ForyCase]` carries `Type`. Known
+case record names are PascalCase FDL case names; payload types are emitted as
+qualified references when needed to avoid name conflicts. A typed union must
+have at least one non-`Unknown` case.
+
+```csharp
+[ForyUnion]
+public abstract partial record Animal
+{
+ private Animal() {}
+
+ [ForyUnknownCase]
+ public sealed partial record Unknown(UnknownCase Value) : Animal;
+
+ [ForyCase(0)]
+ public sealed partial record Dog(global::addressbook.Dog Value) : Animal;
+
+ [ForyCase(1)]
+ public sealed partial record Cat(global::addressbook.Cat Value) : Animal;
+}
+```
+
+## Module Installation
+
+Each schema generates a module class that installs imported modules first and
+then registers the local schema types:
+
+```csharp
+public static class AddressbookForyModule
+{
+ public static void Install(Fory fory)
+ {
+ fory.Register<addressbook.Animal>((uint)106);
+ fory.Register<addressbook.Person>((uint)100);
+ // ...
+ }
+}
+```
+
+The C# model file basename and module class both use the normalized source file
+stem. They do not use `csharp_namespace` and they do not use gRPC service names.
+For example, `service.fdl` generates `Service.cs` and `ServiceForyModule`,
+while `order-events.fdl` generates `OrderEvents.cs` and
+`OrderEventsForyModule`. A gRPC service named `Greeter` generates the service
+companion `GreeterGrpc.cs`; it does not change the schema module name. To get
+`GreeterForyModule`, name the schema file `greeter.fdl` or `Greeter.fdl`.
+
+This source-file rule lets several schemas target the same C# namespace without
+colliding. No namespace-derived or service-derived module alias is generated.
+
+When explicit type IDs are not provided, generated installation uses computed
+numeric IDs (same behavior as other targets).
+
+## gRPC Service Companions
+
+With `--grpc`, C# emits one `<ServiceName>Grpc.cs` per service. Its static service holder contains descriptors, `<ServiceName>Base`, `<ServiceName>Client`, and both `BindService` overloads. See [C# gRPC](../../grpc/csharp.md) for hosting, clients, dependencies, and streaming.
diff --git a/docs/compiler/generated-code/dart.md b/docs/compiler/generated-code/dart.md
new file mode 100644
index 000000000..6c1ac412c
--- /dev/null
+++ b/docs/compiler/generated-code/dart.md
@@ -0,0 +1,194 @@
+---
+title: Dart
+sidebar_position: 10
+id: dart
+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.
+---
+
+## Output Layout
+
+Dart output is two files per schema: a main `.dart` file with annotated types and the IDL module owner, and a `.fory.dart` part file with generated serializers and metadata.
+
+- `<dart_out>/package/package.dart`
+- `<dart_out>/package/package.fory.dart`
+
+## Type Generation
+
+Messages generate `@ForyStruct` annotated `final class` declarations with `@ForyField` on each field:
+
+```dart
+@ForyStruct()
+final class Person {
+ Person();
+
+ @ForyField(id: 1)
+ String name = '';
+
+ @ForyField(id: 2, type: Int32Type())
+ int id = 0;
+
+ @ForyField(id: 7)
+ List<Person_PhoneNumber> phones = <Person_PhoneNumber>[];
+
+ @ForyField(id: 8)
+ Animal pet = Animal._empty();
+}
+```
+
+Enums generate Dart `enum` declarations with a `rawValue` getter and `fromRawValue` factory:
+
+```dart
+enum Person_PhoneType {
+ mobile,
+ home,
+ work;
+
+ int get rawValue => switch (this) {
+ Person_PhoneType.mobile => 0,
+ Person_PhoneType.home => 1,
+ Person_PhoneType.work => 2,
+ };
+
+ static Person_PhoneType fromRawValue(int value) => switch (value) {
+ 0 => Person_PhoneType.mobile,
+ 1 => Person_PhoneType.home,
+ 2 => Person_PhoneType.work,
+ _ => throw StateError('Unknown Person_PhoneType raw value $value.'),
+ };
+}
+```
+
+Unions generate `@ForyUnion` annotated classes with factory constructors, a case enum, and a custom serializer:
+
+```dart
+enum AnimalCase {
+ dog,
+ cat;
+
+ int get id => switch (this) {
+ AnimalCase.dog => 1,
+ AnimalCase.cat => 2,
+ };
+}
+
+@ForyUnion()
+final class Animal {
+ final AnimalCase _case;
+ final Object? _value;
+
+ const Animal._(this._case, this._value);
+
+ factory Animal.dog(Dog value) => Animal._(AnimalCase.dog, value);
+ factory Animal.cat(Cat value) => Animal._(AnimalCase.cat, value);
+
+ bool get isDog => _case == AnimalCase.dog;
+ Dog get dogValue => _value as Dog;
+ // ...
+}
+```
+
+Nested types use flat underscore naming (e.g., `Person_PhoneNumber`, `Person_PhoneType`).
+
+`list<T>` fields generate ordered collection carriers and use the Fory list
+protocol. `array<T>` fields generate dense one-dimensional bool or numeric
+carriers and use the specialized dense-array protocol. Generated code must not
+choose `array<T>` only because a language has an optimized list-like carrier;
+the schema kind comes from the IDL.
+
+| IDL schema | Dart generated carrier | Notes |
+| ------------------- | ---------------------- | ------------------------------------------ |
+| `list<int32>` | `List<int>` | List protocol, varint element encoding |
+| `list<fixed int32>` | `List<int>` | List protocol, fixed-width element segment |
+| `array<bool>` | `BoolList` | One byte per bool |
+| `array<int8>` | `Int8List` | Dense signed bytes |
+| `array<int16>` | `Int16List` | Dense little-endian int16 |
+| `array<int32>` | `Int32List` | Dense little-endian int32 |
+| `array<int64>` | `Int64List` | Dense little-endian int64 |
+| `array<uint8>` | `Uint8List` | Dense unsigned bytes |
+| `array<uint16>` | `Uint16List` | Dense little-endian uint16 |
+| `array<uint32>` | `Uint32List` | Dense little-endian uint32 |
+| `array<uint64>` | `Uint64List` | Dense little-endian uint64 |
+| `array<float16>` | `Float16List` | Dense binary16 storage |
+| `array<bfloat16>` | `Bfloat16List` | Dense bfloat16 storage |
+| `array<float32>` | `Float32List` | Dense little-endian float32 |
+| `array<float64>` | `Float64List` | Dense little-endian float64 |
+
+Generated Dart fields that use `ArrayType(element: BoolType())` must use
+`BoolList`; plain `List<bool>` remains the generated and handwritten carrier
+for `list<bool>`.
+
+Reference tracking on list elements or map values uses the container sugar annotations:
+
+```dart
+@ListField(element: DeclaredType(ref: true))
+@ForyField(id: 3)
+List<Node> children = <Node>[];
+
+@MapField(value: DeclaredType(ref: true))
+@ForyField(id: 2)
+Map<String, Node> byName = <String, Node>{};
+```
+
+## Module Installation
+
+Each generated Dart IDL library includes a module owner named after the input
+file, such as `AddressbookForyModule` for `addressbook.dart`. The module
+installs imported modules first and then registers every local schema type with
+its default IDL identity:
+
+```dart
+abstract final class AddressbookForyModule {
+ static void install(Fory fory) {
+ complex_pb.ComplexPbForyModule.install(fory);
+ _registerType(fory, Person);
+ _registerType(fory, Dog);
+ }
+
+ static Fory getFory() { ... }
+
+ static void _registerType(Fory fory, Type type) {
+ if (type == Person) {
+ registerGeneratedStruct(fory, _personForySchema, id: 100, namespace: null, typeName: null);
+ return;
+ }
+ // ... other types
+ }
+}
+```
+
+## Usage
+
+```dart
+import 'package:fory/fory.dart';
+import 'generated/addressbook/addressbook.dart';
+
+void main() {
+ final fory = Fory();
+ AddressbookForyModule.install(fory);
+
+ final person = Person()
+ ..name = 'Alice'
+ ..id = 1;
+
+ final bytes = fory.serialize(person);
+ final roundTrip = fory.deserialize<Person>(bytes);
+}
+```
+
+## gRPC Service Companions
+
+With `--grpc`, Dart emits one `<stem>_grpc.dart` per schema containing `<Service>Client` and `<Service>ServiceBase`. The service base registers its own method descriptors, so no separate registrar is generated. See [Dart gRPC](../../grpc/dart.md) for generated module naming, Fory installation, and usage.
diff --git a/docs/compiler/generated-code/go.md b/docs/compiler/generated-code/go.md
new file mode 100644
index 000000000..96033254b
--- /dev/null
+++ b/docs/compiler/generated-code/go.md
@@ -0,0 +1,148 @@
+---
+title: Go
+sidebar_position: 6
+id: go
+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.
+---
+
+## Output Layout
+
+Go output path depends on schema options and `--go_out`.
+
+For `addressbook.fdl`, `go_package` is configured and generated output follows the configured import path/package (for example under your `--go_out` root).
+
+Without `go_package`, output uses the requested `--go_out` directory and package-derived file naming.
+
+## Type Generation
+
+Nested types use underscore naming by default (`Person_PhoneType`, `Person_PhoneNumber`):
+
+```go
+type Person_PhoneType int32
+
+const (
+ Person_PhoneTypeMobile Person_PhoneType = 0
+ Person_PhoneTypeHome Person_PhoneType = 1
+ Person_PhoneTypeWork Person_PhoneType = 2
+)
+
+type Person_PhoneNumber struct {
+ Number string `fory:"id=1"`
+ PhoneType Person_PhoneType `fory:"id=2"`
+}
+```
+
+Messages generate structs with `fory` tags and byte helpers:
+
+```go
+type Person struct {
+ Name string `fory:"id=1"`
+ Id int32 `fory:"id=2"`
+ Phones []Person_PhoneNumber `fory:"id=7,type=list"`
+ Pet Animal `fory:"id=8"`
+}
+
+func (m *Person) ToBytes() ([]byte, error) { ... }
+func (m *Person) FromBytes(data []byte) error { ... }
+```
+
+Unions generate typed case structs with constructors/accessors/visitor APIs:
+
+```go
+type AnimalCase uint32
+
+type Animal struct {
+ case_ AnimalCase
+ value any
+}
+
+func DogAnimal(v *Dog) Animal { ... }
+func CatAnimal(v *Cat) Animal { ... }
+
+func (u Animal) Case() AnimalCase { ... }
+func (u Animal) AsDog() (*Dog, bool) { ... }
+func (u Animal) Visit(visitor AnimalVisitor) error { ... }
+```
+
+## Registration
+
+Generated registration function:
+
+```go
+func RegisterTypes(f *fory.Fory) error {
+ if err := f.RegisterUnion(Animal{}, 106, fory.NewUnionSerializer(...)); err != nil {
+ return err
+ }
+ if err := f.RegisterEnum(Person_PhoneType(0), 101); err != nil {
+ return err
+ }
+ if err := f.RegisterStruct(Person_PhoneNumber{}, 102); err != nil {
+ return err
+ }
+ if err := f.RegisterStruct(Person{}, 100); err != nil {
+ return err
+ }
+ return nil
+}
+```
+
+For schemas without explicit `[id=...]`, generated registration uses computed numeric IDs:
+
+```go
+if err := f.RegisterEnum(Status(0), 1124725126); err != nil { ... }
+if err := f.RegisterUnion(Wrapper{}, 1471345060, fory.NewUnionSerializer(...)); err != nil { ... }
+if err := f.RegisterStruct(Envelope{}, 3022445236); err != nil { ... }
+if err := f.RegisterUnion(Envelope_Detail{}, 1609214087, fory.NewUnionSerializer(...)); err != nil { ... }
+if err := f.RegisterStruct(Envelope_Payload{}, 2862577837); err != nil { ... }
+```
+
+If `option enable_auto_type_id = false;` is set:
+
+```go
+if err := f.RegisterStructByName(Config{}, "myapp.models.Config"); err != nil { ... }
+if err := f.RegisterUnionByName(Holder{}, "myapp.models.Holder", fory.NewUnionSerializer(...)); err != nil { ... }
+```
+
+`go_nested_type_style` controls nested type naming:
+
+```protobuf
+option go_nested_type_style = "camelcase";
+```
+
+The CLI flag `--go_nested_type_style` overrides this schema option when both are set.
+
+## Usage
+
+```go
+person := &Person{
+ Name: "Alice",
+ Pet: DogAnimal(&Dog{Name: "Rex"}),
+}
+
+data, err := person.ToBytes()
+if err != nil {
+ panic(err)
+}
+var restored Person
+if err := restored.FromBytes(data); err != nil {
+ panic(err)
+}
+```
+
+## gRPC Service Companions
+
+With `--grpc`, Go emits `<module>_grpc.go` containing the generated `CodecV2`, client and server interfaces, stream types, descriptors, and registration helpers. See [Go gRPC](../../grpc/go.md) for codec configuration and usage.
diff --git a/docs/compiler/generated-code/index.md b/docs/compiler/generated-code/index.md
new file mode 100644
index 000000000..bf76e92ae
--- /dev/null
+++ b/docs/compiler/generated-code/index.md
@@ -0,0 +1,176 @@
+---
+title: Generated Code
+sidebar_position: 1
+id: index
+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 document explains generated code for each target language.
+
+Fory IDL generated types are idiomatic in host languages and can be used directly as domain objects. Generated types also include `to/from bytes` helpers and schema modules or registration helpers, depending on the target language.
+
+Generated schema modules are named from the schema source file, not from the
+package or namespace. In targets that expose the module directly in a language
+package or namespace, names such as `AddressbookForyModule` or
+`ComplexPbForyModule` let multiple IDL files target the same package or
+namespace without producing colliding `ForyModule` types.
+
+## Reference Schemas
+
+The examples below use two real schemas:
+
+1. `addressbook.fdl` (explicit type IDs)
+2. `auto_id.fdl` (no explicit type IDs)
+
+### `addressbook.fdl` Excerpt
+
+```protobuf
+package addressbook;
+
+option go_package = "github.com/myorg/myrepo/gen/addressbook;addressbook";
+
+message Person [id=100] {
+ string name = 1;
+ int32 id = 2;
+
+ enum PhoneType [id=101] {
+ PHONE_TYPE_MOBILE = 0;
+ PHONE_TYPE_HOME = 1;
+ PHONE_TYPE_WORK = 2;
+ }
+
+ message PhoneNumber [id=102] {
+ string number = 1;
+ PhoneType phone_type = 2;
+ }
+
+ list<PhoneNumber> phones = 7;
+ Animal pet = 8;
+}
+
+message Dog [id=104] {
+ string name = 1;
+ int32 bark_volume = 2;
+}
+
+message Cat [id=105] {
+ string name = 1;
+ int32 lives = 2;
+}
+
+union Animal [id=106] {
+ Dog dog = 1;
+ Cat cat = 2;
+}
+
+message AddressBook [id=103] {
+ list<Person> people = 1;
+ map<string, Person> people_by_name = 2;
+}
+```
+
+### `auto_id.fdl` Excerpt
+
+```protobuf
+package auto_id;
+
+enum Status {
+ UNKNOWN = 0;
+ OK = 1;
+}
+
+message Envelope {
+ string id = 1;
+
+ message Payload {
+ int32 value = 1;
+ }
+
+ union Detail {
+ Payload payload = 1;
+ string note = 2;
+ }
+
+ Payload payload = 2;
+ Detail detail = 3;
+ Status status = 4;
+}
+
+union Wrapper {
+ Envelope envelope = 1;
+ string raw = 2;
+}
+```
+
+## Cross-Language Notes
+
+### Type ID Behavior
+
+- Explicit `[id=...]` values are used directly by generated module installation or registration helpers.
+- When type IDs are omitted, generated code uses computed numeric IDs (see `auto_id.*` outputs).
+- If `option enable_auto_type_id = false;` is set, generated module installation or registration helpers use name-based APIs instead of numeric IDs.
+
+### Nested Type Shape
+
+| Language | Nested type form |
+| --------------------- | ------------------------------ |
+| Java | `Person.PhoneNumber` |
+| Python | `Person.PhoneNumber` |
+| Rust | `person::PhoneNumber` |
+| C++ | `Person::PhoneNumber` |
+| Go | `Person_PhoneNumber` (default) |
+| C# | `Person.PhoneNumber` |
+| JavaScript/TypeScript | `Person.PhoneNumber` |
+| Swift | `Person.PhoneNumber` |
+| Dart | `Person_PhoneNumber` |
+| Kotlin | `PersonPhoneNumber` |
+| Scala | `Person.PhoneNumber` |
+
+### Byte Helper Naming
+
+| Language | Helpers |
+| --------------------- | ------------------------- |
+| Java | `toBytes` / `fromBytes` |
+| Kotlin | `toBytes` / `fromBytes` |
+| Scala | `toBytes` / `fromBytes` |
+| Python | `to_bytes` / `from_bytes` |
+| Rust | `to_bytes` / `from_bytes` |
+| C++ | `to_bytes` / `from_bytes` |
+| Go | `ToBytes` / `FromBytes` |
+| C# | `ToBytes` / `FromBytes` |
+| JavaScript/TypeScript | (via `fory.serialize()`) |
+| Swift | `toBytes` / `fromBytes` |
+| Dart | (via `fory.serialize()`) |
+
+## Runtime References
+
+Choose the generated-code reference for the output runtime. Generated models remain ordinary
+runtime-owned types and use that runtime's supported Fory serialization APIs.
+
+| Runtime | Generated-code reference |
+| --------------------- | -------------------------------------- |
+| Java | [Java](java.md) |
+| Python | [Python](python.md) |
+| C++ | [C++](cpp.md) |
+| Go | [Go](go.md) |
+| Rust | [Rust](rust.md) |
+| JavaScript/TypeScript | [JavaScript/TypeScript](javascript.md) |
+| C# | [C#](csharp.md) |
+| Swift | [Swift](swift.md) |
+| Dart | [Dart](dart.md) |
+| Scala | [Scala](scala.md) |
+| Kotlin | [Kotlin](kotlin.md) |
diff --git a/docs/compiler/generated-code/java.md b/docs/compiler/generated-code/java.md
new file mode 100644
index 000000000..43f532692
--- /dev/null
+++ b/docs/compiler/generated-code/java.md
@@ -0,0 +1,150 @@
+---
+title: Java
+sidebar_position: 2
+id: java
+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.
+---
+
+## Output Layout
+
+For `package addressbook`, Java output is generated under:
+
+- `<java_out>/addressbook/`
+- Type files: `AddressBook.java`, `Person.java`, `Dog.java`, `Cat.java`, `Animal.java`
+- Schema module: `AddressbookForyModule.java`
+
+For schemas without a Java package, the schema module name is derived from the
+source file stem, for example `main.fdl` generates `MainForyModule.java`.
+Java import graphs cannot mix default-package schemas with named Java packages.
+
+## Type Generation
+
+Messages generate Java classes with `@ForyField`, default constructors, getters/setters, and byte helpers:
+
+```java
+public class Person {
+ public static enum PhoneType {
+ MOBILE,
+ HOME,
+ WORK;
+ }
+
+ public static class PhoneNumber {
+ @ForyField(id = 1)
+ private String number;
+
+ @ForyField(id = 2)
+ private PhoneType phoneType;
+
+ public byte[] toBytes() { ... }
+ public static PhoneNumber fromBytes(byte[] bytes) { ... }
+ }
+
+ @ForyField(id = 1)
+ private String name;
+
+ @ForyField(id = 8)
+ private Animal pet;
+
+ public byte[] toBytes() { ... }
+ public static Person fromBytes(byte[] bytes) { ... }
+}
+```
+
+Messages with `evolving=false` are generated with Java fixed-schema struct encoding.
+
+Unions generate classes extending `org.apache.fory.type.union.Union`:
+
+```java
+public final class Animal extends Union {
+ public enum AnimalCase {
+ DOG(1),
+ CAT(2);
+ public final int id;
+ AnimalCase(int id) { this.id = id; }
+ }
+
+ public static Animal ofDog(Dog v) { ... }
+ public AnimalCase getAnimalCase() { ... }
+ public int getAnimalCaseId() { ... }
+
+ public boolean hasDog() { ... }
+ public Dog getDog() { ... }
+ public void setDog(Dog v) { ... }
+}
+```
+
+## Schema Module
+
+Each JVM schema generates a `ForyModule`. Imported schema modules are installed
+through `fory.register(...)`, so shared imports are deduplicated by the Fory instance.
+
+```java
+public final class AddressbookForyModule implements org.apache.fory.ForyModule {
+ public static final AddressbookForyModule INSTANCE = new AddressbookForyModule();
+
+ static ThreadSafeFory getFory() { ... }
+
+ @Override
+ public void install(Fory fory) {
+ org.apache.fory.resolver.TypeResolver resolver = fory.getTypeResolver();
+ resolver.registerUnion(Animal.class, 106L, new org.apache.fory.serializer.UnionSerializer(resolver, Animal.class));
+ resolver.register(Person.class, 100L);
+ resolver.register(Person.PhoneType.class, 101L);
+ resolver.register(Person.PhoneNumber.class, 102L);
+ resolver.register(Dog.class, 104L);
+ resolver.register(Cat.class, 105L);
+ resolver.register(AddressBook.class, 103L);
+ }
+}
+```
+
+For schemas without explicit `[id=...]`, generated registration uses computed numeric IDs (for example from `auto_id.fdl`):
+
+```java
+resolver.register(Status.class, 1124725126L);
+resolver.registerUnion(Wrapper.class, 1471345060L, new org.apache.fory.serializer.UnionSerializer(resolver, Wrapper.class));
+resolver.register(Envelope.class, 3022445236L);
+resolver.registerUnion(Envelope.Detail.class, 1609214087L, new org.apache.fory.serializer.UnionSerializer(resolver, Envelope.Detail.class));
+resolver.register(Envelope.Payload.class, 2862577837L);
+```
+
+If `option enable_auto_type_id = false;` is set, registration uses symbolic names:
+
+```java
+resolver.register(Config.class, "myapp.models", "Config");
+resolver.registerUnion(
+ Holder.class,
+ "myapp.models",
+ "Holder",
+ new org.apache.fory.serializer.UnionSerializer(resolver, Holder.class));
+```
+
+## Usage
+
+```java
+Person person = new Person();
+person.setName("Alice");
+person.setPet(Animal.ofDog(new Dog()));
+
+byte[] data = person.toBytes();
+Person restored = Person.fromBytes(data);
+```
+
+## gRPC Service Companions
+
+With `--grpc`, Java emits one `<ServiceName>Grpc.java` per service. It exposes `SERVICE_NAME`, service and method descriptors, `<ServiceName>ImplBase`, and async, blocking, and future stub factories. See [Java gRPC](../../grpc/java.md) for dependencies and server, client, and streaming usage.
diff --git a/docs/compiler/generated-code/javascript.md b/docs/compiler/generated-code/javascript.md
new file mode 100644
index 000000000..a4a39959e
--- /dev/null
+++ b/docs/compiler/generated-code/javascript.md
@@ -0,0 +1,94 @@
+---
+title: JavaScript/TypeScript
+sidebar_position: 8
+id: javascript
+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.
+---
+
+## Output Layout
+
+JavaScript/TypeScript output is one `.ts` file per schema, for example:
+
+- `<javascript_out>/addressbook.ts`
+
+When the schema contains services, JavaScript can also emit service companions:
+
+- `<javascript_out>/addressbook_grpc.ts` with `--grpc`
+- `<javascript_out>/addressbook_grpc_web.ts` with `--grpc-web`
+
+## Type Generation
+
+Messages generate `export interface` declarations with camelCase field names:
+
+```typescript
+export interface Person {
+ name: string;
+ id: number;
+ phones: PhoneNumber[];
+ pet?: Animal | null;
+}
+```
+
+Enums generate `export enum` declarations:
+
+```typescript
+export enum PhoneType {
+ MOBILE = 0,
+ HOME = 1,
+ WORK = 2,
+}
+```
+
+Unions generate a discriminated union with a case enum:
+
+```typescript
+export enum AnimalCase {
+ DOG = 1,
+ CAT = 2,
+}
+
+export type Animal =
+ { case: AnimalCase.DOG; value: Dog } | { case: AnimalCase.CAT; value: Cat };
+```
+
+## Schema Helpers
+
+Each generated model file exports a registration helper for custom `Fory`
+instances and root serialization helpers. The public API looks like:
+
+```typescript
+import type Fory, { Serializer } from "@apache-fory/core";
+
+export function registerAddressbookTypes(fory: Fory): {
+ person: {
+ serialize: (value: Person | null) => Uint8Array;
+ deserialize: (bytes: Uint8Array) => Person;
+ serializer: Serializer;
+ };
+};
+export const serializePerson: (value: Person | null) => Uint8Array;
+export const deserializePerson: (bytes: Uint8Array) => Person;
+```
+
+Imported schema modules are registered automatically by `registerXxxTypes(fory)`.
+Use `serializeX` and `deserializeX` for the generated default serialization
+path. Call `registerXxxTypes(fory)` when the application manages its own `Fory`
+instance. Generated gRPC companions import the generated helpers automatically.
+
+## gRPC Service Companions
+
+`--grpc` emits `<module>_grpc.ts` with service and path constants, handler interfaces, service-definition and registration helpers, and both `<Service>Client` classes and `create<Service>Client` factories. `--grpc-web` emits `<module>_grpc_web.ts` with callback clients and, for unary RPCs, promise clients; both classes and factory functions are exported. See [JavaScript gRPC](../../grpc/javascript.md) for Node.js and browser usage.
diff --git a/docs/compiler/generated-code/kotlin.md b/docs/compiler/generated-code/kotlin.md
new file mode 100644
index 000000000..910ddbf00
--- /dev/null
+++ b/docs/compiler/generated-code/kotlin.md
@@ -0,0 +1,161 @@
+---
+title: Kotlin
+sidebar_position: 11
+id: kotlin
+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.
+---
+
+The Kotlin target emits Kotlin source only. The compiler does not generate Java
+files.
+
+## Output Layout
+
+For source file `addressbook.fdl` with `package addressbook`, Kotlin output is
+generated under:
+
+- `<kotlin_out>/addressbook/`
+- Type files: `AddressBook.kt`, `Person.kt`, `Dog.kt`, `Cat.kt`, `Animal.kt`
+- Schema module: `AddressbookForyModule.kt`
+
+The schema module name is derived from the source file stem. Schemas in the same
+Kotlin package need distinct generated file names; duplicate generated Kotlin
+file paths are rejected before files are written.
+
+If `option kotlin_package = "...";` is present, the output path and Kotlin
+package use that option. Otherwise Kotlin uses the FDL package. A Kotlin import
+graph cannot mix default-package schemas with named Kotlin packages.
+Registration still uses the FDL package so cross-language type names stay
+stable.
+
+## Type Generation
+
+Messages generate Kotlin `data class` declarations by default:
+
+```kotlin
+@ForyStruct
+public data class Person(
+ @field:ForyField(id = 1)
+ public val name: String,
+
+ @field:ForyField(id = 7)
+ public val phones: List<PersonPhoneNumber>,
+
+ @field:ForyField(id = 8)
+ public val pet: Animal,
+) {
+ public fun toBytes(): ByteArray = AddressbookForyModule.getFory().serialize(this)
+
+ public companion object {
+ public fun fromBytes(bytes: ByteArray): Person =
+ AddressbookForyModule.getFory().deserialize(bytes, Person::class.java)
+ }
+}
+```
+
+Messages that participate in compiler-detected construction cycles generate
+normal mutable classes so the generated serializer can publish the instance
+before reading back-references:
+
+```kotlin
+@ForyStruct
+public class Node() {
+ @ForyField(id = 1)
+ public var id: String = ""
+
+ @Ref
+ @ForyField(id = 2)
+ public var parent: Node? = null
+}
+```
+
+Generated Kotlin IDL sources express nullability with Kotlin `?`, not Fory
+`@Nullable`, including mutable classes emitted for compiler-detected
+construction cycles.
+
+Enums generate Kotlin enum classes with stable Fory enum IDs. Unions generate
+sealed classes with `@ForyUnion`; the Fory-provided `Unknown(UnknownCase)`
+carrier is marked with `@ForyUnknownCase`. The marker only selects the carrier
+and does not add an entry to the schema case table. Schema-defined cases may use
+case IDs `0..N` and hold a single `value` property. A typed union must have at
+least one non-`Unknown` case.
+
+```kotlin
+package addressbook
+
+import org.apache.fory.annotation.ForyCase
+import org.apache.fory.annotation.ForyUnion
+import org.apache.fory.annotation.ForyUnknownCase
+import org.apache.fory.type.union.UnknownCase
+
+@ForyUnion
+public sealed class Animal {
+ @ForyUnknownCase
+ public data class Unknown(public val value: UnknownCase) : Animal()
+
+ @ForyCase(id = 0)
+ public data class Dog(public val value: addressbook.Dog) : Animal()
+}
+```
+
+Packaged Kotlin output keeps the schema case name and qualifies the payload
+type when both have the same simple name. If a target output mode cannot express
+a legal qualifier for a conflict, the compiler appends `Case` to the generated
+case class name.
+
+Kotlin `int32`, `int64`, `uint32`, and `uint64` fields use xlang varint
+encoding by default, so generated Kotlin does not emit `@VarInt` for the
+default case. It emits `@Fixed` or `@Tagged` only when the schema requests that
+non-default encoding. `duration` maps to `kotlin.time.Duration`, and infinite
+durations are rejected when encoded. Dense `array<float16>` and
+`array<bfloat16>` use the Java core `Float16Array` and `BFloat16Array`
+carriers. Generated Kotlin IDL uses `@ArrayType ByteArray` for `array<int8>`,
+including nested positions.
+
+## Schema Module
+
+Generated schema modules register schema types and resolve KSP-generated
+serializers from the target class name. The package-owned helper Fory instance uses
+`ForyKotlin.builder().withXlang(true)` with the schema module installed, so message
+`toBytes`/`fromBytes` helpers work without caller-managed Fory setup. For
+`addressbook.fdl`:
+
+```kotlin
+public object AddressbookForyModule : ForyModule {
+ private val fory: ThreadSafeFory by lazy {
+ ForyKotlin.builder()
+ .withXlang(true)
+ .withRefTracking(true)
+ .withModule(this)
+ .buildThreadSafeFory()
+ }
+
+ internal fun getFory(): ThreadSafeFory = fory
+
+ override fun install(fory: Fory) {
+ KotlinSerializers.registerType(fory, Person::class.java, 100L)
+ KotlinSerializers.registerSerializer(fory, Person::class.java)
+ KotlinSerializers.registerUnion(fory, Animal::class.java, 106L)
+ }
+}
+```
+
+`registerUnion` discovers the generated `<Target>_ForySerializer`; callers do
+not pass a serializer instance.
+
+## gRPC Service Companions
+
+With `--grpc`, Kotlin emits one `<ServiceName>GrpcKt.kt` per service, not a Java companion. The generated object exposes `SERVICE_NAME`, service and method descriptors, `<ServiceName>CoroutineImplBase`, and `<ServiceName>CoroutineStub`. See [Kotlin gRPC](../../grpc/kotlin.md) for dependencies, KSP setup, and coroutine and `Flow` usage.
diff --git a/docs/compiler/generated-code/python.md b/docs/compiler/generated-code/python.md
new file mode 100644
index 000000000..c27120e43
--- /dev/null
+++ b/docs/compiler/generated-code/python.md
@@ -0,0 +1,120 @@
+---
+title: Python
+sidebar_position: 3
+id: python
+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.
+---
+
+## Output Layout
+
+Python output is one module per schema file, for example:
+
+- `<python_out>/addressbook.py`
+
+## Type Generation
+
+Unions generate a case enum plus a `Union` subclass with typed helpers:
+
+```python
+class AnimalCase(Enum):
+ DOG = 1
+ CAT = 2
+
+class Animal(Union):
+ @classmethod
+ def dog(cls, v: Dog) -> "Animal": ...
+
+ def case(self) -> AnimalCase: ...
+ def case_id(self) -> int: ...
+
+ def is_dog(self) -> bool: ...
+ def dog_value(self) -> Dog: ...
+ def set_dog(self, v: Dog) -> None: ...
+```
+
+Messages generate `@pyfory.dataclass` types, and nested types stay nested:
+
+```python
+@pyfory.dataclass
+class Person:
+ class PhoneType(IntEnum):
+ MOBILE = 0
+ HOME = 1
+ WORK = 2
+
+ @pyfory.dataclass
+ class PhoneNumber:
+ number: str = pyfory.field(id=1, default="")
+ phone_type: Person.PhoneType = pyfory.field(id=2, default=None)
+
+ name: str = pyfory.field(id=1, default="")
+ phones: List[Person.PhoneNumber] = pyfory.field(id=7, default_factory=list)
+ pet: Animal = pyfory.field(id=8, default=None)
+
+ def to_bytes(self) -> bytes: ...
+ @classmethod
+ def from_bytes(cls, data: bytes) -> "Person": ...
+```
+
+## Registration
+
+Generated registration function:
+
+```python
+def register_addressbook_types(fory: pyfory.Fory):
+ fory.register_union(Animal, type_id=106, serializer=AnimalSerializer(fory))
+ fory.register_type(Person, type_id=100)
+ fory.register_type(Person.PhoneType, type_id=101)
+ fory.register_type(Person.PhoneNumber, type_id=102)
+ fory.register_type(Dog, type_id=104)
+ fory.register_type(Cat, type_id=105)
+ fory.register_type(AddressBook, type_id=103)
+```
+
+For schemas without explicit `[id=...]`, generated registration uses computed numeric IDs:
+
+```python
+fory.register_type(Status, type_id=1124725126)
+fory.register_union(Wrapper, type_id=1471345060, serializer=WrapperSerializer(fory))
+fory.register_type(Envelope, type_id=3022445236)
+fory.register_union(Envelope.Detail, type_id=1609214087, serializer=Envelope.DetailSerializer(fory))
+fory.register_type(Envelope.Payload, type_id=2862577837)
+```
+
+If `option enable_auto_type_id = false;` is set:
+
+```python
+fory.register_type(Config, name="myapp.models.Config")
+fory.register_union(
+ Holder,
+ name="myapp.models.Holder",
+ serializer=HolderSerializer(fory),
+)
+```
+
+## Usage
+
+```python
+person = Person(name="Alice", pet=Animal.dog(Dog(name="Rex", bark_volume=10)))
+
+data = person.to_bytes()
+restored = Person.from_bytes(data)
+```
+
+## gRPC Service Companions
+
+With `--grpc`, Python emits `<module>_grpc.py` with stubs, servicer bases, and registration functions. Async `grpc.aio` is the default; `--grpc-python-mode=sync` keeps the same filename and public names. See [Python gRPC](../../grpc/python.md) for dependencies and usage.
diff --git a/docs/compiler/generated-code/rust.md b/docs/compiler/generated-code/rust.md
new file mode 100644
index 000000000..f150a5c98
--- /dev/null
+++ b/docs/compiler/generated-code/rust.md
@@ -0,0 +1,154 @@
+---
+title: Rust
+sidebar_position: 4
+id: rust
+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.
+---
+
+## Output Layout
+
+Rust output is one module file per schema, for example:
+
+- `<rust_out>/addressbook.rs`
+
+When `--grpc` is used and the schema contains services, Rust also emits:
+
+- `<rust_out>/addressbook_service.rs`
+- `<rust_out>/addressbook_service_grpc.rs`
+
+## Type Generation
+
+Unions map to Rust enums with `#[fory(id = ...)]` schema case attributes.
+`#[fory(unknown)] Unknown(::fory::UnknownCase)` marks the Fory-provided
+forward-compatibility carrier. The marker only selects the carrier and does not
+add an entry to the schema case table; schema cases still use the full `0..N`
+ID range. A generated typed union must have at least one non-`Unknown` case. The
+compiler marks the first declared non-`Unknown` case as `#[fory(default)]`.
+When that case's payload implements Rust's standard `Default` trait, the
+compiler also emits a standard `Default` implementation from that case:
+
+```rust
+#[derive(::fory::ForyUnion, Clone, Debug, PartialEq, Eq, Hash)]
+pub enum Animal {
+ #[fory(unknown)]
+ Unknown(::fory::UnknownCase),
+ #[fory(id = 0, default)]
+ Dog(self::Dog),
+ #[fory(id = 1)]
+ Cat(self::Cat),
+}
+
+impl ::std::default::Default for Animal {
+ fn default() -> Self {
+ Self::Dog(<self::Dog as ::std::default::Default>::default())
+ }
+}
+```
+
+If the selected payload does not implement standard `Default`, such as an
+`any` payload, the generated union has no infallible `Default`
+implementation. This model-level default is independent of Fory's fallible
+deserialization default, which reconstructs the selected case through its
+codecs and active read context.
+
+Nested types generate nested modules:
+
+```rust
+pub mod person {
+ #[derive(ForyEnum, Debug, Clone, PartialEq, Default)]
+ #[repr(i32)]
+ pub enum PhoneType {
+ #[default]
+ Mobile = 0,
+ Home = 1,
+ Work = 2,
+ }
+
+ #[derive(ForyStruct, Debug, Clone, PartialEq, Default)]
+ pub struct PhoneNumber {
+ #[fory(id = 1)]
+ pub number: String,
+ #[fory(id = 2)]
+ pub phone_type: PhoneType,
+ }
+}
+```
+
+Messages derive `ForyStruct` and include `to_bytes`/`from_bytes` helpers:
+
+```rust
+#[derive(ForyStruct, Debug, Clone, PartialEq, Default)]
+pub struct Person {
+ #[fory(id = 1)]
+ pub name: String,
+ #[fory(id = 7)]
+ pub phones: Vec<person::PhoneNumber>,
+ #[fory(id = 8)]
+ pub pet: Animal,
+}
+```
+
+## Registration
+
+Generated registration function:
+
+```rust
+pub fn register_types(fory: &mut Fory) -> Result<(), fory::Error> {
+ fory.register_union::<Animal>(106)?;
+ fory.register::<person::PhoneType>(101)?;
+ fory.register::<person::PhoneNumber>(102)?;
+ fory.register::<Person>(100)?;
+ fory.register::<Dog>(104)?;
+ fory.register::<Cat>(105)?;
+ fory.register::<AddressBook>(103)?;
+ Ok(())
+}
+```
+
+For schemas without explicit `[id=...]`, generated registration uses computed numeric IDs:
+
+```rust
+fory.register::<Status>(1124725126)?;
+fory.register_union::<Wrapper>(1471345060)?;
+fory.register::<Envelope>(3022445236)?;
+fory.register_union::<envelope::Detail>(1609214087)?;
+fory.register::<envelope::Payload>(2862577837)?;
+```
+
+If `option enable_auto_type_id = false;` is set:
+
+```rust
+fory.register_by_name::<Config>("myapp.models.Config")?;
+fory.register_union_by_name::<Holder>("myapp.models.Holder")?;
+```
+
+## Usage
+
+```rust
+let person = Person {
+ name: "Alice".into(),
+ pet: Animal::Dog(self::Dog::default()),
+ ..Default::default()
+};
+
+let bytes = person.to_bytes()?;
+let restored = Person::from_bytes(&bytes)?;
+```
+
+## gRPC Service Companions
+
+With `--grpc`, Rust emits `<module>_service.rs` for the async service trait and path constants, and `<module>_service_grpc.rs` for tonic clients, servers, codecs, and `ForyGrpcPayload` implementations backed by the model's `to_bytes` and `from_bytes` helpers. See [Rust gRPC](../../grpc/rust.md) for dependencies and usage.
diff --git a/docs/compiler/generated-code/scala.md b/docs/compiler/generated-code/scala.md
new file mode 100644
index 000000000..aca455dea
--- /dev/null
+++ b/docs/compiler/generated-code/scala.md
@@ -0,0 +1,164 @@
+---
+title: Scala
+sidebar_position: 12
+id: scala
+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.
+---
+
+The Scala target emits Scala 3 source only. The `fory-scala` artifact still
+supports Scala 2.13 and Scala 3, but generated IDL source and macro derivation
+require Scala 3.
+
+## Output Layout
+
+For `package addressbook`, Scala output is generated under:
+
+- `<scala_out>/addressbook/`
+- Type files: `AddressBook.scala`, `Person.scala`, `Dog.scala`, `Cat.scala`, `Animal.scala`
+- Schema module: `AddressbookForyModule.scala`
+
+For schemas without a Scala package, the schema module name is derived from the
+source file stem, for example `main.fdl` generates `MainForyModule.scala`.
+Scala import graphs cannot mix default-package schemas with named Scala
+packages.
+
+## Type Generation
+
+Messages outside compiler-detected construction cycles generate case classes:
+
+```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 = 3) email: Option[String],
+ @ForyField(id = 7) phones: List[Person.PhoneNumber],
+ @ForyField(id = 8) pet: Animal
+) derives ForySerializer {
+ def toBytes(): Array[Byte] =
+ AddressbookForyModule.getFory.serialize(this)
+}
+
+object Person {
+ def fromBytes(bytes: Array[Byte]): Person =
+ AddressbookForyModule.getFory.deserialize(bytes).asInstanceOf[Person]
+}
+```
+
+Messages in circular construction cycles generate normal classes with mutable
+serialized fields so reads can register the object before reading back-references:
+
+```scala
+import org.apache.fory.annotation.{ForyField, ForyStruct, Ref}
+import org.apache.fory.scala.ForySerializer
+
+@ForyStruct
+final class Node() derives ForySerializer {
+ @ForyField(id = 1)
+ var id: String = ""
+
+ @Ref
+ @ForyField(id = 2)
+ var parent: Option[Node] = None
+}
+```
+
+Enums generate Scala 3 enums with stable Fory IDs:
+
+```scala
+import org.apache.fory.annotation.ForyEnumId
+
+enum PhoneType {
+ @ForyEnumId(0)
+ case Mobile
+
+ @ForyEnumId(1)
+ case Home
+
+ @ForyEnumId(2)
+ case Work
+}
+```
+
+Unions generate Scala 3 ADT enums. `Unknown(UnknownCase)` is the Fory-provided
+forward-compatibility carrier marked with `@ForyUnknownCase`. It is omitted
+from the schema case table because the marker only selects the carrier and does
+not add a schema entry. Schema-defined cases use non-negative `@ForyCase` IDs.
+A typed union must have at least one
+non-`Unknown` case.
+
+```scala
+package addressbook
+
+import org.apache.fory.annotation.{ForyCase, ForyUnion, ForyUnknownCase}
+import org.apache.fory.scala.ForySerializer
+import org.apache.fory.`type`.union.UnknownCase
+
+@ForyUnion
+enum Animal derives ForySerializer {
+ @ForyUnknownCase
+ case Unknown(value: UnknownCase)
+
+ @ForyCase(id = 0)
+ case Dog(value: _root_.addressbook.Dog)
+
+ @ForyCase(id = 1)
+ case Cat(value: _root_.addressbook.Cat)
+}
+```
+
+Packaged Scala output keeps the schema case name and qualifies the payload type
+when both have the same simple name. If a target output mode cannot express a
+legal qualifier for a conflict, the compiler appends `Case` to the generated
+case name.
+
+`optional T` fields generate `Option[T]`. Top-level message references use
+`@Ref` on the field or constructor parameter. Nested element/value references
+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 Fory instance uses
+`ForyScala.builder().withXlang(true)` with the schema module installed, so
+message `toBytes`/`fromBytes` helpers work without caller-managed Fory setup:
+
+```scala
+object AddressbookForyModule extends org.apache.fory.ForyModule {
+ private lazy val fory: ThreadSafeFory =
+ ForyScala.builder()
+ .withXlang(true)
+ .withRefTracking(true)
+ .withModule(this)
+ .buildThreadSafeFory()
+
+ private[addressbook] def getFory: ThreadSafeFory = fory
+
+ override def install(fory: Fory): Unit = {
+ ScalaSerializers.registerEnum(fory, classOf[Person.PhoneType], 101L)
+ ForySerializer.register(fory, classOf[Person.PhoneNumber], 102L)
+ ForySerializer.register(fory, classOf[Person], 100L)
+ ForySerializer.register(fory, classOf[Animal], 106L)
+ }
+}
+```
+
+## gRPC Service Companions
+
+With `--grpc`, Scala emits one `<ServiceName>Grpc.scala` object per local service in the generated models' package. It exposes `SERVICE_NAME`, service and method descriptors, `<ServiceName>ImplBase`, and `<ServiceName>Client`. See [Scala gRPC](../../grpc/scala.md) for `RpcFuture`, `RpcIterator`, grpc-java variants, and lifecycle guidance.
diff --git a/docs/compiler/generated-code/swift.md b/docs/compiler/generated-code/swift.md
new file mode 100644
index 000000000..f53f0cec7
--- /dev/null
+++ b/docs/compiler/generated-code/swift.md
@@ -0,0 +1,108 @@
+---
+title: Swift
+sidebar_position: 9
+id: swift
+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.
+---
+
+## Output Layout
+
+Swift output is one `.swift` file per schema, for example:
+
+- `<swift_out>/addressbook/addressbook.swift`
+
+## Type Generation
+
+The generator creates Swift models with split model macros and stable field/case IDs.
+A typed union must include `@ForyUnknownCase case unknown(UnknownCase)` and at
+least one non-`unknown` case; `unknown(UnknownCase)` is only the
+Fory-provided forward-compatibility carrier. The marker only selects the carrier
+and does not add an entry to the schema case table.
+
+When package/namespace is non-empty, namespace shaping is controlled by `swift_namespace_style`:
+
+- `enum` (default): nested enum namespace wrappers.
+- `flatten`: package-derived prefix on top-level type names (for example `Demo_Foo_User`).
+
+When package/namespace is empty, no enum wrapper or flatten prefix is applied.
+
+For non-empty package with default `enum` style:
+
+```swift
+public enum Addressbook {
+ @ForyUnion
+ public enum Animal {
+ @ForyUnknownCase
+ case unknown(UnknownCase)
+ @ForyCase(id: 0)
+ case dog(Addressbook.Dog)
+ @ForyCase(id: 1)
+ case cat(Addressbook.Cat)
+ }
+
+ @ForyStruct
+ public struct Person: Equatable {
+ @ForyField(id: 1)
+ public var name: String = ""
+ @ForyField(id: 8)
+ public var pet: Addressbook.Animal =
+ Addressbook.Animal.dog(Addressbook.Dog())
+ }
+}
+```
+
+For non-empty package with `flatten` style:
+
+```swift
+@ForyStruct
+public struct Addressbook_Person: Equatable { ... }
+```
+
+The CLI flag `--swift_namespace_style` overrides schema option `swift_namespace_style` when both are set.
+
+Unions are generated as tagged Swift enums with associated payload values.
+Recursive unions are emitted as `indirect` enums. The first known union case
+must have a finite recursively constructible default; the compiler rejects a
+first-case default cycle instead of emitting a non-terminating initializer.
+Messages with `ref`/`weak_ref` fields are generated as `final class` models to preserve reference semantics.
+A directly stored message cycle must mark at least one cycle edge `ref`; otherwise
+the compiler rejects the schema because Swift value types cannot represent it.
+Fixed or tagged integer encodings inside list/map fields are emitted as Swift
+field type hints, for example `@ListField(element: .encoding(.fixed))` or
+`@MapField(value: .encoding(.tagged))`.
+For non-null fixed-width integer list elements, Swift classifies the field as
+the corresponding Fory primitive packed-array type; fixed-width integer sets
+remain Fory sets.
+
+## Module Installation
+
+Each schema includes a `ForyModule` owner with transitive import installation:
+
+```swift
+public enum ForyModule {
+ public static func install(_ fory: Fory) throws {
+ try ComplexPb.ForyModule.install(fory)
+ try fory.register(Addressbook.Person.self, id: 100)
+ try fory.register(Addressbook.Animal.self, id: 106)
+ }
+}
+```
+
+With non-empty package and `flatten` style, the helper is prefixed too (for example `Addressbook_ForyModule`).
+
+For schemas without explicit `[id=...]`, installation uses computed numeric IDs.
+If `option enable_auto_type_id = false;` is set, generated code uses name-based registration APIs.
diff --git a/docs/compiler/getting-started.md b/docs/compiler/getting-started.md
new file mode 100644
index 000000000..6b396a9f6
--- /dev/null
+++ b/docs/compiler/getting-started.md
@@ -0,0 +1,46 @@
+---
+title: Getting Started
+sidebar_position: 1
+id: getting-started
+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 guide covers installation, usage, and integration of the Fory IDL compiler.
+
+## Installation
+
+### From Source
+
+```bash
+cd compiler
+pip install -e .
+```
+
+### Verify Installation
+
+```bash
+foryc --help
+```
+
+## First generation
+
+```bash
+foryc example.fdl --lang java,python --output ./generated
+```
+
+Continue with the [CLI reference](cli.md) for frontend and output options or
+[Build Integration](build-integration.md) to run generation from the application build.
diff --git a/docs/compiler/index.md b/docs/compiler/index.md
index 4d89c4634..21590e64d 100644
--- a/docs/compiler/index.md
+++ b/docs/compiler/index.md
@@ -1,6 +1,6 @@
---
title: Overview
-sidebar_position: 1
+sidebar_position: 0
id: index
license: |
Licensed to the Apache Software Foundation (ASF) under one or more
@@ -96,15 +96,9 @@ foryc animals.fdl --java_out=./generated/java --python_out=./generated/python --
```
The generated service code uses normal gRPC APIs, but request and response
-objects are serialized with Fory. Applications provide their own grpc-java,
-grpc-kotlin, Scala grpc-java APIs, `grpcio`, grpc-go, Rust `tonic` and `bytes`,
-gRPC C++ 1.39.0 or later, C# `Grpc.Core.Api` and hosting/client dependencies, or
-Dart `package:grpc`; Fory packages do not add gRPC as a hard dependency. See
-[C++ gRPC Support](../guide/cpp/grpc-support.md) for C++ linking instructions.
-Python companions use `grpc.aio` by default and can be generated in sync mode
-with `--grpc-python-mode=sync`. JavaScript Node.js companions use
-`@grpc/grpc-js`; browser clients are generated separately with `--grpc-web` and
-use `grpc-web`.
+objects are serialized with Fory. See [Fory gRPC](../grpc/index.md) for runtime
+dependencies, server and client setup, streaming modes, browser support, and
+interoperability boundaries.
## Why Fory IDL?
@@ -218,8 +212,9 @@ const person = deserializePerson(data);
| [Fory IDL Syntax](schema-idl.md) | Complete language syntax and grammar |
| [Type System](schema-idl.md#type-system) | Primitive types, collections, and type rules |
| [RPC Services](schema-idl.md#service-definition) | Service and RPC method syntax |
-| [Compiler Guide](compiler-guide.md) | CLI options and build integration |
-| [Generated Code](generated-code.md) | Output format for each target language |
+| [Compiler CLI](cli.md) | Compiler commands and options |
+| [Build Integration](build-integration.md) | Maven, Gradle, build.rs, CMake, Bazel, and more |
+| [Generated Code](generated-code/index.md) | Output format for each target language |
| [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 |
diff --git a/docs/compiler/protobuf-idl.md b/docs/compiler/protobuf-idl.md
index d9355deec..e75b39db8 100644
--- a/docs/compiler/protobuf-idl.md
+++ b/docs/compiler/protobuf-idl.md
@@ -1,7 +1,7 @@
---
title: Protobuf IDL Support
-sidebar_position: 10
-id: protobuf_idl_support
+sidebar_position: 5
+id: protobuf-idl
license: |
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@@ -356,7 +356,7 @@ migrate first.
## Performance References
-- Benchmarks: https://fory.apache.org/docs/introduction/benchmark
+- Benchmarks: https://fory.apache.org/docs/benchmarks/
- Benchmark code: https://github.com/apache/fory/tree/main/benchmarks
## Summary
diff --git a/docs/compiler/schema-idl.md b/docs/compiler/schema-idl.md
index d26083e5f..d9f8bd942 100644
--- a/docs/compiler/schema-idl.md
+++ b/docs/compiler/schema-idl.md
@@ -1,7 +1,7 @@
---
title: Schema IDL
-sidebar_position: 2
-id: syntax
+sidebar_position: 3
+id: schema-idl
license: |
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@@ -22,7 +22,7 @@ license: |
This document provides the syntax and semantic reference for Fory IDL.
For compiler usage and build integration, see
-[Compiler Guide](compiler-guide.md). For protobuf/FlatBuffers frontend mapping
+[Compiler Guide](cli.md). For protobuf/FlatBuffers frontend mapping
rules, see [Protocol Buffers IDL Support](protobuf-idl.md) and
[FlatBuffers IDL Support](flatbuffers-idl.md).
diff --git a/docs/guide/dart/_category_.json b/docs/development/_category_.json
similarity index 71%
copy from docs/guide/dart/_category_.json
copy to docs/development/_category_.json
index b2d06cfcd..42b3616cf 100644
--- a/docs/guide/dart/_category_.json
+++ b/docs/development/_category_.json
@@ -1,6 +1,6 @@
{
- "label": "Dart",
"position": 9,
+ "label": "Development",
"collapsible": true,
"collapsed": true
}
diff --git a/docs/DEVELOPMENT.md b/docs/development/building.md
similarity index 99%
rename from docs/DEVELOPMENT.md
rename to docs/development/building.md
index 411255a4a..4e6bb1384 100644
--- a/docs/DEVELOPMENT.md
+++ b/docs/development/building.md
@@ -1,7 +1,7 @@
---
title: Development
sidebar_position: 20
-id: development
+id: building
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/cpp_debug.md b/docs/development/cpp-debugging.md
similarity index 76%
rename from docs/cpp_debug.md
rename to docs/development/cpp-debugging.md
index 9fd583835..360eef894 100644
--- a/docs/cpp_debug.md
+++ b/docs/development/cpp-debugging.md
@@ -18,6 +18,10 @@ license: |
limitations under the License.
---
+import JumpGeneratedFile from "@site/docs/images/jmp_generate_file.png";
+import VscodeDebugFory from "@site/docs/images/vscode_debug_fory.jpg";
+import VscodeSelectDebugRun from "@site/docs/images/vscode_select_debug_run.png";
+
## Debugging C++
Debug C++ is tricky and binary protocol bug is hard to locate without debugging support, but setup
@@ -29,7 +33,7 @@ Just open the fory root directory in vscode, then open a test file such as `src/
enter `F5`, then you can debug the c++ code line by line, watch variables, add breakpoints in vscode just like
java/python debugging.
-<img src="images/vscode_debug_fory.jpg" alt="Apache Fory™ Debugging Example" width="50%" height="50%" />
+<img src={VscodeDebugFory} alt="Apache Fory™ Debugging Example" width="50%" height="50%" />
## Notes
@@ -38,6 +42,6 @@ java/python debugging.
- install gdb for fedora/centos by `sudo yum install -y gdb`
- install gdb for fedora/centos by `sudo apt-get install gdb`
- If debugging doesn't work, please select correct debug/run options:
- <img src="images/vscode_select_debug_run.png" alt="select correct debug/run options" width="50%" height="50%" />
+ <img src={VscodeSelectDebugRun} alt="select correct debug/run options" width="50%" height="50%" />
- Debug will jump to source files in bazel generated directory, we need to return raw files in source tree
- for debugging again: <img src="images/jmp_generate_file.png" alt="jump to generated files" width="50%" height="50%" />
+ for debugging again: <img src={JumpGeneratedFile} alt="jump to generated files" width="50%" height="50%" />
diff --git a/docs/development/index.md b/docs/development/index.md
new file mode 100644
index 000000000..7044b921d
--- /dev/null
+++ b/docs/development/index.md
@@ -0,0 +1,32 @@
+---
+title: Development
+sidebar_position: 1
+id: index
+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.
+---
+
+Contributor documentation covers repository setup, builds, tests, debugging, and release-oriented
+workflows. It is separate from the user guides for released artifacts.
+
+## Start here
+
+- [Build and test the repository](building.md)
+- [Debug the C++ implementation](cpp-debugging.md)
+- [Contributing guide](https://github.com/apache/fory/blob/main/CONTRIBUTING.md)
+
+Runtime-specific contributor instructions remain in each runtime's source tree and contributor
+README when they are not part of the shared repository build.
diff --git a/docs/guide/swift/_category_.json b/docs/grpc/_category_.json
similarity index 72%
copy from docs/guide/swift/_category_.json
copy to docs/grpc/_category_.json
index dfdaf5996..44de805e1 100644
--- a/docs/guide/swift/_category_.json
+++ b/docs/grpc/_category_.json
@@ -1,6 +1,6 @@
{
- "label": "Swift",
"position": 8,
+ "label": "Fory gRPC",
"collapsible": true,
"collapsed": true
}
diff --git a/docs/grpc/architecture.md b/docs/grpc/architecture.md
new file mode 100644
index 000000000..ffe4c3016
--- /dev/null
+++ b/docs/grpc/architecture.md
@@ -0,0 +1,41 @@
+---
+title: Architecture
+sidebar_position: 2
+id: architecture
+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.
+---
+
+Generated service companions use normal gRPC servers, channels, method descriptors, deadlines,
+status codes, interceptors, and streaming APIs. Fory-generated marshallers encode and decode the
+generated request and response models.
+
+## Ownership boundary
+
+Fory can generate service companions for application-provided gRPC runtimes.
+Those companions provide Fory serialization for request and response objects;
+the application and gRPC stack still own listeners, channels, credentials,
+authentication, authorization, deadlines, retries, and transport lifecycle.
+
+Fory packages do not add a gRPC implementation as a hard dependency. The application selects and
+configures the runtime's gRPC libraries.
+
+## Generated service surface
+
+The compiler emits runtime-idiomatic service bases, clients or stubs, method metadata, and Fory
+marshallers. Model generation is documented under
+[Generated Code](../compiler/generated-code/index.md); the runtime pages document server and client
+integration.
diff --git a/docs/guide/cpp/grpc-support.md b/docs/grpc/cpp.md
similarity index 99%
rename from docs/guide/cpp/grpc-support.md
rename to docs/grpc/cpp.md
index 4fe547d32..499cb51c1 100644
--- a/docs/guide/cpp/grpc-support.md
+++ b/docs/grpc/cpp.md
@@ -1,7 +1,7 @@
---
-title: gRPC Support
-sidebar_position: 12
-id: grpc_support
+title: C++ gRPC
+sidebar_position: 6
+id: cpp
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/grpc-support.md b/docs/grpc/csharp.md
similarity index 99%
rename from docs/guide/csharp/grpc-support.md
rename to docs/grpc/csharp.md
index 1d584de4d..fb468cf83 100644
--- a/docs/guide/csharp/grpc-support.md
+++ b/docs/grpc/csharp.md
@@ -1,7 +1,7 @@
---
-title: gRPC Support
-sidebar_position: 12
-id: grpc_support
+title: C# gRPC
+sidebar_position: 10
+id: csharp
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/grpc-support.md b/docs/grpc/dart.md
similarity index 99%
rename from docs/guide/dart/grpc-support.md
rename to docs/grpc/dart.md
index 4e9b44843..cccadffba 100644
--- a/docs/guide/dart/grpc-support.md
+++ b/docs/grpc/dart.md
@@ -1,7 +1,7 @@
---
-title: gRPC Support
-sidebar_position: 14
-id: grpc_support
+title: Dart gRPC
+sidebar_position: 11
+id: dart
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/go/grpc-support.md b/docs/grpc/go.md
similarity index 99%
rename from docs/guide/go/grpc-support.md
rename to docs/grpc/go.md
index c921ed433..b3551d96a 100644
--- a/docs/guide/go/grpc-support.md
+++ b/docs/grpc/go.md
@@ -1,7 +1,7 @@
---
-title: gRPC Support
-sidebar_position: 12
-id: grpc_support
+title: Go gRPC
+sidebar_position: 7
+id: go
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/grpc/index.md b/docs/grpc/index.md
new file mode 100644
index 000000000..5e019a79f
--- /dev/null
+++ b/docs/grpc/index.md
@@ -0,0 +1,73 @@
+---
+title: Fory gRPC
+sidebar_position: 1
+id: index
+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 gRPC combines normal gRPC transport semantics with request and response models generated by
+the Fory compiler. The payload marshaller uses Fory rather than protobuf message bytes.
+
+## When to Use It
+
+Use Fory gRPC when every peer is generated from the same Fory IDL, protobuf IDL, or FlatBuffers IDL
+contract and supports the matching Fory runtime. Use ordinary protobuf gRPC when generic protobuf
+clients, reflection tools, or protobuf message bytes are required.
+
+## Schema Frontends
+
+Fory gRPC companions can be generated from:
+
+- [Fory IDL](../compiler/schema-idl.md)
+- [Protocol Buffers IDL](../compiler/protobuf-idl.md)
+- [FlatBuffers IDL](../compiler/flatbuffers-idl.md)
+
+The compiler's [Generated Code](../compiler/generated-code/index.md) reference
+describes the model and service artifacts. Use
+[Build Integration](../compiler/build-integration.md) to wire generation into a
+project build.
+
+## Workflow
+
+1. Define messages and services in a supported compiler frontend.
+2. Generate models and gRPC companions with `foryc`.
+3. Add the normal gRPC dependencies for the selected runtime.
+4. Implement the generated server base and call the generated client.
+5. Verify unary and streaming calls between generated peers.
+
+Python generates asynchronous `grpc.aio` companions by default and supports sync
+generation with `--grpc-python-mode=sync`. JavaScript uses `@grpc/grpc-js` for
+Node.js; browser clients are generated separately with `--grpc-web` and use
+`grpc-web`.
+
+## Runtime Guides
+
+| Runtime | Guide |
+| --------------------- | ------------------------------------- |
+| Java | [Java](java.md) |
+| Python | [Python](python.md) |
+| C++ | [C++](cpp.md) |
+| Go | [Go](go.md) |
+| Rust | [Rust](rust.md) |
+| JavaScript/TypeScript | [Node.js and gRPC-Web](javascript.md) |
+| C# | [C#](csharp.md) |
+| Dart | [Dart](dart.md) |
+| Scala | [Scala](scala.md) |
+| Kotlin | [Kotlin](kotlin.md) |
+
+See [Architecture](architecture.md) for payload and transport ownership and
+[Interoperability](interoperability.md) for peer compatibility.
diff --git a/docs/grpc/interoperability.md b/docs/grpc/interoperability.md
new file mode 100644
index 000000000..1de36c488
--- /dev/null
+++ b/docs/grpc/interoperability.md
@@ -0,0 +1,41 @@
+---
+title: Interoperability
+sidebar_position: 3
+id: interoperability
+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 gRPC peers interoperate only when they use the same generated service contract, matching Fory
+type identities, and compatible generated model schemas.
+
+## Protocol boundary
+
+The transport is gRPC, but the message bytes are Fory payloads. Generic protobuf clients and server
+reflection tools cannot decode those payloads as protobuf messages. Generate every peer through a
+supported Fory compiler frontend.
+
+## Supported generated companions
+
+Java, Python, C++, Go, Rust, JavaScript/TypeScript, C#, Dart, Scala, and Kotlin have documented gRPC
+companions. Use the [support matrix](../introduction/support-matrix.md) and the selected runtime page
+for current dependencies and streaming support.
+
+## Verification
+
+Test at least one unary call and every streaming shape used by the service. A protobuf
+`UNIMPLEMENTED` or decode failure usually means the peer used an ordinary protobuf stub or a
+different generated service contract.
diff --git a/docs/guide/java/grpc-support.md b/docs/grpc/java.md
similarity index 98%
rename from docs/guide/java/grpc-support.md
rename to docs/grpc/java.md
index 3e1c608a9..555a029e8 100644
--- a/docs/guide/java/grpc-support.md
+++ b/docs/grpc/java.md
@@ -1,7 +1,7 @@
---
-title: gRPC Support
-sidebar_position: 17
-id: grpc_support
+title: Java gRPC
+sidebar_position: 4
+id: java
license: |
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
@@ -31,8 +31,8 @@ must be consumed by generic protobuf clients, reflection tools, or components
that expect protobuf message bytes.
For Scala generated grpc-java companions, see
-[Scala gRPC Support](../scala/grpc-support.md). For Kotlin coroutine stubs and
-service bases, see [Kotlin gRPC Support](../kotlin/grpc-support.md).
+[Scala gRPC Support](scala.md). For Kotlin coroutine stubs and
+service bases, see [Kotlin gRPC Support](kotlin.md).
## Add Dependencies
diff --git a/docs/guide/javascript/grpc-support.md b/docs/grpc/javascript.md
similarity index 99%
rename from docs/guide/javascript/grpc-support.md
rename to docs/grpc/javascript.md
index 2cbc68dbe..2c0a62300 100644
--- a/docs/guide/javascript/grpc-support.md
+++ b/docs/grpc/javascript.md
@@ -1,7 +1,7 @@
---
-title: gRPC Support
-sidebar_position: 25
-id: grpc_support
+title: JavaScript gRPC
+sidebar_position: 9
+id: javascript
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/kotlin/grpc-support.md b/docs/grpc/kotlin.md
similarity index 99%
rename from docs/guide/kotlin/grpc-support.md
rename to docs/grpc/kotlin.md
index 79c32848b..09f87223d 100644
--- a/docs/guide/kotlin/grpc-support.md
+++ b/docs/grpc/kotlin.md
@@ -1,7 +1,7 @@
---
title: Kotlin gRPC Support
-sidebar_position: 6
-id: grpc_support
+sidebar_position: 13
+id: kotlin
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/python/grpc-support.md b/docs/grpc/python.md
similarity index 99%
rename from docs/guide/python/grpc-support.md
rename to docs/grpc/python.md
index 0c5921cc6..c8bb3f69d 100644
--- a/docs/guide/python/grpc-support.md
+++ b/docs/grpc/python.md
@@ -1,7 +1,7 @@
---
-title: gRPC Support
-sidebar_position: 13
-id: grpc_support
+title: Python gRPC
+sidebar_position: 5
+id: python
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/rust/grpc-support.md b/docs/grpc/rust.md
similarity index 99%
rename from docs/guide/rust/grpc-support.md
rename to docs/grpc/rust.md
index 56ea6c73a..fe2d2b775 100644
--- a/docs/guide/rust/grpc-support.md
+++ b/docs/grpc/rust.md
@@ -1,7 +1,7 @@
---
-title: gRPC Support
-sidebar_position: 12
-id: grpc_support
+title: Rust gRPC
+sidebar_position: 8
+id: rust
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/scala/grpc-support.md b/docs/grpc/scala.md
similarity index 99%
rename from docs/guide/scala/grpc-support.md
rename to docs/grpc/scala.md
index 9d207c4c2..43e9386b3 100644
--- a/docs/guide/scala/grpc-support.md
+++ b/docs/grpc/scala.md
@@ -1,7 +1,7 @@
---
-title: gRPC Support
-sidebar_position: 6
-id: grpc_support
+title: Scala gRPC
+sidebar_position: 12
+id: scala
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/grpc/troubleshooting.md b/docs/grpc/troubleshooting.md
new file mode 100644
index 000000000..916e816bb
--- /dev/null
+++ b/docs/grpc/troubleshooting.md
@@ -0,0 +1,37 @@
+---
+title: Troubleshooting
+sidebar_position: 20
+id: troubleshooting
+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.
+---
+
+Use the selected runtime guide for dependency- and API-specific diagnostics.
+
+## `UNIMPLEMENTED`
+
+Confirm that the client and server use the same generated service name and method descriptors and
+that the generated service was registered with the server.
+
+## A protobuf client cannot decode the service
+
+Fory gRPC uses Fory message bytes. Generate a Fory gRPC peer from the same contract or expose a
+separate ordinary protobuf service.
+
+## A generated file does not compile
+
+Check the runtime page's required gRPC packages and versions, then regenerate all model and service
+files with the same compiler version.
diff --git a/docs/guide/_category_.json b/docs/guide/_category_.json
deleted file mode 100644
index 40e7c85c1..000000000
--- a/docs/guide/_category_.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "position": 4,
- "label": "Guide"
-}
diff --git a/docs/guide/java/json-support.md b/docs/guide/java/json-support.md
deleted file mode 100644
index 96fb5357e..000000000
--- a/docs/guide/java/json-support.md
+++ /dev/null
@@ -1,1354 +0,0 @@
----
-title: JSON Support
-sidebar_position: 18
-id: json_support
-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 JSON is Apache Fory's thread-safe Java JSON codec. It supports Java objects, records,
-immutable creator-based classes, common JDK types, generic containers, custom complete-value
-codecs, and finite annotation-declared polymorphism through interpreted and runtime-generated
-codecs.
-
-Fory JSON is separate from Fory's binary native and xlang protocols. Use JSON for interoperable
-text payloads such as HTTP APIs, browser traffic, logs, and configuration. Use the binary protocol
-when you need reference identity, circular graphs, cross-language schema metadata, or Fory's
-binary-only features.
-
-## Requirements and installation
-
-Fory JSON supports Java 8 and later on standard JDKs, GraalVM native images, and Android. Java
-records are supported on Java 17 and later.
-
-Fory JSON is available from Maven Central.
-
-Maven:
-
-```xml
-<dependency>
- <groupId>org.apache.fory</groupId>
- <artifactId>fory-json</artifactId>
- <version>1.5.0</version>
-</dependency>
-```
-
-Gradle:
-
-```kotlin
-implementation("org.apache.fory:fory-json:1.5.0")
-```
-
-Keep all Fory modules on the same version.
-
-### JDK 25 and later
-
-Open `java.lang.invoke` to Fory core. On the classpath:
-
-```bash
---add-opens=java.base/java.lang.invoke=ALL-UNNAMED
-```
-
-On the module path:
-
-```bash
---add-opens=java.base/java.lang.invoke=org.apache.fory.core
-```
-
-Fory JSON's JPMS module name is `org.apache.fory.json`.
-
-## Quick start
-
-Create one `ForyJson` instance and reuse it. It is thread-safe and has no close lifecycle.
-
-```java
-import java.nio.charset.StandardCharsets;
-import org.apache.fory.json.ForyJson;
-
-public final class JsonExample {
- private static final ForyJson JSON = ForyJson.builder().build();
-
- public static final class User {
- public long id;
- public String name;
-
- public User() {}
-
- User(long id, String name) {
- this.id = id;
- this.name = name;
- }
- }
-
- public static void main(String[] args) {
- User input = new User(7, "Alice");
- String text = JSON.toJson(input);
- byte[] utf8 = JSON.toJsonBytes(input);
-
- User fromText = JSON.fromJson(text, User.class);
- User fromUtf8 = JSON.fromJson(utf8, User.class);
-
- System.out.println(text);
- System.out.println(new String(utf8, StandardCharsets.UTF_8));
- System.out.println(fromText.name + " / " + fromUtf8.name);
- }
-}
-```
-
-Unknown input properties are skipped unless a read-enabled Any field or any-setter receives them.
-Null object properties are omitted by default. Default JSON member discovery order is not a
-compatibility contract; use `JsonPropertyOrder` or `JsonProperty.index` when emitted member order
-must be explicit.
-
-## Reading and writing
-
-Fory JSON supports String and UTF-8 byte input/output. There is no `InputStream` parsing API.
-
-| Operation | Runtime type | Declared `Class` | Declared `TypeRef` |
-| -------------------- | ------------------------- | ------------------------------- | ---------------------------------- |
-| String output | `toJson(value)` | `toJson(value, type)` | `toJson(value, typeRef)` |
-| UTF-8 bytes | `toJsonBytes(value)` | `toJsonBytes(value, type)` | `toJsonBytes(value, typeRef)` |
-| UTF-8 `OutputStream` | `writeJsonTo(value, out)` | `writeJsonTo(value, type, out)` | `writeJsonTo(value, typeRef, out)` |
-| String input | - | `fromJson(text, type)` | `fromJson(text, typeRef)` |
-| UTF-8 input | - | `fromJson(bytes, type)` | `fromJson(bytes, typeRef)` |
-
-Parsing consumes exactly one value and rejects trailing non-whitespace. String and byte-array
-outputs are detached from internal buffers.
-
-`writeJsonTo` buffers the complete document, performs one `OutputStream.write`, and neither flushes
-nor closes the stream. It is not incremental streaming. I/O failures are wrapped in
-`ForyJsonException`.
-
-### Generic and declared types
-
-Use `TypeRef` for generic roots:
-
-```java
-import java.util.List;
-import org.apache.fory.reflect.TypeRef;
-
-TypeRef<List<User>> usersType = new TypeRef<List<User>>() {};
-List<User> users = json.fromJson("[{\"id\":7,\"name\":\"Alice\"}]", usersType);
-String encoded = json.toJson(users, usersType);
-```
-
-Typed writes require fully bound types and reject wildcards and type variables. Values must be
-assignable to the declared raw type. The declared schema controls serialization, including nested
-generic element types and closed subtype metadata.
-
-Use a declared base type when it owns `JsonSubTypes`:
-
-```java
-Shape shape = new Circle(2);
-
-json.toJson(shape); // Concrete runtime representation
-json.toJson(shape, Shape.class); // Configured Shape subtype representation
-```
-
-For `List<Shape>`, use `new TypeRef<List<Shape>>() {}` so each element retains the declared subtype
-schema.
-
-## Thread safety and code generation
-
-`ForyJson` is immutable and thread-safe after `build()`. Registered and annotation-selected
-`JsonValueCodec` instances and type checkers are shared and must also be thread-safe.
-
-Code generation and asynchronous compilation are enabled by default. Disable them for diagnostics
-or environments that prohibit runtime compilation:
-
-```java
-ForyJson json =
- ForyJson.builder()
- .withCodegen(false)
- .withAsyncCompilation(false)
- .build();
-```
-
-`withConcurrencyLevel` sets the maximum number of root operations that execute concurrently.
-Additional callers wait until one of those fixed execution states is available. Root APIs on one
-`ForyJson` instance are not reentrant: a custom codec must continue through the concrete reader or
-writer passed to it instead of calling `toJson`, `toJsonBytes`, `writeJsonTo`, or `fromJson` on that
-instance.
-
-## Object mapping
-
-Default discovery merges members with the same Java logical property name:
-
-- eligible instance fields across the hierarchy, regardless of Java visibility;
-- public non-static `getX()` and boolean `isX()` getters;
-- public non-static void `setX(value)` setters.
-
-Static, transient, synthetic, and `Class<?>` fields are excluded. Class-valued accessors and
-`getClass()` are excluded. An annotation on an ineligible member fails rather than becoming a
-silent no-op.
-
-An ordinary final field is a write source but not a mutable read sink. Use a record, `JsonCreator`,
-or custom codec for immutable construction. Records use their canonical constructor.
-
-Enable field-only discovery with:
-
-```java
-ForyJson json = ForyJson.builder().withFieldMode(true).build();
-```
-
-In field mode, getter/setter annotations are invalid. For ordinary properties, unknown members are
-skipped and duplicate members use the last value. Polymorphic discriminator members are stricter
-and must occur exactly once. JSON null is rejected for primitive targets. Most reference targets
-return null, but a selected built-in or custom codec may define another result; declared Optional
-targets return the corresponding empty Optional.
-
-An ordinary class with a no-argument constructor runs it before readable properties are assigned,
-so missing properties retain values established by field initializers or the constructor. On an
-ordinary JVM, a class without such a constructor is allocated without running its constructors or
-field initializers, and missing properties retain JVM zero or null values. Android cannot construct
-an ordinary class without a usable no-argument constructor. GraalVM native image on JDK 25 and
-later also requires one for most ordinary classes; the supported exception is a `Serializable`
-class whose first non-serializable superclass is `Object`.
-
-For portable construction, use a record, `JsonCreator`, or a no-argument constructor. Do not use an
-ordinary constructor as a deserialization completion hook: property assignment follows a
-no-argument constructor, while constructor-bypassing paths do not run it.
-
-## Supported Java types
-
-| Group | Types and behavior |
-| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| Scalars | Primitive and boxed booleans/numbers/chars, strings and string builders, `BigInteger`, `BigDecimal`, Fory half-precision numbers, enums |
-| Containers | Primitive/boxed/object arrays; collection, list, set, queue, deque, blocking, sorted, and navigable interfaces; map, sorted-map, navigable-map, and concurrent-map interfaces; supported concrete implementations; `EnumSet`; `EnumMap`; `Optional` variants; atomic values and arrays |
-| Time | `Date`, `Calendar`, `TimeZone`, Java time types, supported chronology dates, optional `java.sql.Date`, `Time`, and `Timestamp` |
-| Other JDK | `UUID`, `URI`, `File`, `Path`, `Locale`, `Charset`, `Currency`, `Pattern`, `BitSet`, `ByteBuffer` |
-| Optional libraries | Guava `ImmutableList`, `ImmutableSet`, `ImmutableSortedSet`, `ImmutableMap`, `ImmutableBiMap`, `ImmutableSortedMap`, and `ImmutableIntArray` when Guava is present |
-| Objects | Mutable concrete classes, records, creator classes, `JsonObject`, `JsonArray` |
-
-Interfaces are reconstructed with appropriate standard mutable implementations. `ArrayBlockingQueue`,
-`Arrays.asList` results, JDK immutable collections, empty/singleton/unmodifiable wrappers,
-constructor-constrained implementations, and unlisted Guava immutable implementations cannot be
-reconstructed. Guava remains optional.
-
-Non-finite float/double values use quoted `"NaN"`, `"Infinity"`, and `"-Infinity"` tokens.
-
-### Built-in representations
-
-| Java type | JSON representation |
-| ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
-| Enum | Constant name string |
-| `Date`, `Calendar`, `java.sql.Date`, `Time`, `Timestamp` | Epoch-millisecond number |
-| `TimeZone` | Time-zone ID string |
-| Java time and supported chronology date types | Standard textual string |
-| `UUID`, `URI`, `File`, `Path`, `Locale`, `Charset`, `Currency`, `Pattern` | Type-specific text string; `File` and `Path` use path text, `Locale` uses a language tag, and `Pattern` loses flags |
-| `BitSet` | Array of signed `long` words |
-| `ByteBuffer` | Array of signed byte values from position to limit |
-| Optional and atomic wrappers | Contained scalar, array, or value directly |
-
-`Calendar` is reconstructed as a new `GregorianCalendar`, so its original subtype, time zone, and
-other configuration are not retained. A null Optional reference and an empty Optional both write
-JSON null; reading JSON null as a declared Optional type returns the corresponding empty Optional.
-
-### Dynamic JSON trees
-
-Reading `Object.class` produces natural JSON values:
-
-| JSON | Java |
-| ------------------- | ------------------------------------------ |
-| Object | `JsonObject` |
-| Array | `JsonArray` |
-| String/boolean/null | `String`, `Boolean`, null |
-| Integer | `Long`, or `BigInteger` outside long range |
-| Fraction/exponent | `Double` |
-
-`JsonObject` preserves insertion order and `JsonArray` is mutable.
-
-### Map keys
-
-Declared keys support String, byte, short, int, long, their boxed types, and enums. `Object` keys
-can write String, number, boolean, character, and enum values, but read back as strings. Null keys
-are rejected.
-
-## Builder configuration
-
-| Method | Default | Effect |
-| ---------------------------- | -------------------------------------------- | ------------------------------------------------------ |
-| `writeNullFields` | `false` | Default null-property inclusion |
-| `withCodegen` | `true` | Generated object codecs |
-| `withAsyncCompilation` | `true` | Asynchronous generated-code compilation |
-| `withFieldMode` | `false` | Field-only discovery when true |
-| `withPropertyNamingStrategy` | `LOWER_CAMEL_CASE` | Naming of properties without explicit names |
-| `withClassLoader` | Snapshotted context loader, then Fory loader | Resolve annotation subtype class names |
-| `maxDepth` | `20` | Maximum nested object/array depth |
-| `withMaxCachedFieldNames` | `DEFAULT_MAX_CACHED_FIELD_NAMES` (`8192`) | Field-name cache entries per reader; zero disables it |
-| `withMaxGraphMemoryBytes` | `DEFAULT_MAX_GRAPH_MEMORY_BYTES` (128 MiB) | Approximate retained graph limit for each root read |
-| `withConcurrencyLevel` | `max(1, 2 * processors)` | Maximum concurrent root operations |
-| `withBufferSizeLimitBytes` | 2 MiB | Reusable capacity retained by each pooled writer |
-| `registerCodec` | None | Exact-class complete-value codec |
-| `registerMixin` | None | Annotation Mixin for its exact declared target |
-| `withTypeChecker` | None | Application policy in addition to Fory's disallow list |
-
-Depth, graph-memory, concurrency, and retained buffer limits must be positive. The
-cached-field-name limit applies independently to each reader; zero disables the cache, and the
-setting does not limit accepted input. The buffer setting does not limit output size. Builder
-changes after `build()` do not mutate an existing runtime.
-
-In a GraalVM native image, runtime compilation and asynchronous compilation are unavailable.
-Configurations returned by a reachable `ForyJsonProvider` use codecs generated while the image is
-built; other configurations use interpreted codecs with build-time-prepared access metadata. Every
-other builder option keeps the behavior described above.
-
-## Annotations
-
-Fory JSON provides `JsonProperty`, `JsonPropertyOrder`, `JsonIgnore`, `JsonAnyProperty`,
-`JsonAnyGetter`, `JsonAnySetter`, `JsonCreator`, `JsonCodec`, `JsonValue`, `JsonRawValue`,
-`JsonBase64`, `JsonFormat`, `JsonUnwrapped`, `JsonSubTypes`, and `JsonValidator` as mapping and
-validation annotations under
-`org.apache.fory.json.annotation`. `JsonType` is a separate build-time generation marker. They are
-not Jackson, Gson, or Fory binary-protocol annotations.
-
-```java
-import java.util.LinkedHashMap;
-import java.util.Map;
-import org.apache.fory.json.PropertyNamingStrategy;
-import org.apache.fory.json.annotation.JsonAnyGetter;
-import org.apache.fory.json.annotation.JsonAnyProperty;
-import org.apache.fory.json.annotation.JsonAnySetter;
-import org.apache.fory.json.annotation.JsonBase64;
-import org.apache.fory.json.annotation.JsonCodec;
-import org.apache.fory.json.annotation.JsonCreator;
-import org.apache.fory.json.annotation.JsonFormat;
-import org.apache.fory.json.annotation.JsonIgnore;
-import org.apache.fory.json.annotation.JsonMixin;
-import org.apache.fory.json.annotation.JsonMixinRemove;
-import org.apache.fory.json.annotation.JsonProperty;
-import org.apache.fory.json.annotation.JsonPropertyOrder;
-import org.apache.fory.json.annotation.JsonRawValue;
-import org.apache.fory.json.annotation.JsonSubTypes;
-import org.apache.fory.json.annotation.JsonType;
-import org.apache.fory.json.annotation.JsonValidator;
-import org.apache.fory.json.annotation.JsonValue;
-import org.apache.fory.json.annotation.JsonUnwrapped;
-```
-
-`JsonType` asks the annotation processor to generate direct property and creator operations plus
-the exact retention rules for an eligible concrete object model on the JVM and Android. A directly
-annotated `JsonValue` Record also receives a companion so its value accessor and canonical
-constructor work after Android desugaring. The annotation is not inherited; a concrete subtype
-needs its own direct annotation to receive a companion on those runtimes. A directly annotated
-model that uses the default object codec requires that generated companion outside Native Image;
-the runtime reports a configuration error if the processor output is missing.
-
-GraalVM Native Image discovers `JsonType` directly and does not use annotation-processor output.
-See [GraalVM Support](graalvm-support.md) for optional provider-based hosted code generation and
-[Android Support](android-support.md) for annotation-processor setup.
-
-### Mixins
-
-Use a Mixin to configure an existing class without changing its source:
-
-```java
-import org.apache.fory.json.ForyJson;
-import org.apache.fory.json.annotation.JsonMixin;
-import org.apache.fory.json.annotation.JsonProperty;
-import org.apache.fory.json.annotation.JsonUnwrapped;
-
-@JsonMixin(target = ThirdPartyUser.class)
-abstract class ThirdPartyUserMixin {
- @JsonProperty("user_id")
- long id;
-
- @JsonUnwrapped(prefix = "address_")
- Address address;
-}
-
-ForyJson json = ForyJson.builder().registerMixin(ThirdPartyUserMixin.class).build();
-```
-
-The source must be a named abstract class or interface, must not be local or anonymous, must not
-extend or implement another type, and is never instantiated. An annotated source field, method,
-constructor, or parameter selects an existing target declaration by its Java signature. The source
-cannot invent a Java field, method, constructor, generic type, subtype implementation, or executable
-body. It may assign supported JSON roles such as property, creator, factory, subtype table, or value
-representation to matching declarations that already exist on the target. Every Java type, access
-operation, invocation, and runtime value still comes from the target.
-
-Registration is exact-target only. A base-class registration does not change a subclass, and an
-interface registration does not change an implementation. A subclass Mixin may select a member
-that the subclass inherits, but the resulting annotation applies only while that exact subclass is
-mapped.
-
-All Fory JSON mapping and validation annotations are supported: `JsonAnyGetter`, `JsonAnyProperty`,
-`JsonAnySetter`, `JsonBase64`, `JsonCodec`, `JsonCreator`, `JsonFormat`, `JsonIgnore`, `JsonProperty`,
-`JsonPropertyOrder`, `JsonRawValue`, `JsonSubTypes`, `JsonUnwrapped`, `JsonValidator`, and
-`JsonValue`. `JsonType` cannot be added or removed because it controls build-time generation rather
-than the JSON schema.
-
-A source annotation replaces the target annotation of the same type on the matched declaration.
-The complete annotation is replaced, so omitted members use their declared defaults instead of
-inheriting values from the target annotation. Other annotation types on that target declaration
-remain effective.
-
-Use `JsonMixinRemove` to make selected target annotations ineffective:
-
-```java
-import org.apache.fory.json.annotation.JsonMixin;
-import org.apache.fory.json.annotation.JsonMixinRemove;
-import org.apache.fory.json.annotation.JsonRawValue;
-
-@JsonMixin(target = ThirdPartyMessage.class)
-abstract class QuotedMessageMixin {
- @JsonMixinRemove(JsonRawValue.class)
- String body;
-}
-```
-
-Removal affects only the matched declaration in the exact-target configuration. Removing
-`JsonRawValue` restores ordinary quoted String output; removing `JsonBase64` restores the ordinary
-`byte[]` representation; removing `JsonFormat` restores the ordinary date/time representation;
-removing `JsonUnwrapped` restores a nested object property. Type-level
-removal can mask inherited `JsonCodec` or `JsonPropertyOrder` declarations for the exact target.
-Removing an absent annotation is harmless, but the selector must still match exactly one target
-declaration. A source cannot both declare and remove the same annotation type on one declaration.
-
-Only one source is enabled for an exact target in a built runtime. Registering a different source
-for the same target replaces the earlier registration, while registering the same source again is
-idempotent. `build()` snapshots the current last-registration-wins mapping; a later registration on
-the builder does not change a previously built `ForyJson`. A source with no mapping or validation
-annotations is a no-op; registering it after another source for the same target clears the earlier
-overlay for subsequent builds.
-
-A `JsonCodec` supplied by a Mixin is the target's effective annotation and follows the ordinary
-codec precedence below. In particular, an exact `registerCodec` registration wins over a type-level
-Mixin codec, while a type-level Mixin codec wins over the built-in mapping for that target.
-
-Records use their existing field, accessor, and canonical-constructor parameter declarations. A
-Mixin does not introduce a separate record-component model. Use source selectors for those real
-declarations and keep repeated annotations consistent as required by normal record property
-mapping.
-
-On Android, compile non-empty Mixin sources with the Fory annotation processor so required
-generated operations and platform configuration are available. GraalVM Native Image discovers
-reachable Mixins directly. See [Android Support](android-support.md) and
-[GraalVM Support](graalvm-support.md).
-
-### `JsonProperty`
-
-An annotation on a field, getter, or setter configures the complete merged logical property:
-
-```java
-@JsonProperty("user_id")
-private long id;
-
-@JsonProperty(include = JsonProperty.Include.ALWAYS)
-private String displayName;
-
-@JsonProperty(index = 10)
-private String email;
-```
-
-Supported inclusion values are:
-
-- `DEFAULT`: inherit `writeNullFields`;
-- `ALWAYS`: include null;
-- `NON_NULL`: omit null.
-
-`index` controls relative serialization order. Indexed properties are written in ascending index
-order before unindexed properties. Indexes must be non-negative, may contain gaps, and must be
-unique among writable properties. `-1` means unspecified; lower values are invalid. An index on a
-setter-only, creator-only, or write-ignored property is invalid.
-
-Inclusion affects writing only. Identical repeated declarations are allowed; conflicting explicit
-names, indexes, or non-default policies fail. Two properties cannot normalize to the same JSON
-name. `JsonProperty` cannot be combined with an Any logical property or declared on a
-`JsonAnySetter`. `NON_EMPTY`, aliases, and formatting are unsupported.
-
-### `JsonPropertyOrder`
-
-Use `JsonPropertyOrder` to combine a named prefix, property indexes, and final-name alphabetic
-ordering:
-
-```java
-@JsonPropertyOrder(value = {"id", "display_name"}, alphabetic = true)
-public final class User {
- @JsonProperty(index = 20)
- public String name;
-
- @JsonProperty(value = "display_name", index = 10)
- public String displayName;
-
- public long id;
- public int age;
- public String address;
-}
-```
-
-The output order is `id`, `display_name`, `name`, `address`, then `age`:
-
-```json
-{ "id": 1, "display_name": "Alice", "name": "alice", "address": "x", "age": 30 }
-```
-
-The named prefix is written first. Remaining indexed properties follow in ascending index order.
-When `alphabetic = true`, remaining unindexed properties are sorted by final JSON name; otherwise
-they keep their existing relative order. Use `@JsonPropertyOrder(alphabetic = true)` when no named
-prefix is needed. Alphabetic comparison uses Java's natural, case-sensitive String order and does
-not depend on the locale.
-
-Order entries match the final JSON name first and the Java logical property name second. This lets
-`display_name` match an explicit `JsonProperty` name while an unannotated `displayName` can still be
-addressed by either `display_name` under `SNAKE_CASE` or its Java name `displayName`.
-
-The list may be empty only when `alphabetic` is true. Its entries must be non-empty, unique writable
-properties; unknown and duplicate entries fail when the object metadata is built. A subclass
-declaration replaces both settings from its superclass as a whole; declarations are not merged. If
-the subclass has no declaration, the nearest superclass declaration is used and resolved against the
-subclass properties. Interface declarations are not considered.
-
-Property order affects serialization only. Deserialization remains name-based and accepts members
-in any order. Subtype discriminators remain before user properties.
-
-An unwrapped group also occupies one position, selected by the group's Java logical property name.
-Its child members remain adjacent and retain the child's own order.
-
-A write-enabled `JsonAnyProperty` or `JsonAnyGetter` participates as one position identified by its
-Java logical property name:
-
-```java
-@JsonPropertyOrder({"id", "properties", "timestamp"})
-public final class Event {
- public String id;
-
- @JsonAnyProperty
- public Map<String, Object> properties;
-
- public long timestamp;
-}
-```
-
-The position emits every `properties` entry in Map iteration order between `id` and `timestamp`; it
-does not emit a member named `properties`. Naming strategies do not transform the Any ordering
-name. Input-only Any fields and `JsonAnySetter` have no write position. Dynamic keys cannot appear
-in `JsonPropertyOrder`, and alphabetic ordering never sorts entries inside the Map.
-
-### Naming strategy
-
-```java
-ForyJson json =
- ForyJson.builder()
- .withPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE)
- .build();
-```
-
-The default `LOWER_CAMEL_CASE` preserves the discovered Java logical property name. `SNAKE_CASE`
-maps `userName` to `user_name`, `URLValue` to `url_value`, and `version2FA` to `version2_fa`.
-Explicit `JsonProperty` names, parameter-local creator names, and subtype discriminator properties
-bypass the strategy. Dynamic Any keys also bypass it.
-
-### `JsonIgnore`
-
-`JsonIgnore` is field-targeted and controls both directions of the complete logical property:
-
-```java
-@JsonIgnore(ignoreRead = false, ignoreWrite = true)
-private String serverManagedValue;
-```
-
-Both flags default to true. Accessors cannot restore an ignored direction, and `JsonProperty`
-cannot override it. Fory core's `Expose` has no effect in Fory JSON.
-
-### `JsonValue`
-
-Use `JsonValue` when one exact `String` member is the complete JSON representation of its owning
-type. Fory writes it as an ordinary quoted and escaped JSON string instead of an object:
-
-```java
-import org.apache.fory.json.annotation.JsonCreator;
-import org.apache.fory.json.annotation.JsonValue;
-
-public final class UserId {
- private final String value;
-
- @JsonCreator
- public UserId(String value) {
- this.value = value;
- }
-
- @JsonValue
- public String value() {
- return value;
- }
-}
-```
-
-The method may use any name but must be public, non-static, zero-argument, and return exactly
-`String`. A field must be an eligible non-static instance field. Only one effective member is
-allowed, and an unannotated override suppresses an inherited method annotation.
-
-The annotation controls writing on its own. Reading requires a `JsonCreator` constructor or public
-static factory with one exact `String` parameter, empty `JsonCreator.value()`, and no `JsonProperty`
-on the parameter. This shape is inferred as the reverse String constructor; no creator mode is
-needed. Existing property-based creator forms are unchanged. JSON null maps directly to Java null
-without invoking the value member or creator.
-
-### `JsonRawValue`
-
-Use `JsonRawValue` on a fixed ordinary `String` property whose contents are already one complete
-JSON value:
-
-```java
-import org.apache.fory.json.annotation.JsonRawValue;
-
-public final class Response {
- public int status;
-
- @JsonRawValue
- public String body;
-}
-```
-
-For `body = "{\"id\":1}"`, Fory emits the object directly at the `body` value position. It does not
-quote, escape, parse, validate, or normalize the String. This is a trusted write-only escape hatch;
-invalid or attacker-controlled content can make the entire output invalid or change its structure.
-Java null follows the property's existing inclusion rule and is written as JSON null when included.
-
-Reading is unchanged and still expects a JSON string. A raw object or array written through the
-property cannot be read back into that `String`. The annotation does not apply to setters, creator
-parameters, Any declarations, container elements, or Map values, and it cannot share an occurrence
-with `JsonCodec` or `JsonFormat`.
-As an occurrence-local representation, it keeps the raw String shape even when the value type has
-an exact builder-registered codec.
-
-Neither annotation collects unknown sibling fields. Unknown fields are skipped unless an existing
-`JsonAnyProperty` or `JsonAnyGetter`/`JsonAnySetter` mapping owns them. Combining `JsonValue` and
-`JsonRawValue` on the same String member writes the owning object as a trusted raw root value, but
-does not add a raw-fragment read contract.
-
-### `JsonBase64`
-
-Use `JsonBase64` on one exact `byte[]` field or getter to represent it as a quoted standard Base64
-JSON string:
-
-```java
-import org.apache.fory.json.annotation.JsonBase64;
-
-public final class Attachment {
- @JsonBase64
- public byte[] content;
-}
-```
-
-Bytes `{1, 2, 3}` are written as `{"content":"AQID"}` and decoded back to the original array.
-Encoding and decoding do not create an intermediate String. Null handling follows the property's
-normal inclusion rule.
-
-The annotation is not a type-use annotation and does not affect ordinary `byte[]` properties,
-container elements, or Map values. It cannot share a logical property with `JsonRawValue`, an
-occurrence `JsonCodec`, `JsonFormat`, or an Any declaration. The equivalent explicit codec is
-`@JsonCodec(Base64ByteArrayCodec.class)`.
-
-### `JsonFormat`
-
-Use `JsonFormat` on a date/time field to select its JSON text pattern in both directions. Patterns
-use `DateTimeFormatter` syntax and the root locale:
-
-```java
-import java.time.Instant;
-import java.time.LocalDate;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import org.apache.fory.json.annotation.JsonFormat;
-
-public final class Schedule {
- @JsonFormat(pattern = "dd/MM/uuuu")
- public LocalDate day;
-
- @JsonFormat(pattern = "dd/MM/uuuu")
- public Optional<LocalDate> optionalDay;
-
- @JsonFormat(pattern = "dd/MM/uuuu")
- public List<LocalDate> days;
-
- @JsonFormat(pattern = "dd/MM/uuuu")
- public Map<String, LocalDate> daysByName;
-
- @JsonFormat(pattern = "uuuu-MM-dd HH:mm:ss XXX", timezone = "Asia/Shanghai")
- public Instant timestamp;
-}
-```
-
-For `day = LocalDate.of(2024, 1, 2)`, the property is written as `"day":"02/01/2024"` and
-the same text reads back to that date. The annotation applies to the field value when it is a
-supported date/time type. For one direct wrapper, it applies to an array or collection element, an
-`AtomicReferenceArray` element, an `Optional` or `AtomicReference` content value, or a Map value.
-This includes `List`, `Set`, and their concrete `Collection` implementations. Null handling still
-follows the property's ordinary inclusion rule.
-
-Supported values are exact `LocalDate`, `LocalTime`, `LocalDateTime`, `Instant`, `ZonedDateTime`,
-`Year`, `YearMonth`, `MonthDay`, `OffsetTime`, `OffsetDateTime`, `HijrahDate`, `JapaneseDate`,
-`MinguoDate`, and `ThaiBuddhistDate` types. `Instant` uses UTC; zoned and offset types use the zone or
-offset carried by the value. The pattern must contain enough information to reconstruct the
-declared type.
-
-Set `timezone` to a valid `ZoneId` identifier to format and parse `Instant`, `ZonedDateTime`, or
-`OffsetDateTime` in that zone. For example, the `timestamp` field above writes
-`Instant.parse("2024-01-02T03:04:05Z")` as `"2024-01-02 11:04:05 +08:00"`. The parsed value keeps
-the same instant for matching timezone text. The configured zone supplies missing zone or offset
-information during parsing; an explicit zone or offset in the JSON text participates in the usual
-`DateTimeFormatter` resolution. Include an offset in the pattern when an exact instant must survive
-a daylight saving time overlap. Omitting `timezone` preserves the default behavior described
-above. Invalid zone identifiers and a non-empty `timezone` on other supported date/time types are
-rejected.
-
-`JsonFormat` is a field annotation, not a type-use annotation. A record component works through its
-generated field. Nested wrappers, Map keys, raw or wildcard direct children, JSON Any values, and
-unwrapped values are intentionally rejected. Types with ambiguous formatting semantics, including
-legacy and SQL date types, `Duration`, `Period`, `TimeZone`, `ZoneId`, and `ZoneOffset`, are not
-supported. A wrapper with a complete registered, annotation-selected, polymorphic, or `JsonValue`
-representation is also rejected because that representation owns the whole wrapper.
-`JsonFormat` cannot share a field with `JsonCodec`, `JsonBase64`, `JsonRawValue`, `JsonAnyProperty`,
-`JsonUnwrapped`, or `JsonValue`.
-
-### `JsonUnwrapped`
-
-Use `JsonUnwrapped` when an object-valued property should keep its Java object boundary but place
-its members in the containing JSON object:
-
-```java
-import org.apache.fory.json.annotation.JsonUnwrapped;
-
-public final class Person {
- public int age;
-
- @JsonUnwrapped(prefix = "name_")
- public Name name;
-}
-
-public final class Name {
- public String first;
- public String last;
-}
-```
-
-This writes `{"age":18,"name_first":"Ada","name_last":"Lovelace"}` instead of a
-nested `name` object. `prefix` and `suffix` apply to every final child name after `JsonProperty` and
-the configured naming strategy. Nested groups compose these transformations from the inside out.
-
-A null child emits no members. During reading, the child is created only when at least one
-flattened member is present. A missing group preserves a mutable parent's initialized value and
-leaves a record or creator argument at its normal missing-property default. Partial input creates
-the child and uses ordinary defaults for its other members.
-
-Mutable classes, records, and `JsonCreator` classes are supported as parents and children. A
-parameter-local creator parameter can define a read-only group; its required `JsonProperty` value
-identifies the Java argument and is not a wrapper name. The containing parent may be parameterized,
-but each unwrapped child and intermediate must be an exact raw, non-generic class using the standard
-Fory object mapping.
-
-The complete group occupies one position in parent serialization order. Position it with
-`JsonProperty.index`, or list its Java logical property name in `JsonPropertyOrder`. Child ordering
-is preserved inside the group. Input matches parent fixed properties first, flattened properties
-second, and dynamic Any members last.
-
-Fory rejects duplicate final names, recursive chains made only of unwrapped properties,
-parameterized children, JSON Any children, polymorphic or custom-codec child roots, and scalar,
-array, collection, or Map children. Flatten Maps with `JsonAnyProperty`, `JsonAnyGetter`, or
-`JsonAnySetter`. An unwrapped property cannot use `JsonProperty.value`, a non-default
-`JsonProperty.include`, `JsonCodec`, or `JsonFormat`; ordinary leaf properties inside the child keep their normal
-annotations.
-
-### Dynamic object members
-
-Use `JsonAnyProperty` to flatten a `Map<String, V>` field into the containing JSON object and store
-otherwise unknown input members:
-
-```java
-public final class Event {
- public String id;
-
- @JsonAnyProperty
- public Map<String, Object> properties = new LinkedHashMap<>();
-}
-```
-
-For `properties` containing `"source" -> "mobile"`, the result contains `"source":"mobile"`
-beside `id`; no nested `properties` member is written. The field reads and writes by default.
-`JsonIgnore` may select one direction, but it cannot disable both. During reading, Fory reuses an
-existing Map or initializes a null non-final field on the first unknown member. A readable final
-field on an ordinary mutable object must already contain a mutable Map. Records and property-list
-`JsonCreator` types instead receive the accumulated Map through their construction argument.
-
-Use `JsonAnyGetter` and `JsonAnySetter` for method-backed writing and reading:
-
-```java
-public final class Event {
- private final Map<String, Object> properties = new LinkedHashMap<>();
-
- @JsonAnyGetter
- public Map<String, Object> getProperties() {
- return properties;
- }
-
- @JsonAnySetter
- public void putProperty(String name, Object value) {
- properties.put(name, value);
- }
-}
-```
-
-An any-getter must be a public instance method with no arguments and a `Map<String, V>` return type.
-An any-setter must be a public instance method with signature `void method(String, V)`. Either may
-be used independently. When paired, their resolved value types must match after primitive types are
-boxed. A primitive any-setter value rejects JSON null. Any-setters are not supported on records or
-types using `JsonCreator`.
-
-A read-enabled `JsonAnyProperty` on a record component supplies that component from unknown input.
-In property-list creator mode, a read-enabled Any field must correspond to one listed creator
-argument; parameter-local creator mode cannot bind it. A write-only Any field or any-getter cannot
-occupy a creator argument. If one claims a record component, that component receives its normal
-Java default during reading.
-
-An any-getter claims its complete Java logical property: both `getProperties()` and `properties()`
-claim `properties`, so same-named ordinary fields and accessors are not mapped again as a fixed
-member. Fory does not infer a differently named backing field; use `JsonIgnore` if that field must
-not be mapped separately. An any-setter has no logical property name and does not claim a field.
-
-The logical name is used only for grouping and `JsonPropertyOrder`; it is not a fixed JSON member.
-An input member with that name is a dynamic entry rather than a nested aggregate. The same output
-key remains valid unless another fixed property conflicts with it.
-
-One effective type hierarchy may use either one Any field or up to one any-getter and one
-any-setter. Field-backed and method-backed forms cannot be mixed, and method annotations are invalid
-in field mode. An unannotated override disables an inherited method annotation. `JsonProperty` is
-invalid on an any-setter and on every member claimed by an Any field or getter. A same-named field
-cannot use `JsonIgnore` to suppress an any-getter's write direction, and its `ignoreRead` flag does
-not disable a separate any-setter.
-
-Dynamic keys are emitted unchanged in Map iteration order. A null Map emits nothing, while a null
-Map value emits JSON null regardless of fixed-property null settings. Null and non-String output
-keys are rejected. Raw Maps, wildcard or unresolved keys, and non-String key types are invalid.
-Declared fixed members, including members excluded from reading, are not delivered to an Any
-input. An output key that conflicts with a fixed property is rejected. Fory does not inspect an Any
-Map for a key that duplicates an inline subtype discriminator; such a key emits a duplicate JSON
-member. Applications must keep dynamic keys distinct from the active discriminator. Repeated
-unknown names replace the Map value; an any-setter is called for every occurrence. Escaped input
-names are decoded before delivery.
-
-### `JsonCreator`
-
-The compact mode lists existing Java logical property names in parameter order and reuses their
-normalized metadata:
-
-```java
-public final class User {
- public final long id;
- public final String name;
-
- @JsonCreator({"id", "name"})
- public User(long id, String name) {
- this.id = id;
- this.name = name;
- }
-}
-```
-
-Parameter-local mode gives every parameter an explicit JSON name and permits creator-only inputs:
-
-```java
-@JsonCreator
-public static User create(
- @JsonProperty("user_id") long id,
- @JsonProperty("display_name") String name) {
- return new User(id, name);
-}
-```
-
-The modes cannot be mixed. Compact names must be non-empty and unique, their count must match the
-parameter count, and compact parameters cannot also declare `JsonProperty`. Parameter-local mode
-requires a non-empty, unique `JsonProperty` name on every parameter. The creator is the complete
-read schema and setters do not run after it.
-
-For a type with `JsonValue`, the empty form also accepts exactly one `String` parameter without
-`JsonProperty` and reconstructs the owning value from its ordinary JSON string representation.
-
-Exactly one creator is allowed. It must be public, have at least one parameter, and be neither
-varargs nor generic. A factory is also static, declares the target class as its exact return type,
-and returns a non-null value whose runtime class is exactly the target. Missing references use null,
-missing primitives use zero, duplicate members use the last value, and explicit primitive null
-fails. Records cannot declare a property-based `JsonCreator`; a record with `JsonValue` may annotate
-its one-String canonical constructor for the value form.
-
-### `JsonValidator`
-
-Use `JsonValidator` to run application validation after an object is completely constructed and
-populated:
-
-```java
-public final class Account {
- public String id;
- public long balance;
-
- @JsonValidator
- public void validate() {
- if (id == null || id.isEmpty() || balance < 0) {
- throw new IllegalArgumentException("invalid account");
- }
- }
-}
-```
-
-A validator must be a public concrete instance method with no arguments and a `void` return type.
-Every effective validator runs exactly once after its object is complete, including creator-built
-objects, records, nested and unwrapped objects, and selected subtypes. JSON null skips validation.
-Multiple validators have no guaranteed order, and the first failure stops validation. `Error` is
-propagated; other invocation failures become `ForyJsonException` with the original cause.
-`JsonValidator` has no index or ordering member. A `JsonCreator` may instead enforce the invariant
-during construction.
-
-A Mixin can add a validator to a matching public target method:
-
-```java
-@JsonMixin(target = ThirdPartyAccount.class)
-abstract class ThirdPartyAccountMixin {
- @JsonValidator
- public abstract void checkValid();
-}
-```
-
-The Mixin method must match the target method's exact name, parameters, and return type. Use
-`@JsonMixinRemove(JsonValidator.class)` on a matching source method to remove the target annotation
-for that configuration. An unannotated override does not inherit the overridden method's
-validator.
-
-Validation applies to the default object mapping. A complete codec selected through
-`registerCodec` or a type-level `JsonCodec`, and a complete `JsonValue` representation, performs
-its own validation. On Android, compile direct validator models with `JsonType` and the Fory
-annotation processor; a Mixin validator uses the generated exact Mixin-target pair. GraalVM Native
-Image discovers a direct `JsonType` or registered Mixin and prepares its effective validators
-without annotation-processor output.
-
-### `JsonSubTypes`
-
-`JsonSubTypes` defines a complete finite table on an interface or abstract base. Each entry has a
-case-sensitive logical name and exactly one Java source: a class literal or trusted binary
-`className`. JSON never supplies class names or expands the table.
-
-Default property inclusion:
-
-```java
-@JsonSubTypes(
- property = "kind",
- value = {
- @JsonSubTypes.Type(value = Circle.class, name = "circle"),
- @JsonSubTypes.Type(
- className = "com.example.shape.Rectangle",
- name = "rectangle")
- })
-public interface Shape {}
-```
-
-```json
-{ "kind": "circle", "radius": 2 }
-```
-
-The discriminator is emitted first but may appear at any direct input member position. It must
-occur exactly once, contain a known String name, and not collide with a subtype property.
-
-Wrapper object:
-
-```java
-@JsonSubTypes(
- inclusion = JsonSubTypes.Inclusion.WRAPPER_OBJECT,
- value = {@JsonSubTypes.Type(value = Circle.class, name = "circle")})
-public interface Shape {}
-```
-
-```json
-{ "circle": { "radius": 2 } }
-```
-
-Wrapper array:
-
-```java
-@JsonSubTypes(
- inclusion = JsonSubTypes.Inclusion.WRAPPER_ARRAY,
- value = {@JsonSubTypes.Type(value = Circle.class, name = "circle")})
-public interface Shape {}
-```
-
-```json
-["circle", { "radius": 2 }]
-```
-
-| Inclusion | `property` | Value rule |
-| ---------------- | ---------------------- | ----------------------------------- |
-| `PROPERTY` | Required and non-empty | Ordinary subtype object members |
-| `WRAPPER_OBJECT` | Must be empty | Complete subtype value |
-| `WRAPPER_ARRAY` | Must be empty | Complete subtype value as element 1 |
-
-Wrapper inclusions support exact custom subtype codecs. Logical names and resolved concrete,
-assignable classes must each be unique. Only exact listed runtime classes are accepted; listing a
-parent does not admit descendants. The annotation is read from the declared base itself and is not
-inherited from another annotated base. Null is plain JSON null unless codec precedence selects a
-custom complete-value codec for the declared base, replacing the annotation. Readers accept only
-the configured shape, so changing inclusion is a wire-format change. At GraalVM native-image
-runtime, annotate the base with `JsonType` and use class literals instead of `className`. Listed
-class-literal subtypes are registered automatically.
-
-## Custom codecs
-
-`JsonValueCodec<T>` reads and writes one complete JSON value, including null, through Fory's
-concrete String/UTF-8 writers and Latin-1/UTF-16/UTF-8 readers. It is a direct streaming-value SPI,
-not a JSON abstract syntax tree (AST) codec. It never handles Map keys; `MapKeyCodec` owns JSON
-object member names.
-
-For an application codec with the same semantics in every representation, extend
-`AbstractJsonValueCodec<T>` and implement the JSON shape once:
-
-```java
-import java.math.BigDecimal;
-import org.apache.fory.json.codec.AbstractJsonValueCodec;
-import org.apache.fory.json.reader.JsonReader;
-import org.apache.fory.json.writer.JsonWriter;
-
-public final class MoneyCodec extends AbstractJsonValueCodec<Money> {
- @Override
- public void write(JsonWriter writer, Money value) {
- if (value == null) {
- writer.writeNull();
- } else {
- writer.writeBigDecimal(value.amount);
- }
- }
-
- @Override
- public Money read(JsonReader reader) {
- return reader.tryReadNullToken() ? null : new Money(reader.readBigDecimal());
- }
-}
-
-final class Money {
- final BigDecimal amount;
-
- Money(BigDecimal amount) {
- this.amount = amount;
- }
-}
-```
-
-`AbstractJsonValueCodec` adds one virtual method call per operation. For a
-performance-sensitive codec, or when behavior depends on a concrete reader or writer, implement
-`JsonValueCodec<T>` directly and provide all five representation-specific methods.
-
-Register it once:
-
-```java
-import org.apache.fory.json.ForyJson;
-
-ForyJson json =
- ForyJson.builder()
- .registerCodec(Money.class, new MoneyCodec())
- .build();
-```
-
-The parent property still controls its name, ignore direction, and null inclusion before the codec
-runs. An emitted property and every array, collection, map-value, Optional, or atomic-reference
-position delegate the complete value, including null. The registered codec instance is shared
-concurrently and must be thread-safe. A custom codec on a subtype is compatible with wrapper
-inclusion, not inline property inclusion. A codec on the base replaces its `JsonSubTypes`
-annotation.
-
-A custom codec that materializes composite graph owners must call `JsonReader.reserveGraphMemory`
-with an application-defined byte estimate before creating each owner. This applies to composite
-application objects, collections, maps, and reference arrays. Reserve collection and map reference
-storage before the mutation that may grow it. A custom scalar or other dedicated leaf
-representation, such as `MoneyCodec` above, needs no reservation. Complete codecs also run any
-required application validation themselves; Fory JSON does not wrap a complete custom
-representation with the target type's `JsonValidator` methods.
-
-### Selecting codecs with `JsonCodec`
-
-Use `@JsonCodec` on a class, record, enum, or interface to declare its default complete-value
-codec. The positional form is shorthand for `value`:
-
-```java
-@JsonCodec(MoneyCodec.class)
-public final class Money {}
-
-@JsonCodec(AccountCodec.class)
-public interface Account {}
-
-public final class RetailAccount implements Account {}
-```
-
-Type declarations are inherited through both superclasses and interfaces. The most-specific
-declaration wins. Unrelated declarations using the same codec are consistent; unrelated
-declarations using different codecs fail instead of depending on reflection order.
-
-On a field or effective ordinary getter, `value` replaces the complete property value. The same
-annotation is supported on an effective setter value parameter, a `JsonCreator` constructor or
-factory parameter, and a record component through Java's field, accessor, and constructor-parameter
-propagation:
-
-```java
-public final class Invoice {
- @JsonCodec(MoneyCodec.class)
- public Money total;
- private Money tax;
- private Money discount;
-
- @JsonCodec(MoneyCodec.class)
- public Money getTax() {
- return tax;
- }
-
- public void setDiscount(@JsonCodec(MoneyCodec.class) Money discount) {
- this.discount = discount;
- }
-
- @JsonCreator
- public Invoice(@JsonProperty("total") @JsonCodec(MoneyCodec.class) Money total) {
- this.total = total;
- }
-}
-```
-
-Use a child member when the standard container should remain in control and only its direct child
-needs a custom codec:
-
-```java
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.concurrent.atomic.AtomicReferenceArray;
-
-public final class InvoiceGroup {
- @JsonCodec(elementCodec = MoneyCodec.class)
- public List<Money> items;
-
- @JsonCodec(elementCodec = MoneyCodec.class)
- public Money[] itemArray;
-
- @JsonCodec(elementCodec = MoneyCodec.class)
- public AtomicReferenceArray<Money> atomicItems;
-
- @JsonCodec(contentCodec = MoneyCodec.class)
- public Optional<Money> optional;
-
- @JsonCodec(contentCodec = MoneyCodec.class)
- public AtomicReference<Money> current;
-
- @JsonCodec(keyCodec = CurrencyKeyCodec.class, valueCodec = MoneyCodec.class)
- public Map<Currency, Money> byCurrency;
-}
-```
-
-The child members have these meanings:
-
-| Member | Supported current value | Direct child handled by the codec |
-| -------------- | ------------------------------------------------- | --------------------------------- |
-| `elementCodec` | `Collection<E>`, `E[]`, `AtomicReferenceArray<E>` | `E` |
-| `contentCodec` | `Optional<T>`, `AtomicReference<T>` | `T` |
-| `keyCodec` | `Map<K, V>` | JSON member name for `K` |
-| `valueCodec` | `Map<K, V>` | direct `V` value |
-
-A custom Map-key codec converts between the declared key and a JSON member name:
-
-```java
-import java.util.Locale;
-import org.apache.fory.json.codec.MapKeyCodec;
-
-public final class CurrencyKeyCodec implements MapKeyCodec {
- @Override
- public String toName(Object key) {
- return ((Currency) key).name().toLowerCase(Locale.ROOT);
- }
-
- @Override
- public Object fromName(String name) {
- return Currency.valueOf(name.toUpperCase(Locale.ROOT));
- }
-}
-```
-
-Code that used the removed type-use form should move the codec to the owning declaration:
-
-```java
-// Before
-List<@JsonCodec(MoneyCodec.class) Money> items;
-
-// Now
-@JsonCodec(elementCodec = MoneyCodec.class)
-List<Money> items;
-```
-
-Use `contentCodec` for an `Optional` or `AtomicReference`, `valueCodec` for a Map value, and
-`elementCodec` for an array or `AtomicReferenceArray` element.
-
-`Iterable<E>` values that are not `Collection<E>` do not support `elementCodec`. Use `value` when a
-complete codec should own such a value.
-
-Child configuration is intentionally one level deep. For `List<List<Money>>`, `elementCodec`
-handles each complete `List<Money>`. For `Money[][]`, it handles each `Money[]`. To customize a
-deeper descendant, implement a codec for the complete current value and select it with `value`.
-
-`value` is mutually exclusive with every child member because it already owns the complete current
-value. An empty annotation, an unsupported child member, or an outer complete codec combined with
-a child member fails during model construction. A configured direct child must resolve to a
-concrete type; raw containers, direct wildcards, and unresolved direct type variables are rejected.
-
-`JsonAnyProperty` and `JsonAnyGetter` flatten their Map into the enclosing object. Configure their
-dynamic values with `valueCodec`:
-
-```java
-@JsonAnyProperty
-@JsonCodec(valueCodec = MoneyCodec.class)
-public Map<String, Money> extra;
-```
-
-The first `JsonAnySetter` parameter is the String property name. Its second parameter may use
-`@JsonCodec(value = ...)` or another configuration valid for that parameter's own shape.
-
-### Codec precedence and repeated declarations
-
-Fory resolves each current value in this order:
-
-| Priority | Source |
-| -------: | ------------------------------------------------- |
-| 1 | Current property or parameter `JsonCodec` |
-| 2 | Exact `registerCodec` registration |
-| 3 | Exact-target type `JsonCodec` after Mixin overlay |
-| 4 | Inherited-frontier type `JsonCodec` declaration |
-| 5 | Built-in or default JSON mapping |
-
-One logical property may expose the annotation from its field, getter, setter parameter, creator
-parameter, or record propagation. Repeated configurations must be identical; Fory does not merge
-partial configurations from different declarations. An unannotated effective override suppresses
-an inherited method annotation.
-
-A child member replaces only that direct child. Unconfigured Map siblings continue through the
-normal precedence. If an exact registration or type declaration supplies a complete codec for the
-outer container, a property child member is unreachable and therefore rejected.
-
-Map keys are JSON object member names and use `MapKeyCodec`, not `JsonValueCodec`. A custom key
-codec class follows the same construction rules as a value codec. Null Map keys are rejected, and
-decoded keys must match the declared key type.
-
-### Codec construction and platform support
-
-An annotation codec class must be public, concrete, top-level or static nested, and have a public
-no-argument constructor. One instance is shared by all annotated sites and concurrent operations of
-the built `ForyJson`, so it must be thread-safe. Use `registerCodec(Target.class, instance)` when a
-complete-value codec needs configuration.
-
-Outside GraalVM Native Image, a named Java module must export or open the codec package to
-`org.apache.fory.json`. Native Image prepares annotation-codec constructors during image
-construction and does not require that package access. When an inherited type-declaration codec is
-used for a more specific target, every decoded value must be null or assignable to that target.
-
-The annotation has the same FIELD, METHOD, and PARAMETER behavior on the JVM, Android, and GraalVM
-Native Image. Ordinary Android classes may omit `JsonType` and provide equivalent exact rules.
-Android-desugared Records, including `JsonValue` Records, require processor-generated operations
-from either a direct `JsonType` declaration or a compiled exact `JsonMixin` pair. GraalVM object
-models follow the build-time workflow in
-[GraalVM Support](graalvm-support.md).
-
-## Type validation and untrusted input
-
-Fory JSON always applies its fixed disallow list. Add an application policy with:
-
-```java
-ForyJson json =
- ForyJson.builder()
- .withTypeChecker(
- (className, context) ->
- className.startsWith("com.example.model.")
- || className.equals("java.util.List")
- || className.equals("java.util.Map"))
- .build();
-```
-
-Allow every application model and non-built-in container type that the declared schema uses. The
-checker applies while application types are prepared for serialization and parsing and must be
-thread-safe. Built-in scalars normally skip the custom checker, but selecting an application codec
-for a built-in target makes that target subject to the checker. Custom codecs cannot bypass the
-fixed disallow list.
-
-`withClassLoader` fixes subtype `className` resolution. Otherwise `build()` snapshots the thread
-context class loader and falls back to the Fory JSON loader.
-
-`maxDepth` is not an input-size or memory quota. `withMaxGraphMemoryBytes` separately limits the
-approximate retained graph created by each root read and defaults to a fixed 128 MiB. Every String
-or UTF-8 byte-array root starts with the full configured limit, including after a prior success or
-failure.
-
-The graph budget includes shallow POJO and record storage, collections and sets plus candidate
-element-reference slots, maps plus candidate key/value-reference slots, reference arrays plus their
-slots, and Java primitive arrays plus their primitive storage. Natural `JsonObject` and `JsonArray`
-values use the same rules. Repeated set elements and duplicate or overwritten map members consume
-candidate-slot budget for every occurrence. Dedicated leaves are excluded: null, strings,
-characters, booleans, numbers including big numbers, enums, temporal and other scalar values, and
-binary values. A `byte[]` decoded from a JSON numeric array counts as a primitive array, while a
-`byte[]` handled by a binary or Base64 codec remains a binary leaf. A reference array still counts
-when its elements are leaves, and an object still counts when all properties are leaves.
-`AtomicReference`, `AtomicReferenceArray`, and generic `Optional<T>` values include wrapper and
-reference storage; primitive optionals and atomic primitive values are leaves.
-
-This is a portable approximate estimate, not exact JVM heap accounting. Custom-codec allocations
-that the codec does not reserve, application constructor or validator work, temporary parsing
-storage, and other process memory can exceed the limit. Enforce request size, timeout, and resource
-limits at the transport boundary. `Class`, `URL`, `InetAddress`, and `InetSocketAddress` are
-unsupported by default. URL and arbitrary unsupported Number/CharSequence subclasses require exact
-custom codecs.
-
-## Limits and unsupported features
-
-- No shared-reference identity or circular-reference protocol. Use Fory binary when needed.
-- No open polymorphism, JSON class-name IDs, subtype discovery, or runtime subtype-table extension.
-- No InputStream parser, incremental `OutputStream` writer on the `ForyJson` root API, or
- pretty-print configuration.
-- No Jackson/Gson annotation compatibility.
-- No aliases, views, filters, injection, managed/back references, object identity annotations, or
- root wrapping.
-- Fory core's `Expose` is ignored.
-
-Circular graphs eventually fail `maxDepth`; they are not reconstructed.
-
-## Errors and troubleshooting
-
-| Symptom | Action |
-| ---------------------------------- | ----------------------------------------------------------------------------------------------- |
-| `ForyJsonException` | Check grammar, target type, mapping, depth, graph memory, validation, trailing input, or output |
-| `InsecureException` | Check Fory's disallow list and the configured type checker |
-| Builder `IllegalArgumentException` | Check depth, graph-memory, concurrency, retained-buffer, and cached-field-name limits |
-| Declared write fails | Remove wildcard/type variables and pass an assignable value; primitive declarations reject null |
-| Immutable value is empty | Use a record, valid creator, or custom codec |
-| `JsonValue` read fails | Add one plain `String` creator, or register an exact custom codec |
-| Raw JSON output is invalid | Supply one trusted, complete JSON value to the `JsonRawValue` property |
-| Ordinary object cannot be created | Add a usable no-argument constructor, use a record or creator, or register a codec |
-| Ordinary accessor annotation fails | Use an eligible public JavaBean accessor and disable field mode |
-| Any annotation fails | Use one field form or one valid method pair with resolved `Map<String, V>` types |
-| Codec annotation fails | Resolve same-node or hierarchy conflicts, hidden nested overrides, or codec constructor access |
-| Subtype fails | Write with the declared base, list the exact runtime class, and use the configured wire shape |
-| Collection fails | Target a supported interface/common implementation or register a codec |
-
-Creator and validator failures other than `Error` are wrapped with their original cause. User codec
-code may still throw its own runtime exceptions.
-
-## Related Java guides
-
-- [Java serialization overview](index.md)
-- [Native binary serialization](native-serialization.md)
-- [Xlang binary serialization](xlang-serialization.md)
-- [Java configuration](configuration.md)
-- [Troubleshooting](troubleshooting.md)
diff --git a/docs/guide/xlang/_category_.json b/docs/guide/xlang/_category_.json
deleted file mode 100644
index ab2d183de..000000000
--- a/docs/guide/xlang/_category_.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "label": "Cross Language",
- "position": 12,
- "collapsible": true,
- "collapsed": true
-}
diff --git a/docs/guide/xlang/getting-started.md b/docs/guide/xlang/getting-started.md
deleted file mode 100644
index f27ce8c09..000000000
--- a/docs/guide/xlang/getting-started.md
+++ /dev/null
@@ -1,465 +0,0 @@
----
-title: Getting Started
-sidebar_position: 10
-id: getting_started
-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 guide covers installation and basic setup for cross-language serialization in all supported languages.
-
-## Installation
-
-### Java
-
-**Maven:**
-
-```xml
-<dependency>
- <groupId>org.apache.fory</groupId>
- <artifactId>fory-core</artifactId>
- <version>1.5.0</version>
-</dependency>
-```
-
-**Gradle:**
-
-```gradle
-implementation 'org.apache.fory:fory-core:1.5.0'
-```
-
-### Python
-
-```bash
-pip install pyfory
-```
-
-### Go
-
-```bash
-go get github.com/apache/fory/go/fory
-```
-
-### Rust
-
-```toml
-[dependencies]
-fory = "1.5.0"
-```
-
-### JavaScript/TypeScript
-
-```bash
-npm install @apache-fory/core
-```
-
-For the optional Node.js string fast path:
-
-```bash
-npm install @apache-fory/core @apache-fory/hps
-```
-
-### C\#
-
-```bash
-dotnet add package Apache.Fory --version 1.5.0
-```
-
-### Dart
-
-```bash
-dart pub add fory:^1.5.0
-dart pub add dev:build_runner
-```
-
-### Swift
-
-Add Fory to `Package.swift`:
-
-```swift
-dependencies: [
- .package(url: "https://github.com/apache/fory.git", exact: "1.5.0")
-]
-```
-
-### Scala
-
-```scala
-libraryDependencies += "org.apache.fory" %% "fory-scala" % "1.5.0"
-```
-
-### Kotlin
-
-```kotlin
-implementation("org.apache.fory:fory-kotlin:1.5.0")
-```
-
-### C++
-
-Use Bazel or CMake to build from source. See [C++ Guide](../cpp/index.md) for details.
-
-## Create an Xlang Fory Instance
-
-Xlang mode is the default for implementations 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
-
-```java
-import org.apache.fory.*;
-import org.apache.fory.config.*;
-
-Fory fory = Fory.builder()
- .withXlang(true)
- .withRefTracking(true) // Optional: for circular references
- .build();
-```
-
-### Python
-
-```python
-import pyfory
-
-fory = pyfory.Fory(xlang=True)
-
-# Enable reference tracking when needed
-fory = pyfory.Fory(xlang=True, ref=True)
-```
-
-### Go
-
-```go
-import forygo "github.com/apache/fory/go/fory"
-
-fory := forygo.NewFory(forygo.WithXlang(true))
-// Or with reference tracking
-fory := forygo.NewFory(forygo.WithXlang(true), forygo.WithTrackRef(true))
-```
-
-### Rust
-
-```rust
-use fory::Fory;
-
-let fory = Fory::builder().xlang(true).build();
-```
-
-### JavaScript/TypeScript
-
-```javascript
-import Fory, { Type } from "@apache-fory/core";
-
-const fory = new Fory();
-```
-
-### C\#
-
-```csharp
-using Apache.Fory;
-
-Fory fory = Fory.Builder().Build();
-```
-
-### Dart
-
-```dart
-import 'package:fory/fory.dart';
-
-final fory = Fory();
-```
-
-### Swift
-
-```swift
-import Fory
-
-let fory = Fory()
-```
-
-### Scala
-
-```scala
-import org.apache.fory.scala.ForyScala
-
-val fory = ForyScala.builder()
- .withXlang(true)
- .build()
-```
-
-### Kotlin
-
-```kotlin
-import org.apache.fory.kotlin.ForyKotlin
-
-val fory = ForyKotlin.builder()
- .withXlang(true)
- .build()
-```
-
-### C++
-
-```cpp
-#include "fory/serialization/fory.h"
-
-using namespace fory::serialization;
-
-auto fory = Fory::builder().xlang(true).build();
-```
-
-## Type Registration
-
-Custom types must be registered with consistent names or IDs across all languages.
-
-### Register by Name (Recommended)
-
-Using string names is more flexible and less prone to conflicts:
-
-**Java:**
-
-```java
-fory.register(Person.class, "example.Person");
-```
-
-**Python:**
-
-```python
-fory.register_type(Person, name="example.Person")
-```
-
-**Go:**
-
-```go
-fory.RegisterStructByName(Person{}, "example.Person")
-```
-
-**Rust:**
-
-```rust
-use fory::{Fory, ForyStruct};
-
-#[derive(ForyStruct)]
-struct Person {
- name: String,
- age: i32,
-}
-
-let mut fory = Fory::builder().xlang(true).build();
-fory
- .register_by_name::<Person>("example.Person")
- .expect("register Person");
-```
-
-**JavaScript/TypeScript:**
-
-```javascript
-const personType = Type.struct(
- { typeName: "example.Person" },
- {
- name: Type.string(),
- age: Type.int32(),
- },
-);
-const { serialize, deserialize } = fory.register(personType);
-```
-
-**C++:**
-
-```cpp
-fory.register_struct<Person>("example.Person");
-// For enums, use register_enum:
-// fory.register_enum<Color>("example.Color");
-```
-
-**C#:**
-
-```csharp
-fory.Register<Person>("example.Person");
-```
-
-**Dart:**
-
-```dart
-PersonForyModule.register(
- fory,
- Person,
- name: 'example.Person',
-);
-```
-
-**Swift:**
-
-```swift
-try fory.register(Person.self, name: "example.Person")
-```
-
-**Scala:**
-
-```scala
-fory.register(classOf[Person], "example.Person")
-```
-
-**Kotlin:**
-
-```kotlin
-fory.register(Person::class.java, "example.Person")
-```
-
-### Register by ID
-
-Using numeric IDs is faster and produces smaller binary output:
-
-**Java:**
-
-```java
-fory.register(Person.class, 100);
-```
-
-**Python:**
-
-```python
-fory.register_type(Person, type_id=100)
-```
-
-**Go:**
-
-```go
-fory.RegisterStruct(Person{}, 100)
-```
-
-**Rust:**
-
-```rust
-fory.register::<Person>(100)?;
-```
-
-**JavaScript/TypeScript:**
-
-```javascript
-const personType = Type.struct(
- { typeId: 100 },
- {
- name: Type.string(),
- age: Type.int32(),
- },
-);
-```
-
-**C++:**
-
-```cpp
-fory.register_struct<Person>(100);
-// For enums, use register_enum:
-// fory.register_enum<Color>(101);
-```
-
-**C#:**
-
-```csharp
-fory.Register<Person>(100);
-```
-
-**Dart:**
-
-```dart
-PersonForyModule.register(fory, Person, id: 100);
-```
-
-**Swift:**
-
-```swift
-fory.register(Person.self, id: 100)
-```
-
-**Scala:**
-
-```scala
-fory.register(classOf[Person], 100)
-```
-
-**Kotlin:**
-
-```kotlin
-fory.register(Person::class.java, 100)
-```
-
-## Hello World Example
-
-A complete example showing serialization in Java and deserialization in Python:
-
-### Java (Serializer)
-
-```java
-import org.apache.fory.*;
-import org.apache.fory.config.*;
-import java.nio.file.*;
-
-public class Person {
- public String name;
- public int age;
-}
-
-public class HelloWorld {
- public static void main(String[] args) throws Exception {
- Fory fory = Fory.builder().withXlang(true).build();
- fory.register(Person.class, "example.Person");
-
- Person person = new Person();
- person.name = "Alice";
- person.age = 30;
-
- byte[] bytes = fory.serialize(person);
- Files.write(Path.of("person.bin"), bytes);
- System.out.println("Serialized to person.bin");
- }
-}
-```
-
-### Python (Deserializer)
-
-```python
-import pyfory
-from dataclasses import dataclass
-
-@dataclass
-class Person:
- name: str
- age: pyfory.Int32
-
-fory = pyfory.Fory(xlang=True)
-fory.register_type(Person, name="example.Person")
-
-with open("person.bin", "rb") as f:
- data = f.read()
-
-person = fory.deserialize(data)
-print(f"Name: {person.name}, Age: {person.age}")
-# Output: Name: Alice, Age: 30
-```
-
-## Best Practices
-
-1. **Use consistent type names**: Ensure all languages use the same type name or ID
-2. **Enable reference tracking**: If your data has circular or shared references
-3. **Reuse Fory instances**: Creating Fory is expensive; reuse instances
-4. **Use type annotations**: In Python, use markers such as `pyfory.Int32` for precise type mapping
-5. **Test cross-language**: Verify serialization works across all target languages
-
-## Next Steps
-
-- [Type Mapping](../../specification/xlang_type_mapping.md) - Cross-language type mapping reference
-- [Serialization](serialization.md) - Detailed serialization examples
-- [Troubleshooting](troubleshooting.md) - Common issues and solutions
diff --git a/docs/guide/xlang/row_format.md b/docs/guide/xlang/row_format.md
deleted file mode 100644
index 9ff1e4baa..000000000
--- a/docs/guide/xlang/row_format.md
+++ /dev/null
@@ -1,239 +0,0 @@
----
-title: Row Format
-sidebar_position: 60
-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.
----
-
-Fory Row Format is a cache-friendly binary format designed for efficient random access. Unlike object graph serialization, Row Format allows readers to access individual fields without reconstructing the complete object.
-
-## Features
-
-- **Zero-Copy Random Access**: Read specific fields directly from binary data
-- **Selective Access**: Read only the fields or collection elements an application needs
-- **Cross-Language Compatible**: Standard Row Format data can be shared between Java, Python, C++, and Rust
-- **Apache Arrow Integration**: Convert row format to/from Arrow RecordBatch for analytics (Java/Python)
-
-| Format | Implementations | Purpose |
-| ------------------- | ----------------------- | -------------------------- |
-| Standard Row Format | Java, Python, C++, Rust | Cross-language interchange |
-| Compact Row Format | Java | Smaller Java-only rows |
-
-## Java
-
-```java
-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;
-// Can be zero-copy read by Python
-BinaryRow binaryRow = encoder.toRow(foo);
-// Can be data from Python
-Foo newFoo = encoder.fromRow(binaryRow);
-// Zero-copy read List<Integer> f2
-BinaryArray binaryArray2 = binaryRow.getArray(1);
-// Zero-copy read List<Bar> f4
-BinaryArray binaryArray4 = binaryRow.getArray(3);
-// Zero-copy read 11th element of List<Bar> f4
-BinaryRow barStruct = binaryArray4.getStruct(10);
-
-// Zero-copy read 6th element of f2 of 11th element of List<Bar> f4
-barStruct.getArray(1).getInt64(5);
-RowEncoder<Bar> barEncoder = Encoders.bean(Bar.class);
-// Deserialize part of data
-Bar newBar = barEncoder.fromRow(barStruct);
-Bar newBar2 = barEncoder.fromRow(binaryArray4.getStruct(20));
-```
-
-## Python
-
-```python
-import datetime
-import pickle
-from dataclasses import dataclass
-from typing import Dict, List
-
-import pyfory
-
-@dataclass
-class Bar:
- f1: str
- f2: List[pyfory.Int64]
-@dataclass
-class Foo:
- f1: pyfory.Int32
- f2: List[pyfory.Int32]
- f3: Dict[str, pyfory.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)])
-binary: bytes = encoder.to_row(foo).to_bytes()
-print(f"start: {datetime.datetime.now()}")
-foo_row = pyfory.RowData(encoder.schema, binary)
-print(foo_row.f2[100000], foo_row.f4[100000].f1, foo_row.f4[200000].f2[5])
-print(f"end: {datetime.datetime.now()}")
-
-binary = pickle.dumps(foo)
-print(f"pickle start: {datetime.datetime.now()}")
-new_foo = pickle.loads(binary)
-print(new_foo.f2[100000], new_foo.f4[100000].f1, new_foo.f4[200000].f2[5])
-print(f"pickle end: {datetime.datetime.now()}")
-```
-
-## Rust
-
-Rust derives a static Row Format schema from a named struct. Struct fields use source declaration order, and `Option<T>` declares nullable fields or array elements. The Rust type used by `from_row` must match the producer's field types and order.
-
-```rust
-use fory::{from_row, to_row, Error, ForyRow};
-
-#[derive(ForyRow)]
-struct Metric {
- id: i64,
- label: String,
- samples: Vec<f64>,
- note: Option<String>,
-}
-
-fn main() -> Result<(), Error> {
- let bytes = to_row(&Metric {
- id: 7,
- label: "latency".to_string(),
- samples: vec![1.5, 2.0],
- note: None,
- })?;
-
- let row = from_row::<Metric>(&bytes)?;
- assert_eq!(row.id()?, 7);
- assert_eq!(row.label()?, "latency");
- assert_eq!(row.note()?, None);
-
- let samples = row.samples()?;
- assert_eq!(samples.get(1)?, 2.0);
- Ok(())
-}
-```
-
-Rust reads and writes the exact Standard Row Format, including natural-width primitive array elements, little-endian offset-size slots, null bitmaps, and 8-byte zero padding. See the [Rust Row Format Guide](../rust/row-format.md) for supported types and API details.
-
-## Apache Arrow Support
-
-Fory Row Format supports automatic conversion from/to Arrow Table/RecordBatch for analytics workloads.
-
-### Java
-
-```java
-Schema schema = TypeInference.inferSchema(BeanA.class);
-ArrowWriter arrowWriter = ArrowUtils.createArrowWriter(schema);
-Encoder<BeanA> encoder = Encoders.rowEncoder(BeanA.class);
-for (int i = 0; i < 10; i++) {
- BeanA beanA = BeanA.createBeanA(2);
- arrowWriter.write(encoder.toRow(beanA));
-}
-return arrowWriter.finishAsRecordBatch();
-```
-
-### Python
-
-```python
-import pyfory
-encoder = pyfory.encoder(Foo)
-encoder.to_arrow_record_batch([foo] * 10000)
-encoder.to_arrow_table([foo] * 10000)
-```
-
-## Support for Interface and Extension Types
-
-Fory supports row format mapping for Java `interface` types and subclassed (`extends`) types, enabling more dynamic and flexible data schemas.
-
-These enhancements were introduced in [#2243](https://github.com/apache/fory/pull/2243), [#2250](https://github.com/apache/fory/pull/2250), and [#2256](https://github.com/apache/fory/pull/2256).
-
-### Example: Interface Mapping with RowEncoder
-
-```java
-public interface Animal {
- String speak();
-}
-
-public class Dog implements Animal {
- public String name;
-
- @Override
- public String speak() {
- return "Woof";
- }
-}
-
-// Encode and decode using RowEncoder with interface type
-RowEncoder<Animal> encoder = Encoders.bean(Animal.class);
-Dog dog = new Dog();
-dog.name = "Bingo";
-BinaryRow row = encoder.toRow(dog);
-Animal decoded = encoder.fromRow(row);
-System.out.println(decoded.speak()); // Woof
-```
-
-### Example: Extension Type with RowEncoder
-
-```java
-public class Parent {
- public String parentField;
-}
-
-public class Child extends Parent {
- public String childField;
-}
-
-// Encode and decode using RowEncoder with parent class type
-RowEncoder<Parent> encoder = Encoders.bean(Parent.class);
-Child child = new Child();
-child.parentField = "Hello";
-child.childField = "World";
-BinaryRow row = encoder.toRow(child);
-Parent decoded = encoder.fromRow(row);
-```
-
-## See Also
-
-- [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
-- [Rust Row Format Guide](../rust/row-format.md) - Rust-specific row format documentation
diff --git a/docs/guide/xlang/serialization.md b/docs/guide/xlang/serialization.md
deleted file mode 100644
index 7e5914f1e..000000000
--- a/docs/guide/xlang/serialization.md
+++ /dev/null
@@ -1,596 +0,0 @@
----
-title: Serialization
-sidebar_position: 30
-id: serialization
-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 demonstrates common cross-language serialization patterns. Data serialized in one
-supported language can be deserialized in any other supported language when peers use matching type
-identity, field schema, and compatibility settings.
-
-## Remote Schema Metadata Limits
-
-Compatible mode may receive remote metadata (`TypeDef` or `TypeMeta`) for types that are not already
-known by the reader. Fory limits how many distinct remote metadata versions can be accepted, and
-also limits the size of each received metadata body:
-
-- `maxSchemaVersionsPerType`: maximum accepted remote metadata versions for one logical type. The
- default is `10`.
-- `maxAverageSchemaVersionsPerType`: average accepted remote metadata versions across all accepted
- remote types. The default is `3`; the effective global floor is `8192` metadata entries.
-- `maxTypeFields`: maximum fields declared by one received struct metadata body. The default is
- `512`.
-- `maxTypeMetaBytes`: maximum encoded metadata body bytes for one received TypeDef or TypeMeta body,
- excluding the 8-byte header and any extended-size varint. The default is `4096`.
-
-These limits are resource protections. They do not change wire format, registration requirements,
-dynamic type loading, unknown-type handling, or schema-evolution compatibility.
-
-Raise these values only when the data is not malicious and a trusted peer sends larger metadata or
-many schema versions.
-
-| Language | Field-count option | Metadata-bytes option | Per-type option | Average option |
-| --------------------- | ------------------- | ---------------------- | ------------------------------ | -------------------------------------- |
-| Java | `withMaxTypeFields` | `withMaxTypeMetaBytes` | `withMaxSchemaVersionsPerType` | `withMaxAverageSchemaVersionsPerType` |
-| Scala | `withMaxTypeFields` | `withMaxTypeMetaBytes` | `withMaxSchemaVersionsPerType` | `withMaxAverageSchemaVersionsPerType` |
-| Kotlin | `withMaxTypeFields` | `withMaxTypeMetaBytes` | `withMaxSchemaVersionsPerType` | `withMaxAverageSchemaVersionsPerType` |
-| Python | `max_type_fields` | `max_type_meta_bytes` | `max_schema_versions_per_type` | `max_average_schema_versions_per_type` |
-| JavaScript/TypeScript | `maxTypeFields` | `maxTypeMetaBytes` | `maxSchemaVersionsPerType` | `maxAverageSchemaVersionsPerType` |
-| C++ | `max_type_fields` | `max_type_meta_bytes` | `max_schema_versions_per_type` | `max_average_schema_versions_per_type` |
-| Go | `WithMaxTypeFields` | `WithMaxTypeMetaBytes` | `WithMaxSchemaVersionsPerType` | `WithMaxAverageSchemaVersionsPerType` |
-| Rust | `max_type_fields` | `max_type_meta_bytes` | `max_schema_versions_per_type` | `max_average_schema_versions_per_type` |
-| C# | `MaxTypeFields` | `MaxTypeMetaBytes` | `MaxSchemaVersionsPerType` | `MaxAverageSchemaVersionsPerType` |
-| Swift | `maxTypeFields` | `maxTypeMetaBytes` | `maxSchemaVersionsPerType` | `maxAverageSchemaVersionsPerType` |
-| Dart | `maxTypeFields` | `maxTypeMetaBytes` | `maxSchemaVersionsPerType` | `maxAverageSchemaVersionsPerType` |
-
-## Count-driven Container Work Limit
-
-Every runtime limits collection elements and map entries whose repeated read
-bodies do not consume proportional input. The default root allowance is `8192`.
-Zero is a strict limit, and negative values are rejected. Raise the limit only
-for trusted payloads that intentionally use compact zero-byte element codecs or
-empty Struct bodies. This is a reader resource limit and does not change the
-wire format or writer behavior.
-
-| Language | Option |
-| --------------------- | ------------------------------- |
-| Java | `withMaxUnbackedContainerItems` |
-| Scala | `withMaxUnbackedContainerItems` |
-| Kotlin | `withMaxUnbackedContainerItems` |
-| Python | `max_unbacked_container_items` |
-| JavaScript/TypeScript | `maxUnbackedContainerItems` |
-| C++ | `max_unbacked_container_items` |
-| Go | `WithMaxUnbackedContainerItems` |
-| Rust | `max_unbacked_container_items` |
-| C# | `MaxUnbackedContainerItems` |
-| Swift | `maxUnbackedContainerItems` |
-| Dart | `maxUnbackedContainerItems` |
-
-## Serialize Built-in Types
-
-Common types can be serialized automatically without registration: primitive numeric types, string, binary, array, list, map, and more.
-
-Reduced-precision floating-point values are also part of the built-in xlang type system:
-
-- `float16` and `array<float16>`
-- `bfloat16` and `array<bfloat16>`
-
-Use the language-specific carrier types documented in the type mapping reference. Python uses `pyfory.Float16` and `pyfory.BFloat16` as annotation markers only; scalar values are native Python `float`, and dense reduced-precision arrays use `pyfory.Float16Array` and `pyfory.BFloat16Array`. Go uses the `float16` and `bfloat16` packages for scalar, slice, and array carriers; JavaScript uses `number` for scalar `float16` and `bfloat16`, and dense array carriers `BoolArray`, `Float16Array`, [...]
-
-When `compatible=true`, a direct struct/class field can evolve between `list<T>` and `array<T>` for dense bool/numeric `T`. Integer list element encodings in the same signedness and width domain match the corresponding dense array element domain. This applies only to the immediate matched field schema. It does not apply to nested collection, map, array, union, or generic positions. A peer `list<T?>` schema can be read into a local `array<T>` field when the actual payload has no null elem [...]
-
-### Java
-
-```java
-import org.apache.fory.*;
-import org.apache.fory.config.*;
-
-import java.util.*;
-
-public class Example1 {
- public static void main(String[] args) {
- Fory fory = Fory.builder().withXlang(true).build();
- List<Object> 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
- fory.deserialize(bytes);
- Map<Object, Object> map = new HashMap<>();
- map.put("k1", "v1");
- map.put("k2", list);
- map.put("k3", -1);
- bytes = fory.serialize(map);
- // bytes can be deserialized by other languages
- fory.deserialize(bytes);
- }
-}
-```
-
-### Python
-
-```python
-import pyfory
-import numpy as np
-
-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)
-# bytes can be deserialized by other languages
-new_list = fory.deserialize(data)
-object_map = {"k1": "v1", "k2": object_list, "k3": -1}
-data = fory.serialize(object_map)
-# bytes can be deserialized by other languages
-new_map = fory.deserialize(data)
-print(new_map)
-```
-
-### Go
-
-```go
-package main
-
-import forygo "github.com/apache/fory/go/fory"
-import "fmt"
-
-func main() {
- list := []any{true, false, "str", -1.1, 1, make([]int32, 10), make([]float64, 20)}
- fory := forygo.NewFory(forygo.WithXlang(true))
- bytes, err := fory.Marshal(list)
- if err != nil {
- panic(err)
- }
- var newValue any
- // bytes can be deserialized by other languages
- if err := fory.Unmarshal(bytes, &newValue); err != nil {
- panic(err)
- }
- fmt.Println(newValue)
- dict := map[string]any{
- "k1": "v1",
- "k2": list,
- "k3": -1,
- }
- bytes, err = fory.Marshal(dict)
- if err != nil {
- panic(err)
- }
- // bytes can be deserialized by other languages
- if err := fory.Unmarshal(bytes, &newValue); err != nil {
- panic(err)
- }
- fmt.Println(newValue)
-}
-```
-
-### JavaScript
-
-```javascript
-import Fory from "@apache-fory/core";
-
-const fory = new Fory();
-const input = fory.serialize("hello fory");
-const result = fory.deserialize(input);
-console.log(result);
-```
-
-### Rust
-
-```rust
-use fory::Fory;
-
-fn run() {
- 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);
-}
-```
-
-## Serialize Custom Types
-
-User-defined types must be registered using the register API to establish the mapping relationship between types in different languages. Use consistent type names across all languages.
-
-### Java
-
-```java
-import org.apache.fory.*;
-import org.apache.fory.config.*;
-import java.util.*;
-
-public class Example2 {
- public static class SomeClass1 {
- Object f1;
- Map<Byte, Integer> f2;
- }
-
- public static class SomeClass2 {
- Object f1;
- String f2;
- List<Object> f3;
- Map<Byte, Integer> f4;
- Byte f5;
- Short f6;
- Integer f7;
- Long f8;
- Float f9;
- Double f10;
- short[] f11;
- List<Short> f12;
- }
-
- public static Object createObject() {
- SomeClass1 obj1 = new SomeClass1();
- obj1.f1 = true;
- obj1.f2 = ofHashMap((byte) -1, 2);
- SomeClass2 obj = new SomeClass2();
- obj.f1 = obj1;
- obj.f2 = "abc";
- obj.f3 = ofArrayList("abc", "abc");
- obj.f4 = ofHashMap((byte) 1, 2);
- obj.f5 = Byte.MAX_VALUE;
- obj.f6 = Short.MAX_VALUE;
- obj.f7 = Integer.MAX_VALUE;
- obj.f8 = Long.MAX_VALUE;
- obj.f9 = 1.0f / 2;
- obj.f10 = 1 / 3.0;
- obj.f11 = new short[]{(short) 1, (short) 2};
- obj.f12 = ofArrayList((short) -1, (short) 4);
- return obj;
- }
-
- // mvn exec:java -Dexec.mainClass="org.apache.fory.examples.Example2"
- public static void main(String[] args) {
- Fory fory = Fory.builder().withXlang(true).build();
- fory.register(SomeClass1.class, "example.SomeClass1");
- fory.register(SomeClass2.class, "example.SomeClass2");
- byte[] bytes = fory.serialize(createObject());
- // bytes can be deserialized by other languages
- System.out.println(fory.deserialize(bytes));
- }
-}
-```
-
-### Python
-
-```python
-from dataclasses import dataclass
-from typing import List, Dict, Any
-import pyfory, array
-
-
-@dataclass
-class SomeClass1:
- f1: Any
- f2: Dict[pyfory.Int8, pyfory.Int32]
-
-
-@dataclass
-class SomeClass2:
- f1: Any = None
- f2: str = None
- f3: List[str] = None
- f4: Dict[pyfory.Int8, pyfory.Int32] = None
- f5: pyfory.Int8 = None
- f6: pyfory.Int16 = None
- f7: pyfory.Int32 = None
- # int type will be taken as `pyfory.Int64`.
- # use `pyfory.Int32` for type hint if peer uses more narrow type.
- f8: int = None
- f9: pyfory.Float32 = None
- # float type will be taken as `pyfory.Float64`
- f10: float = None
- f11: pyfory.Array[pyfory.Int16] = None
- f12: List[pyfory.Int16] = None
-
-
-if __name__ == "__main__":
- f = pyfory.Fory(xlang=True)
- f.register_type(SomeClass1, name="example.SomeClass1")
- f.register_type(SomeClass2, name="example.SomeClass2")
- obj1 = SomeClass1(f1=True, f2={-1: 2})
- obj = SomeClass2(
- f1=obj1,
- f2="abc",
- f3=["abc", "abc"],
- f4={1: 2},
- f5=2 ** 7 - 1,
- f6=2 ** 15 - 1,
- f7=2 ** 31 - 1,
- f8=2 ** 63 - 1,
- f9=1.0 / 2,
- f10=1 / 3.0,
- f11=array.array("h", [1, 2]),
- f12=[-1, 4],
- )
- data = f.serialize(obj)
- # bytes can be deserialized by other languages
- print(f.deserialize(data))
-```
-
-### Go
-
-```go
-package main
-
-import forygo "github.com/apache/fory/go/fory"
-import "fmt"
-
-func main() {
- type SomeClass1 struct {
- F1 any
- F2 map[int8]int32
- }
-
- type SomeClass2 struct {
- F1 any
- F2 string
- F3 []any
- F4 map[int8]int32
- F5 int8
- F6 int16
- F7 int32
- F8 int64
- F9 float32
- F10 float64
- F11 []int16
- F12 []int16
- }
- serializer := forygo.NewFory(forygo.WithXlang(true))
- if err := serializer.RegisterStructByName(SomeClass1{}, "example.SomeClass1"); err != nil {
- panic(err)
- }
- if err := serializer.RegisterStructByName(SomeClass2{}, "example.SomeClass2"); err != nil {
- panic(err)
- }
- obj1 := &SomeClass1{F1: true, F2: map[int8]int32{-1: 2}}
- obj := &SomeClass2{
- F1: obj1,
- F2: "abc",
- F3: []any{"abc", "abc"},
- F4: map[int8]int32{1: 2},
- F5: 127,
- F6: 32767,
- F7: 2147483647,
- F8: 9223372036854775807,
- F9: 1.0 / 2,
- F10: 1.0 / 3.0,
- F11: []int16{1, 2},
- F12: []int16{-1, 4},
- }
- bytes, err := serializer.Marshal(obj)
- if err != nil {
- panic(err)
- }
- var newValue any
- // bytes can be deserialized by other languages
- if err := serializer.Unmarshal(bytes, &newValue); err != nil {
- panic(err)
- }
- fmt.Println(newValue)
-}
-```
-
-### JavaScript
-
-```javascript
-import Fory, { Type } from "@apache-fory/core";
-
-// Describe data structures using JSON schema
-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);
-```
-
-### Rust
-
-```rust
-use chrono::{NaiveDate, NaiveDateTime};
-use fory::{Fory, ForyStruct};
-use std::collections::HashMap;
-
-#[test]
-fn complex_struct() {
- #[derive(ForyStruct, Debug, PartialEq)]
- struct Animal {
- category: String,
- }
-
- #[derive(ForyStruct, Debug, PartialEq)]
- struct Person {
- c1: Vec<u8>, // binary
- c2: Vec<i16>, // primitive array
- animal: Vec<Animal>,
- c3: Vec<Vec<u8>>,
- name: String,
- c4: HashMap<String, String>,
- age: u16,
- op: Option<String>,
- op2: Option<String>,
- date: NaiveDate,
- time: NaiveDateTime,
- c5: f32,
- c6: f64,
- }
- let person: Person = Person {
- c1: vec![1, 2, 3],
- c2: vec![5, 6, 7],
- c3: vec![vec![1, 2], vec![1, 3]],
- animal: vec![Animal {
- category: "Dog".to_string(),
- }],
- c4: HashMap::from([
- ("hello1".to_string(), "hello2".to_string()),
- ("hello2".to_string(), "hello3".to_string()),
- ]),
- age: 12,
- name: "helo".to_string(),
- op: Some("option".to_string()),
- op2: None,
- date: NaiveDate::from_ymd_opt(2025, 12, 12).unwrap(),
- time: NaiveDateTime::from_timestamp_opt(1689912359, 0).unwrap(),
- c5: 2.0,
- c6: 4.0,
- };
-
- let mut fory = Fory::builder().xlang(true).build();
- fory
- .register_by_name::<Animal>("example.foo2")
- .expect("register Animal");
- fory
- .register_by_name::<Person>("example.foo")
- .expect("register Person");
- let bin = fory.serialize(&person).expect("serialize success");
- let obj: Person = fory.deserialize(&bin).expect("deserialize success");
- assert_eq!(person, obj);
-}
-```
-
-## Serialize Shared and Circular References
-
-Shared references and circular references can be serialized automatically with no duplicate data or recursion errors. Enable reference tracking to use this feature.
-
-### Java
-
-```java
-import org.apache.fory.*;
-import org.apache.fory.config.*;
-import java.util.*;
-
-public class ReferenceExample {
- public static class SomeClass {
- SomeClass f1;
- Map<String, String> f2;
- Map<String, String> f3;
- }
-
- public static Object createObject() {
- SomeClass obj = new SomeClass();
- obj.f1 = obj;
- obj.f2 = ofHashMap("k1", "v1", "k2", "v2");
- obj.f3 = obj.f2;
- return obj;
- }
-
- // mvn exec:java -Dexec.mainClass="org.apache.fory.examples.ReferenceExample"
- public static void main(String[] args) {
- 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
- System.out.println(fory.deserialize(bytes));
- }
-}
-```
-
-### Python
-
-```python
-from typing import Dict
-import pyfory
-
-class SomeClass:
- f1: "SomeClass"
- f2: Dict[str, str]
- f3: Dict[str, str]
-
-fory = pyfory.Fory(xlang=True, ref=True)
-fory.register_type(SomeClass, name="example.SomeClass")
-obj = SomeClass()
-obj.f2 = {"k1": "v1", "k2": "v2"}
-obj.f1, obj.f3 = obj, obj.f2
-data = fory.serialize(obj)
-# bytes can be deserialized by other languages
-print(fory.deserialize(data))
-```
-
-### Go
-
-```go
-package main
-
-import forygo "github.com/apache/fory/go/fory"
-import "fmt"
-
-func main() {
- type SomeClass struct {
- F1 *SomeClass
- F2 map[string]string
- F3 map[string]string
- }
- fory := forygo.NewFory(forygo.WithXlang(true), forygo.WithTrackRef(true))
- if err := fory.RegisterStruct(SomeClass{}, 65); 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)
- if err != nil {
- panic(err)
- }
- var newValue any
- // bytes can be deserialized by other languages
- if err := fory.Unmarshal(bytes, &newValue); err != nil {
- panic(err)
- }
- fmt.Println(newValue)
-}
-```
-
-### JavaScript
-
-```javascript
-import Fory, { Type } from "@apache-fory/core";
-
-const description = Type.struct("example.foo", {
- foo: Type.string(),
- bar: Type.struct("example.foo").setTrackingRef(true),
-});
-
-const fory = new Fory({ ref: true });
-const { serialize, deserialize } = fory.register(description);
-const data: any = {
- foo: "hello fory",
-};
-data.bar = data;
-const input = serialize(data);
-const result = deserialize(input);
-console.log(result.bar.foo === result.foo);
-```
-
-### Rust
-
-Circular references cannot be implemented in Rust due to ownership restrictions.
-
-## See Also
... 11757 lines suppressed ...
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@fory.apache.org
For additional commands, e-mail: commits-help@fory.apache.org