3 个版本
0.23.2 | 2023 年 10 月 15 日 |
---|---|
0.23.1 | 2023 年 10 月 3 日 |
0.23.0 | 2023 年 10 月 3 日 |
0.22.0 |
|
0.21.4 |
|
#295 in 编码
1,603 每月下载量
用于 2 crates
260KB
4.5K SLoC
simple-base64
这个库是从一个 base64 库分叉出来的,目的是使其易于使用,并且 API 在查看文档时易于理解。
请参阅 文档 了解详细信息。
使用方法
标准编码/解码
可以使用 crate 级别的 encode
和 decode
函数实现标准编码和解码。
use simple_base64::{encode, decode};
let s = String::from("a string");
let encoded = encode(s.clone());
let decoded = decode(encoded).unwrap();
// decode returns a vector of bytes, thus we have to convert it to String
let decoded = String::from_utf8(decoded).unwrap();
assert_eq!(s, decoded);
其他引擎
如果您需要不同的引擎,您也可以使用 encode_engine
和 decode_engine
方法。
use simple_base64::{encode_engine, decode_engine};
use simple_base64::prelude::BASE64_STANDARD_NO_PAD as NO_PAD;
let s = String::from("a string");
let encoded = encode_engine(s.clone(), &NO_PAD);
let decoded = decode_engine(encoded, &NO_PAD).unwrap();
// decode returns a vector of bytes, thus we have to convert it to String
let decoded = String::from_utf8(decoded).unwrap();
assert_eq!(s, decoded);
常见问题解答
我需要解码包含空格/空字节/其他随机内容的 base64。我该怎么办?
在解码之前从输入中删除非 base64 字符。
如果您有一个 base64 的 Vec
,可以使用 retain 来移除您需要移除的内容。
如果您有一个 Read
(例如,读取文件或网络套接字),有各种方法。
- 使用 iter_read 与
Read
的bytes()
一起使用以过滤掉不需要的字节。 - 实现一个将委托到您的实际
Read
的Read
的read()
impl,然后丢弃您不想要的任何字节。
我需要换行 base64,例如 MIME/PEM。
line-wrap 就是这样做的。
我想进行规范化的 base64 编码/解码。
首先,不要这样做。您不应期望 Base64 是规范的,就像您不应期望压缩算法在野外的所有使用中都产生规范输出一样(提示:它们不)。然而,人们像飞蛾扑火一样走向自己的毁灭,所以我们在这里。
存在两种非规范编码的机会(因此在解码过程中检测到相同的情况):在两个或三个标记后缀的最后几个位,以及用于将后缀扩展为完整四个标记的 =
标记。
尾随位的問題是不可避免的:每个编码标记中有6位可用,1个输入字节占用2个标记,第二个标记有一些位未使用。对于两个输入字节也是如此:16位,但3个标记有18位。除非我们决定停止传输整个字节,否则我们将陷入这些额外的位,这些位可能被狡猾或错误的编码器设置为1而不是0。
另一方面,=
填充字节完全是Base64标准的自我承担。它们除了提供“该填充不正确”的机会外,不会影响解码。毫无疑问,已经浪费了大量的存储和传输在无意义的 =
字节上。不知何故,我们都似乎对,比如,十六进制编码的数据在完成时停止而不是需要确认编码器的作者能数到四感到相当自在。无论如何,有两种方法可以使填充字节可预测:按照RFC要求,要求进行规范填充到下一个4的倍数字节,或者如果您控制所有生产者和消费者,通过要求不进行填充来节省一些字节(特别是适用于url安全的字母表)。
所有 Engine
实现至少必须支持将两种类型的非规范填充都视为错误,并可选择允许其他行为。
Rust版本兼容性
最低支持的Rust版本是1.48.0。
贡献
贡献非常欢迎。然而,由于这个库被广泛使用,并且在安全性敏感的环境中,所有PR都将受到仔细审查。除此之外,这种低级别库需要100%正确。没有人希望追逐任何编码中的错误。
这意味着我花了很多时间来审查每个PR,因此可能需要相当长的时间才能抽出时间来给予每个PR应有的关注。我会最终处理每个人的!
开发
基准测试在 benches/
中。
cargo bench
no_std
这个crate支持no_std。默认情况下,该crate通过 std
功能针对std。您可以通过取消激活 default-features
来针对 core
。在这种情况下,您将失去围绕 std::io
、std::error::Error
和堆分配的所有功能。还有一个额外的 alloc
功能,您可以激活它以恢复对堆分配的支持。
性能分析
在Linux上,您可以使用 perf 进行性能分析。然后使用 cargo bench --no-run
编译基准测试。
使用 perf
运行基准二进制文件(此处展示的是过滤到一个特定的基准,这将使结果更容易阅读)。在大多数系统中,perf
只对 root 用户可用,因为它会操作 CPU 中的事件计数器,所以需要使用 sudo
。我们需要运行实际的基准二进制文件,因此需要进入 target
的路径。您可以使用 cargo bench -v
来查看实际的全路径;它会打印出它运行的命令。如果您使用 bench
输出的确切路径,请确保您得到的是针对基准的,而不是测试的。您还可能想使用 cargo clean
,以确保只有一个 benchmarks-
二进制文件(它们往往会累积)。
sudo perf record target/release/deps/benchmarks-* --bench decode_10mib_reuse
然后使用 perf 分析结果。
sudo perf annotate -l
您将看到一些交织的 Rust 源代码和汇编代码,如下所示。带有 lib.rs:327
的部分告诉我们,4.02% 的样本看到了 movzbl
即位移动作为活动的指令。然而,由于一个称为 skid 的现象,这个百分比并不像看起来那么精确。基本上,现代 CPU 的复杂性的一个后果是这种指令分析本质上是不准确的,尤其是在分支密集型的代码中。
lib.rs:322 0.70 : 10698: mov %rdi,%rax
2.82 : 1069b: shr $0x38,%rax
: if morsel == decode_tables::INVALID_VALUE {
: bad_byte_index = input_index;
: break;
: };
: accum = (morsel as u64) << 58;
lib.rs:327 4.02 : 1069f: movzbl (%r9,%rax,1),%r15d
: // fast loop of 8 bytes at a time
: while input_index < length_of_full_chunks {
: let mut accum: u64;
:
: let input_chunk = BigEndian::read_u64(&input_bytes[input_index..(input_index + 8)]);
: morsel = decode_table[(input_chunk >> 56) as usize];
lib.rs:322 3.68 : 106a4: cmp $0xff,%r15
: if morsel == decode_tables::INVALID_VALUE {
0.00 : 106ab: je 1090e <base64::decode_config_buf::hbf68a45fefa299c1+0x46e>
模糊测试
这使用 cargo-fuzz。有关可用的模糊测试脚本,请参阅 fuzz/fuzzers
。要运行,请使用类似以下调用
cargo +nightly fuzz run roundtrip
cargo +nightly fuzz run roundtrip_no_pad
cargo +nightly fuzz run roundtrip_random_config -- -max_len=10240
cargo +nightly fuzz run decode_random
许可证
此项目同时受 MIT 和 Apache 2.0 许可证的双许可。