pi_async_fs 0.1.2

Runtime-agnostic asynchronous filesystem contracts for local and remote storage
// 文件写入源的按需脱离能力。
//
// 轮询式后端通常只需要在 Future 生命周期内借用待写字节;完成式或只接受
// owned buffer 的后端则可能要求数据脱离调用方借用期并独立存活。本模块为
// 两类实现提供同一个公共语义接缝:能借用时直接使用连续只读视图,确实
// 需要独立所有权时才移动、共享或复制。
//
// 这里的“脱离”(detach)不是传统写时复制。文件写入不会修改输入;转换
// 的触发条件是后端所有权与生命周期要求。owned allocation 可以直接移动,
// 引用计数载体可以共享,只有无法独立存活的借用值才需要复制字节。
//
// 常用标准库与 `bytes` 类型具有真实、可调用的脱离和恢复行为;文件 I/O
// 只依赖本 trait 的合同,不依赖这些具体类型的私有表示。

use pi_result::RawResult;

use crate::BufferFailure;

// 可以为文件写入按需产生独立载体的连续字节缓冲区。
//
// # 使用位置
//
// 本 trait 共同约束 `FileIo` 严格尾部追加和全文件原地覆盖方法的写入源。
// 它不用于固定读取目标、增长型读取承载体、MMAP 映射句柄内的范围写入、
// 目录项、元信息、监控事件、后端缓存或向量化 I/O。本版不声明顺序写入或
// 任意偏移随机写入。
//
// 轮询式 adapter 可以只调用 [`AsRef::as_ref`],借用本次标量写入的完整、
// 连续、已初始化权威视图。完成式或 owned-only adapter 才会使用后续关联
// 类型与方法取得独立载体。
//
// 只读映射句柄可以作为不同文件的普通写入源:脱离时移动句柄或增加一次
// 共享引用计数,不需要先把映射字节复制到新的用户态 buffer。目标文件自身的
// 活动映射及从该映射派生的任何视图必须在脱离、复制或文件副作用前拒绝;
// 本 trait 只提供 buffer 能力,不能单独证明源和目标是不同的稳定文件身份。
//
// # `AsRef<[u8]>` 的项目合同
//
// `as_ref()` 返回的切片覆盖本次调用的全部待写字节,从逻辑索引零开始,不
// 含隐藏游标。取得视图不得推进状态、修改内容、分配新字节存储或执行 I/O;
// 同一次操作在脱离前重复取得视图时,长度和内容必须一致。空视图合法。
//
// 本 trait 不继承 `AsMut<[u8]>`、`Borrow<[u8]>`、`bytes::Buf` 或
// `bytes::BufMut`。当前合同只表示单段连续标量输入;离散片段必须由未来独立
// 向量化接口建模。
//
// # 线程、生命周期与对象安全
//
// trait 本身不要求 `Send`、`Sync` 或 `'static`。需要跨线程迁移的具体
// `FileIo` 方法会要求 `B: Send + 'a`;因此当 `B = &'a T` 时,Rust 会通过
// 引用的自动 trait 规则自然要求 `T: Sync`,而 owned buffer 不会被无条件
// 强加 `Sync`。
//
// `Sized` 表示后续脱离操作能够按值消费外层 `B`。`&[u8]` 等胖指针自身仍
// 是有固定大小的值,因此可以实现本 trait。第一版只服务静态泛型核心,
// 不承诺 `dyn DetachableWriteBuffer` 或其它对象安全表面。
//
// # 实现开放性与安全边界
//
// 本 trait 公开且不封闭,第三方可以为池化缓冲区、注册内存或业务载体提供
// 实现。它是安全 trait:违反视图、脱离成本或恢复身份合同属于实现缺陷,
// 但 adapter 不得仅凭本 trait 跳过自身裸指针、初始化、地址稳定与取消安全
// 验证。实现者不能利用它扩大后端可访问范围或伪造已初始化内存。
/// 可按需产生独立载体并在普通失败时恢复原值的连续写入缓冲区。
///
/// [`AsRef<[u8]>`] 必须暴露本次写入的全部、连续、已初始化字节,且在一次
/// 脱离之前重复借用时长度和内容保持一致。trait 本身不要求 `Send`、`Sync`
/// 或 `'static`;具体 I/O 方法按其线程和生命周期需要追加约束。
///
/// “脱离”只解决异步操作可能需要在调用期借用之外持有字节的问题,不表示
/// 后端会修改输入,也不要求总是复制。第三方实现必须维持字节等价、原值恢复
/// 和一次性配对合同。
pub trait DetachableWriteBuffer: AsRef<[u8]> + Sized {
    // 能够脱离本次调用借用期并独立存活的完整写入载体。
    //
    // # 字节等价关系
    //
    // 成功脱离得到的 `Detached` 必须通过 [`AsRef<[u8]>`] 暴露与脱离瞬间
    // `Self::as_ref()` 长度相同、逐字节相同的完整视图。实现不得执行编码
    // 转换、内容规范化、前后缀裁剪或追加,也不得通过载体隐藏一个已经推进
    // 的写入游标。空输入必须对应空视图。
    //
    // # “脱离”不等于独占所有权
    //
    // 本类型可以是移动后的 `Vec<u8>`/`Box<[u8]>`,也可以是共享存储的
    // `Arc<[u8]>`/`bytes::Bytes`,甚至可以是无需调用期借用的
    // `&'static [u8]`。因此名称使用 `Detached`,不使用容易被理解为唯一所有
    // 权的 `Owned`。它可以与 `Self` 相同,也可以是完全不同的具体类型。
    //
    // # 生命周期与线程
    //
    // `'static` 保证该载体不再依赖本次 `FileIo` 调用或原 `B` 的借用期,
    // 可以安全保留到 owned-only/completion 操作真正停止访问为止。这里不
    // 强制 `Send` 或 `Sync`;跨线程方法会在自身 `where` 子句中要求
    // `Self::Detached: Send`,单线程环境不被能力 trait 本身排除。
    //
    // # 非保证项
    //
    // 本关联类型不要求 `Clone`、`Copy`、`AsMut<[u8]>`、`bytes::Buf`、
    // `Debug`、`Unpin` 或递归实现 `DetachableWriteBuffer`。adapter 必须在
    // 取得任何裸指针前完成最终存放或固定,并独立证明后端访问期间的地址
    // 稳定、初始化范围和取消安全;不能仅凭本关联类型跳过这些验证。
    /// 能独立于原调用借用期存活的完整写入载体。
    ///
    /// 其字节视图必须与脱离瞬间 `Self::as_ref()` 长度相同且逐字节相同,不能
    /// 裁剪、追加、转换编码或隐藏已推进游标。`'static` 只表示不借用调用期,
    /// 不代表唯一所有权,也不隐含 `Send` 或 `Sync`。
    type Detached: AsRef<[u8]> + 'static;

