时间在说话
技术复盘、生活碎片,和一些想明白了的道理。写给自己,也留给时间。

认领旧进度——模糊匹配为什么只提议不决定
BookKey 是全书哈希:内容差一个字节就是陌生人。换来源重新导入的同一本书,进度会凭空消失。兜底是 书名0.6+作者0.2+字数0.2 的模糊匹配——但它只递候选不定罪:误合并不可逆,漏合并随时可补,覆盖进度的按钮必须由用户来按。

删除也是同步——墓碑、复活与 outbox 队列
删本地文件是三行代码,难的是让其他设备知道"这本书没了"。删除是事件不是状态:不删行、立墓碑,与写入共享同一条时间轴;客户端先入 outbox 队列再发送、按内容 ack;收到墓碑只停主张、不删文件——谁还在读,谁有权复活它。

时长凑不出来——阅读统计的跨端合并难题
手机 10 小时、电脑 5 小时,合计的 15 谁来算?时长是计数器语义不是寄存器语义:按设备拆表、上报全量快照、合并取 max 换幂等。而 v1.0.3 顺手把服务端合计写回本机计数器的一行 if,让时长每同步一轮就膨胀一倍。

从"后写覆盖"到三层裁决——两台设备同时改了进度,听谁的
LWW 在阅读场景的两个误判:回看也是新写入、离线设备永远翻身。v1.0.5 把"读到哪"拆成水位/位置/时间三层,信任分级:前进无条件、小幅回退看时间、大幅回退要显式跳转。

差之毫厘谬以千里——进度锚点为什么用绝对下标
章内偏移是相对解析结果的坐标,解析器一进化就错位。同步协议的权威位置改为全书绝对字符下标——BookKey 一致时逐字符精确,且与章节切分、排版彻底解耦。

只算一次——身份计算的成本预算
BookKey 在导入时对落盘文件算一次,之后打开书、同步、翻页全部读缓存。性能不靠算得快,靠少算——以及身份稳定优先于内容新鲜的取舍。

一个换行符——CRLF 跨块血案与修复
两个字节被缓冲区边界劈开,42 MB 的小说在两台设备上领了不同的身份证。复盘 v1.0.2 那次修复:跨块挂起的 NewlineNormalizer、精确构造的边界测试,以及老数据为何救不回来。

不用抽样——指纹为什么必须算全量
先替这个方案说句公道话,把它写到最能打的形态。设计意图:一本 100MB 的书,全量算要读 100MB;那只读几个关键窗口、比如 1MB,理论上快 100 倍:

接缝——块边界上的六个陷阱
O(1) 内存的代价,是把一个连续的整体切成块处理。而切块的地方就是接缝:上一块的最后一个字节和下一块的第一个字节,本来在文件里是挨着的,处理逻辑却分别在两次循环里——任何"需要看邻居才能下结论"的规则,都会在接缝处露馅。

100MB 的指纹——从 60 秒到 424 毫秒
txt 小说动辄几十上百 MB。假设我们在手机端用最直觉的方式算指纹:

同一本书,两台设备认不出彼此——BookKey 与归一化
从这一篇开始进入系列的核心:BookKey。它是旦阅整个同步体系的根——三端靠它认出"我们说的是同一本书"。这一篇讲它最基础的一层:怎么给一本内容相同、字节不同的 txt,算出同一个身份。

技术选型的取舍——三端技术栈为什么这样选
上一篇把旦阅的整体架构摊开讲了一遍:三端一套算法,靠一份跨端契约(BookKey + 进度参数)把手机、电脑、服务端绑成一个整体。这一篇往回退一步,讲讲这三端的技术栈是怎么选出来的——以及每一个"选了它"的背后,都对应着一个"放弃了什么"。

三端一套算法——旦阅的整体架构
先从一个具体场景说起。

偷懒——把发博客这活儿交给 AI
重建博客到现在十来天,拢共就写了一篇《断舍离》。不是没话说,是每次想写的时候,一想到还要开后台、建文章、传图、排版,那点兴致就先耗掉一半。

缘起——为什么要自己写一个阅读器
手机和电脑之间,书签对不上这件事,我忍了很久。