From 1618b8e0e1c417c8e9772651e006f29a08c522a9 Mon Sep 17 00:00:00 2001 From: chaokunyang Date: Tue, 10 Feb 2026 16:03:18 +0800 Subject: [PATCH] fix(i18n): track zh-CN specification docs to prevent english fallback --- .gitignore | 6 + .../specification/java_serialization_spec.md | 381 ++++++++++++ .../current/specification/row_format_spec.md | 356 +++++++++++ .../xlang_implementation_guide.md | 251 ++++++++ .../specification/xlang_serialization_spec.md | 558 ++++++++++++++++++ .../specification/xlang_type_mapping.md | 146 +++++ .../specification/java_serialization_spec.md | 381 ++++++++++++ .../specification/row_format_spec.md | 356 +++++++++++ .../xlang_implementation_guide.md | 251 ++++++++ .../specification/xlang_serialization_spec.md | 558 ++++++++++++++++++ .../specification/xlang_type_mapping.md | 146 +++++ 11 files changed, 3390 insertions(+) create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/specification/java_serialization_spec.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/specification/row_format_spec.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/specification/xlang_implementation_guide.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/specification/xlang_serialization_spec.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/current/specification/xlang_type_mapping.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-0.15/specification/java_serialization_spec.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-0.15/specification/row_format_spec.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-0.15/specification/xlang_implementation_guide.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-0.15/specification/xlang_serialization_spec.md create mode 100644 i18n/zh-CN/docusaurus-plugin-content-docs/version-0.15/specification/xlang_type_mapping.md diff --git a/.gitignore b/.gitignore index 36792b22df..a27d557c01 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,13 @@ .cache-loader # Generated i18n specification folders (copied by prebuild script) +# Keep fallback-generated specification folders ignored by default. i18n/zh-CN/docusaurus-plugin-content-docs/*/specification/ +# Explicitly track manually translated specification docs for current and latest release. +!i18n/zh-CN/docusaurus-plugin-content-docs/current/specification/ +!i18n/zh-CN/docusaurus-plugin-content-docs/current/specification/*.md +!i18n/zh-CN/docusaurus-plugin-content-docs/version-0.15/specification/ +!i18n/zh-CN/docusaurus-plugin-content-docs/version-0.15/specification/*.md # Misc .DS_Store diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/specification/java_serialization_spec.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/specification/java_serialization_spec.md new file mode 100644 index 0000000000..959eeb2b6b --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/specification/java_serialization_spec.md @@ -0,0 +1,381 @@ +--- +title: Java 序列化格式 +sidebar_position: 1 +id: java_serialization_spec +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. +--- + +本文定义 Apache Fory Java 原生序列化格式的编码格式细节。 + +## 规范概览 + +Fory Java 原生格式面向 Java 对象图,支持: + +- 共享引用与循环引用 +- 多态对象 +- 可选 schema 演进 +- 流式写入(共享 type meta 按需内联,不要求预先 meta 区) + +Java native 是 xlang 编码格式的扩展,复用相同核心帧与基础编码。 + +总体布局: + +``` +| fory header | object ref meta | object type meta | object value data | +``` + +字节序统一为 little-endian。大端平台实现需在数组等路径做字节序转换,确保线上格式一致。 + +## Fory 头部 + +Java 原生序列化头部是 1-byte 位图: + +``` +| 5 bits | 1 bit | 1 bit | 1 bit | ++--------------+-------+-------+-------+ +| reserved | oob | xlang | null | +``` + +含义: + +- `null`:对象是否为 null(为 1 时其余位不应置位) +- `xlang`:1 表示 xlang 格式,0 表示 Java native +- `oob`:1 表示存在 `BufferCallback` + +头部始终 1 字节,不额外写 language ID。 + +## 引用元信息 + +引用跟踪标记与 xlang 一致: + +| 标记 | 字节值 | 说明 | +| -------------------- | ------ | ------------------------------------------------------ | +| NULL FLAG | `-3` | 对象为 null,后续不写对象内容 | +| REF FLAG | `-2` | 已出现对象,后接 `varuint32` 的 reference ID | +| NOT_NULL VALUE FLAG | `-1` | 非空但该类型未启用引用跟踪,后接对象内容 | +| REF VALUE FLAG | `0` | 可引用对象首次出现,后接对象内容,并分配新 reference ID | + +当全局或字段级禁用引用跟踪时,仅使用 `NULL FLAG` 与 `NOT_NULL VALUE FLAG`。 + +## Type system and type IDs + +Java native 使用与 xlang 统一的类型 ID 组合方式: + +``` +full_type_id = (user_type_id << 8) | internal_type_id +``` + +- `internal_type_id`:低 8 位,表示类型种类 +- `user_type_id`:用户注册数值 ID(适用于用户 enum/struct/ext) +- named 类型使用 `NAMED_*`,其标识来自元信息(命名空间+类型名) + +### Shared internal type IDs (0-63) + +Java native 与 xlang 共享 `< 64` 的 internal IDs: + +- `0~56` 已定义 +- `57~63` 预留 + +详见 [Xlang Serialization Format](xlang_serialization_spec.md#internal-type-id-table)。 + +### Java native built-in type IDs + +Java 专有内建类型从 `Types.BOUND + 5` 开始(`Types.BOUND=64`,预留 5 个)。 + +| Type ID | Name | Description | +| ------- | -------------------------- | ------------------------------ | +| 69 | VOID_ID | java.lang.Void | +| 70 | CHAR_ID | java.lang.Character | +| 71 | PRIMITIVE_VOID_ID | void | +| 72 | PRIMITIVE_BOOL_ID | boolean | +| 73 | PRIMITIVE_INT8_ID | byte | +| 74 | PRIMITIVE_CHAR_ID | char | +| 75 | PRIMITIVE_INT16_ID | short | +| 76 | PRIMITIVE_INT32_ID | int | +| 77 | PRIMITIVE_FLOAT32_ID | float | +| 78 | PRIMITIVE_INT64_ID | long | +| 79 | PRIMITIVE_FLOAT64_ID | double | +| 80 | PRIMITIVE_BOOLEAN_ARRAY_ID | boolean[] | +| 81 | PRIMITIVE_BYTE_ARRAY_ID | byte[] | +| 82 | PRIMITIVE_CHAR_ARRAY_ID | char[] | +| 83 | PRIMITIVE_SHORT_ARRAY_ID | short[] | +| 84 | PRIMITIVE_INT_ARRAY_ID | int[] | +| 85 | PRIMITIVE_FLOAT_ARRAY_ID | float[] | +| 86 | PRIMITIVE_LONG_ARRAY_ID | long[] | +| 87 | PRIMITIVE_DOUBLE_ARRAY_ID | double[] | +| 88 | STRING_ARRAY_ID | String[] | +| 89 | OBJECT_ARRAY_ID | Object[] | +| 90 | ARRAYLIST_ID | java.util.ArrayList | +| 91 | HASHMAP_ID | java.util.HashMap | +| 92 | HASHSET_ID | java.util.HashSet | +| 93 | CLASS_ID | java.lang.Class | +| 94 | EMPTY_OBJECT_ID | empty object stub | +| 95 | LAMBDA_STUB_ID | lambda stub | +| 96 | JDK_PROXY_STUB_ID | JDK proxy stub | +| 97 | REPLACE_STUB_ID | writeReplace/readResolve stub | +| 98 | NONEXISTENT_META_SHARED_ID | meta-shared unknown class stub | + +### Registration and named types + +用户类型可按数值或名称注册: + +- 数值注册:`full_type_id = (user_id << 8) | internal_type_id` +- 名称注册:通过 namespace + typename +- 未注册类型按 named 类型写出(namespace=包名,typename=类名) + +未注册时命名类型选择: + +- enum -> `NAMED_ENUM` +- struct-like -> `NAMED_STRUCT`(兼容模式下 `NAMED_COMPATIBLE_STRUCT`) +- 其他自定义 serializer -> `NAMED_EXT` + +## Type meta encoding + +每个值写入流程: + +1. 写 `type_id`(varuint32 small7) +2. 对 `NAMED_*` / `COMPATIBLE_STRUCT` 等类型按规则写额外元信息 +3. 其余类型无需额外 meta + +### Shared class meta (streaming) + +开启 meta share 时,使用流式共享 class meta: + +``` +| varuint32: index_marker | [class def bytes if new] | + +index_marker = (index << 1) | flag +flag = 1 -> reference +flag = 0 -> new type +``` + +- `flag=1`:引用历史 type,后续无 class def bytes +- `flag=0`:新 type,后续内联 class def +- index 按首次出现顺序递增 + +## Schema modes + +Java native 支持两种模式: + +- **Schema consistent**(compatible 关闭):字段按固定顺序写,不需要 ClassDef +- **Schema evolution**(compatible 开启):按 ClassDef 元信息支持演进 + +## ClassDef format (compatible mode) + +ClassDef 是 compatible struct 的演进元信息。 + +### Binary layout + +``` +| 8 bytes header | [varuint32 extra size] | class meta bytes | +``` + +header 位布局: + +``` +| 50-bit hash | 4 bits reserved | 1 bit compress | 1 bit has_fields_meta | 8-bit size | +``` + +规则: + +- `size` 为低 8 位;若为 `0xFF`,后接扩展长度 +- `compress`:payload 是否压缩 +- `has_fields_meta`:是否含字段元信息 +- `reserved` 必须为 0 +- `hash` 为 payload + flags 的 50-bit 哈希 + +### Class meta bytes + +表示线性化继承层次(父 -> 子)与字段信息: + +``` +| num_classes | class_layer_0 | class_layer_1 | ... | + +class_layer: +| num_fields << 1 | registered_flag | [type_id if registered] | +| namespace | type_name | field_infos | +``` + +### Field info + +每个字段: + +``` +| field_header | [field_name_bytes] | field_type | +``` + +`field_header`: + +- bit0: trackingRef +- bit1: nullable +- bit2-3: 字段名编码 +- bit4-6: name length/tag ID(7 表示扩展) +- bit7: reserved=0 + +字段名编码: + +- 0: UTF8 +- 1: ALL_TO_LOWER_SPECIAL +- 2: LOWER_UPPER_DIGIT_SPECIAL +- 3: TAG_ID(省略字段名,存 tag) + +### Field type encoding + +字段类型使用 type tag + 可选嵌套类型信息: + +| Tag | 字段类型 | +| --- | ----------------------------------------- | +| 0 | Object (ObjectFieldType) | +| 1 | Map (MapFieldType) | +| 2 | Collection/List/Set (CollectionFieldType) | +| 3 | Array (ArrayFieldType) | +| 4 | Enum (EnumFieldType) | +| 5+ | Registered type (RegisteredFieldType) | + +嵌套类型头部低位可携带 `nullable/tracking_ref` 标志。 + +## Meta string encoding + +namespace、type name、field name 复用 xlang 的 meta string 编码。 + +### Package and type names + +头部: + +``` +| 6 bits size | 2 bits encoding | +``` + +- `size=63` 时追加扩展长度 +- package 支持:UTF8 / ALL_TO_LOWER_SPECIAL / LOWER_UPPER_DIGIT_SPECIAL +- type name 支持:UTF8 / LOWER_UPPER_DIGIT_SPECIAL / FIRST_TO_LOWER_SPECIAL / ALL_TO_LOWER_SPECIAL + +### Field names + +字段名编码与 ClassDef 的 field header 规则一致;若使用 TAG_ID,则不写名字字节。 + +### Encoding algorithms + +具体算法参见 xlang 规范 `#meta-string`。 + +## Value encodings + +以下为 Java native 常见内建 serializer 的值布局。自定义 `EXT` 可定义自身值格式,但必须遵循“引用元信息 + 类型元信息”通道规则。 + +### Primitives + +- boolean: 1 byte(0/1) +- byte: 1 byte +- short: 2 bytes little-endian +- char: 2 bytes little-endian(UTF-16 code unit) +- int: fixed(4 bytes) 或 varint32(ZigZag) +- long: fixed(8 bytes) / varint64(ZigZag) / tagged int64 +- float: IEEE754 float32 +- double: IEEE754 float64 + +### String + +``` +| varuint36_small: (num_bytes << 2) | coder | string bytes | +``` + +- coder(2 bits):LATIN1 / UTF16 / UTF8 +- UTF16 采用 little-endian code unit + +### Enum + +- `serializeEnumByName=true`:写 enum 名称(meta string) +- 否则:写 ordinal(varuint32 small7) + +### Binary (byte[]) + +``` +| varuint32: num_bytes | raw bytes | +``` + +### Primitive arrays + +默认: + +``` +| varuint32: byte_length | raw bytes | +``` + +可选压缩: + +- `compressIntArray`:`| length | varint32... |` +- `compressLongArray`:`| length | varint64/tagged... |` + +### Object arrays + +``` +| varuint32_small7: (length << 1) | mono_flag | +``` + +- `mono_flag=1`:单态数组,共享组件 serializer +- `mono_flag=0`:每元素独立 class info + data + +### Collections (List/Set) + +``` +| varuint32_small7: length | elements_header | [elem_class_info] | elements... | +``` + +`elements_header`: + +- bit0: TRACKING_REF +- bit1: HAS_NULL +- bit2: IS_DECL_ELEMENT_TYPE +- bit3: IS_SAME_TYPE + +### Maps + +``` +| varuint32_small7: size | chunk_1 | chunk_2 | ... | + +chunk: +| header | chunk_size | [key_class_info] | [value_class_info] | entries... | +``` + +Map 采用 chunk 编码,每块最多 255 entries;key/value 的引用跟踪与类型声明按 header 位图控制。 + +#### Null key/value entries + +`null key` 或 `null value` 使用特殊单条 chunk,不写 `chunk_size`,只写对应 payload。 + +### Objects and structs + +对象值通道: + +``` +| ref meta | type meta | field data | +``` + +标准对象序列化中: + +- 字段按稳定顺序(DescriptorGrouper)排序 +- compatible 模式可借助 ClassDef 映射字段并跳过未知字段 +- 字段是否写 ref/type meta 由 nullable/ref/polymorphic 策略决定 + +### Extensions (EXT) + +EXT 类型值由注册 serializer 定义;但其外层仍遵循统一的引用与类型元信息协议。 + +## Out-of-band buffers + +当提供 `BufferCallback` 时,header 的 `oob` 位置 1。此时 serializer 可输出“缓冲区引用”而非内联字节(如大数组)。 +主流中仅保留引用信息,具体 OOB 缓冲区协议由 callback 实现定义。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/specification/row_format_spec.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/specification/row_format_spec.md new file mode 100644 index 0000000000..3f7f9866ef --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/specification/row_format_spec.md @@ -0,0 +1,356 @@ +--- +title: Row 格式 +sidebar_position: 2 +id: row_format_spec +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. +--- + +## 概述 + +Apache Fory Row Format 是面向高性能数据处理的二进制布局,目标是“可随机访问 + 低拷贝 + 跨语言一致”。 + +相较于必须整对象反序列化的格式,Row Format 支持: + +- **随机字段读取**:只读目标字段 +- **零拷贝访问**:在可行场景直接基于内存切片读取 +- **缓存友好布局**:降低 CPU cache miss +- **跨语言一致性**:Java/C++/Python 可共享标准格式 + +Fory 提供两种变体: + +| 格式 | 支持语言 | 典型用途 | +| ---------------- | -------------------- | -------------------------------- | +| Standard Format | Java、C++、Python | 跨语言一致、实现简单 | +| Compact Format | 仅 Java | 更小体积、更高局部性 | + +## 格式对比 + +| 特性 | Standard Format | Compact Format | +| ---------------------- | --------------------------------- | -------------------------------------- | +| 字段槽位大小 | 固定 8 字节 | 按自然宽度(1/2/4/8 字节) | +| Null Bitmap | 8 字节对齐 | 字节对齐,可借用尾部 padding | +| Null Bitmap 位置 | 字段槽位之前 | 字段槽位之后(尾部) | +| 固定大小 struct | 放在 variable region(offset+size) | 可内联到 fixed region | +| 字段顺序 | 按 schema 定义顺序 | 按对齐规则排序 | +| 全非空字段 | 仍保留 bitmap | 可完全省略 bitmap | +| 对齐策略 | 严格 8 字节 | 放宽(2/4/8 字节) | + +## 标准 Row 格式 + +标准格式强调跨语言统一与实现稳定:字段槽位统一 8 字节。 + +### 设计原则 + +1. **8 字节对齐**:主要结构按 8-byte 对齐 +2. **固定槽位**:每字段固定 8-byte slot,便于常数时间寻址 +3. **位图标记 null**:通过 bitset 跟踪空值 +4. **相对偏移**:变长数据通过相对偏移定位 + +### Row 二进制布局 + +``` ++----------------+------------------+------------------+-----+------------------+------------------+ +| Null Bitmap | Field 0 Slot | Field 1 Slot | ... | Field N-1 Slot | Variable Data | ++----------------+------------------+------------------+-----+------------------+------------------+ +| B bytes | 8 bytes | 8 bytes | | 8 bytes | Variable size | +``` + +#### Null Bitmap + +- 大小:`((num_fields + 63) / 64) * 8` 字节(向上取整到 8-byte word) +- 编码:每 bit 对应一个字段 +- bit=1 表示 null,bit=0 表示非 null +- 第一字节 bit0 对应 field0 + +#### 字段槽位 + +- 任意字段都占 8 字节 slot +- 槽位偏移:`bitmap_size + field_index * 8` +- 固定区总大小:`bitmap_size + num_fields * 8` + +槽位内容: + +| 字段类别 | 槽位内容 | +| ---------------- | ----------------------------------------- | +| 定长类型 | 值直接写入(不足补零) | +| 变长类型 | `offset + size` 打包 | + +#### 变长字段编码 + +变长字段(string/array/map/nested struct)在 slot 中写入: + +``` ++---------------------------+---------------------------+ +| Relative Offset | Size | +| (32 bits) | (32 bits) | ++---------------------------+---------------------------+ +``` + +- 高 32 位:相对 row 起始地址偏移 +- 低 32 位:数据长度(字节) + +编码: + +``` +offset_and_size = (relative_offset << 32) | size +``` + +解码: + +``` +relative_offset = (offset_and_size >> 32) & 0xFFFFFFFF +size = offset_and_size & 0xFFFFFFFF +``` + +#### Variable Data 区 + +- 位于 fixed region 之后 +- 变长字段按写入顺序顺排 +- 每个条目按 8-byte 对齐 +- padding 字节清零,保证输出确定性 + +### Array 二进制布局 + +``` ++------------------+------------------+------------------+ +| Element Count | Null Bitmap | Element Data | ++------------------+------------------+------------------+ +| 8 bytes | B bytes | Variable size | +``` + +#### Array Header + +| 字段 | 大小 | 说明 | +| --------------- | ------------------------------- | ----------------------- | +| Element Count | 8 字节 | 元素数量(uint64) | +| Null Bitmap | `((count + 63) / 64) * 8` 字节 | 每元素 null 标记 | + +#### Array Element Data + +- 定长元素按自然宽度连续存储 +- 变长元素存 8-byte `offset+size` +- 元素偏移:`header_size + element_index * element_size` +- 数据区总大小按 8-byte 对齐 + +#### Array 元素大小 + +| 元素类型 | 元素占用 | +| ---------------- | -------------------------- | +| bool/int8 | 1 byte | +| int16 | 2 bytes | +| int32/float32 | 4 bytes | +| int64/float64 | 8 bytes | +| string/binary | 8 bytes(offset+size) | + +### Map 二进制布局 + +Map 在 Row Format 中可视为键值对数组,并包含类型信息与可空位图。 + +#### Map 结构 + +推荐逻辑结构: + +1. entry count +2. key/value null bitmap(按实现可拆分) +3. key data +4. value data + +#### 嵌套 Struct 布局 + +嵌套 struct 在标准格式中作为变长字段处理: + +- slot 写 `offset+size` +- value region 中存其完整 row 二进制 +- 可递归嵌套 + +## Compact Row Format(仅 Java) + +Compact 格式针对 Java 本地执行链路做体积与局部性优化,不保证与标准格式完全同布局。 + +### 设计原则 + +1. 字段槽按自然宽度缩小 +2. 字段重排以减少 padding +3. null bitmap 可省略或压缩 +4. 定长嵌套 struct 尽可能内联 + +### Compact Row Binary Layout + +总体仍是“fixed + variable”双区结构,但 fixed region 更紧凑,null bitmap 可能后置。 + +#### 与标准格式的关键差异 + +- slot 宽度非固定 8 字节 +- bitmap 可后置 +- 全非空时 bitmap 可省略 +- 可内联定长 nested struct + +#### Null Bitmap(Compact) + +- 按字节对齐 +- 可能复用尾部 padding +- 无可空字段时可完全移除 + +#### 字段排序算法 + +典型排序目标: + +- 优先高对齐需求字段 +- 次序兼顾读取热度与 padding 最小化 +- 保持可重复计算(deterministic) + +#### 定长 Struct 内联 + +若嵌套 struct 满足定长条件,可直接内联到 fixed region,减少一次间接寻址。 + +#### 定宽计算 + +定宽字段总大小由字段类型宽度与对齐约束共同决定,实际布局由编译器/运行时规划器输出。 + +### Compact Array Binary Layout + +与标准数组类似,但 header 与元素布局可采用更紧凑表示。 + +#### Compact Array Header + +包含:元素数量、null 信息(可选)、元素布局元信息(按实现)。 + +#### 与标准数组关键差异 + +- 头部更短 +- 元素存储更贴近自然宽度 +- 在 Java 热路径下更节省内存带宽 + +## 通用规范 + +### 类型编码 + +#### Primitive Types + +基础类型按既定宽度和 endian 读写(默认 little-endian)。 + +#### Temporal Types + +- `date`:通常以 epoch day 表示 +- `timestamp`:通常以 epoch 纳秒或秒+纳秒表示 + +#### String 与 Binary + +都属于变长类型,使用 `offset+size` 指向真实 payload。 + +### Null 处理 + +#### Row Null 处理 + +由 row bitmap 标记字段 null 状态,null 字段对应 slot 内容可忽略。 + +#### Array Null 处理 + +由数组 bitmap 标记每个元素是否为 null。 + +#### 变长 null 语义 + +null 与空值(如空字符串、空数组)必须区分;空值应写长度为 0 的实体。 + +### 对齐与 Padding + +#### Standard 对齐 + +以 8-byte 为主,保证跨语言一致实现简单。 + +#### Compact 对齐 + +允许 2/4/8 灵活对齐,目标是减少浪费。 + +#### Padding 字节 + +padding 建议写 0,避免未初始化内存泄露并提高可重复性。 + +## 大小计算 + +### Standard Row 大小 + +``` +row_size = bitmap_size + num_fields * 8 + aligned(variable_region_size) +``` + +### Compact Row 大小 + +``` +row_size = compact_fixed_size + optional_bitmap + aligned(variable_region_size) +``` + +### Standard Array 大小 + +``` +array_size = header_size + aligned(element_data_size) +``` + +### Compact Array 大小 + +``` +array_size = compact_header_size + aligned(compact_element_data_size) +``` + +### Map 大小 + +``` +map_size = map_header + aligned(keys_region) + aligned(values_region) +``` + +## 汇总表 + +### 布局总结 + +| 结构 | Standard | Compact (Java) | +| --------------- | -------------------------------- | -------------------------------- | +| Row | bitmap + 8-byte slots + var data | compact fixed + optional bitmap + var data | +| Array | count + bitmap + elements | compact header + elements | +| Map | count + key/value data regions | 依实现可紧凑化 | + +### 类型宽度总结 + +| 类型族 | 典型宽度 | +| ---------------------- | --------------------------- | +| bool/int8 | 1 byte | +| int16 | 2 bytes | +| int32/float32 | 4 bytes | +| int64/float64 | 8 bytes | +| string/binary/复杂类型 | slot 中 8-byte offset+size | + +## 实现说明 + +### Endianness + +统一使用 little-endian,跨语言实现必须一致。 + +### 内存安全 + +- 读取前先做边界检查 +- 对 offset/size 做溢出检查 +- 避免使用未初始化 padding + +### 性能建议 + +- 批量顺序写入,减少随机写 +- 热字段优先放 fixed region 前部 +- 通过 profile 决定是否启用 compact + +### 何时选择哪种格式 + +- **Standard**:跨语言互通、调试友好、长期兼容 +- **Compact**:Java 单语言链路、内存敏感、高吞吐场景 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/specification/xlang_implementation_guide.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/specification/xlang_implementation_guide.md new file mode 100644 index 0000000000..9e1e2dea33 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/specification/xlang_implementation_guide.md @@ -0,0 +1,251 @@ +--- +title: Xlang 实现指南 +sidebar_position: 10 +id: xlang_implementation_guide +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. +--- + +## 实现建议 + +### 如何减少内存读写开销 + +- 尽量把多次 byte 写入合并为 int/long 写入,降低内存 IO 与边界检查成本。 +- 读取时可一次读取 int/long,再拆分字节,减少内存访问次数。 +- 尽可能把 flag 与 length 合并到同一个 varint/long,减少字节数与 IO。 +- 在分支数量可控的前提下,分支开销通常小于内存 IO 开销。 + +### 静态语言(无运行时代码生成)如何做高性能反序列化 + +在类型演进场景,序列化数据会携带 type meta。反序列化端会比较“数据内 meta”与“本地类 meta”,根据差异执行字段映射。 + +对 Java/Javascript/Python,可在运行时根据 diff 动态生成并加载反序列化逻辑,从而接近 schema-consistent 模式性能。 + +对 C++/Rust,通常无法在运行时动态生成 serializer,只能在编译期元编程生成。此时无法提前知道对端 schema, +因此需要在运行时根据字段元信息逐个判断“赋值或跳过”。 + +可行优化:将字符串字段匹配尽量提前转换为 `field id`,再在反序列化路径用 `switch` 处理,利用连续 case 的跳转优化。 + +思路: + +- 假设本地类型字段数为 `n`,对端字段数为 `n1` +- 编译期先为本地排序字段分配连续 `field id`(从 0 开始) +- 运行时比较对端 type meta:同名字段复用本地 id,不同字段分配从 `n` 开始的新 id,并缓存映射 +- 反序列化遍历对端字段时对 `field_id` 做 `switch`,命中则赋值,未命中则跳过 + +示例:A 进程持有 `Foo1`,B 进程持有演进后的 `Foo2`: + +```c++ +// class Foo with version 1 +class Foo1 { + int32_t v1; // id 0 + std::string v2; // id 1 +}; +// class Foo with version 2 +class Foo2 { + // id 0, but will have id 2 in process A + bool v0; + // id 1, but will have id 0 in process A + int32_t v1; + // id 2, but will have id 3 in process A + int64_t long_value; + // id 3, but will have id 1 in process A + std::string v2; + // id 4, but will have id 4 in process A + std::vector list; +}; +``` + +A 收到 B 发送的 `Foo2` 数据后,可按 `field_id` 快速分发: + +```c++ +Foo1 foo1 = ...; +const std::vector &field_infos = type_meta.field_infos; +for (const auto &field_info : field_infos) { + switch (field_info.field_id) { + case 0: + foo1.v1 = buffer.read_varint32(); + break; + case 1: + foo1.v2 = fory.read_string(); + break; + default: + fory.skip_data(field_info); + } +} +``` + +## 新语言实现检查清单 + +本节给出在新语言中落地 Fory xlang 序列化的阶段性清单。 + +### 阶段 1:核心基础设施 + +1. **Buffer 实现** + - [ ] 提供带读写游标的字节缓冲区 + - [ ] 所有多字节读写统一使用 little-endian + - [ ] 实现 `write_int8/int16/int32/int64` + - [ ] 实现 `write_float32/write_float64` + - [ ] 实现对应 `read_*` 方法 + - [ ] 实现缓冲区扩容策略(如倍增) + +2. **Varint 编码** + - [ ] `write_varuint32/read_varuint32` + - [ ] `write_varint32/read_varint32`(含 ZigZag) + - [ ] `write_varuint64/read_varuint64` + - [ ] `write_varint64/read_varint64`(含 ZigZag) + - [ ] `write_varuint36_small/read_varuint36_small`(字符串头使用) + - [ ] 可选:实现 int64 Hybrid 编码(TAGGED_INT64/TAGGED_UINT64) + +3. **头部处理** + - [ ] 读写 bitmap flags(null、xlang、oob) + +### 阶段 2:基础类型序列化 + +4. **基础类型** + - [ ] bool(1 字节:0/1) + - [ ] int8/int16/int32/int64(little-endian) + - [ ] float32/float64(IEEE 754,little-endian) + +5. **字符串序列化** + - [ ] 实现字符串头:`(byte_length << 2) | encoding` + - [ ] 支持 UTF-8(xlang 必需) + - [ ] 可选支持 LATIN1 与 UTF-16 + +6. **时间类型** + - [ ] Duration(seconds + nanoseconds) + - [ ] Timestamp(epoch 以来 seconds + nanoseconds) + - [ ] Date(epoch 以来天数) + +7. **引用跟踪** + - [ ] 写侧对象跟踪(object -> ref_id) + - [ ] 读侧对象跟踪(ref_id -> object) + - [ ] 支持 4 种引用标记:NULL(-3)、REF(-2)、NOT_NULL(-1)、REF_VALUE(0) + - [ ] 支持按类型或全局关闭引用跟踪 + +### 阶段 3:集合类型 + +8. **List/Array** + - [ ] 长度写为 varuint32 + - [ ] 写 elements header byte + - [ ] 处理同构/异构元素 + - [ ] 处理 null 元素 + +9. **Map** + - [ ] 总大小写为 varuint32 + - [ ] 实现分块格式(每块最多 255 对) + - [ ] 每块写 KV header + - [ ] 处理 key/value 类型变化 + +10. **Set** + - [ ] 与 List 复用同一套实现 + +### 阶段 4:Meta String 编码 + +meta string 用于 enum/struct 的字段名、类型名、命名空间编码。 + +11. **Meta String 压缩** + - [ ] LOWER_SPECIAL(5 bits/char) + - [ ] LOWER_UPPER_DIGIT_SPECIAL(6 bits/char) + - [ ] FIRST_TO_LOWER_SPECIAL + - [ ] ALL_TO_LOWER_SPECIAL + - [ ] 编码选择算法 + - [ ] meta string 去重 + +### 阶段 5:Enum 序列化 + +12. **Enum** + - [ ] ordinal 写为 varuint32 + - [ ] 支持命名 enum(namespace + typename) + +### 阶段 6:Struct 序列化 + +13. **类型注册** + - [ ] 支持按数值 ID 注册 + - [ ] 支持按 namespace + typename 注册 + - [ ] 维护 type -> serializer 映射 + - [ ] 生成 type ID:先写 internal type ID,再写 `user_type_id(varuint32)` + +14. **字段排序** + - [ ] 实现规范定义的分组与排序(primitive/boxed/builtin、collections/maps、other) + - [ ] 组内使用稳定比较器(type ID + name) + - [ ] 指纹字段标识使用 tag ID 或 snake_case 字段名 + +15. **Schema Consistent 模式** + - [ ] 开启类版本校验时,按字段标识计算 schema hash + - [ ] 在字段前写 4-byte schema hash + - [ ] 按 Fory 字段顺序序列化 + +16. **Compatible/Meta Share 模式** + - [ ] 实现共享 TypeDef 流(新 TypeDef 内联,已存在用索引) + - [ ] 按字段名或 tag ID 做映射,未知字段跳过 + - [ ] 按 TypeDef 元信息应用 nullable/ref 规则 + +### 阶段 7:其他类型 + +17. **Binary/Array** + +- [ ] 基础类型数组支持直接 buffer copy +- [ ] 多维数组按嵌套 list 表达(不做 tensor 专用编码) + +### 测试策略 + +18. **跨语言兼容测试** + - [ ] 新语言写 -> Java/Python 读 + - [ ] Java/Python 写 -> 新语言读 + - [ ] 覆盖所有基础类型 + - [ ] 覆盖不同字符串编码 + - [ ] 覆盖集合(空、单元素、多元素) + - [ ] 覆盖不同 key/value map + - [ ] 覆盖嵌套 struct + - [ ] 覆盖循环引用(若支持) + +## 各语言实现备注 + +### Java + +- 通过运行时代码生成(JIT)获得高性能 +- 支持完整引用跟踪模式 +- 利用 String coder 做编码选择 +- 可通过 `ThreadSafeFory` 提供线程安全封装 + +### Python + +- 提供纯 Python(调试)与 Cython(性能)两种模式 +- 使用 `id(obj)` 做引用跟踪 +- xlang 模式支持 LATIN1/UTF-16/UTF-8 +- 通过代码生成增强 `dataclass` 支持 + +### C++ + +- 通过宏(`FORY_STRUCT`)做编译期反射 +- 模板元编程进行类型分发与 serializer 选择 +- 使用 `std::shared_ptr` 支持引用跟踪 +- 编译期字段排序 +- 不依赖运行时代码生成 + +### Rust + +- 使用 derive 宏自动生成序列化代码(`#[derive(ForyObject)]`) +- 使用 `Rc`/`Arc` 做引用跟踪 +- 通过线程本地上下文缓存优化性能 +- 编译期字段排序 + +### Go + +- 支持反射模式与代码生成模式 +- 通过 struct tag 提供字段元信息 +- 通过接口类型支持多态语义 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/specification/xlang_serialization_spec.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/specification/xlang_serialization_spec.md new file mode 100644 index 0000000000..1a1800bbc6 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/specification/xlang_serialization_spec.md @@ -0,0 +1,558 @@ +--- +title: Xlang 序列化格式 +sidebar_position: 4 +id: xlang_serialization_spec +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. +--- + +## 跨语言序列化规范 + +本文定义 Apache Fory xlang 二进制协议的通用线格式,适用于多语言互操作场景。 + +目标: + +- 二进制布局跨语言稳定 +- 支持引用跟踪、类型元信息和 schema 演进 +- 在流式序列化中支持增量写入共享元信息 + +## 类型系统 + +### 数据类型 + +xlang 类型分为: + +- 基础类型:bool、整数、浮点、string、binary +- 容器类型:list、set、map、array +- 结构类型:enum、struct、union、ext +- 时间类型:duration、timestamp、date + +### Polymorphisms + +协议支持多态对象。解码端可依据 type meta 判断运行时真实类型,并选择对应 serializer。 + +### Type disambiguation + +当某语言类型可映射到多个 Fory 类型(如 fixed/varint/tagged 整数)时,必须通过字段元信息或类型注解消歧。 + +### Type ID + +类型由 `internal_type_id` 与(可选)`user_type_id` 共同表达: + +- 内建类型通常直接由 internal ID 唯一表示 +- 用户类型通过 internal kind + user ID(或命名类型)表示 + +#### Internal Type ID Table + +核心 internal IDs(示例): + +| ID | 类型 | +| --- | ------------------- | +| 1 | bool | +| 2-20| 各类数字类型 | +| 21 | string | +| 22 | list | +| 23 | set | +| 24 | map | +| 25 | enum | +| 27 | struct | +| 28 | compatible_struct | +| 31 | ext | +| 33 | union | +| 36 | none | +| 37 | duration | +| 38 | timestamp | +| 39 | date | +| 40+ | decimal/binary/array 等 | + +完整映射见 [Xlang 类型映射](xlang_type_mapping.md)。 + +#### Type ID Encoding for User Types + +用户类型采用拆分编码: + +- 先写 internal type ID(8-bit kind) +- 再写 `user_type_id`(varuint32) + +不做 bit packing,便于实现与调试。 + +### Type mapping + +跨语言类型映射总表见 [xlang_type_mapping.md](xlang_type_mapping.md)。 + +## 规范概览 + +顶层布局: + +``` +| fory header | reference meta | type meta | value payload | +``` + +协议默认 little-endian。 + +## Fory 头部 + +头部是 1-byte bitmap: + +``` +| reserved(5) | oob(1) | xlang(1) | null(1) | +``` + +- `null=1` 时值为空,不再写值数据 +- `xlang=1` 表示采用 xlang 格式 +- `oob=1` 表示存在 out-of-band 缓冲区引用 + +## Reference Meta + +### Reference Flags + +| 标记 | 值 | 含义 | +| -------------------- | ---- | ----------------------------------------- | +| NULL_FLAG | -3 | null | +| REF_FLAG | -2 | 已出现对象,后接 ref id | +| NOT_NULL_VALUE_FLAG | -1 | 非空但不跟踪引用 | +| REF_VALUE_FLAG | 0 | 首次出现的可引用对象 | + +### Reference Tracking Algorithm + +写侧: + +1. 先判断 null +2. 若可引用且已出现,写 `REF_FLAG + ref_id` +3. 若可引用且首次出现,写 `REF_VALUE_FLAG` 并登记 +4. 若不可引用,写 `NOT_NULL_VALUE_FLAG` + +读侧: + +1. 读取标记 +2. `REF_FLAG` 时按 ref_id 回表 +3. `REF_VALUE_FLAG` 时先构造对象再登记 +4. `NOT_NULL_VALUE_FLAG` 时直接读值 + +### Reference ID Assignment + +ref id 按对象首次出现顺序递增分配,从 0 开始。 + +### When Reference Tracking is Disabled + +禁用引用跟踪时,仅使用 null / not-null 两类标记,不维护 ref 表。 + +### Language-Specific Considerations + +不同语言应保证: + +- 对象身份判定一致(身份而非值相等) +- 容器元素引用语义一致 +- 循环引用场景先占位后填充 + +## Type Meta + +### Type ID encoding + +type id 使用 varuint 编码写入。 + +### Type meta payload + +在以下情况写额外 type meta: + +- 命名类型(`NAMED_*`) +- compatible struct 需要 TypeDef +- 运行时未声明类型需要动态 type info + +### Shared Type Meta (streaming) + +共享 type meta 采用“索引 + 可选定义体”流式写法: + +``` +index_marker = (index << 1) | is_ref +``` + +- `is_ref=1`:引用已有 type +- `is_ref=0`:新 type,后接定义体 + +### TypeDef (schema evolution metadata) + +TypeDef 用于描述 compatible 模式的字段元信息(字段名/tag、nullable/ref、字段类型)。 + +#### Global header + +TypeDef 头部包含: + +- payload size +- flags(如 compress、has_fields_meta) +- payload hash + +#### TypeDef body + +主体包含: + +- class 层次信息(父类到子类) +- 每层字段数量与类型标识 +- 字段级元信息(名称编码/tag、nullable/ref、field type) + +## Meta String + +meta string 用于 namespace、typename、fieldname 的压缩表示。 + +### Encoding Type IDs + +常见编码族: + +- UTF8 +- LOWER_SPECIAL +- LOWER_UPPER_DIGIT_SPECIAL +- FIRST_TO_LOWER_SPECIAL +- ALL_TO_LOWER_SPECIAL + +### Character Mapping Tables + +#### LOWER_SPECIAL (5 bits per character) + +适用于小写字母 + 高频特殊字符集合。 + +#### LOWER_UPPER_DIGIT_SPECIAL (6 bits per character) + +适用于大小写字母、数字与特殊字符混合场景。 + +### Encoding Algorithms + +#### LOWER_SPECIAL Encoding + +按 5-bit 映射逐字符编码,无法映射的字符需回退至其他编码。 + +#### FIRST_TO_LOWER_SPECIAL Encoding + +首字符单独处理,其余字符按 LOWER_SPECIAL 编码。 + +#### ALL_TO_LOWER_SPECIAL Encoding + +先归一化,再按 LOWER_SPECIAL 编码。 + +### Encoding Selection Algorithm + +编码选择策略: + +1. 尝试最紧凑编码 +2. 若字符集合不满足则降级 +3. 必要时回退 UTF8 + +### Meta String Header Format + +``` +| size_bits | encoding_bits | +``` + +当 `size` 超过短头范围时追加 varuint 扩展长度。 + +### Special Character Sets by Context + +不同上下文(包名、类型名、字段名)允许字符集合可不同,编码器需按上下文选择合法表。 + +### Deduplication + +meta string 可按会话去重,减少重复写入。 + +## Value Format + +### Basic types + +#### bool + +1 字节:`0x00/0x01`。 + +#### int8 + +1 字节有符号整型。 + +#### int16 + +2 字节 little-endian。 + +#### unsigned int32 + +固定 4 字节无符号整型。 + +#### unsigned varint32 + +varint32(无符号)编码。 + +#### signed int32 + +固定 4 字节有符号整型。 + +#### signed varint32 + +ZigZag + varint32。 + +#### unsigned int64 + +固定 8 字节无符号整型。 + +#### unsigned varint64 + +varint64(无符号)编码。 + +#### unsigned hybrid int64 (TAGGED_UINT64) + +tagged 编码,兼顾小值空间效率与大值表示范围。 + +#### VarUint36Small + +用于字符串头等场景的紧凑长度编码。 + +#### signed int64 + +固定 8 字节有符号整型。 + +#### signed varint64 + +ZigZag + varint64。 + +#### signed hybrid int64 (TAGGED_INT64) + +tagged int64 编码。 + +#### float8 + +预留/实验类型,生产互操作需谨慎。 + +#### float16 + +16-bit 浮点。 + +#### bfloat16 + +bfloat16 表示。 + +#### float32 + +IEEE 754 float32 little-endian。 + +#### float64 + +IEEE 754 float64 little-endian。 + +### string + +字符串编码: + +``` +| header(varuint36_small) | bytes | +``` + +header 中包含 byte length 与 coder 信息。 + +#### String Header + +`(byte_length << 2) | coder`,coder 表示 UTF8/LATIN1/UTF16 等。 + +#### Encoding Algorithm + +按候选编码尝试,优先选择更紧凑且可无损表示的编码。 + +#### Encoding Selection by Language + +各语言实现可按本地字符串内部表示优化,但线上编码结果必须与规范一致。 + +#### Empty String + +空串长度为 0,仍应写合法 header。 + +### duration + +通常写 `seconds + nanos`。 + +### collection/list + +列表布局: + +1. 长度 +2. elements header +3. (可选)元素类型信息 +4. 元素数据 + +#### Elements Header + +header 位用于表达: + +- 是否跟踪元素引用 +- 是否含 null +- 是否同构 +- 是否使用声明类型 + +#### Type Info After Header + +在同构且非声明类型场景,可在 header 后一次性写 element type info。 + +#### Element Serialization Based on Header + +根据 header 走不同元素序列化路径(同构快路径 / 异构慢路径)。 + +#### elements data + +元素数据按顺序编码;null 与 ref 标记按配置插入。 + +### array + +#### primitive array + +基础类型数组可直接按内存块拷贝(注意 endian 与对齐)。 + +#### Multi-dimensional arrays + +多维数组按嵌套 array/list 递归表达。 + +#### object array + +对象数组元素逐个编码,支持引用与多态。 + +### map + +map 使用分块编码(chunk-based)。 + +#### Map Chunk Format + +``` +| map_size | chunk_1 | chunk_2 | ... | +``` + +#### KV Header Bits + +header 位描述 key/value 的: + +- 是否跟踪引用 +- 是否含 null +- 是否使用声明类型 + +#### Chunk Size + +每个 chunk 大小上限通常为 255 entries。 + +#### Why Chunk-Based Format? + +减少每个 entry 重复写 type info 的成本,提升吞吐。 + +#### Why serialize chunk by chunk? + +在 key/value 类型局部一致时可批量走快路径,并简化解码分支。 + +### enum + +enum 可按 ordinal 或名称编码(取决于配置与目标兼容要求)。 + +### timestamp + +通常写 `seconds + nanos` 或统一 epoch 精度表示。 + +### date + +通常写 epoch day。 + +### decimal + +decimal 由 scale + unscaled value 表示(实现可用大整数)。 + +### struct + +struct 编码:字段按稳定顺序写入。 + +#### Field order + +推荐使用规范定义的稳定分组排序,避免语言实现差异导致 hash 不一致。 + +##### Step 1: Field identifier + +字段标识优先使用 tag ID;否则使用标准化字段名(如 snake_case)。 + +##### Step 2: Group assignment + +按字段类别分组(primitive、builtin、collection/map、other)。 + +##### Step 3: Intra-group ordering + +组内使用稳定比较器(type + name/tag)排序。 + +##### Notes + +实现必须保证排序确定性(deterministic)。 + +#### Schema consistent (meta share disabled) + +不共享 meta 时,双方 schema 需一致;通常直接按固定顺序写字段值。 + +#### Compatible mode (meta share enabled) + +共享 TypeDef 后可按字段名/tag 做映射,未知字段跳过。 + +### Union + +#### IDL syntax + +```protobuf +union Animal { + Dog dog = 1; + Cat cat = 2; +} +``` + +#### Type IDs and type meta + +union 本体有独立 type id,case 使用 case id 区分分支。 + +#### Union value payload + +``` +| case_id | case_value | +``` + +#### Wire layouts + +根据 case 类型是否需要引用/类型元信息决定具体布局。 + +#### Decoding rules + +先读 case_id,再按 case 类型规则读 payload;未知 case 可按兼容策略跳过或报错。 + +#### When to use each type ID + +- 结构稳定、跨语言常驻类型:建议固定数值 type id +- 动态类型/未注册类型:使用命名类型路径 + +#### Compatibility notes + +新增 union case 应使用新 case id,不应复用旧 id。 + +### Type + +动态 `type` 值应携带足够 type meta,确保接收端可判别并解码。 + +## Common Pitfalls + +常见问题: + +1. 字段排序不稳定导致 schema/hash 不一致 +2. varint 与 fixed/tagged 配置不一致 +3. null 与空值语义混淆 +4. 引用跟踪开关两端不一致 +5. 命名类型 namespace/typename 不稳定 + +## Language Implementation Guidelines + +- 统一 little-endian +- 明确对象身份语义(用于 ref tracking) +- 对所有 offset/size 做边界与溢出检查 +- 维护跨版本回归用例(含演进、循环引用、复杂容器) +- 与 Java/Python/C++/Rust/Go 做双向互测 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/specification/xlang_type_mapping.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/specification/xlang_type_mapping.md new file mode 100644 index 0000000000..59b1171838 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/specification/xlang_type_mapping.md @@ -0,0 +1,146 @@ +--- +title: Xlang 类型映射 +sidebar_position: 7 +id: xlang_type_mapping +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. +--- + +说明: + +- 类型定义请参见 [Type Systems in Spec](xlang_serialization_spec.md#type-systems) +- `int16_t[n]/vector` 表示 `int16_t[n]/vector` 这一类数组/向量形式 +- 跨语言序列化协议仍在演进,生产环境请先做严格兼容验证 + +## 用户类型 ID + +注册用户类型(struct/ext/enum/union)时: + +- type 字段先写入 8-bit internal kind +- 用户类型 ID (`user_type_id`) 单独以 `varuint32` 写入 +- 不做 bit shift 或 packing +- `user_type_id` 范围可用 `0 ~ 0xFFFFFFFE` + +示例: + +| User ID | Type | Internal ID | Encoded User ID | Decimal | +| ------- | ----------------- | ----------- | --------------- | ------- | +| 0 | STRUCT | 27 | 0 | 0 | +| 0 | ENUM | 25 | 0 | 0 | +| 1 | STRUCT | 27 | 1 | 1 | +| 1 | COMPATIBLE_STRUCT | 28 | 1 | 1 | +| 2 | NAMED_STRUCT | 29 | 2 | 2 | + +读取类型 ID 时: + +- 先读取 internal type ID +- 若属于用户注册类型,再继续读取 `user_type_id(varuint32)` + +## 类型映射 + +| Fory Type | Fory Type ID | Java | Python | Javascript | C++ | Golang | Rust | +| ----------------------- | ------------ | --------------- | ------------------------ | ------------------- | ------------------------------ | ---------------- | ----------------- | +| bool | 1 | bool/Boolean | bool | Boolean | bool | bool | bool | +| int8 | 2 | byte/Byte | int/pyfory.int8 | Type.int8() | int8_t | int8 | i8 | +| int16 | 3 | short/Short | int/pyfory.int16 | Type.int16() | int16_t | int16 | i16 | +| int32 | 4 | int/Integer | int/pyfory.fixed_int32 | Type.int32() | int32_t | int32 | i32 | +| varint32 | 5 | int/Integer | int/pyfory.int32 | Type.varint32() | int32_t | int32 | i32 | +| int64 | 6 | long/Long | int/pyfory.fixed_int64 | Type.int64() | int64_t | int64 | i64 | +| varint64 | 7 | long/Long | int/pyfory.int64 | Type.varint64() | int64_t | int64 | i64 | +| tagged_int64 | 8 | long/Long | int/pyfory.tagged_int64 | Type.tagged_int64() | int64_t | int64 | i64 | +| uint8 | 9 | short/Short | int/pyfory.uint8 | Type.uint8() | uint8_t | uint8 | u8 | +| uint16 | 10 | int/Integer | int/pyfory.uint16 | Type.uint16() | uint16_t | uint16 | u16 | +| uint32 | 11 | long/Long | int/pyfory.fixed_uint32 | Type.uint32() | uint32_t | uint32 | u32 | +| var_uint32 | 12 | long/Long | int/pyfory.uint32 | Type.varUInt32() | uint32_t | uint32 | u32 | +| uint64 | 13 | long/Long | int/pyfory.fixed_uint64 | Type.uint64() | uint64_t | uint64 | u64 | +| var_uint64 | 14 | long/Long | int/pyfory.uint64 | Type.varUInt64() | uint64_t | uint64 | u64 | +| tagged_uint64 | 15 | long/Long | int/pyfory.tagged_uint64 | Type.taggedUInt64() | uint64_t | uint64 | u64 | +| float8 | 16 | / | / | / | / | / | / | +| float16 | 17 | float/Float | float/pyfory.float16 | Type.float16() | fory::float16_t | fory.float16 | fory::f16 | +| bfloat16 | 18 | / | / | / | / | / | / | +| float32 | 19 | float/Float | float/pyfory.float32 | Type.float32() | float | float32 | f32 | +| float64 | 20 | double/Double | float/pyfory.float64 | Type.float64() | double | float64 | f64 | +| string | 21 | String | str | String | string | string | String/str | +| list | 22 | List/Collection | list/tuple | array | vector | slice | Vec | +| set | 23 | Set | set | / | set | fory.Set | Set | +| map | 24 | Map | dict | Map | unordered_map | map | HashMap | +| enum | 25 | Enum subclasses | enum subclasses | / | enum | / | enum | +| named_enum | 26 | Enum subclasses | enum subclasses | / | enum | / | enum | +| struct | 27 | pojo/record | data class | object | struct/class | struct | struct | +| compatible_struct | 28 | pojo/record | data class | object | struct/class | struct | struct | +| named_struct | 29 | pojo/record | data class | object | struct/class | struct | struct | +| named_compatible_struct | 30 | pojo/record | data class | object | struct/class | struct | struct | +| ext | 31 | pojo/record | data class | object | struct/class | struct | struct | +| named_ext | 32 | pojo/record | data class | object | struct/class | struct | struct | +| union | 33 | Union | typing.Union | / | `std::variant` | / | tagged union enum | +| none | 36 | null | None | null | `std::monostate` | nil | `()` | +| duration | 37 | Duration | timedelta | Number | duration | Duration | Duration | +| timestamp | 38 | Instant | datetime | Number | std::chrono::nanoseconds | Time | DateTime | +| date | 39 | Date | datetime | Number | fory::serialization::Date | Time | DateTime | +| decimal | 40 | BigDecimal | Decimal | bigint | / | / | / | +| binary | 41 | byte[] | bytes | / | `uint8_t[n]/vector` | `[n]uint8/[]T` | `Vec` | +| array | 42 | array | np.ndarray | / | / | array/slice | Vec | +| bool_array | 43 | bool[] | ndarray(np.bool\_) | / | `bool[n]` | `[n]bool/[]T` | `Vec` | +| int8_array | 44 | byte[] | ndarray(int8) | / | `int8_t[n]/vector` | `[n]int8/[]T` | `Vec` | +| int16_array | 45 | short[] | ndarray(int16) | / | `int16_t[n]/vector` | `[n]int16/[]T` | `Vec` | +| int32_array | 46 | int[] | ndarray(int32) | / | `int32_t[n]/vector` | `[n]int32/[]T` | `Vec` | +| int64_array | 47 | long[] | ndarray(int64) | / | `int64_t[n]/vector` | `[n]int64/[]T` | `Vec` | +| uint8_array | 48 | short[] | ndarray(uint8) | / | `uint8_t[n]/vector` | `[n]uint8/[]T` | `Vec` | +| uint16_array | 49 | int[] | ndarray(uint16) | / | `uint16_t[n]/vector` | `[n]uint16/[]T` | `Vec` | +| uint32_array | 50 | long[] | ndarray(uint32) | / | `uint32_t[n]/vector` | `[n]uint32/[]T` | `Vec` | +| uint64_array | 51 | long[] | ndarray(uint64) | / | `uint64_t[n]/vector` | `[n]uint64/[]T` | `Vec` | +| float8_array | 52 | / | / | / | / | / | / | +| float16_array | 53 | float[] | ndarray(float16) | / | `fory::float16_t[n]/vector` | `[n]float16/[]T` | `Vec` | +| bfloat16_array | 54 | / | / | / | / | / | / | +| float32_array | 55 | float[] | ndarray(float32) | / | `float[n]/vector` | `[n]float32/[]T` | `Vec` | +| float64_array | 56 | double[] | ndarray(float64) | / | `double[n]/vector` | `[n]float64/[]T` | `Vec` | + +## 类型信息(当前尚未完整实现) + +由于不同语言类型系统存在差异,部分类型无法一一对应映射。 + +如果某语言中的一个类型可能对应多个 Fory 类型(例如 Java `long` 可能对应 `int64/varint64/tagged_int64`), +说明该语言缺少更细粒度区分,此时调用方需要额外提供类型信息。 + +## 类型注解 + +当某类型作为其他类的字段时,可以通过字段级或类型级元数据提示具体编码语义。不同语言的配置方式: + +- Java:注解 +- C++:宏与模板 +- Go:struct tag +- Python:type hint +- Rust:宏 + +示例: + +- Java: + + ```java + class Foo { + @Int32Type(varint = true) + int f1; + List<@Int32Type(varint = true) Integer> f2; + } + ``` + +- Python: + + ```python + class Foo: + f1: pyfory.varint32 + f2: List[pyfory.varint32] + ``` diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.15/specification/java_serialization_spec.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.15/specification/java_serialization_spec.md new file mode 100644 index 0000000000..959eeb2b6b --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.15/specification/java_serialization_spec.md @@ -0,0 +1,381 @@ +--- +title: Java 序列化格式 +sidebar_position: 1 +id: java_serialization_spec +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. +--- + +本文定义 Apache Fory Java 原生序列化格式的编码格式细节。 + +## 规范概览 + +Fory Java 原生格式面向 Java 对象图,支持: + +- 共享引用与循环引用 +- 多态对象 +- 可选 schema 演进 +- 流式写入(共享 type meta 按需内联,不要求预先 meta 区) + +Java native 是 xlang 编码格式的扩展,复用相同核心帧与基础编码。 + +总体布局: + +``` +| fory header | object ref meta | object type meta | object value data | +``` + +字节序统一为 little-endian。大端平台实现需在数组等路径做字节序转换,确保线上格式一致。 + +## Fory 头部 + +Java 原生序列化头部是 1-byte 位图: + +``` +| 5 bits | 1 bit | 1 bit | 1 bit | ++--------------+-------+-------+-------+ +| reserved | oob | xlang | null | +``` + +含义: + +- `null`:对象是否为 null(为 1 时其余位不应置位) +- `xlang`:1 表示 xlang 格式,0 表示 Java native +- `oob`:1 表示存在 `BufferCallback` + +头部始终 1 字节,不额外写 language ID。 + +## 引用元信息 + +引用跟踪标记与 xlang 一致: + +| 标记 | 字节值 | 说明 | +| -------------------- | ------ | ------------------------------------------------------ | +| NULL FLAG | `-3` | 对象为 null,后续不写对象内容 | +| REF FLAG | `-2` | 已出现对象,后接 `varuint32` 的 reference ID | +| NOT_NULL VALUE FLAG | `-1` | 非空但该类型未启用引用跟踪,后接对象内容 | +| REF VALUE FLAG | `0` | 可引用对象首次出现,后接对象内容,并分配新 reference ID | + +当全局或字段级禁用引用跟踪时,仅使用 `NULL FLAG` 与 `NOT_NULL VALUE FLAG`。 + +## Type system and type IDs + +Java native 使用与 xlang 统一的类型 ID 组合方式: + +``` +full_type_id = (user_type_id << 8) | internal_type_id +``` + +- `internal_type_id`:低 8 位,表示类型种类 +- `user_type_id`:用户注册数值 ID(适用于用户 enum/struct/ext) +- named 类型使用 `NAMED_*`,其标识来自元信息(命名空间+类型名) + +### Shared internal type IDs (0-63) + +Java native 与 xlang 共享 `< 64` 的 internal IDs: + +- `0~56` 已定义 +- `57~63` 预留 + +详见 [Xlang Serialization Format](xlang_serialization_spec.md#internal-type-id-table)。 + +### Java native built-in type IDs + +Java 专有内建类型从 `Types.BOUND + 5` 开始(`Types.BOUND=64`,预留 5 个)。 + +| Type ID | Name | Description | +| ------- | -------------------------- | ------------------------------ | +| 69 | VOID_ID | java.lang.Void | +| 70 | CHAR_ID | java.lang.Character | +| 71 | PRIMITIVE_VOID_ID | void | +| 72 | PRIMITIVE_BOOL_ID | boolean | +| 73 | PRIMITIVE_INT8_ID | byte | +| 74 | PRIMITIVE_CHAR_ID | char | +| 75 | PRIMITIVE_INT16_ID | short | +| 76 | PRIMITIVE_INT32_ID | int | +| 77 | PRIMITIVE_FLOAT32_ID | float | +| 78 | PRIMITIVE_INT64_ID | long | +| 79 | PRIMITIVE_FLOAT64_ID | double | +| 80 | PRIMITIVE_BOOLEAN_ARRAY_ID | boolean[] | +| 81 | PRIMITIVE_BYTE_ARRAY_ID | byte[] | +| 82 | PRIMITIVE_CHAR_ARRAY_ID | char[] | +| 83 | PRIMITIVE_SHORT_ARRAY_ID | short[] | +| 84 | PRIMITIVE_INT_ARRAY_ID | int[] | +| 85 | PRIMITIVE_FLOAT_ARRAY_ID | float[] | +| 86 | PRIMITIVE_LONG_ARRAY_ID | long[] | +| 87 | PRIMITIVE_DOUBLE_ARRAY_ID | double[] | +| 88 | STRING_ARRAY_ID | String[] | +| 89 | OBJECT_ARRAY_ID | Object[] | +| 90 | ARRAYLIST_ID | java.util.ArrayList | +| 91 | HASHMAP_ID | java.util.HashMap | +| 92 | HASHSET_ID | java.util.HashSet | +| 93 | CLASS_ID | java.lang.Class | +| 94 | EMPTY_OBJECT_ID | empty object stub | +| 95 | LAMBDA_STUB_ID | lambda stub | +| 96 | JDK_PROXY_STUB_ID | JDK proxy stub | +| 97 | REPLACE_STUB_ID | writeReplace/readResolve stub | +| 98 | NONEXISTENT_META_SHARED_ID | meta-shared unknown class stub | + +### Registration and named types + +用户类型可按数值或名称注册: + +- 数值注册:`full_type_id = (user_id << 8) | internal_type_id` +- 名称注册:通过 namespace + typename +- 未注册类型按 named 类型写出(namespace=包名,typename=类名) + +未注册时命名类型选择: + +- enum -> `NAMED_ENUM` +- struct-like -> `NAMED_STRUCT`(兼容模式下 `NAMED_COMPATIBLE_STRUCT`) +- 其他自定义 serializer -> `NAMED_EXT` + +## Type meta encoding + +每个值写入流程: + +1. 写 `type_id`(varuint32 small7) +2. 对 `NAMED_*` / `COMPATIBLE_STRUCT` 等类型按规则写额外元信息 +3. 其余类型无需额外 meta + +### Shared class meta (streaming) + +开启 meta share 时,使用流式共享 class meta: + +``` +| varuint32: index_marker | [class def bytes if new] | + +index_marker = (index << 1) | flag +flag = 1 -> reference +flag = 0 -> new type +``` + +- `flag=1`:引用历史 type,后续无 class def bytes +- `flag=0`:新 type,后续内联 class def +- index 按首次出现顺序递增 + +## Schema modes + +Java native 支持两种模式: + +- **Schema consistent**(compatible 关闭):字段按固定顺序写,不需要 ClassDef +- **Schema evolution**(compatible 开启):按 ClassDef 元信息支持演进 + +## ClassDef format (compatible mode) + +ClassDef 是 compatible struct 的演进元信息。 + +### Binary layout + +``` +| 8 bytes header | [varuint32 extra size] | class meta bytes | +``` + +header 位布局: + +``` +| 50-bit hash | 4 bits reserved | 1 bit compress | 1 bit has_fields_meta | 8-bit size | +``` + +规则: + +- `size` 为低 8 位;若为 `0xFF`,后接扩展长度 +- `compress`:payload 是否压缩 +- `has_fields_meta`:是否含字段元信息 +- `reserved` 必须为 0 +- `hash` 为 payload + flags 的 50-bit 哈希 + +### Class meta bytes + +表示线性化继承层次(父 -> 子)与字段信息: + +``` +| num_classes | class_layer_0 | class_layer_1 | ... | + +class_layer: +| num_fields << 1 | registered_flag | [type_id if registered] | +| namespace | type_name | field_infos | +``` + +### Field info + +每个字段: + +``` +| field_header | [field_name_bytes] | field_type | +``` + +`field_header`: + +- bit0: trackingRef +- bit1: nullable +- bit2-3: 字段名编码 +- bit4-6: name length/tag ID(7 表示扩展) +- bit7: reserved=0 + +字段名编码: + +- 0: UTF8 +- 1: ALL_TO_LOWER_SPECIAL +- 2: LOWER_UPPER_DIGIT_SPECIAL +- 3: TAG_ID(省略字段名,存 tag) + +### Field type encoding + +字段类型使用 type tag + 可选嵌套类型信息: + +| Tag | 字段类型 | +| --- | ----------------------------------------- | +| 0 | Object (ObjectFieldType) | +| 1 | Map (MapFieldType) | +| 2 | Collection/List/Set (CollectionFieldType) | +| 3 | Array (ArrayFieldType) | +| 4 | Enum (EnumFieldType) | +| 5+ | Registered type (RegisteredFieldType) | + +嵌套类型头部低位可携带 `nullable/tracking_ref` 标志。 + +## Meta string encoding + +namespace、type name、field name 复用 xlang 的 meta string 编码。 + +### Package and type names + +头部: + +``` +| 6 bits size | 2 bits encoding | +``` + +- `size=63` 时追加扩展长度 +- package 支持:UTF8 / ALL_TO_LOWER_SPECIAL / LOWER_UPPER_DIGIT_SPECIAL +- type name 支持:UTF8 / LOWER_UPPER_DIGIT_SPECIAL / FIRST_TO_LOWER_SPECIAL / ALL_TO_LOWER_SPECIAL + +### Field names + +字段名编码与 ClassDef 的 field header 规则一致;若使用 TAG_ID,则不写名字字节。 + +### Encoding algorithms + +具体算法参见 xlang 规范 `#meta-string`。 + +## Value encodings + +以下为 Java native 常见内建 serializer 的值布局。自定义 `EXT` 可定义自身值格式,但必须遵循“引用元信息 + 类型元信息”通道规则。 + +### Primitives + +- boolean: 1 byte(0/1) +- byte: 1 byte +- short: 2 bytes little-endian +- char: 2 bytes little-endian(UTF-16 code unit) +- int: fixed(4 bytes) 或 varint32(ZigZag) +- long: fixed(8 bytes) / varint64(ZigZag) / tagged int64 +- float: IEEE754 float32 +- double: IEEE754 float64 + +### String + +``` +| varuint36_small: (num_bytes << 2) | coder | string bytes | +``` + +- coder(2 bits):LATIN1 / UTF16 / UTF8 +- UTF16 采用 little-endian code unit + +### Enum + +- `serializeEnumByName=true`:写 enum 名称(meta string) +- 否则:写 ordinal(varuint32 small7) + +### Binary (byte[]) + +``` +| varuint32: num_bytes | raw bytes | +``` + +### Primitive arrays + +默认: + +``` +| varuint32: byte_length | raw bytes | +``` + +可选压缩: + +- `compressIntArray`:`| length | varint32... |` +- `compressLongArray`:`| length | varint64/tagged... |` + +### Object arrays + +``` +| varuint32_small7: (length << 1) | mono_flag | +``` + +- `mono_flag=1`:单态数组,共享组件 serializer +- `mono_flag=0`:每元素独立 class info + data + +### Collections (List/Set) + +``` +| varuint32_small7: length | elements_header | [elem_class_info] | elements... | +``` + +`elements_header`: + +- bit0: TRACKING_REF +- bit1: HAS_NULL +- bit2: IS_DECL_ELEMENT_TYPE +- bit3: IS_SAME_TYPE + +### Maps + +``` +| varuint32_small7: size | chunk_1 | chunk_2 | ... | + +chunk: +| header | chunk_size | [key_class_info] | [value_class_info] | entries... | +``` + +Map 采用 chunk 编码,每块最多 255 entries;key/value 的引用跟踪与类型声明按 header 位图控制。 + +#### Null key/value entries + +`null key` 或 `null value` 使用特殊单条 chunk,不写 `chunk_size`,只写对应 payload。 + +### Objects and structs + +对象值通道: + +``` +| ref meta | type meta | field data | +``` + +标准对象序列化中: + +- 字段按稳定顺序(DescriptorGrouper)排序 +- compatible 模式可借助 ClassDef 映射字段并跳过未知字段 +- 字段是否写 ref/type meta 由 nullable/ref/polymorphic 策略决定 + +### Extensions (EXT) + +EXT 类型值由注册 serializer 定义;但其外层仍遵循统一的引用与类型元信息协议。 + +## Out-of-band buffers + +当提供 `BufferCallback` 时,header 的 `oob` 位置 1。此时 serializer 可输出“缓冲区引用”而非内联字节(如大数组)。 +主流中仅保留引用信息,具体 OOB 缓冲区协议由 callback 实现定义。 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.15/specification/row_format_spec.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.15/specification/row_format_spec.md new file mode 100644 index 0000000000..3f7f9866ef --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.15/specification/row_format_spec.md @@ -0,0 +1,356 @@ +--- +title: Row 格式 +sidebar_position: 2 +id: row_format_spec +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. +--- + +## 概述 + +Apache Fory Row Format 是面向高性能数据处理的二进制布局,目标是“可随机访问 + 低拷贝 + 跨语言一致”。 + +相较于必须整对象反序列化的格式,Row Format 支持: + +- **随机字段读取**:只读目标字段 +- **零拷贝访问**:在可行场景直接基于内存切片读取 +- **缓存友好布局**:降低 CPU cache miss +- **跨语言一致性**:Java/C++/Python 可共享标准格式 + +Fory 提供两种变体: + +| 格式 | 支持语言 | 典型用途 | +| ---------------- | -------------------- | -------------------------------- | +| Standard Format | Java、C++、Python | 跨语言一致、实现简单 | +| Compact Format | 仅 Java | 更小体积、更高局部性 | + +## 格式对比 + +| 特性 | Standard Format | Compact Format | +| ---------------------- | --------------------------------- | -------------------------------------- | +| 字段槽位大小 | 固定 8 字节 | 按自然宽度(1/2/4/8 字节) | +| Null Bitmap | 8 字节对齐 | 字节对齐,可借用尾部 padding | +| Null Bitmap 位置 | 字段槽位之前 | 字段槽位之后(尾部) | +| 固定大小 struct | 放在 variable region(offset+size) | 可内联到 fixed region | +| 字段顺序 | 按 schema 定义顺序 | 按对齐规则排序 | +| 全非空字段 | 仍保留 bitmap | 可完全省略 bitmap | +| 对齐策略 | 严格 8 字节 | 放宽(2/4/8 字节) | + +## 标准 Row 格式 + +标准格式强调跨语言统一与实现稳定:字段槽位统一 8 字节。 + +### 设计原则 + +1. **8 字节对齐**:主要结构按 8-byte 对齐 +2. **固定槽位**:每字段固定 8-byte slot,便于常数时间寻址 +3. **位图标记 null**:通过 bitset 跟踪空值 +4. **相对偏移**:变长数据通过相对偏移定位 + +### Row 二进制布局 + +``` ++----------------+------------------+------------------+-----+------------------+------------------+ +| Null Bitmap | Field 0 Slot | Field 1 Slot | ... | Field N-1 Slot | Variable Data | ++----------------+------------------+------------------+-----+------------------+------------------+ +| B bytes | 8 bytes | 8 bytes | | 8 bytes | Variable size | +``` + +#### Null Bitmap + +- 大小:`((num_fields + 63) / 64) * 8` 字节(向上取整到 8-byte word) +- 编码:每 bit 对应一个字段 +- bit=1 表示 null,bit=0 表示非 null +- 第一字节 bit0 对应 field0 + +#### 字段槽位 + +- 任意字段都占 8 字节 slot +- 槽位偏移:`bitmap_size + field_index * 8` +- 固定区总大小:`bitmap_size + num_fields * 8` + +槽位内容: + +| 字段类别 | 槽位内容 | +| ---------------- | ----------------------------------------- | +| 定长类型 | 值直接写入(不足补零) | +| 变长类型 | `offset + size` 打包 | + +#### 变长字段编码 + +变长字段(string/array/map/nested struct)在 slot 中写入: + +``` ++---------------------------+---------------------------+ +| Relative Offset | Size | +| (32 bits) | (32 bits) | ++---------------------------+---------------------------+ +``` + +- 高 32 位:相对 row 起始地址偏移 +- 低 32 位:数据长度(字节) + +编码: + +``` +offset_and_size = (relative_offset << 32) | size +``` + +解码: + +``` +relative_offset = (offset_and_size >> 32) & 0xFFFFFFFF +size = offset_and_size & 0xFFFFFFFF +``` + +#### Variable Data 区 + +- 位于 fixed region 之后 +- 变长字段按写入顺序顺排 +- 每个条目按 8-byte 对齐 +- padding 字节清零,保证输出确定性 + +### Array 二进制布局 + +``` ++------------------+------------------+------------------+ +| Element Count | Null Bitmap | Element Data | ++------------------+------------------+------------------+ +| 8 bytes | B bytes | Variable size | +``` + +#### Array Header + +| 字段 | 大小 | 说明 | +| --------------- | ------------------------------- | ----------------------- | +| Element Count | 8 字节 | 元素数量(uint64) | +| Null Bitmap | `((count + 63) / 64) * 8` 字节 | 每元素 null 标记 | + +#### Array Element Data + +- 定长元素按自然宽度连续存储 +- 变长元素存 8-byte `offset+size` +- 元素偏移:`header_size + element_index * element_size` +- 数据区总大小按 8-byte 对齐 + +#### Array 元素大小 + +| 元素类型 | 元素占用 | +| ---------------- | -------------------------- | +| bool/int8 | 1 byte | +| int16 | 2 bytes | +| int32/float32 | 4 bytes | +| int64/float64 | 8 bytes | +| string/binary | 8 bytes(offset+size) | + +### Map 二进制布局 + +Map 在 Row Format 中可视为键值对数组,并包含类型信息与可空位图。 + +#### Map 结构 + +推荐逻辑结构: + +1. entry count +2. key/value null bitmap(按实现可拆分) +3. key data +4. value data + +#### 嵌套 Struct 布局 + +嵌套 struct 在标准格式中作为变长字段处理: + +- slot 写 `offset+size` +- value region 中存其完整 row 二进制 +- 可递归嵌套 + +## Compact Row Format(仅 Java) + +Compact 格式针对 Java 本地执行链路做体积与局部性优化,不保证与标准格式完全同布局。 + +### 设计原则 + +1. 字段槽按自然宽度缩小 +2. 字段重排以减少 padding +3. null bitmap 可省略或压缩 +4. 定长嵌套 struct 尽可能内联 + +### Compact Row Binary Layout + +总体仍是“fixed + variable”双区结构,但 fixed region 更紧凑,null bitmap 可能后置。 + +#### 与标准格式的关键差异 + +- slot 宽度非固定 8 字节 +- bitmap 可后置 +- 全非空时 bitmap 可省略 +- 可内联定长 nested struct + +#### Null Bitmap(Compact) + +- 按字节对齐 +- 可能复用尾部 padding +- 无可空字段时可完全移除 + +#### 字段排序算法 + +典型排序目标: + +- 优先高对齐需求字段 +- 次序兼顾读取热度与 padding 最小化 +- 保持可重复计算(deterministic) + +#### 定长 Struct 内联 + +若嵌套 struct 满足定长条件,可直接内联到 fixed region,减少一次间接寻址。 + +#### 定宽计算 + +定宽字段总大小由字段类型宽度与对齐约束共同决定,实际布局由编译器/运行时规划器输出。 + +### Compact Array Binary Layout + +与标准数组类似,但 header 与元素布局可采用更紧凑表示。 + +#### Compact Array Header + +包含:元素数量、null 信息(可选)、元素布局元信息(按实现)。 + +#### 与标准数组关键差异 + +- 头部更短 +- 元素存储更贴近自然宽度 +- 在 Java 热路径下更节省内存带宽 + +## 通用规范 + +### 类型编码 + +#### Primitive Types + +基础类型按既定宽度和 endian 读写(默认 little-endian)。 + +#### Temporal Types + +- `date`:通常以 epoch day 表示 +- `timestamp`:通常以 epoch 纳秒或秒+纳秒表示 + +#### String 与 Binary + +都属于变长类型,使用 `offset+size` 指向真实 payload。 + +### Null 处理 + +#### Row Null 处理 + +由 row bitmap 标记字段 null 状态,null 字段对应 slot 内容可忽略。 + +#### Array Null 处理 + +由数组 bitmap 标记每个元素是否为 null。 + +#### 变长 null 语义 + +null 与空值(如空字符串、空数组)必须区分;空值应写长度为 0 的实体。 + +### 对齐与 Padding + +#### Standard 对齐 + +以 8-byte 为主,保证跨语言一致实现简单。 + +#### Compact 对齐 + +允许 2/4/8 灵活对齐,目标是减少浪费。 + +#### Padding 字节 + +padding 建议写 0,避免未初始化内存泄露并提高可重复性。 + +## 大小计算 + +### Standard Row 大小 + +``` +row_size = bitmap_size + num_fields * 8 + aligned(variable_region_size) +``` + +### Compact Row 大小 + +``` +row_size = compact_fixed_size + optional_bitmap + aligned(variable_region_size) +``` + +### Standard Array 大小 + +``` +array_size = header_size + aligned(element_data_size) +``` + +### Compact Array 大小 + +``` +array_size = compact_header_size + aligned(compact_element_data_size) +``` + +### Map 大小 + +``` +map_size = map_header + aligned(keys_region) + aligned(values_region) +``` + +## 汇总表 + +### 布局总结 + +| 结构 | Standard | Compact (Java) | +| --------------- | -------------------------------- | -------------------------------- | +| Row | bitmap + 8-byte slots + var data | compact fixed + optional bitmap + var data | +| Array | count + bitmap + elements | compact header + elements | +| Map | count + key/value data regions | 依实现可紧凑化 | + +### 类型宽度总结 + +| 类型族 | 典型宽度 | +| ---------------------- | --------------------------- | +| bool/int8 | 1 byte | +| int16 | 2 bytes | +| int32/float32 | 4 bytes | +| int64/float64 | 8 bytes | +| string/binary/复杂类型 | slot 中 8-byte offset+size | + +## 实现说明 + +### Endianness + +统一使用 little-endian,跨语言实现必须一致。 + +### 内存安全 + +- 读取前先做边界检查 +- 对 offset/size 做溢出检查 +- 避免使用未初始化 padding + +### 性能建议 + +- 批量顺序写入,减少随机写 +- 热字段优先放 fixed region 前部 +- 通过 profile 决定是否启用 compact + +### 何时选择哪种格式 + +- **Standard**:跨语言互通、调试友好、长期兼容 +- **Compact**:Java 单语言链路、内存敏感、高吞吐场景 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.15/specification/xlang_implementation_guide.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.15/specification/xlang_implementation_guide.md new file mode 100644 index 0000000000..9e1e2dea33 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.15/specification/xlang_implementation_guide.md @@ -0,0 +1,251 @@ +--- +title: Xlang 实现指南 +sidebar_position: 10 +id: xlang_implementation_guide +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. +--- + +## 实现建议 + +### 如何减少内存读写开销 + +- 尽量把多次 byte 写入合并为 int/long 写入,降低内存 IO 与边界检查成本。 +- 读取时可一次读取 int/long,再拆分字节,减少内存访问次数。 +- 尽可能把 flag 与 length 合并到同一个 varint/long,减少字节数与 IO。 +- 在分支数量可控的前提下,分支开销通常小于内存 IO 开销。 + +### 静态语言(无运行时代码生成)如何做高性能反序列化 + +在类型演进场景,序列化数据会携带 type meta。反序列化端会比较“数据内 meta”与“本地类 meta”,根据差异执行字段映射。 + +对 Java/Javascript/Python,可在运行时根据 diff 动态生成并加载反序列化逻辑,从而接近 schema-consistent 模式性能。 + +对 C++/Rust,通常无法在运行时动态生成 serializer,只能在编译期元编程生成。此时无法提前知道对端 schema, +因此需要在运行时根据字段元信息逐个判断“赋值或跳过”。 + +可行优化:将字符串字段匹配尽量提前转换为 `field id`,再在反序列化路径用 `switch` 处理,利用连续 case 的跳转优化。 + +思路: + +- 假设本地类型字段数为 `n`,对端字段数为 `n1` +- 编译期先为本地排序字段分配连续 `field id`(从 0 开始) +- 运行时比较对端 type meta:同名字段复用本地 id,不同字段分配从 `n` 开始的新 id,并缓存映射 +- 反序列化遍历对端字段时对 `field_id` 做 `switch`,命中则赋值,未命中则跳过 + +示例:A 进程持有 `Foo1`,B 进程持有演进后的 `Foo2`: + +```c++ +// class Foo with version 1 +class Foo1 { + int32_t v1; // id 0 + std::string v2; // id 1 +}; +// class Foo with version 2 +class Foo2 { + // id 0, but will have id 2 in process A + bool v0; + // id 1, but will have id 0 in process A + int32_t v1; + // id 2, but will have id 3 in process A + int64_t long_value; + // id 3, but will have id 1 in process A + std::string v2; + // id 4, but will have id 4 in process A + std::vector list; +}; +``` + +A 收到 B 发送的 `Foo2` 数据后,可按 `field_id` 快速分发: + +```c++ +Foo1 foo1 = ...; +const std::vector &field_infos = type_meta.field_infos; +for (const auto &field_info : field_infos) { + switch (field_info.field_id) { + case 0: + foo1.v1 = buffer.read_varint32(); + break; + case 1: + foo1.v2 = fory.read_string(); + break; + default: + fory.skip_data(field_info); + } +} +``` + +## 新语言实现检查清单 + +本节给出在新语言中落地 Fory xlang 序列化的阶段性清单。 + +### 阶段 1:核心基础设施 + +1. **Buffer 实现** + - [ ] 提供带读写游标的字节缓冲区 + - [ ] 所有多字节读写统一使用 little-endian + - [ ] 实现 `write_int8/int16/int32/int64` + - [ ] 实现 `write_float32/write_float64` + - [ ] 实现对应 `read_*` 方法 + - [ ] 实现缓冲区扩容策略(如倍增) + +2. **Varint 编码** + - [ ] `write_varuint32/read_varuint32` + - [ ] `write_varint32/read_varint32`(含 ZigZag) + - [ ] `write_varuint64/read_varuint64` + - [ ] `write_varint64/read_varint64`(含 ZigZag) + - [ ] `write_varuint36_small/read_varuint36_small`(字符串头使用) + - [ ] 可选:实现 int64 Hybrid 编码(TAGGED_INT64/TAGGED_UINT64) + +3. **头部处理** + - [ ] 读写 bitmap flags(null、xlang、oob) + +### 阶段 2:基础类型序列化 + +4. **基础类型** + - [ ] bool(1 字节:0/1) + - [ ] int8/int16/int32/int64(little-endian) + - [ ] float32/float64(IEEE 754,little-endian) + +5. **字符串序列化** + - [ ] 实现字符串头:`(byte_length << 2) | encoding` + - [ ] 支持 UTF-8(xlang 必需) + - [ ] 可选支持 LATIN1 与 UTF-16 + +6. **时间类型** + - [ ] Duration(seconds + nanoseconds) + - [ ] Timestamp(epoch 以来 seconds + nanoseconds) + - [ ] Date(epoch 以来天数) + +7. **引用跟踪** + - [ ] 写侧对象跟踪(object -> ref_id) + - [ ] 读侧对象跟踪(ref_id -> object) + - [ ] 支持 4 种引用标记:NULL(-3)、REF(-2)、NOT_NULL(-1)、REF_VALUE(0) + - [ ] 支持按类型或全局关闭引用跟踪 + +### 阶段 3:集合类型 + +8. **List/Array** + - [ ] 长度写为 varuint32 + - [ ] 写 elements header byte + - [ ] 处理同构/异构元素 + - [ ] 处理 null 元素 + +9. **Map** + - [ ] 总大小写为 varuint32 + - [ ] 实现分块格式(每块最多 255 对) + - [ ] 每块写 KV header + - [ ] 处理 key/value 类型变化 + +10. **Set** + - [ ] 与 List 复用同一套实现 + +### 阶段 4:Meta String 编码 + +meta string 用于 enum/struct 的字段名、类型名、命名空间编码。 + +11. **Meta String 压缩** + - [ ] LOWER_SPECIAL(5 bits/char) + - [ ] LOWER_UPPER_DIGIT_SPECIAL(6 bits/char) + - [ ] FIRST_TO_LOWER_SPECIAL + - [ ] ALL_TO_LOWER_SPECIAL + - [ ] 编码选择算法 + - [ ] meta string 去重 + +### 阶段 5:Enum 序列化 + +12. **Enum** + - [ ] ordinal 写为 varuint32 + - [ ] 支持命名 enum(namespace + typename) + +### 阶段 6:Struct 序列化 + +13. **类型注册** + - [ ] 支持按数值 ID 注册 + - [ ] 支持按 namespace + typename 注册 + - [ ] 维护 type -> serializer 映射 + - [ ] 生成 type ID:先写 internal type ID,再写 `user_type_id(varuint32)` + +14. **字段排序** + - [ ] 实现规范定义的分组与排序(primitive/boxed/builtin、collections/maps、other) + - [ ] 组内使用稳定比较器(type ID + name) + - [ ] 指纹字段标识使用 tag ID 或 snake_case 字段名 + +15. **Schema Consistent 模式** + - [ ] 开启类版本校验时,按字段标识计算 schema hash + - [ ] 在字段前写 4-byte schema hash + - [ ] 按 Fory 字段顺序序列化 + +16. **Compatible/Meta Share 模式** + - [ ] 实现共享 TypeDef 流(新 TypeDef 内联,已存在用索引) + - [ ] 按字段名或 tag ID 做映射,未知字段跳过 + - [ ] 按 TypeDef 元信息应用 nullable/ref 规则 + +### 阶段 7:其他类型 + +17. **Binary/Array** + +- [ ] 基础类型数组支持直接 buffer copy +- [ ] 多维数组按嵌套 list 表达(不做 tensor 专用编码) + +### 测试策略 + +18. **跨语言兼容测试** + - [ ] 新语言写 -> Java/Python 读 + - [ ] Java/Python 写 -> 新语言读 + - [ ] 覆盖所有基础类型 + - [ ] 覆盖不同字符串编码 + - [ ] 覆盖集合(空、单元素、多元素) + - [ ] 覆盖不同 key/value map + - [ ] 覆盖嵌套 struct + - [ ] 覆盖循环引用(若支持) + +## 各语言实现备注 + +### Java + +- 通过运行时代码生成(JIT)获得高性能 +- 支持完整引用跟踪模式 +- 利用 String coder 做编码选择 +- 可通过 `ThreadSafeFory` 提供线程安全封装 + +### Python + +- 提供纯 Python(调试)与 Cython(性能)两种模式 +- 使用 `id(obj)` 做引用跟踪 +- xlang 模式支持 LATIN1/UTF-16/UTF-8 +- 通过代码生成增强 `dataclass` 支持 + +### C++ + +- 通过宏(`FORY_STRUCT`)做编译期反射 +- 模板元编程进行类型分发与 serializer 选择 +- 使用 `std::shared_ptr` 支持引用跟踪 +- 编译期字段排序 +- 不依赖运行时代码生成 + +### Rust + +- 使用 derive 宏自动生成序列化代码(`#[derive(ForyObject)]`) +- 使用 `Rc`/`Arc` 做引用跟踪 +- 通过线程本地上下文缓存优化性能 +- 编译期字段排序 + +### Go + +- 支持反射模式与代码生成模式 +- 通过 struct tag 提供字段元信息 +- 通过接口类型支持多态语义 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.15/specification/xlang_serialization_spec.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.15/specification/xlang_serialization_spec.md new file mode 100644 index 0000000000..1a1800bbc6 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.15/specification/xlang_serialization_spec.md @@ -0,0 +1,558 @@ +--- +title: Xlang 序列化格式 +sidebar_position: 4 +id: xlang_serialization_spec +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. +--- + +## 跨语言序列化规范 + +本文定义 Apache Fory xlang 二进制协议的通用线格式,适用于多语言互操作场景。 + +目标: + +- 二进制布局跨语言稳定 +- 支持引用跟踪、类型元信息和 schema 演进 +- 在流式序列化中支持增量写入共享元信息 + +## 类型系统 + +### 数据类型 + +xlang 类型分为: + +- 基础类型:bool、整数、浮点、string、binary +- 容器类型:list、set、map、array +- 结构类型:enum、struct、union、ext +- 时间类型:duration、timestamp、date + +### Polymorphisms + +协议支持多态对象。解码端可依据 type meta 判断运行时真实类型,并选择对应 serializer。 + +### Type disambiguation + +当某语言类型可映射到多个 Fory 类型(如 fixed/varint/tagged 整数)时,必须通过字段元信息或类型注解消歧。 + +### Type ID + +类型由 `internal_type_id` 与(可选)`user_type_id` 共同表达: + +- 内建类型通常直接由 internal ID 唯一表示 +- 用户类型通过 internal kind + user ID(或命名类型)表示 + +#### Internal Type ID Table + +核心 internal IDs(示例): + +| ID | 类型 | +| --- | ------------------- | +| 1 | bool | +| 2-20| 各类数字类型 | +| 21 | string | +| 22 | list | +| 23 | set | +| 24 | map | +| 25 | enum | +| 27 | struct | +| 28 | compatible_struct | +| 31 | ext | +| 33 | union | +| 36 | none | +| 37 | duration | +| 38 | timestamp | +| 39 | date | +| 40+ | decimal/binary/array 等 | + +完整映射见 [Xlang 类型映射](xlang_type_mapping.md)。 + +#### Type ID Encoding for User Types + +用户类型采用拆分编码: + +- 先写 internal type ID(8-bit kind) +- 再写 `user_type_id`(varuint32) + +不做 bit packing,便于实现与调试。 + +### Type mapping + +跨语言类型映射总表见 [xlang_type_mapping.md](xlang_type_mapping.md)。 + +## 规范概览 + +顶层布局: + +``` +| fory header | reference meta | type meta | value payload | +``` + +协议默认 little-endian。 + +## Fory 头部 + +头部是 1-byte bitmap: + +``` +| reserved(5) | oob(1) | xlang(1) | null(1) | +``` + +- `null=1` 时值为空,不再写值数据 +- `xlang=1` 表示采用 xlang 格式 +- `oob=1` 表示存在 out-of-band 缓冲区引用 + +## Reference Meta + +### Reference Flags + +| 标记 | 值 | 含义 | +| -------------------- | ---- | ----------------------------------------- | +| NULL_FLAG | -3 | null | +| REF_FLAG | -2 | 已出现对象,后接 ref id | +| NOT_NULL_VALUE_FLAG | -1 | 非空但不跟踪引用 | +| REF_VALUE_FLAG | 0 | 首次出现的可引用对象 | + +### Reference Tracking Algorithm + +写侧: + +1. 先判断 null +2. 若可引用且已出现,写 `REF_FLAG + ref_id` +3. 若可引用且首次出现,写 `REF_VALUE_FLAG` 并登记 +4. 若不可引用,写 `NOT_NULL_VALUE_FLAG` + +读侧: + +1. 读取标记 +2. `REF_FLAG` 时按 ref_id 回表 +3. `REF_VALUE_FLAG` 时先构造对象再登记 +4. `NOT_NULL_VALUE_FLAG` 时直接读值 + +### Reference ID Assignment + +ref id 按对象首次出现顺序递增分配,从 0 开始。 + +### When Reference Tracking is Disabled + +禁用引用跟踪时,仅使用 null / not-null 两类标记,不维护 ref 表。 + +### Language-Specific Considerations + +不同语言应保证: + +- 对象身份判定一致(身份而非值相等) +- 容器元素引用语义一致 +- 循环引用场景先占位后填充 + +## Type Meta + +### Type ID encoding + +type id 使用 varuint 编码写入。 + +### Type meta payload + +在以下情况写额外 type meta: + +- 命名类型(`NAMED_*`) +- compatible struct 需要 TypeDef +- 运行时未声明类型需要动态 type info + +### Shared Type Meta (streaming) + +共享 type meta 采用“索引 + 可选定义体”流式写法: + +``` +index_marker = (index << 1) | is_ref +``` + +- `is_ref=1`:引用已有 type +- `is_ref=0`:新 type,后接定义体 + +### TypeDef (schema evolution metadata) + +TypeDef 用于描述 compatible 模式的字段元信息(字段名/tag、nullable/ref、字段类型)。 + +#### Global header + +TypeDef 头部包含: + +- payload size +- flags(如 compress、has_fields_meta) +- payload hash + +#### TypeDef body + +主体包含: + +- class 层次信息(父类到子类) +- 每层字段数量与类型标识 +- 字段级元信息(名称编码/tag、nullable/ref、field type) + +## Meta String + +meta string 用于 namespace、typename、fieldname 的压缩表示。 + +### Encoding Type IDs + +常见编码族: + +- UTF8 +- LOWER_SPECIAL +- LOWER_UPPER_DIGIT_SPECIAL +- FIRST_TO_LOWER_SPECIAL +- ALL_TO_LOWER_SPECIAL + +### Character Mapping Tables + +#### LOWER_SPECIAL (5 bits per character) + +适用于小写字母 + 高频特殊字符集合。 + +#### LOWER_UPPER_DIGIT_SPECIAL (6 bits per character) + +适用于大小写字母、数字与特殊字符混合场景。 + +### Encoding Algorithms + +#### LOWER_SPECIAL Encoding + +按 5-bit 映射逐字符编码,无法映射的字符需回退至其他编码。 + +#### FIRST_TO_LOWER_SPECIAL Encoding + +首字符单独处理,其余字符按 LOWER_SPECIAL 编码。 + +#### ALL_TO_LOWER_SPECIAL Encoding + +先归一化,再按 LOWER_SPECIAL 编码。 + +### Encoding Selection Algorithm + +编码选择策略: + +1. 尝试最紧凑编码 +2. 若字符集合不满足则降级 +3. 必要时回退 UTF8 + +### Meta String Header Format + +``` +| size_bits | encoding_bits | +``` + +当 `size` 超过短头范围时追加 varuint 扩展长度。 + +### Special Character Sets by Context + +不同上下文(包名、类型名、字段名)允许字符集合可不同,编码器需按上下文选择合法表。 + +### Deduplication + +meta string 可按会话去重,减少重复写入。 + +## Value Format + +### Basic types + +#### bool + +1 字节:`0x00/0x01`。 + +#### int8 + +1 字节有符号整型。 + +#### int16 + +2 字节 little-endian。 + +#### unsigned int32 + +固定 4 字节无符号整型。 + +#### unsigned varint32 + +varint32(无符号)编码。 + +#### signed int32 + +固定 4 字节有符号整型。 + +#### signed varint32 + +ZigZag + varint32。 + +#### unsigned int64 + +固定 8 字节无符号整型。 + +#### unsigned varint64 + +varint64(无符号)编码。 + +#### unsigned hybrid int64 (TAGGED_UINT64) + +tagged 编码,兼顾小值空间效率与大值表示范围。 + +#### VarUint36Small + +用于字符串头等场景的紧凑长度编码。 + +#### signed int64 + +固定 8 字节有符号整型。 + +#### signed varint64 + +ZigZag + varint64。 + +#### signed hybrid int64 (TAGGED_INT64) + +tagged int64 编码。 + +#### float8 + +预留/实验类型,生产互操作需谨慎。 + +#### float16 + +16-bit 浮点。 + +#### bfloat16 + +bfloat16 表示。 + +#### float32 + +IEEE 754 float32 little-endian。 + +#### float64 + +IEEE 754 float64 little-endian。 + +### string + +字符串编码: + +``` +| header(varuint36_small) | bytes | +``` + +header 中包含 byte length 与 coder 信息。 + +#### String Header + +`(byte_length << 2) | coder`,coder 表示 UTF8/LATIN1/UTF16 等。 + +#### Encoding Algorithm + +按候选编码尝试,优先选择更紧凑且可无损表示的编码。 + +#### Encoding Selection by Language + +各语言实现可按本地字符串内部表示优化,但线上编码结果必须与规范一致。 + +#### Empty String + +空串长度为 0,仍应写合法 header。 + +### duration + +通常写 `seconds + nanos`。 + +### collection/list + +列表布局: + +1. 长度 +2. elements header +3. (可选)元素类型信息 +4. 元素数据 + +#### Elements Header + +header 位用于表达: + +- 是否跟踪元素引用 +- 是否含 null +- 是否同构 +- 是否使用声明类型 + +#### Type Info After Header + +在同构且非声明类型场景,可在 header 后一次性写 element type info。 + +#### Element Serialization Based on Header + +根据 header 走不同元素序列化路径(同构快路径 / 异构慢路径)。 + +#### elements data + +元素数据按顺序编码;null 与 ref 标记按配置插入。 + +### array + +#### primitive array + +基础类型数组可直接按内存块拷贝(注意 endian 与对齐)。 + +#### Multi-dimensional arrays + +多维数组按嵌套 array/list 递归表达。 + +#### object array + +对象数组元素逐个编码,支持引用与多态。 + +### map + +map 使用分块编码(chunk-based)。 + +#### Map Chunk Format + +``` +| map_size | chunk_1 | chunk_2 | ... | +``` + +#### KV Header Bits + +header 位描述 key/value 的: + +- 是否跟踪引用 +- 是否含 null +- 是否使用声明类型 + +#### Chunk Size + +每个 chunk 大小上限通常为 255 entries。 + +#### Why Chunk-Based Format? + +减少每个 entry 重复写 type info 的成本,提升吞吐。 + +#### Why serialize chunk by chunk? + +在 key/value 类型局部一致时可批量走快路径,并简化解码分支。 + +### enum + +enum 可按 ordinal 或名称编码(取决于配置与目标兼容要求)。 + +### timestamp + +通常写 `seconds + nanos` 或统一 epoch 精度表示。 + +### date + +通常写 epoch day。 + +### decimal + +decimal 由 scale + unscaled value 表示(实现可用大整数)。 + +### struct + +struct 编码:字段按稳定顺序写入。 + +#### Field order + +推荐使用规范定义的稳定分组排序,避免语言实现差异导致 hash 不一致。 + +##### Step 1: Field identifier + +字段标识优先使用 tag ID;否则使用标准化字段名(如 snake_case)。 + +##### Step 2: Group assignment + +按字段类别分组(primitive、builtin、collection/map、other)。 + +##### Step 3: Intra-group ordering + +组内使用稳定比较器(type + name/tag)排序。 + +##### Notes + +实现必须保证排序确定性(deterministic)。 + +#### Schema consistent (meta share disabled) + +不共享 meta 时,双方 schema 需一致;通常直接按固定顺序写字段值。 + +#### Compatible mode (meta share enabled) + +共享 TypeDef 后可按字段名/tag 做映射,未知字段跳过。 + +### Union + +#### IDL syntax + +```protobuf +union Animal { + Dog dog = 1; + Cat cat = 2; +} +``` + +#### Type IDs and type meta + +union 本体有独立 type id,case 使用 case id 区分分支。 + +#### Union value payload + +``` +| case_id | case_value | +``` + +#### Wire layouts + +根据 case 类型是否需要引用/类型元信息决定具体布局。 + +#### Decoding rules + +先读 case_id,再按 case 类型规则读 payload;未知 case 可按兼容策略跳过或报错。 + +#### When to use each type ID + +- 结构稳定、跨语言常驻类型:建议固定数值 type id +- 动态类型/未注册类型:使用命名类型路径 + +#### Compatibility notes + +新增 union case 应使用新 case id,不应复用旧 id。 + +### Type + +动态 `type` 值应携带足够 type meta,确保接收端可判别并解码。 + +## Common Pitfalls + +常见问题: + +1. 字段排序不稳定导致 schema/hash 不一致 +2. varint 与 fixed/tagged 配置不一致 +3. null 与空值语义混淆 +4. 引用跟踪开关两端不一致 +5. 命名类型 namespace/typename 不稳定 + +## Language Implementation Guidelines + +- 统一 little-endian +- 明确对象身份语义(用于 ref tracking) +- 对所有 offset/size 做边界与溢出检查 +- 维护跨版本回归用例(含演进、循环引用、复杂容器) +- 与 Java/Python/C++/Rust/Go 做双向互测 diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.15/specification/xlang_type_mapping.md b/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.15/specification/xlang_type_mapping.md new file mode 100644 index 0000000000..59b1171838 --- /dev/null +++ b/i18n/zh-CN/docusaurus-plugin-content-docs/version-0.15/specification/xlang_type_mapping.md @@ -0,0 +1,146 @@ +--- +title: Xlang 类型映射 +sidebar_position: 7 +id: xlang_type_mapping +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. +--- + +说明: + +- 类型定义请参见 [Type Systems in Spec](xlang_serialization_spec.md#type-systems) +- `int16_t[n]/vector` 表示 `int16_t[n]/vector` 这一类数组/向量形式 +- 跨语言序列化协议仍在演进,生产环境请先做严格兼容验证 + +## 用户类型 ID + +注册用户类型(struct/ext/enum/union)时: + +- type 字段先写入 8-bit internal kind +- 用户类型 ID (`user_type_id`) 单独以 `varuint32` 写入 +- 不做 bit shift 或 packing +- `user_type_id` 范围可用 `0 ~ 0xFFFFFFFE` + +示例: + +| User ID | Type | Internal ID | Encoded User ID | Decimal | +| ------- | ----------------- | ----------- | --------------- | ------- | +| 0 | STRUCT | 27 | 0 | 0 | +| 0 | ENUM | 25 | 0 | 0 | +| 1 | STRUCT | 27 | 1 | 1 | +| 1 | COMPATIBLE_STRUCT | 28 | 1 | 1 | +| 2 | NAMED_STRUCT | 29 | 2 | 2 | + +读取类型 ID 时: + +- 先读取 internal type ID +- 若属于用户注册类型,再继续读取 `user_type_id(varuint32)` + +## 类型映射 + +| Fory Type | Fory Type ID | Java | Python | Javascript | C++ | Golang | Rust | +| ----------------------- | ------------ | --------------- | ------------------------ | ------------------- | ------------------------------ | ---------------- | ----------------- | +| bool | 1 | bool/Boolean | bool | Boolean | bool | bool | bool | +| int8 | 2 | byte/Byte | int/pyfory.int8 | Type.int8() | int8_t | int8 | i8 | +| int16 | 3 | short/Short | int/pyfory.int16 | Type.int16() | int16_t | int16 | i16 | +| int32 | 4 | int/Integer | int/pyfory.fixed_int32 | Type.int32() | int32_t | int32 | i32 | +| varint32 | 5 | int/Integer | int/pyfory.int32 | Type.varint32() | int32_t | int32 | i32 | +| int64 | 6 | long/Long | int/pyfory.fixed_int64 | Type.int64() | int64_t | int64 | i64 | +| varint64 | 7 | long/Long | int/pyfory.int64 | Type.varint64() | int64_t | int64 | i64 | +| tagged_int64 | 8 | long/Long | int/pyfory.tagged_int64 | Type.tagged_int64() | int64_t | int64 | i64 | +| uint8 | 9 | short/Short | int/pyfory.uint8 | Type.uint8() | uint8_t | uint8 | u8 | +| uint16 | 10 | int/Integer | int/pyfory.uint16 | Type.uint16() | uint16_t | uint16 | u16 | +| uint32 | 11 | long/Long | int/pyfory.fixed_uint32 | Type.uint32() | uint32_t | uint32 | u32 | +| var_uint32 | 12 | long/Long | int/pyfory.uint32 | Type.varUInt32() | uint32_t | uint32 | u32 | +| uint64 | 13 | long/Long | int/pyfory.fixed_uint64 | Type.uint64() | uint64_t | uint64 | u64 | +| var_uint64 | 14 | long/Long | int/pyfory.uint64 | Type.varUInt64() | uint64_t | uint64 | u64 | +| tagged_uint64 | 15 | long/Long | int/pyfory.tagged_uint64 | Type.taggedUInt64() | uint64_t | uint64 | u64 | +| float8 | 16 | / | / | / | / | / | / | +| float16 | 17 | float/Float | float/pyfory.float16 | Type.float16() | fory::float16_t | fory.float16 | fory::f16 | +| bfloat16 | 18 | / | / | / | / | / | / | +| float32 | 19 | float/Float | float/pyfory.float32 | Type.float32() | float | float32 | f32 | +| float64 | 20 | double/Double | float/pyfory.float64 | Type.float64() | double | float64 | f64 | +| string | 21 | String | str | String | string | string | String/str | +| list | 22 | List/Collection | list/tuple | array | vector | slice | Vec | +| set | 23 | Set | set | / | set | fory.Set | Set | +| map | 24 | Map | dict | Map | unordered_map | map | HashMap | +| enum | 25 | Enum subclasses | enum subclasses | / | enum | / | enum | +| named_enum | 26 | Enum subclasses | enum subclasses | / | enum | / | enum | +| struct | 27 | pojo/record | data class | object | struct/class | struct | struct | +| compatible_struct | 28 | pojo/record | data class | object | struct/class | struct | struct | +| named_struct | 29 | pojo/record | data class | object | struct/class | struct | struct | +| named_compatible_struct | 30 | pojo/record | data class | object | struct/class | struct | struct | +| ext | 31 | pojo/record | data class | object | struct/class | struct | struct | +| named_ext | 32 | pojo/record | data class | object | struct/class | struct | struct | +| union | 33 | Union | typing.Union | / | `std::variant` | / | tagged union enum | +| none | 36 | null | None | null | `std::monostate` | nil | `()` | +| duration | 37 | Duration | timedelta | Number | duration | Duration | Duration | +| timestamp | 38 | Instant | datetime | Number | std::chrono::nanoseconds | Time | DateTime | +| date | 39 | Date | datetime | Number | fory::serialization::Date | Time | DateTime | +| decimal | 40 | BigDecimal | Decimal | bigint | / | / | / | +| binary | 41 | byte[] | bytes | / | `uint8_t[n]/vector` | `[n]uint8/[]T` | `Vec` | +| array | 42 | array | np.ndarray | / | / | array/slice | Vec | +| bool_array | 43 | bool[] | ndarray(np.bool\_) | / | `bool[n]` | `[n]bool/[]T` | `Vec` | +| int8_array | 44 | byte[] | ndarray(int8) | / | `int8_t[n]/vector` | `[n]int8/[]T` | `Vec` | +| int16_array | 45 | short[] | ndarray(int16) | / | `int16_t[n]/vector` | `[n]int16/[]T` | `Vec` | +| int32_array | 46 | int[] | ndarray(int32) | / | `int32_t[n]/vector` | `[n]int32/[]T` | `Vec` | +| int64_array | 47 | long[] | ndarray(int64) | / | `int64_t[n]/vector` | `[n]int64/[]T` | `Vec` | +| uint8_array | 48 | short[] | ndarray(uint8) | / | `uint8_t[n]/vector` | `[n]uint8/[]T` | `Vec` | +| uint16_array | 49 | int[] | ndarray(uint16) | / | `uint16_t[n]/vector` | `[n]uint16/[]T` | `Vec` | +| uint32_array | 50 | long[] | ndarray(uint32) | / | `uint32_t[n]/vector` | `[n]uint32/[]T` | `Vec` | +| uint64_array | 51 | long[] | ndarray(uint64) | / | `uint64_t[n]/vector` | `[n]uint64/[]T` | `Vec` | +| float8_array | 52 | / | / | / | / | / | / | +| float16_array | 53 | float[] | ndarray(float16) | / | `fory::float16_t[n]/vector` | `[n]float16/[]T` | `Vec` | +| bfloat16_array | 54 | / | / | / | / | / | / | +| float32_array | 55 | float[] | ndarray(float32) | / | `float[n]/vector` | `[n]float32/[]T` | `Vec` | +| float64_array | 56 | double[] | ndarray(float64) | / | `double[n]/vector` | `[n]float64/[]T` | `Vec` | + +## 类型信息(当前尚未完整实现) + +由于不同语言类型系统存在差异,部分类型无法一一对应映射。 + +如果某语言中的一个类型可能对应多个 Fory 类型(例如 Java `long` 可能对应 `int64/varint64/tagged_int64`), +说明该语言缺少更细粒度区分,此时调用方需要额外提供类型信息。 + +## 类型注解 + +当某类型作为其他类的字段时,可以通过字段级或类型级元数据提示具体编码语义。不同语言的配置方式: + +- Java:注解 +- C++:宏与模板 +- Go:struct tag +- Python:type hint +- Rust:宏 + +示例: + +- Java: + + ```java + class Foo { + @Int32Type(varint = true) + int f1; + List<@Int32Type(varint = true) Integer> f2; + } + ``` + +- Python: + + ```python + class Foo: + f1: pyfory.varint32 + f2: List[pyfory.varint32] + ```