jcestruct2 1.1.0
基于 Pydantic v2 的现代化 JCE (Tars) 协议 Python 实现
License: MIT
Requires Python: >=3.10
Keywords: binary, jce, protocol, pydantic, serialization, struct, tars

# JceStruct

[![PyPI version](https://img.shields.io/pypi/v/jcestruct2)](https://pypi.org/project/jcestruct2/)
[![Python](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
[![License](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
[![Pydantic v2](https://img.shields.io/badge/pydantic-v2-blue.svg)](https://docs.pydantic.dev/)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)

**JceStruct** 是一个现代化的 Python JCE (Jce Encoding) 协议实现，基于 Pydantic v2 构建。JCE 是腾讯 Tars 框架使用的高效二进制序列化协议。

## 特性

- **类型安全**: 基于 Pydantic v2，完整的类型提示和运行时验证。
- **高性能**: 智能整数压缩、零值优化、字节数组优化、零拷贝读取。
- **灵活性**: 支持 Schema (JceStruct) 和无 Schema (dict) 两种模式。
- **流式处理**: 提供 `Packer`/`Unpacker` 支持增量打包和长度前缀协议。
- **文件支持**: 提供 `dump`/`load` 直接读写文件类对象（IO[bytes]）。
- **上下文**: 支持序列化/反序列化上下文传递 (`context`) 和字段钩子（Serializer/Deserializer）。
- **泛型支持**: 完整支持 Python Generic 类型系统。
- **CLI 工具**: 基于 Click 的强大命令行工具，支持文件读写、格式化输出和调试。
- **安全防护**: 递归深度限制、容器大小限制，防止 DoS 攻击。
- **纯 Python**: 无需编译 C 扩展，易于安装和跨平台使用。

## 安装

### 使用 pip

```bash
pip install jcestruct2
# 安装 CLI 支持
pip install "jcestruct2[cli]"
```

### 使用 uv (推荐)

```bash
uv add jcestruct2
# 安装 CLI 支持
uv add "jcestruct2[cli]"
```

## 快速开始

### 基础示例

```python
from jce import JceField, JceStruct, dumps, loads, types

# 定义数据模型
class User(JceStruct):
    uid: int = JceField(jce_id=0, jce_type=types.INT)
    name: str = JceField(jce_id=1, jce_type=types.STRING)

# 序列化
user = User(uid=1001, name="Alice")
encoded = dumps(user)
print(f"Encoded hex: {encoded.hex()}")

# 反序列化
restored = loads(encoded, User)
assert restored.name == "Alice"
```

## 功能演示

### 1. 文件读写 (dump/load)

`dump` 和 `load` 函数允许你直接与文件或类文件对象（如 `BytesIO`）进行交互。

```python
from jce import dump, load

user = User(uid=1001, name="Alice")

# 写入文件
with open("data.jce", "wb") as f:
    dump(user, f)

# 从文件读取
with open("data.jce", "rb") as f:
    restored = load(f, User)
```

### 2. 流式处理 (Packer/Unpacker)

针对网络流式数据，JceStruct 提供了 `LengthPrefixedPacker` 和 `LengthPrefixedUnpacker`，支持常见的“长度+数据”协议格式。

```python
from jce import LengthPrefixedPacker, LengthPrefixedUnpacker

# 1. 编码 (Pack)
# length_type=4 表示使用 4 字节长度头 (大端序)
packer = LengthPrefixedPacker(length_type=4) 
packer.pack(User(uid=1, name="A"))
packer.pack(User(uid=2, name="B"))
data = packer.get_buffer()

# 2. 解码 (Unpack)
unpacker = LengthPrefixedUnpacker(target=User, length_type=4)
unpacker.feed(data) # 可以多次 feed 增量数据

for user in unpacker:
    print(f"Got user: {user.name}")
```

### 3. 字段序列化钩子与上下文

你可以通过 `@field_serializer` 和 `@field_deserializer` 自定义特定字段的转换逻辑，并通过 `context` 参数传递外部状态。

```python
from jce import JceStruct, JceField, field_serializer, field_deserializer, SerializationInfo, dumps

class SecretConfig(JceStruct):
    password: str = JceField(jce_id=0)

    @field_serializer("password")
    def encrypt_password(self, value, info: SerializationInfo):
        # 从上下文获取密钥进行加密
        key = info.context.get("key", "default-key")
        return f"encrypted({value}, {key})"

# 使用上下文序列化
cfg = SecretConfig(password="123456")
# 注意: JceStruct.encode() 不支持 context 参数, 必须使用全局 dumps()
encoded = dumps(cfg, context={"key": "my-secret-key"})
```

### 4. 无模式 (Dict) 编解码

无需定义模型即可解析 JCE 数据，适用于快速探索未知协议。

```python
from jce import dumps, loads

# 编码: 使用字典, 键为 JCE Tag ID
raw_data = {
    0: 123,
    1: "JceStruct",
    2: [1, 2, 3]
}
encoded = dumps(raw_data)

# 解码: bytes_mode="auto" 会尝试自动转换嵌套的字节流为字符串或字典
decoded = loads(encoded, target=dict, bytes_mode="auto")
print(decoded[0]) # 123
```

## API 参考

### 主要函数

| 函数 | 说明 | 主要参数 |
| :--- | :--- | :--- |
| `dumps(obj, ...)` | 将对象序列化为 JCE 字节 | `option`, `context`, `default` |
| `loads(data, target, ...)` | 将 JCE 字节反序列化为对象 | `target` (类型或 dict), `bytes_mode`, `context` |
| `dump(obj, fp, ...)` | 序列化并写入文件类对象 | `fp` (IO[bytes]), `option`, `context` |
| `load(fp, target, ...)` | 从文件类对象读取并反序列化 | `fp`, `target`, `bytes_mode`, `context` |

### `loads` 的 `bytes_mode`

| 模式 | 说明 |
| :--- | :--- |
| `"raw"` | 保持原始 `bytes` 类型，不进行任何自动转换。 |
| `"string"` | 尝试将所有 `bytes` 转换为 UTF-8 字符串。 |
| `"auto"` (默认) | 智能模式：优先尝试递归解析为嵌套 JCE 结构，失败则尝试转换为 UTF-8 字符串，最后保持原始字节。 |

### 编码选项 (Options)

| 选项 | 值 | 说明 |
| :--- | :--- | :--- |
| `OPT_NETWORK_BYTE_ORDER` | `0x0000` | 默认：大端字节序。 |
| `OPT_LITTLE_ENDIAN` | `0x0001` | 非标准：小端字节序。 |
| `OPT_STRICT_MAP` | `0x0002` | 严格模式：验证 MAP 的 Key 标签为 0，Value 标签为 1。 |
| `OPT_SERIALIZE_NONE` | `0x0004` | 强制序列化值为 `None` 的字段。 |
| `OPT_ZERO_COPY` | `0x0010` | 零拷贝模式：在反序列化时尽可能返回 `memoryview`。 |
| `OPT_OMIT_DEFAULT` | `0x0020` | 省略与默认值相等的字段以减小体积。 |

## 命令行工具 (CLI)

安装 `jcestruct2[cli]` 后，你可以使用 `jce` 命令直接在终端调试数据。

```bash
# 解码 Hex 字符串
jce "160472636e62"

# 从文件读取并以 JSON 格式输出
jce -f data.bin --format json

# 查看详细的解码过程 (Verbose 模式)
jce "160472636e62" -v
```

## 安全特性

- **递归限制**: 默认限制深度，防止恶意构造的嵌套数据导致栈溢出。
- **大小限制**: 对列表和映射的元素数量进行限制，防止内存耗尽攻击。
- **字段验证**: 基于 Pydantic 的严格类型校验。

## 开发与贡献

1. 克隆仓库：`git clone https://github.com/L-1124/JceStruct.git`
2. 安装环境：`uv sync`
3. 运行测试：`uv run pytest`
4. 代码检查：`uv run ruff check .`

## 协议文档

详细的 JCE 协议规范请参阅 [JCE_PROTOCOL.md](JCE_PROTOCOL.md)。

## 许可

本项目采用 **MIT 许可证**。详情请参阅 [LICENSE](LICENSE) 文件。