zhixi-document-runtime/docs/c-abi-out-pointer-pattern.md
wangdl bb0422682e
Some checks failed
CI/CD Pipeline / Lint (push) Has been cancelled
CI/CD Pipeline / Test (push) Has been cancelled
CI/CD Pipeline / Build (push) Has been cancelled
CI/CD Pipeline / Release (macos-15, aarch64-apple-ios, aarch64-apple-ios) (push) Has been cancelled
CI/CD Pipeline / Release (macos-15, aarch64-apple-ios-sim, aarch64-apple-ios-sim) (push) Has been cancelled
CI/CD Pipeline / Package xcframework (push) Has been cancelled
fix: complete _separate function table (10→17) in out-pointer doc
- Add missing: build_document_info, search_epub, update_position,
  create_note_anchor, create_note_anchor_from_search
- Add memory management functions (rustbuffer_free/from_bytes)
- Organize by category: memory, parse, search, events, anchors

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-18 15:00:57 +08:00

271 lines
7.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# C-ABI Out-Pointer 模式文档iOS 兼容性)
## 1. 背景
ARM64 iOS ABI 对跨 FFI 边界的 struct 传参有严格限制。UniFFI 默认的 `RustBuffer` 结构体(包含 `{ capacity: i32, len: i32, data: *mut u8 }` 三个字段)在 ARM64 上不保证稳定传递。
**问题表现**:函数返回的 `RustBuffer` 在 iOS 真机上可能得到损坏的 capacity/len 值,导致数据丢失或 crash。
**解决方案**C-ABI out-pointer 模式 — 将 struct 成员拆分为独立的标量参数/返回。
---
## 2. 模式对比
### 2.1 Standard UniFFI有问题
```rust
// ❌ ARM64 不稳定RustBuffer struct 跨 FFI 传参
#[uniffi::export]
fn parse_markdown(content: String) -> Result<Vec<DocumentBlock>, DocumentError> {
// UniFFI 自动生成 RustBuffer 序列化
}
```
生成的 C-ABI
```c
// ❌ 返回 struct — ARM64 可能损坏
RustBuffer ffi_parse_markdown(RustBuffer content, RustCallStatus* status);
```
### 2.2 Out-Pointer 模式(安全)
```rust
// ✅ 所有参数/返回值均为标量int/pointer
#[no_mangle]
pub extern "C" fn ffi_parse_markdown_separate(
content_len: i32,
content_data: *const u8,
out_result_capacity: *mut u64,
out_result_len: *mut u64,
out_result_data: *mut *mut u8,
out_error_capacity: *mut u64,
out_error_len: *mut u64,
out_error_data: *mut *mut u8,
)
```
生成的 C-ABI — 所有参数为标量,兼容 ARM64。
---
## 3. 通用 Out-Pointer 模板
### 3.1 Result 序列化
```rust
/// Write Result<T, DocumentError> into out-pointers.
fn write_result_to_out_ptrs(
result: Result<Vec<u8>, DocumentError>,
out_result_cap: *mut u64, out_result_len: *mut u64, out_result_data: *mut *mut u8,
out_error_cap: *mut u64, out_error_len: *mut u64, out_error_data: *mut *mut u8,
) {
match result {
Ok(value) => {
let buf = uniffi::RustBuffer::from_vec(value);
unsafe {
*out_result_capacity = buf.capacity() as u64;
*out_result_len = buf.len() as u64;
*out_result_data = buf.data_pointer() as *mut u8;
*out_error_capacity = 0;
*out_error_len = 0;
*out_error_data = std::ptr::null_mut();
}
std::mem::forget(buf); // caller now owns the buffer
}
Err(e) => {
let err_json = serde_json::to_string(&e).unwrap_or_default().into_bytes();
let buf = uniffi::RustBuffer::from_vec(err_json);
unsafe {
*out_result_capacity = 0;
*out_result_len = 0;
*out_result_data = std::ptr::null_mut();
*out_error_capacity = buf.capacity() as u64;
*out_error_len = buf.len() as u64;
*out_error_data = buf.data_pointer() as *mut u8;
}
std::mem::forget(buf);
}
}
}
```
### 3.2 完整 FFI 函数
```rust
#[no_mangle]
pub extern "C" fn ffi_my_function_separate(
// Input: raw bytes (len + data ptr)
input_len: i32,
input_data: *const u8,
// Output: result (out-pointers for capacity/len/data)
out_result_cap: *mut u64,
out_result_len: *mut u64,
out_result_data: *mut *mut u8,
// Output: error (out-pointers)
out_error_cap: *mut u64,
out_error_len: *mut u64,
out_error_data: *mut *mut u8,
) {
// 1. Deserialize input
let input = unsafe { std::slice::from_raw_parts(input_data, input_len as usize) };
let args: MyArgs = match serde_json::from_slice(input) {
Ok(a) => a,
Err(_) => { /* error handling */ return; }
};
// 2. Execute logic
let result = my_core_function(args);
// 3. Serialize via out-pointers
write_result_to_out_ptrs(
result.map(|v| serde_json::to_vec(&v).unwrap_or_default()),
out_result_cap, out_result_len, out_result_data,
out_error_cap, out_error_len, out_error_data,
);
}
```
---
## 4. 内存管理
### 4.1 所有权
| 参数方向 | 所有权 | 释放责任 |
|----------|:--:|------|
| Input data | 借用caller 保留) | Caller |
| Output data | 转移给 caller | Caller 调用 `rustbuffer_free_separate` |
### 4.2 释放函数
```rust
#[no_mangle]
pub extern "C" fn ffi_rustbuffer_free_separate(
capacity: u64,
len: u64,
data: *mut u8,
) {
if data.is_null() { return; }
unsafe {
let _v = Vec::from_raw_parts(data, len as usize, capacity as usize);
// Drop here
}
}
```
**Swift 调用示例**
```swift
var resultCap: UInt64 = 0, resultLen: UInt64 = 0, resultData: UnsafeMutablePointer<UInt8>?
var errorCap: UInt64 = 0, errorLen: UInt64 = 0, errorData: UnsafeMutablePointer<UInt8>?
ffi_parse_markdown_separate(
Int32(jsonData.count), pointerToData,
&resultCap, &resultLen, &resultData,
&errorCap, &errorLen, &errorData
)
// 使用 result...
if let data = resultData {
let buf = Data(bytes: data, count: Int(resultLen))
// ... parse JSON
}
// 释放
ffi_rustbuffer_free_separate(resultCap, resultLen, resultData)
ffi_rustbuffer_free_separate(errorCap, errorLen, errorData)
```
---
## 5. 设计原则
### 5.1 标量仅传
-`i32`, `u64`, `*mut u8` — 单寄存器传参
-`RustBuffer { capacity, len, data }` — 多字段 struct
### 5.2 成对输出
每个输出 buffer 拆为 3 个 out-pointer
```
capacity: *mut u64 → buffer 容量
len: *mut u64 → 有效数据长度
data: *mut *mut u8 → 数据指针(双重指针:写入指针值)
```
### 5.3 序列化格式
输入/输出均使用 JSON`serde_json`)序列化,因为:
- C-ABI 仅传递字节数组
- JSON 在 Swift 侧有原生支持(`JSONDecoder`
- 避免复杂 struct 布局的 ABI 问题
---
## 6. 当前使用范围
项目中有以下函数使用 out-pointer 模式(`_separate` 后缀):
### 内存管理
| 函数 | 说明 |
|------|------|
| `rustbuffer_free_separate` | 释放 out-pointer 分配的 buffer |
| `rustbuffer_from_bytes_separate` | 从 raw bytes 构造 RustBuffer |
### 文档解析
| 函数 | 输入 | 输出 |
|------|:--:|:--:|
| `build_document_info_separate` | JSON args | `DocumentInfo` |
| `parse_markdown_separate` | JSON args | `Vec<DocumentBlock>` |
| `parse_text_separate` | JSON args | `Vec<DocumentBlock>` |
| `detect_material_type_separate` | String | `MaterialType` |
| `read_image_meta_separate` | String | `ImageMeta` |
| `read_text_stats_separate` | String | `TextStats` |
### 搜索
| 函数 | 输入 | 输出 |
|------|:--:|:--:|
| `search_markdown_blocks_separate` | JSON args | `Vec<SearchResult>` |
| `search_text_content_separate` | JSON args | `Vec<SearchResult>` |
| `search_pdf_pages_separate` | JSON args | `Vec<SearchResult>` |
| `search_epub_chapters_ffi_separate` | JSON args | `Vec<SearchResult>` |
### V1 事件deprecated
| 函数 | 输入 | 输出 |
|------|:--:|:--:|
| `push_reading_event_separate` | JSON event | () |
| `update_reading_position_separate` | JSON args | () |
| `export_pending_events_separate` | () | `Vec<ReadingEvent>` |
### 锚点
| 函数 | 输入 | 输出 |
|------|:--:|:--:|
| `create_note_anchor_separate` | JSON args | `NoteAnchor` |
| `create_note_anchor_from_search_separate` | JSON args | `NoteAnchor` |
**总计**17 个 `_separate` 函数(含 2 个内存管理 + 15 个业务函数)
---
## 7. 何时使用 Out-Pointer
| 场景 | 推荐方式 |
|------|----------|
| V2 Session / Event新接口 | Standard UniFFI已验证稳定 |
| 返回复杂类型Vec/struct且有 ARM64 兼容需求 | Out-pointer + JSON |
| 简单标量输入输出 | Standard UniFFI |
| 需要高性能避免 JSON 开销 | Standard UniFFI + 测试验证 |
---
## 8. 相关文档
- [UniFFI UDL 编写规范](./uniffi-udl-spec.md)
- [iOS FFI 调用指南](./ios-ffi-integration-guide.md)