#port #original #version #was #found #repo #greenleaf

bin+lib archivelib

Greenleaf ArchiveLib 压缩/解压缩算法的实现

3个不稳定版本

0.2.0 2019年9月19日
0.1.2 2019年8月4日
0.1.1 2019年1月24日

#239 in 压缩

GPL-2.0-only

700KB
12K SLoC

Rust 10K SLoC // 0.0% comments C++ 1.5K SLoC // 0.7% comments Python 581 SLoC // 0.0% comments

archivelib-rs

这是1994年由Greenleaf Software编写的'archivelib'压缩算法的Rust版本。代码基于1994年8月10日发布的1.0B版本,并从此仓库中找到的代码移植而来。

库目标

将代码从C语言移植到Rust的目标

  • 提供纯Rust实现。

    这将使用户能够在Rust可以构建的任何地方构建此代码,从WASM到嵌入式设备。它还将允许将代码转换为其他语言(如Python)。

  • 提供“安全”实现。

    实现必须是内存安全的,不会崩溃,特别是在有限的环境中。

  • 必须完美复制原始库,包括所有缺陷

    对于定义良好的代码路径(即“快乐路径”),端口必须与原始版本表现相同。对于未定义的行为(如数组越界读取),

  • 允许非算法偏差

    原始库的使用允许输出最多65,536字节的输出;对于超过该值的输出将引发错误。Rust版本默认也有这个限制;但可以通过使用自定义的ArchivelibConfig来输出更大的文件。

故事

故事始于我决定将EmbroiderModder移植到Rust的时候,更具体地说,是读写各种格式。为了与两种常见的刺绣机格式husvip一起工作,我需要使用名为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