当前位置: 首页 > news >正文

用Rust实现二进制文件差异工具

一、项目概述

项目简介

本项目是一款基于 Rust 开发的高性能 二进制文件差异计算与补丁生成工具,目标类似于经典工具 **bsdiff****bspatch**。它可以高效比较两个二进制文件的差异,生成紧凑的补丁文件,并支持从旧文件与补丁恢复新文件。该项目重点展示 Rust 在 字节级操作、算法优化、内存安全与零拷贝I/O 方面的优势。

核心功能

  • 差异计算(diff): 通过滚动哈希算法识别相同字节块与变更部分;
  • 补丁生成(patch): 生成可压缩的二进制补丁文件;
  • 补丁应用(apply): 利用原始文件与补丁重建目标文件;
  • 压缩优化: 使用 flate2 对补丁数据进行压缩,显著降低文件体积;
  • 性能特性: 采用 bytes crate 与零拷贝(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

若无输出,说明文件完全一致。



六、技术要点总结

性能与内存安全

  • 通过 bytes crate 使用零拷贝 Bytes/BytesMut,避免重复内存分配;
  • Rust 编译器在所有权与借用层面保证补丁生成逻辑内存安全;
  • 差异块处理基于流式 API,可处理数 GB 文件而不溢出内存。

滚动哈希算法

  • 利用 Rabin-Karp 思想在窗口内快速更新哈希值;
  • 哈希冲突概率低,计算复杂度 O(n);
  • 适合用于二进制 diff 检测相同块与偏移差异。

零拷贝 I/O 优化

  • 可使用 memmap2::Mmap 实现文件直接映射内存,避免冗余读取;
  • 结合 BytesMut::from 高效拼接字节数据;
  • flate2 压缩结合,保证性能与补丁体积平衡。
http://www.dtcms.com/a/605342.html

相关文章:

  • 代理IP的匿名性测试:如何验证你的真实IP是否已泄露?
  • FreeRTOS 在 AS32系列RISC-V 架构MCU电机驱动中的应用实践与优化
  • 【OpenCV + VS】 图像像素类型转换与归一化
  • 用 Rust 写一个可落地的目录实时监听器:跨平台文件系统事件的可靠表达与工程实践
  • Linux网络--Socket 编程 TCP
  • 【一文了解】C#反射
  • 网站建设seo推广外贸网站建设海外推广
  • 网站ip域名查询安徽省住房城乡建设厅网站电工
  • 202511-Selenium技术深度解析:Web自动化测试的王者之路
  • Android 打开 在线 pdf 文件
  • Python 教程:如何快速在 PDF 中添加水印(文字、图片)
  • 普中51单片机学习笔记-矩阵按键
  • 视觉语言模型新突破!开源项目解读
  • 深圳南山区住房和建设局网站官网天天向上做图网站
  • 微算法科技(NASDAQ MLGO)通过容量证明(PoC)构建全球存储资源池,为Web3应用提供低成本、抗审查的数据存储服务
  • 08-微服务原理篇(Canal-Redis)
  • 填写网站备案信息深圳建设材料价格网站
  • 【Spring Boot 报错已解决】Spring Boot开发避坑指南:Hibernate实体类主键配置详解与异常修复
  • 【CSS】cursor: auto, default, none 有什么区别?
  • 网站备案负责人三网合一营销型全网站
  • 7.2 Dify核心功能与技术架构:前后端分离、API接口、数据存储
  • 观察Springboot AI-Function Tools 执行过程
  • 信贷风控建设的多维意义解析
  • 如何在产品已上线后发现需求遗漏进行补救
  • 重卡充电桩平台支持针对不同车队单独配置计费规则
  • 美丽寮步网站建设高性能广州公关公司有哪些
  • Linux告别搜索卡顿:解决“Argument list too long”与实现文件内容秒搜
  • .NET驾驭Excel之力:工作簿与工作表操作基础
  • 基于 C++ OpenCV 生成小视频
  • 个人网站审批网站防止采集