    // 普通错误时把脱离载体还原为调用时原始 `Self` 所需的一次性令牌。
    //
    // # 为什么不能只保存 `Detached`
    //
    // owned `Vec<u8>` 可以把原 allocation 直接移动进 `Detached`,因此恢复
    // 令牌可以是 `()`。普通 `&'a [u8]` 则必须把字节复制到 `'static` 独立
    // 载体;为了在普通错误时返还调用时的同一个引用,令牌还必须保存原始
    // `&'a [u8]`。如果取消本关联类型并始终另存完整原 `Self`,owned 输入也
    // 只能复制后再提交,会破坏移动或共享的低成本路径。
    //
    // `Recovery` 不需要字节视图,也不能作为写入源提交给后端。它可以是零
    // 大小类型、调用期引用或项目/第三方缓冲区所需的其它恢复状态。成功时
    // adapter 丢弃令牌;普通错误时,必须在后端停止访问以后消费令牌与对应
    // `Detached`,无失败地恢复原始 `Self`。
    //
    // # 生命周期与线程约束
    //
    // 本关联类型故意不要求 `'static`,因为它可以合法保存原始调用期引用;
    // 也不在能力 trait 上要求 `Send` 或 `Sync`。跨线程 `FileIo` 方法会在
    // 自身 `where` 子句中要求 `Self::Recovery: Send + 'a`。它同样不要求
    // `Clone`、`Copy`、`Debug`、`Default`、比较或哈希。
    //
    // # 一次性配对不变量
    //
    // 每个令牌只与同一次脱离产生的载体配对。即使两个操作使用相同 Rust
    // 类型,adapter 也不得互换它们的 `Detached` 与 `Recovery`。恢复必须
    // 消费两者且不能保留第二份恢复能力。Rust 类型系统无法为每次普通方法
    // 调用生成不同的类型身份,因此维护配对关系是 adapter 的显式责任。
    /// 在普通失败时恢复调用前原始 `Self` 所需的一次性令牌。
    ///
    /// 每个令牌只与同一次 [`Self::try_detach`] 产生的载体配对,不得跨操作
    /// 交换。成功写入时令牌可以释放;失败时与对应载体一起消费以恢复原值。
    type Recovery;

