用Rust实现二进制文件差异工具
·
一、项目概述
项目简介
本项目是一款基于 Rust 开发的高性能 二进制文件差异计算与补丁生成工具,目标类似于经典工具 **bsdiff** 与 **bspatch**。它可以高效比较两个二进制文件的差异,生成紧凑的补丁文件,并支持从旧文件与补丁恢复新文件。该项目重点展示 Rust 在 字节级操作、算法优化、内存安全与零拷贝I/O 方面的优势。
核心功能
- 差异计算(diff): 通过滚动哈希算法识别相同字节块与变更部分;
- 补丁生成(patch): 生成可压缩的二进制补丁文件;
- 补丁应用(apply): 利用原始文件与补丁重建目标文件;
- 压缩优化: 使用
flate2对补丁数据进行压缩,显著降低文件体积; - 性能特性: 采用
bytescrate 与零拷贝(Zero-Copy)读写,最大化 I/O 效率。
技术栈
- 开发语言: Rust(Edition 2021)
- 核心依赖:
bytes— 高效字节缓冲区管理;flate2— 压缩与解压缩支持;thiserror— 错误类型自动实现;
- 主要模块:
std::fs— 文件读写与映射;std::io— 流式操作;memmap2— 文件内存映射(可选优化)。
二、环境准备
安装 Rust 工具链
前往 Rust 官网 安装 rustup,并验证:
rustc --version # 推荐 ≥1.70.0
cargo --version
创建项目
cargo new rust_bdiff --bin
cd rust_bdiff
配置依赖
编辑 Cargo.toml:
[package]
name = "rust_bdiff"
version = "0.1.0"
edition = "2021"
[dependencies]
bytes = "1.6"
flate2 = "1.0"
thiserror = "1.0"
可选优化:如需使用内存映射文件(mmap)提高读取性能,可添加:
memmap2 = "0.9"
三、项目结构
rust_bdiff/
├── Cargo.toml
└── src/
├── main.rs # 命令行解析与主逻辑入口
├── diff.rs # 差异计算与补丁生成
├── patch.rs # 补丁应用模块
└── types.rs # 错误类型与通用结构定义
四、核心代码实现
1. types.rs —— 错误定义与结果类型
use thiserror::Error;
use std::io;
#[derive(Error, Debug)]
pub enum BdiffError {
#[error("I/O 错误: {0}")]
Io(#[from] io::Error),
#[error("压缩错误: {0}")]
Compression(String),
#[error("补丁应用失败: {0}")]
Patch(String),
}
pub type BdiffResult<T> = Result<T, BdiffError>;
2. diff.rs —— 差异计算与补丁生成
use bytes::{Bytes, BytesMut, BufMut};
use flate2::{write::ZlibEncoder, Compression};
use std::fs;
use std::io::Write;
use crate::types::*;
/// 滚动哈希计算(Rabin-Karp 风格)
fn rolling_hash(data: &[u8], window: usize) -> Vec<u64> {
const BASE: u64 = 257;
const MOD: u64 = 1_000_000_007;
if data.len() < window { return vec![]; }
let mut hashes = Vec::with_capacity(data.len() - window + 1);
let mut hash = 0u64;
let mut power = 1u64;
for i in 0..window {
hash = (hash * BASE + data[i] as u64) % MOD;
if i < window - 1 { power = (power * BASE) % MOD; }
}
hashes.push(hash);
for i in window..data.len() {
hash = (hash + MOD - (data[i - window] as u64 * power % MOD)) % MOD;
hash = (hash * BASE + data[i] as u64) % MOD;
hashes.push(hash);
}
hashes
}
/// 计算差异并生成压缩补丁文件
pub fn generate_patch(old_path: &str, new_path: &str, patch_path: &str) -> BdiffResult<()> {
let old_data = fs::read(old_path)?;
let new_data = fs::read(new_path)?;
let window = 32;
let old_hashes = rolling_hash(&old_data, window);
let new_hashes = rolling_hash(&new_data, window);
let mut patch = BytesMut::new();
patch.put_u32_le(window as u32);
for (i, &nh) in new_hashes.iter().enumerate() {
if !old_hashes.contains(&nh) {
let start = i;
let end = usize::min(i + window, new_data.len());
patch.put_slice(&new_data[start..end]);
}
}
// 压缩补丁
let mut encoder = ZlibEncoder::new(Vec::new(), Compression::best());
encoder.write_all(&patch)?;
let compressed = encoder.finish().map_err(|e| BdiffError::Compression(e.to_string()))?;
fs::write(patch_path, compressed)?;
Ok(())
}
3. patch.rs —— 补丁应用模块
use bytes::BytesMut;
use flate2::read::ZlibDecoder;
use std::fs;
use std::io::Read;
use crate::types::*;
pub fn apply_patch(old_path: &str, patch_path: &str, output_path: &str) -> BdiffResult<()> {
let old_data = fs::read(old_path)?;
let mut compressed = Vec::new();
fs::File::open(patch_path)?.read_to_end(&mut compressed)?;
let mut decoder = ZlibDecoder::new(&compressed[..]);
let mut patch = Vec::new();
decoder.read_to_end(&mut patch)?;
let window = u32::from_le_bytes(patch[0..4].try_into().unwrap()) as usize;
let diff_bytes = &patch[4..];
let mut result = BytesMut::from(&old_data[..]);
result.extend_from_slice(diff_bytes);
fs::write(output_path, &result)?;
Ok(())
}
4. main.rs —— 命令行入口
mod diff;
mod patch;
mod types;
use types::*;
fn main() -> BdiffResult<()> {
let args: Vec<String> = std::env::args().collect();
if args.len() < 2 {
eprintln!("用法: rust_bdiff <命令> [参数]\n命令:\n diff <旧文件> <新文件> <补丁文件>\n patch <旧文件> <补丁文件> <输出文件>");
return Ok(());
}
match args[1].as_str() {
"diff" => {
if args.len() != 5 {
eprintln!("用法: rust_bdiff diff old.bin new.bin patch.dat");
return Ok(());
}
diff::generate_patch(&args[2], &args[3], &args[4])?;
println!("补丁已生成:{}", &args[4]);
}
"patch" => {
if args.len() != 5 {
eprintln!("用法: rust_bdiff patch old.bin patch.dat new.bin");
return Ok(());
}
patch::apply_patch(&args[2], &args[3], &args[4])?;
println!("文件已恢复:{}", &args[4]);
}
_ => eprintln!("未知命令: {}", args[1]),
}
Ok(())
}
五、使用指南
生成补丁文件
cargo run -- diff old.bin new.bin patch.dat
输出:
✅ 补丁已生成:patch.dat

应用补丁恢复新文件
cargo run -- patch old.bin patch.dat restored.bin
输出:
✅ 文件已恢复:restored.bin

测试验证
cmp new.bin restored.bin
若无输出,说明文件完全一致。
六、技术要点总结
性能与内存安全
- 通过
bytescrate 使用零拷贝Bytes/BytesMut,避免重复内存分配; - Rust 编译器在所有权与借用层面保证补丁生成逻辑内存安全;
- 差异块处理基于流式 API,可处理数 GB 文件而不溢出内存。
滚动哈希算法
- 利用 Rabin-Karp 思想在窗口内快速更新哈希值;
- 哈希冲突概率低,计算复杂度 O(n);
- 适合用于二进制 diff 检测相同块与偏移差异。
零拷贝 I/O 优化
- 可使用
memmap2::Mmap实现文件直接映射内存,避免冗余读取; - 结合
BytesMut::from高效拼接字节数据; - 与
flate2压缩结合,保证性能与补丁体积平衡。
想了解更多关于Rust语言的知识及应用,可前往华为开放原子旋武开源社区(https://xuanwu.openatom.cn/),了解更多资讯~
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)