|
|
本帖最后由 2010hook 于 2026-8-3 20:48 编辑
原作者:Sean Loaring,Slore
原地址:https://github.com/slorelee/binmay
Binmay 是用于文件或流中搜索替换进制字符串的命令行工具,更通用。
近期,DeepSeek 编程能力增强了,我令其以 资深编程专家角色 经几十次深度优化源码,结果喜人。
2026-8-02 更新:优化了编程提示词,提高了代码质量,消灭了一些潜在bug。增加 32bit.exe
2026-7-30 更新:性能、安全提升。防止全通配掩码替换死循环;64位匹配计数,适合处理GB级大文件。
2026-7-24 更新:改用MinGW编译,提高性能和可靠性,修正警告的3处代码写法。
2026-7-09 更新:新增灵活支持掩码 ?? .. 以及 忽略/匹配索引语法。
2026-6-30 发布:binmay2 修复原版所有bug,优化架构,增强性能。
---
MinGW 编译参数:
gcc -std=c17 -O2 -flto -pipe -fstack-protector-strong -D_FORTIFY_SOURCE=2 -Wall -Wextra -Wpedantic -Wconversion -Wsign-conversion -Wl,--nxcompat,--dynamicbase,--gc-sections -ffunction-sections -static -s %src_list%
binmay.7z
(50.6 KB, 下载次数: 115)
-------------------------------------------------------
最新版相对原始代码近乎重写级别的深度优化,在保持原有设计思想的基础上,修复了大量隐性缺陷,大幅提升了健壮性、性能、可移植性以及可维护性。
1. 编译与可移植性
- 原始代码: `f_malloc` 中错误地强制转换为未定义 `(struct input*)`
优化版本: 完全移除无意义的转换,使用统一的 `xmalloc`,返回 `void*`
改进意义: 消除编译错误与未定义行为,并实现 Windows / Linux 跨平台编译
- 原始代码: 未处理 Windows 文本模式可能导致 `\n` → `\r\n` 转换,破坏二进制数据
优化版本: 引入 `set_binary_mode()` 函数,对 `stdin/stdout/stderr` 设置 `O_BINARY` 模式
改进意义: 保证二进制数据在 Windows 下正确读写
- 原始代码: 自定义 `getopt` 存在状态机 bug,且返回 `-1` 非标准
优化版本: 保留自定义 `getopt`,但修复了错误返回逻辑(统一 `return '?'`),并增强错误信息
改进意义: 命令行解析更健壮,行为更符合预期
2. 内存管理与资源泄漏
- 原始代码: 全局变量泛滥(`verbose`、`matches`、`pukebuf`、`plen` 等),资源释放困难
优化版本: 所有配置集中到动态分配的 `struct app_config`,通过指针传递,`main` 末尾统一 `free`
改进意义: 消除全局状态,生命周期清晰,易于测试和复用
- 原始代码: 所有通过 `f_malloc` 分配的句柄、缓冲区、掩码结构从未释放,内存泄漏
优化版本: 实现完备的 `*_free` 系列函数,`main` 采用 `goto` 清理链,无论成功或失败路径均释放所有资源
改进意义: 彻底解决内存泄漏问题,运行更稳定,处理大量文件不会耗尽内存
- 原始代码: 缓冲区固定 1024 字节(`BUF_LEN`),且 `masked_string_setstr` 上游长度检查对 `u:` 格式无效
优化版本: 缓冲区扩大至 `IO_LEN = 128 * 1024`(128KB),大幅减少 I/O 次数;长度检查前置至 `process_string_default` 内部,杜绝越界
改进意义: 提高吞吐量,根除缓冲区溢出隐患
3. 错误处理与健壮性
- 原始代码: 多数函数内部直接 `exit(1)` 或 `abort()`,无法优雅恢复,且资源泄露
优化版本: 所有函数返回错误码或 `LEN_UNSET`,错误层层上传,`main` 统一处理并跳转清理
改进意义: 资源始终得到释放,错误处理路径完整可靠
- 原始代码: `fwrite` 未处理短写(short write),大数据量下可能写入不完整
优化版本: 实现 `fwrite_all`,循环写入直到全部字节发送完毕
改进意义: 确保大数据块完整写入,避免数据损坏
- 原始代码: `-u/-U` 原地更新极不安全:直接 `rename` 原文件后打开原文件名写入,若程序崩溃则数据不可恢复
优化版本: 采用临时文件 + 原子重命名模式:通过 `mkstemp`/`_mktemp_s` 创建安全临时文件,所有输出写入临时文件,成功后才执行重命名;失败时保留临时文件并给出明确恢复提示;若已备份`*.0rg`则不覆盖,保留初始版本
改进意义: 零数据丢失风险,数据完整性得到保障
- 原始代码: 掩码长度不一致时仅警告并截断,可能产生非预期结果
优化版本: 严格校验掩码长度与字符串长度必须相等,否则报错退出
改进意义: 避免静默执行错误操作
- 原始代码: 空搜索字符串或非法前缀无检查
优化版本: 增加 `search->length == 0` 检查,非法前缀明确返回错误
改进意义: 提早暴露参数错误,避免无意义运行
- 原始代码: `buffered_skip` 中偏移量 `coffset` 为 `int`,可能溢出
优化版本: `coffset` 改为 `long long`,并在跳过前检查溢出
改进意义: 支持超过 2GB 文件的处理,无符号溢出风险
4. 搜索算法性能飞跃
- 原始代码: `masked_string_seek` 对每个位置无差别调用 `masked_string_match`(朴素 O(n*m) 匹配)
优化版本: 引入快速跳跃过滤:预计算前两个非通配字节位置及末尾非通配字节,先检查这三个关键字节,不匹配则立即跳过;只有通过快速筛选后才执行完整掩码匹配
改进意义: 对含大量通配符的掩码(如仅固定首尾字节),无效概率仅有1/16M,性能提升一个数量级
- 原始代码: 缓冲区大小为 2KB,搜索时滑动窗口小,频繁 I/O
优化版本: 缓冲区提升至 256KB(`IO_LEN * 2`),单次读入更多数据,大幅减少系统调用
改进意义: I/O 次数锐减,整体吞吐量显著提高
5. 掩码功能创意增强
- 原始代码: 掩码只能通过 `-S`/`-R` 指定十六进制字节序列(如 `00ff`),表达能力有限
优化版本: 新增三种掩码语法(中文使用手册有详细说明示例):
- 正常模式:保留原有十六进制对,同时支持 `??` 或 `..` 作为通配符(对应掩码字节 0x00)
- 忽略列表 `!`:如 `!0,2-4`,表示仅忽略指定字节,其余字节必须完全匹配
- 匹配列表 `=`:如 `=0,2-4`,表示仅匹配指定字节,其余字节忽略。并实现索引列表的范围(`-`)和逗号分隔解析
改进意义: 极大提升二进制补丁的便捷性,可灵活选择“关注/忽略”任意字节位置
常见 ?? 和 .. 属于Byte级,会忽略256种可能组合,并非高大上。索引列表的价值更大。
十六进制 0~F (16x16种组合) 才是精确匹配bit,须用二进制推导!掩码底层是二进制,0 表示忽略,1 表示匹配。
- 原始代码: 无全通配掩码检查,设置全为 0x00 的掩码会永远匹配但毫无意义
优化版本: 检测掩码是否全部为 0(全通配),并报错拒绝
改进意义: 防止意外误用,操作意图更清晰
6. 代码架构与可读性
- 原始代码: `DEBUG` 宏混在代码中,大量无用原型声明(如 `open_outfile`)存在
优化版本: 彻底清除冗余代码,所有内部函数使用 `static` 限制作用域
改进意义: 代码精简,命名空间干净,避免意外冲突
- 原始代码: 函数定义分散,混杂在大量注释括号 `{{ }}` 中,风格老旧
优化版本: 采用现代 C 风格,清晰划分“数据结构 → 前向声明 → 实现”模块,简洁明了
改进意义: 可读性和可维护性大幅提升
- 原始代码: `main` 文件打开、参数解析、搜索、puke 的逻辑纠缠在一起
优化版本: 将参数解析抽离为 `parse_args`,原地更新逻辑独立处理,`main` 仅负责流程调度和清理
改进意义: 职责分离,便于扩展和调试
- 原始代码: 返回匹配数量使用全局变量 `matches`
优化版本: 改为 `do_search_replace` 返回 `long long`,同时支持 64 位大文件统计
改进意义: 无状态函数,可重入,并支持海量匹配
- 原始代码: `hexdumpline` 输出无分隔符,可读性差
优化版本: 格式化输出为空格分隔的十六进制对(`printf("%s%02x", i?" ":"", byte)`)
改进意义: 输出结果更符合常见 hexdump 习惯,便于阅读和后续处理
7. 其他细节优化
- `-u`/`-U` 备份后缀从 `.org` 改为 `.0rg`,减少混淆可能性,语义更明确。
- 新增 `-b` 选项位置约束检查,要求其必须出现在 `-s`/`-r`/`-p` 之前,防止用户误用。
- `-i`/`-o` 与 `-u`/`-U` 互斥检查,提前报错,避免冲突。
- 缓冲区大小定义统一为 `IO_LEN`,使用 `LEN_UNSET` 宏代替 `-1` 传递给 `size_t`,避免符号比较陷阱。
- 中文帮助信息详实全面,包含掩码新语法和多个完整示例,学习成本极低。
总结
从底层修复了原代码的编译错误、内存泄漏、全局状态污染、不安全文件操作等硬伤,并通过临时文件原子写、智能搜索加速、灵活掩码语法、跨平台二进制模式支持、完整错误恢复链等改进,将一个小巧但粗糙的工具提升到了工业生产级质量。 |
|