/* app/shelf_index.h — 书架索引落盘(M32):把一次全量扫描的结果 + 每个目录的 mtime * 存到 `MeowData/os.meow.reader/index/shelf.idx`,冷启动时用「逐个 stat 目录 mtime」校验, * 命中就完全跳过 readdir(真机全量扫描 3.0s 的大头是 434 次 opendir+readdir)。 * * 安全优先(fail-open):版本不符 / 文件缺失 / 截断 / CRC 错 / 任一目录 mtime 变化 / * 任何 IO 错误 → shelf_index_load() 返回 0,调用方照常全量重扫。**绝不**用陈旧索引 * 冒充真实书库,也绝不因为索引写失败而让书架打不开。 * * 索引里**不存阅读进度 ts**:ts 每次由调用方的 ts_fn(生产=rdprog_load)现读, * 否则「刚读完回书架」会显示旧的置顶顺序。索引只存与进度无关的扫描事实。 * * 目录 mtime 失效语义(存「扫描过的每个目录」的 mtime,非只存 level-1): * - 任意目录里新增/删除/改名文件 → 该目录 mtime 变 → 校验失败 → 重扫(含 level-3 深处); * - 一级目录改名 → 父目录 mtime 变 + 旧目录 stat 失败 → 重扫; * - 超深(> SHELF_SCAN_MAX_DEPTH)未扫描的子树不在索引里,其内部变化不会自动失效 * (那些书本来就不显示),需点「刷新」更新 depth_skipped 计数。 * mtime 精度受文件系统限制(exFAT 约 10ms / FAT32 2s):与上次扫描落在同一时间刻度的 * 变更可能漏判,点一次「刷新」必可纠正(刷新=清索引+全量重扫)。 */ #ifndef SHELF_INDEX_H #define SHELF_INDEX_H #include "shelf_model.h" /* 落盘格式版本:**扫描语义变了必须 +1**(新增格式扩展名/改深度/改分组成因等), * 版本不符时旧索引直接作废重扫。 */ #define SHELF_INDEX_VERSION 1 /* 载入并校验索引。成功返回 1(books/rep 填好、已按 ts 排序,等价于一次全量扫描); * 任何异常返回 0(fail-open,books 内容不可用,调用方必须全量重扫)。 * 成功路径**不做 opendir/readdir**:只 open/read 索引 + 逐目录 stat + 每本书 ts_fn。 */ int shelf_index_load(const char *root, shelf_book *books, int cap, shelf_ts_fn ts_fn, shelf_scan_report *rep); /* 增量写索引(扫描时逐目录上报 → 扫描结束提交)。 * open 失败返回 NULL(卡满/写保护/无 MeowData):调用方传 NULL 即可,扫描照常。 * dir 回调签名与 shelf_dir_fn 一致,可直接传给 shelf_scan_books_ex。 * commit 成功返回 1 并原子发布(临时文件 + rename);失败返回 0 且清理临时文件。 * commit 总是接管 writer(成功/失败都会释放),之后不得再用。 */ typedef struct shelf_idx_writer shelf_idx_writer; shelf_idx_writer *shelf_index_writer_open(const char *root); void shelf_index_writer_dir(void *ctx, const char *rel, int64_t mtime_sec, long mtime_nsec, int books_here); int shelf_index_writer_commit(shelf_idx_writer *w, const shelf_book *books, int nbooks, const shelf_scan_report *rep); void shelf_index_writer_abort(shelf_idx_writer *w); /* 删除索引(顶栏「刷新」= 清索引 + 全量重扫;root 只用于定位,不存在也静默)。 */ void shelf_index_remove(const char *root); /* 索引文件路径(测试/诊断用)。0=成功;负值=无数据目录/路径超长。 */ int shelf_index_path(const char *root, char *out, int outsz); #endif /* SHELF_INDEX_H */