一、项目概述

项目简介

本项目是一款基于 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 压缩结合,保证性能与补丁体积平衡。

想了解更多关于Rust语言的知识及应用,可前往华为开放原子旋武开源社区(https://xuanwu.openatom.cn/),了解更多资讯~

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