13个版本 (4个稳定版)
1.1.2 | 2023年5月15日 |
---|---|
1.1.1 | 2022年11月21日 |
1.1.0 | 2022年9月14日 |
1.0.0 | 2022年4月26日 |
0.4.0 | 2021年10月11日 |
#2 in #rope
5,009 每月下载量
用于 2 crates
130KB
2K SLoC
JumpRope
因为字符串插入应该很快。
绳子是一种数据结构,用于高效编辑大字符串或处理编辑跟踪。
据我所知,JumpRope是世界最快的绳子实现。
与传统的字符串不同,JumpRope允许您
- 高效地在文档的任何位置插入或删除任意按键。使用现实世界的编辑跟踪,jumprope每秒可以处理约3500万至4000万次编辑。
- 使用Unicode字符偏移量或wchar偏移量(如你在JS和其他语言中找到的)进行索引。Jumprope可以有效地在这些格式之间转换。
JumpRope针对大字符串进行了优化,如源代码文件和文本文档。如果你的字符串非常小(小于100字节),你可能只需使用Rust内置的std String或类似SmartString的小字符串优化库。
JumpRope与ropey类似。Ropey支持一些更多功能(如转换行/列位置)。然而,jumprope在处理真实的编辑操作时比ropey快约3倍(见下文),并且jumprope编译后的wasm包更小。(Ropey brotli压缩后为30kb,而jumprope为18kb)。
将以下内容添加到Cargo.toml中以使用
jumprope = "1.0.0"
使用方法
JumpRope不是字符串的直接替代品,但它支持许多类似的方法。最重要的新增功能是insert
、remove
和replace
方法 - 这些方法允许您在(通常是)相对于现有文档大小的log(n)
时间内原地编辑字符串。
use jumprope::JumpRope;
fn main() {
let mut rope = JumpRope::from("Some large text document");
rope.insert(5, "really "); // "Some really large text document"
rope.replace(0..4, "My rad"); // "My rad really large text document"
assert_eq!(rope, "My rad really large text document");
// Extract to a string
let s: String = rope.to_string();
assert_eq!(s, "My rad really large text document");
}
您可以通过以下方式从绳子中读取内容
- 使用
rope.to_string()
将绳索转换为字符串(需要分配) - 使用
rope.chars()
遍历字符 - (最快)使用
rope.substrings()
遍历 &str 块。这返回文档中包含的&str
项的迭代器。
如果您想读取绳索的子部分,可以使用 rope.slice_substrings(10..20)
读取绳索中给定范围内的所有内容。例如
fn main() {
let rope = JumpRope::from("xxxGreetings!xxx");
let string = rope.slice_substrings(3..13).collect::<String>();
assert_eq!(string, "Greetings!");
}
有关更多详细信息,请参阅 JumpRope API 文档
宽字符转换
在某些语言中(尤其是 JavaScript、Java 和 C#),字符串的长度是通过使用 UTF16 编码字符串所需的 2 字节“字符”数量来衡量的。
这是因为很难有效地在 Unicode 字符偏移量(由 jumprope、菱形类型和其他编辑器使用)和这些编辑位置之间进行转换。简单的方法是 O(n) 操作。
Jumprope 支持通过向跳表添加额外的索引信息,以 O(log n)
的时间复杂度进行此转换。此功能默认禁用,因为额外的记录会增加 jumprope 的运行时间约 15%。
要使用此功能,启用 wchar_conversion
特性标志
jumprope = { version = "1.0.0", features = ["wchar_conversion"] }
此特性标志启用了与文档交互的许多额外宽字符相关方法
rope.len_wchars() -> usize
:返回字符串的宽字符长度。rope.chars_to_wchars(chars: usize) -> usize
:将 char 偏移量转换为 wchar 偏移量rope.wchars_to_chars(wchars: usize) -> usize
:将 wchar 索引转换回 Unicode 字符计数rope.insert_at_wchar(pos_wchar: usize, content: &str)
:在指定的 wchar 偏移量处插入content
rope.remove_at_wchar(range: Range<usize>)
:使用宽字符偏移量指定的范围删除指定范围rope.replace_at_wchar(range: Range<usize>, content: &str)
:用content
替换指定的范围
有关这些方法的更多信息,请参阅docs.rs上的文档。
缓冲字符串
JumpRope还提供了一个API用于缓冲编辑。通常当人类编辑字符串时,他们会插入或删除一系列字符。如果您在应用这些编辑之前将这些编辑操作合并在一起,JumpRope的速度会再快约10倍。
JumpRope提供了一个包装API,以JumpRopeBuf的形式进行透明处理。JumpRopeBuf会在刷新(写入)包含的JumpRope对象之前尽可能地合并传入的写入操作。
此API可能缺少在JumpRope
上找到的一些方法。您通常可以通过调用rope.borrow()
或rope.as_mut()
来清除挂起的更改并访问底层rope的指针来绕过任何缺失的方法。但请报告您发现的任何缺失函数,因为添加直接实现通常会带来更好的性能。
有关使用说明,请参阅JumpRopeBuf模块文档。
历史/动机
此代码基于我几年前为了练习跳表而编写的较旧的基于跳表的C rope库。它有几个显著的不同之处
- JumpRope不是简单地实现为一个跳表,而是一个跳表,其中每个叶节点包含一个Gap Buffer。
- JumpRope更快。(见下表)
基准测试
运行来自crdt-benchmarks的编辑跟踪,JumpRope比我所知的任何其他cargo库都要快
在Ryzen 5800X的单个核心上运行
数据集 | 原始字符串 | XiRope | Ropey | librope(C) | Jumprope |
---|---|---|---|---|---|
automerge-paper | 3908.13 ms | 518.75 ms | 25.16 ms | 16.28 ms | 6.66 ms |
rustcode | 569.44 ms | DNF | 4.71 ms | 3.93 ms | 1.66 ms |
sveltecomponent | 41.05 ms | 24.83 ms | 2.31 ms | 1.59 ms | 0.59 ms |
seph-blog1 | 1238.44 ms | DNF | 13.04 ms | 10.01 ms | 3.81 ms |
完整的criterion报告在这里。
我也试了AnRope,但在处理这些数据集时它崩溃了。
许可证
根据ISC许可证授权
版权所有 2018 Joseph Gentle
在此授予使用、复制、修改和/或以任何目的(无论是否付费)分发此软件的许可,前提是上述版权声明和本许可声明出现在所有副本中。
软件按“原样”提供,且作者放弃与此软件有关的任何保证,包括所有默示的适销性和适用性保证。在任何情况下,作者均不对任何特殊、直接、间接或后果性损害或任何损害承担责任,无论是否因合同、疏忽或其他侵权行为引起的,无论此类损害是否与使用或性能此软件有关。
依赖项
~390KB