惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

小众软件
小众软件
IT之家
IT之家
博客园 - 聂微东
www.infosecurity-magazine.com
www.infosecurity-magazine.com
P
Privacy International News Feed
人人都是产品经理
人人都是产品经理
PCI Perspectives
PCI Perspectives
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
博客园 - 叶小钗
V
Vulnerabilities – Threatpost
美团技术团队
S
Secure Thoughts
N
News | PayPal Newsroom
L
LINUX DO - 最新话题
腾讯CDC
Application and Cybersecurity Blog
Application and Cybersecurity Blog
雷峰网
雷峰网
B
Blog
MyScale Blog
MyScale Blog
T
The Blog of Author Tim Ferriss
TaoSecurity Blog
TaoSecurity Blog
N
News and Events Feed by Topic
Blog — PlanetScale
Blog — PlanetScale
C
Check Point Blog
T
Tailwind CSS Blog
月光博客
月光博客
Simon Willison's Weblog
Simon Willison's Weblog
Hacker News: Ask HN
Hacker News: Ask HN
The Last Watchdog
The Last Watchdog
Google DeepMind News
Google DeepMind News
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
MongoDB | Blog
MongoDB | Blog
S
Security @ Cisco Blogs
Jina AI
Jina AI
Engineering at Meta
Engineering at Meta
S
Security Affairs
Forbes - Security
Forbes - Security
P
Palo Alto Networks Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
博客园 - 司徒正美
博客园 - 三生石上(FineUI控件)
T
Tor Project blog
O
OpenAI News
L
Lohrmann on Cybersecurity
Security Archives - TechRepublic
Security Archives - TechRepublic
P
Proofpoint News Feed
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
L
LangChain Blog
B
Blog RSS Feed
H
Hackread – Cybersecurity News, Data Breaches, AI and More

See you soon

试试将文章版本化管理吧 | See you soon 使用 Quadlet 将 Podman 中的 Postgres 当作 systemd 服务运行 | See you soon 大他者,那个无时无刻都在盯着你的东西 | See you soon Laws of Software Engineering,软件工程定律 | See you soon 浅记多因素身份认证 | See you soon Linux 内核中的度量单位 | See you soon 重置 GPG 智能密钥 | See you soon 向 NAS 引入 samba | See you soon 无法重复键入的 Fcitx5 | See you soon ZFS 降级事故 | See you soon 记被 XanMod Kernel 和 AppArmor 联合坑的一次踩坑 | See you soon agent 的 skill 与 toolcall | See you soon 记一次服务器被挂恶意挖矿二进制 | See you soon 活着的 Arc | See you soon 令 acme.sh 使用 Cloudflare 的 DNS API 签发与续签证书 | See you soon 于 Tokio 中卸载 CPU Bound 任务 | See you soon 如我所见,梦破碎的时候 | See you soon 74LS 家族手册 | See you soon JDK Projects 备忘录 | See you soon 关于历史 | See you soon 用 curl 下载 OnePlus 的 ROM | See you soon 实用命令切片 | See you soon 再见,Oh My Zsh。 | See you soon 你不应该复用 strings.Builder | See you soon 博客的明日 | See you soon 被 AppArmor 击杀的 Dockge | See you soon AI 时代的自我 | See you soon 支持删除的布隆过滤器 | See you soon 基于栈的虚拟机与基于寄存器的虚拟机 | See you soon RVA23 包含了什么 | See you soon Rust 的边界检查是否已经有很大的进步 | See you soon 使用 Rust 编写操作系统:Barebones | See you soon 使用 Rust 编写操作系统:引言 | See you soon 编译器笔记:CST | See you soon Paradoxical 札记 | See you soon 简单的相似去重算法(基于向量) | See you soon 更加现代的 PaperMC Minecraft 插件设计指南 | See you soon 简单的 CFG 语法分析方法 | See you soon 简单地使用 Caddy 实现 CORS 配置 | See you soon 让 OpenCV 可以被静态链接 | See you soon 图片搜索笔笺 | See you soon 电路板设计笔记-保护 | See you soon 使用 Rust 实现 SnowflakeId | See you soon 农夫乐事 FaQ 启示 | See you soon 使用 Rust 实现拓展系统札记 | See you soon 遗传学定律的代码实现 | See you soon EN:The Journey of Rust and Procps | See you soon ZH:Rust 与 Procps 之旅 | See you soon 使用 debootstrap 与 schroot 构建一个纯净环境 | See you soon GPG添加新的用户信息 | See you soon GSoC2024 笔记:使用 Rust 重新实现 procps | See you soon JWT 小册 | See you soon 使用密钥登录 SSH | See you soon Guava:Cache | See you soon 编译器笔记:增量编译 | See you soon Gradle秘境:添加一个类似modCompileOnly的依赖块 | See you soon Minecraft:原始经济系统设计简述 | See you soon TinyRemapper笔记 | See you soon MicroOS:进阶 | See you soon MicroOS:起步 | See you soon Swapfile 指北 | See you soon GPG 物理密钥的安装与密钥的迁移 | See you soon 用GPG签名告诉大家这就是我的提交 | See you soon DFS:深度优先搜索 | See you soon 文章翻译:从init.vim到init.lua | See you soon 目录遍历攻击 | See you soon 如何挂载.img文件,以及如何使用QEMU模拟arm64环境 | See you soon 在树莓派上编译 OpenCV | See you soon 关于卸载BlueStacks后遇到的问题 | See you soon
编译器笔记:rope | See you soon
Krysztal Huang · 2025-05-22 · via See you soon

