qubit-codec 0.14.0

Core codec traits and buffer conversion primitives for Rust
Documentation
# Transcode 错误与生命周期设计

本文描述当前 `rs-codec` 的实现,而不是历史迁移方案。代码允许破坏性变更,因此
这里以最小、可验证的公共模型为准。

## 设计边界

transcode 层只负责缓冲区游标、容量、进度和流生命周期。codec 或 hook 负责格式域
语义。错误因此分成三层:

1. `TranscodeFailure`:框架能独立判断的结构性失败。
2. `TranscodeDomainError<E>`:带 `reset`、`main`、`finish` 阶段的 codec/hook 域错误。
3. 方向错误:解码、编码、转换分别承载各自可能出现的错误方向。

## 公共错误类型

`TranscodeFailure` 是无泛型、`#[non_exhaustive]` 的框架错误,当前包含:

- 输入/输出下标、输出范围和输出容量错误;
- 输出长度算术溢出;
- 不完整输入和尾随输入;
- 严格单值 decode 不支持 codec 生命周期输出;
- `TranscodeBeforeReset`、`FinishBeforeReset`;
- `TranscodeAfterFinish`、`FinishAfterFinish`;
- reset 或 finish 部分执行失败后的 `LifecyclePoisoned`。

三类方向错误的实际形状如下:

```rust
enum TranscodeDecodeError<E> {
    Failure(TranscodeFailure),
    Domain(TranscodeDomainError<E>),
}

enum TranscodeEncodeError<E, V> {
    Failure(TranscodeFailure),
    Unencodable { input_index: usize, value: Option<V> },
    Domain(TranscodeDomainError<E>),
}

enum TranscodeConvertError<DE, EE, V> {
    Failure(TranscodeFailure),
    DecodeDomain(TranscodeDomainError<DE>),
    EncodeDomain(TranscodeDomainError<EE>),
    Unencodable { input_index: usize, value: Option<V> },
}
```

`TranscodeFailure` 是结构性错误的唯一实现来源。方向错误为了保持调用方迁移成本低,
仍保留 `invalid_*`、`ensure_*`、`map_*` 等转发便利方法;这些方法只把结果转换成
`Failure`,不复制或扩展框架语义。

## 强制生命周期

所有内置 transcode engine 都强制遵循,I/O adapter 则按该顺序调用底层
`Transcoder`:

```text
reset → transcode* → finish
          ↑       ↓
       可返回 NeedInput / NeedOutput
```

新建实例处于 `Uninitialized`,只有一次成功的 `reset` 后才能调用 `transcode` 或
`finish`。`finish` 成功后实例处于 `Finished`,必须再次 `reset` 才能开始下一条流。
reset 或 finish 在 codec/hook 已可能修改状态后失败,会进入 `Poisoned`;只有成功的
后续 reset 能恢复。容量和下标预检失败不改变可重试状态。

`LifecycleGuard` 在所有构建 profile 都执行检查,避免 debug/release 行为分叉。对应
的错误是 `TranscodeBeforeReset`、`FinishBeforeReset`、`TranscodeAfterFinish`、
`FinishAfterFinish` 和 `LifecyclePoisoned`。

`Transcoder` trait 允许自定义实现直接定义三个方法,因此 trait 本身不能替实现者
拦截任意状态突变;自定义实现必须遵守同一契约,或由调用方包在能执行该检查的
engine 中。

## 编码 hook 上下文

`EncodeContext<'a, Value>` 是只读值对象,只暴露:

```rust
input_value() -> &Value
input_index() -> usize
output_index() -> usize
available_output() -> usize
```

hook 不再取得可写输出切片,也不能伪造写入计数。引擎内部的私有
`EncodeAttempt` 独占 `&mut [Unit]`,负责把 hook 返回的策略(拒绝、跳过或替换)落实
到 codec 调用和进度结算中。这样输出写入、边界检查和 `written` 计数只有一个可信来源。

## 引擎执行顺序

- `TranscodeDecodeEngine`:reset hook → `Codec::decode_reset` → 重复 decode →
  `Codec::decode_finish`/finish hook。
- `TranscodeEncodeEngine`:reset hook → `Codec::encode_reset` → 重复 encode →
  `Codec::encode_finish`/finish hook。
- `TranscodeConvertEngine`:先 reset 目标 encoder,再处理源 decoder 的 reset 值;
  streaming 阶段在内部 pending slot 中保留尚未写出的值,finish 时先排空 pending,
  再处理 decoder finish 值,最后完成 encoder finish。

所有阶段都使用绝对下标,返回的 `TranscodeProgress` 计数是相对本次调用的读取和
写入量。容量 bound 必须覆盖 reset、transcode、finish 的所有可达瞬态。

## EOF 语义

普通 `transcode` 只处理当前可见输入;`NeedInput` 的尾部仍由调用方持有。调用方声明
EOF 后,可使用 codec-backed decoder 的 `transcode_eof`,由 codec 的
`decode_eof` 决定是否能完成尾部。`finish` 不接收或重新解释调用方的输入尾部,只
处理引擎已经保留的状态和 codec/hook 的 finish 输出。

## 取舍与暂缓项

- 不恢复旧的 phantom `FailureValue`/统一错误枚举;方向错误直接表达 decode、encode
  和 convert 语义。
- 保留方向错误的 forwarding helper,因为它们是零语义的 API 便利层,并不造成错误
  模型重复。
- `D::Value: Default` 对 converter finish 冷路径的限制暂不改变;普通 transcode 热
  路不依赖该 bound,后续可另行设计无需 Default 的 finish scratch。
- 不把 `EncodeContext` 的内部输出权限暴露给公共 hook API;若未来确需更强扩展点,
  应新增显式的 engine-owned writer,而不是恢复裸 `&mut [Unit]`。

## 验证策略

生命周期测试覆盖首次 reset 前的拒绝、reset 后 streaming、finish 后拒绝和 reset 复用。
编码上下文测试验证 hook 只能观察元数据。`benches/transcode.rs` 中的
`safe_vs_unchecked` 基准对比安全索引与 `get_unchecked`,并配合 release 汇编输出检查
优化器是否已经消除边界检查;基准结果不作为 API 正确性的依据。

上述约束与 `src/transcode`、`tests/transcode`、README 及下游 codec/text adapter 的
当前实现保持一致。