    // 按需把本值转换为独立写入载体和与其配对的恢复令牌。
    //
    // # 成功
    //
    // 方法按值消费 `self`。成功返回的第一个元素可以交给 owned-only 或
    // completion 后端,第二个元素必须由 adapter 留在外层操作中,直到后端
    // 确认停止访问。`Detached::as_ref()` 必须与调用前 `Self::as_ref()` 的
    // 完整字节视图一致。
    //
    // 实现必须按具体类型选择成本最低且保持语义的路径:owned allocation
    // 优先直接移动;引用计数载体可以共享底层存储;只有借用值或其它无法
    // 独立存活的表示才允许复制字节。该同步方法不得执行文件、网络或后端
    // I/O,不得推进文件游标或产生文件副作用。复制路径可以产生 O(n) CPU
    // 与内存成本,其中 `n` 是权威视图长度。
    //
    // # 失败
    //
    // 无法取得所需内存、池租约或其它脱离资源时,必须通过
    // [`BufferFailure`] 返还调用时的同一个原始 `Self`。错误进度严格为
    // `TransferProgress::Exact { bytes: 0 }`;可预期资源不足不得用 panic
    // 表达。临时状态必须在返回前安全释放,原缓冲区的权威视图保持不变。
    //
    // 本方法没有“部分脱离成功”状态:要么返回完整且匹配的
    // `(Detached, Recovery)`,要么返回包含完整原值的失败载体。分配器直接
    // 终止进程等平台行为,以及第三方实现主动 panic,不属于可恢复错误保证。
    //
    // # 所有权与幂等性
    //
    // 该调用消费 `self`,不能对同一个值重复执行,也不承诺幂等。返回元组的
    // 顺序固定为“交给后端的载体、留在外层的恢复令牌”。本必需方法没有
    // 默认实现。
    #[must_use]
    /// 把本值转换为独立载体和与其配对的恢复令牌。
    ///
    /// 成功载体必须满足 [`Self::Detached`] 的字节等价合同。普通失败必须通过
    /// [`BufferFailure`] 返还调用时原始 `Self`,并报告精确零传输进度;不得
    /// 产生文件 I/O 或文件副作用。该方法消费 `self`,没有部分成功状态。
    fn try_detach(self) -> RawResult<(Self::Detached, Self::Recovery), BufferFailure<Self>>;

    // 从同一次脱离产生的载体与令牌,无失败地恢复调用前的原始值。
    //
    // # 参数顺序与一次性所有权
    //
    // 参数顺序和 [`Self::try_detach`] 的成功元组一致:先是曾交给后端的
    // `detached`,再是外层操作保留的 `recovery`。本函数同时消费两者,只能
    // 对同一配对调用一次;它不是可重复执行的幂等操作。
    //
    // # 恢复身份
    //
    // 返回值必须是调用 `try_detach` 前的同一个 `Self`,不能只返回字节相等
    // 的替代品。对 `Vec<u8>`,这包括同一 allocation、长度与容量;对
    // `Box<[u8]>` 和引用计数类型,包括原 allocation 或所有权值;对借用值,
    // 包括同一个引用;对 `Cow<'a, [u8]>`,还必须恢复原来的 Borrowed/Owned
    // 变体。池化与注册缓冲区必须恢复原租约、槽位和公开状态。
    //
    // 合法配对的恢复必须同步、无失败、无新字节分配且不主动 panic。它不
    // 执行文件/网络 I/O,不重试操作,也不修改文件副作用或传输进度。借用
    // 输入的临时脱离副本可以在恢复过程中被释放;owned 输入则通常把原载体
    // 直接移动回 `Self`。
    //
    // # adapter 前置条件
    //
    // 只有在后端普通成功和错误完成时都能归还完整 `Detached` 的情况下,
    // adapter 才能把它直接下传。后端可以使用自己的游标或切片包装器,但在
    // 返回前必须恢复完整权威视图。若后端可能在普通错误时遗失载体,adapter
    // 必须使用能够保留原值的内部中转方案,不能调用本函数伪造恢复保证。
    //
    // completion 操作尚未确认停止访问时绝对禁止恢复,否则可能同时形成
    // 后端裸指针访问和调用方所有权,导致数据竞争或悬垂引用。写入成功不调用
    // 本函数;Future 被丢弃后也只能在底层真正停止访问时安全释放相关状态,
    // 不能同步假装返还原值。
    /// 无失败地恢复调用 [`Self::try_detach`] 前的原始值。
    ///
    /// 参数必须来自同一次成功脱离,返回值必须恢复原始所有权、引用身份、
    /// 容量及具体类型承诺的其它可观察状态,而不只是字节相等。只有在异步
    /// 操作已经确定不再访问 `detached` 后才能调用;合法恢复不得执行文件 I/O、
    /// 重试写入或改变传输进度。
    fn recover_from_detached(detached: Self::Detached, recovery: Self::Recovery) -> Self;
}