在实现 paradoxical 的时候注意到很多语言服务器或者编辑器都会使用一个叫作 rope 的东西来保存对文件的操作结果,因此简单记录下这个神奇的东西。

rope 这个概念最开始是在 1995 年由 Hans-J. BoehmRuss AtkinsonMichael Plass 提出的1。如果你想简单了解的话,你可以直接去看 xi-editor 的相关文档2,写得非常不错

十分感谢他们的贡献 :)

简单介绍

Rope (绳索字符串)就是把一整段文本切成许多小块,用一棵树把这些块串起来;这样随机插入、删除或定位位置时,只需改动或遍历对数级别的节点,而不是线性扫描整段文本

请注意,而不是线性扫描整段文本这段话非常重要,这也是和使用 String 储存文件内容的最大区别

操作?

对于一个编辑器或者文本解析,我们常常使用行列的概念来操作他。

答案是,对于 rope 我们也可以使用行列来操作他!

为什么 rope 可以实现高效编辑

请注意,rope 的主要目的是实现高效的编辑,对于读取他的速度和线性的 String 相比是略微慢了一点点的。

但就这个数量级是常数,非常不明显

从储存结构上优化

rope 最重要的一点是,他不是线性储存文本数据,转而使用了一棵树来维护储存文本数据。

让我们来看看这棵树长什么样子

┌─── weight = 23 ───┐

│ │

[左子树] [右子树]

weight = 10 weight = 7

│ │ │ │

"片段A" "片段B..." "片段C" "片段D..."

简单解释就是:

  • 左子树(LeftSubtree):按照逻辑顺序(即文本顺序)排在当前节点之前的全部内容
  • 右子树(RightSubtree):排在当前节点(或其左子树)之后、紧接着的那部分内容

NOTE

请注意,节点内储存的权重不仅仅只有字符数,也包含行数等信息

在接下来我们定义权重分为以下几种

  • lines: 行权重
  • chars: 字符权重

偏移

当我们需要快速定位光标的时候,权重就显得非常重要了:它保存了左子树累计长度,若要找第 个字符,算法会看 是否小于该权重,按照如下逻辑来搜索:

  • 是 : 目标一定在左子树。
  • 否 : 目标必定在右子树,并将 减去左权重后继续向下搜索。

除此之外我们还有行列偏移的需求:我们在编辑时经常需要行列->偏移,或者偏移->行列,那么 rope 是怎么做的?

行列->偏移
  1. 输入行号 ;从根节点开始。
  2. 比较 与当前节点的 左子树行数:
    1. 若 小于该权重,进入左子树;
    2. 否则 ,转入右子树。

重复直到到达叶节点(叶片约 4–8 KB 大小)。在叶内部用已缓存的换行表(二分搜索)把剩余行数定位到字节/字符位置。

通过这样的设计,这棵树就把线性扫描的复杂度降低到了

偏移->行列

从偏移转换到行列也是个很常见的需求,不过这种转换相比行列转换为偏移更加简单

  1. 将目标字符索引 与字符权重做同样的左/右子树递归
  2. 到叶后用换行表计算它之前的换行符数量,加上在路径中累积的 即得行号。

换行符数量也是可以作为缓存项的,所以这种操作依然非常快速

对 CPU 缓存的充分利用

上文提到,叶片的大小一般是 4-8KB,这样的大小有利于 CPU 缓存。

基于 CPU 的缓存机制,我们可以使用链表或者其他顺序结构将叶子之间链接起来,在遍历的时候就不需要重走整棵树。

NOTE

关于为什么这样的大小可以更好的利用 CPU 缓存,本文不做叙述

简答地讲,对于遍历文件内容的本身的时候有如下优化手段

  • 利用链表链接所有叶子节点,在遍历的时候通过链表对叶子节点进行遍历而不是通过爬树来执行访问
  • 利用 CPU 会预缓存内存内容的特性使得访问内存的频率减少,降低遍历时使用其他结构因为 miss 的原因访问内存

这样既得到了廉价的编辑行为也获得了不俗的遍历性能,代价即为直接读取内容稍慢而已

插入删除等局部更新

在刚刚提到了读取操作,以上的解释注重于解释为什么 rope 在读取方面依然性能十分出众但并没有解释为什么 rope 在编辑方面的性能远大于使用 String [u8] 等线性数据结构

定位

对于定位,请看 偏移

操作

在到达指定位置后就可以执行对叶片的操作了。对于叶片的操作核心在于需要合适的时机分裂叶片使其成为新的叶片并且添加新的节点

先将光标内的内容插入到这个叶片的缓冲区里,然后做如下判断

  • 如果叶片大于 4-8KB:叶片将会分裂成为两个(或者多个)新的叶片
    • 在完成分裂后对叶片添加新的父节点
  • 如果叶片小于 4-8KB:正常插入即可

在完成插入操作后需要回溯所有祖先节点并且完成对祖先节点的权重变更,这样就完成了插入操作

在正常编辑下(只添加一个字符)最多只需要触摸 2 个叶片和 个节点,在特殊情况下(例如剪切板粘贴)会有更复杂的操作,暂且不提(简单讲就是对粘贴内容生成 rope 树并且将树挂载到指定位置上)

NOTE

xi-editorropey 在这方面实现上有区别,不介绍两个实现上所使用的树的具体区别

  1. Ropes: an Alternative to Strings

  2. xi-editor