5 个版本 (重大变更)
0.5.0 | 2022 年 10 月 31 日 |
---|---|
0.4.0 | 2021 年 9 月 19 日 |
0.3.0 | 2020 年 4 月 26 日 |
0.2.0 | 2020 年 4 月 21 日 |
0.1.0 | 2019 年 4 月 22 日 |
#1263 在 文本处理 中
每月 36 次下载
在 4 个 Crates 中使用 (2 个直接使用)
45KB
531 行
六比特 - 一个用于小型打包字符串的 crate。
此 crate 提供了密集打包的 8 位、16 位、32 位、64 位和 128 位“小型字符串”,采用各种自定义脚本特定的每字符 6 位编码,以及 Unicode“统一字符集和顺序” (URO) 块中汉字的特殊每字符 15 位编码。
这种数据类型是一种底层优化,用于在处理大量小型字符串的系统(例如单个单词)中:您可以避免在堆上分配它们,将它们存储在字符串结构标题、NaN 盒或其他小型字面值类型中。
也许最有吸引力的是:您可以 将多个它们打包 到一个 SIMD 向量中,并一次性并行地对(例如搜索)多个此类字符串进行操作,作为一个单独的寄存器。向量寄存器正变得越来越强大。
当然,并非每个字符串都足够短以适应,也并非每个足够短的字符串都具有落在此 crate 提供的“代码页”之一的码点。因此,编码函数都是部分函数。但它们应该可以处理足够数量的字符串,使其变得有价值。
使用总结
编码通过附加到Iterator<char>
的EncodeSixbit
特质来完成,因此您可以这样做:let enc = "hello".chars().encode_sixbit::<u64>()
。如果出现失败(例如,字符串跨越页面或无法适应),您将获得一个带有详细信息的Err(EncodeError)
,否则将返回Ok(n)
,其中n
是一个u64
。
解码是一个实现Iterator<char>
的DecodeSixbitIter
迭代器,附加到各种打包类型上,允许您编写let s:String = someu64.decode_sixbit().collect()
,或任何其他接受Iterator<char>
的模式。
在某些情况下,您需要在将这些接口推送之前对“标准”Unicode文本进行规范化或分解。例如,韩文字符页仅包含兼容音节,因此您必须将标准韩文文本分解到该格式之前编码。同样,半角假名不太可能是标准日文文本到达的字符,而天城文字符串需要在与nuktas映射之前分解。这个crate不执行这些任务:它是一个构建块,而不是一个完整的解决方案。
代码页
这个crate生成的每个打包字符串都以一个小标签开始,指示字符串的“代码页”。这里的代码页是一组64个Unicode字符值,其余字符串的6位码被解释为(或者,在特殊情况下,是URO块)。不支持混合来自多个代码页的字符的字符串。再次强调,“单个单词”。
我尽我所能选择了代码页的内容,结合了脚本知识、猜测、咨询朋友和专家、查阅维基百科等,并受到以下约束。我愿意接受PR以改进它们以更好地捕捉特定脚本中的“许多单词”;因此,代码页的内容将在1.0版本发布之前有所变化,所以如果出于某种奇怪的原因您正在阅读此内容并决定使用这个crate并将值存储在稳定存储中,您应该将您的客户端锁定到crate的特定修订版,直到1.0发布。
约束
标签中空间有限,只能引用少量代码页;不是每个脚本都能通过,但幸运的是,只有少数几个脚本占用了世界上大部分文本。
我们希望避免浪费位,一个N位(模6)打包值的备用位数量会根据其大小而变化:8位、32位和128位值有2个备用位;16位和64位值有4个备用位。
在所有情况下都分配了中文的标签,但在32位、64位和128位值中只有15位码的非零序列有空间,所以只有这些宽度使用它;在8位或16位情况下,标签只是预留。
我们希望能够使用机器提供的整数比较对这些字符串进行排序,并且这种顺序与unicode代码点字典序字符串顺序相匹配(包括“短字符串排序在长字符串之前”)。这决定了标签值、代码库和编码字符串的正常形式。
设计
代码页来自(或在某些情况下,跨越)unicode块,标签按(初始)unicode块排序。每个代码页中的代码进一步按unicode代码点排序:每个代码页实际上是从1个或多个相应的unicode块的内容中的“可能有用的子序列”。遗憾的是,这意味着只有使用拉丁页面的字符串中才有数字(它还有下划线,额外一个空格,这在许多标识符库中很常见)。我尽量在可用块中包含一些额外的标点符号。由于混合页面也不可行,所以“补充”块大部分被避免。
一个脚本只有在有适合于63个代码点的“可能有用的子序列”的情况下才能工作(除非它是URO块)。每个页面的零代码保留为字符串终止符和填充值。编码长度小于其打包值容器的字符串将被左移,始终从最高有效编码位开始,尾随位全部置零(这并不意味着你可以编码nulls——这里的零意味着“字符串末尾”)。
高阶/2位标签可以在unicode范围内(按代码块顺序)分散的4个“主要”(最可能)脚本中选择。这些脚本在所有打包值中都可用
标签 | 页面内容 |
---|---|
00 | 拉丁(带数字和下划线) |
01 | 阿拉伯 |
10 | 德文 |
11 | 中文 |
当打包64位和16位值时,我们得到4个备用位用于标签,而不仅仅是2个。在这种情况下,因此有12个额外的脚本可用,为了简单起见,我们在值大小之间进行上舍和下舍,保持高阶位不变,并在下面添加2位,从主要脚本(再次,按unicode块顺序)之间的代码块范围中选取额外的脚本。
标签 | 页面内容 |
---|---|
00 00 | 拉丁(带数字和下划线) |
00 01 | 希腊 |
00 10 | 西里尔 |
00 11 | 希伯来 |
01 00 | 阿拉伯 |
01 01 | 预留 |
01 10 | 预留 |
01 11 | 预留 |
10 00 | 德文 |
10 01 | 预留 |
10 10 | 预留 |
10 11 | 韩文字符兼容字 |
11 00 | 中文 |
11 01 | 预留 |
11 10 | 预留 |
11 11 | 半角假名 |
预留的情况是我对这个unicode范围内可用的脚本了解不够,或者候选者用完了,或者两者都有。我可能在将来将它们分配给某物,或者“压缩”这些差距/重新分配4位代码,使它们的高位不是2位情况的高位,但我已经超过了我的实际水平的知识和经验,我认为简化设计选择比假装我能做得更好要好。欢迎补丁!
位的整体分配总结如下
打包类型 | 标签位 | 编码位 | 最大6位字符 | 最大15位字符 |
---|---|---|---|---|
u128 | 2 | 126 | 21 | 8 |
u64 | 4 | 60 | 10 | 4 |
u32 | 2 | 30 | 5 | 2 |
u16 | 4 | 12 | 2 | 0 |
u8 | 2 | 6 | 1 | 0 |
许可证:MIT OR Apache-2.0
依赖关系
~92KB