3个不稳定版本
0.2.0 | 2019年9月19日 |
---|---|
0.1.2 | 2019年8月4日 |
0.1.1 | 2019年1月24日 |
#239 in 压缩
700KB
12K SLoC
archivelib-rs
这是1994年由Greenleaf Software编写的'archivelib'压缩算法的Rust版本。代码基于1994年8月10日发布的1.0B版本,并从此仓库中找到的代码移植而来。
库目标
将代码从C语言移植到Rust的目标
-
提供纯Rust实现。
这将使用户能够在Rust可以构建的任何地方构建此代码,从WASM到嵌入式设备。它还将允许将代码转换为其他语言(如Python)。
-
提供“安全”实现。
实现必须是内存安全的,不会崩溃,特别是在有限的环境中。
-
必须完美复制原始库,包括所有缺陷
对于定义良好的代码路径(即“快乐路径”),端口必须与原始版本表现相同。对于未定义的行为(如数组越界读取),
-
允许非算法偏差
原始库的使用允许输出最多65,536字节的输出;对于超过该值的输出将引发错误。Rust版本默认也有这个限制;但可以通过使用自定义的
ArchivelibConfig
来输出更大的文件。
故事
故事始于我决定将EmbroiderModder移植到Rust的时候,更具体地说,是读写各种格式。为了与两种常见的刺绣机格式hus
和vip
一起工作,我需要使用名为archivelib的压缩库。EmbroiderModder的git树中存在一个版本,在多次尝试移植代码后,我意识到我需要找到一个更接近原始版本的版本。
从C++到Rust
原始源代码可在这个仓库中找到,并已复制到archivelib-sys-orig/c-lib/
目录下。压缩算法本身是混淆的C++代码,可以在_rc.cpp
和_re.cpp
中查看,以及一些作为它们被发现时(尽可能)包含的辅助文件。除了修复影响我有效模糊测试代码的一个或两个错误(如这次更改以防止重复释放)之外,对原始源代码进行了最小改动。
从那里开始,删除了大部分不必要的代码,对混淆的C++进行了格式化并拆分出来,然后开始了一个漫长的理解、清理和整理代码的过程。
- 变量名从
_266
更改为更有用的名称,例如run_start226
。 - 从使用C++类重构为更功能化的风格,传递结构体。
- 尝试去除尽可能多的C/C++ '魔法'(如指针运算)。
- 使用一些已知良好的数据为每个函数生成测试用例。
这使得代码可以被转换为Rust,并继续重构和改进。
游戏开始
一旦实现了基本上工作的端口,就变得重要的是测试该端口是否正确。我开始尝试简单地压缩和解压缩给定的输入,并断言它产生了相同的输出。这揭示了一些错误;但我确信还有更多。所以我最终陷入了一个试图模糊测试库的原始C++版本(该库有许多内存安全问题)并确保我的Rust版本表现相同的兔子洞,只是没有崩溃。
为了有效地做到这一点,我最终不得不为原始库构建一个单独的C++ CLI,以便我可以使用所有的内存保护选项(如no-stack-arrays
以在堆栈数组访问上进行边界检查)。
今天的库
Rust库相对经过了良好的测试,并且不会(读:不应该)崩溃,即使在原始版本会崩溃的地方。API需要更多的文档,以及更深入地思考如何使用库。
进一步的工作
我在某处看到,这可能是zlib的不同块大小。我想调查这个说法,只是为了安心。
依赖关系
~2MB
~40K SLoC