diff --git a/.editorconfig b/.editorconfig index 87122f7..a64d5da 100644 --- a/.editorconfig +++ b/.editorconfig @@ -3,4 +3,4 @@ root = true [*] indent_style = space insert_final_newline = true -trim_trailing_whitespace = true \ No newline at end of file +trim_trailing_whitespace = true diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 3c13d1b..c41fc79 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -15,8 +15,10 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Build - run: cargo build --verbose - - name: Run tests - run: cargo test --verbose + - uses: actions/checkout@v2 + - name: Build + run: RUSTFLAGS='-D warnings' cargo build --verbose + - name: Run tests + run: cargo test --verbose + - name: Clippy + run: cargo clippy -- -D warnings diff --git a/.gitignore b/.gitignore index 635ba70..7a1f750 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,4 @@ target/ .DS_Store -Cargo.lock \ No newline at end of file +Cargo.lock diff --git a/Cargo.toml b/Cargo.toml index 806179b..290139d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,5 +9,5 @@ keywords = ["bspatch", "patch", "diff", "delta", "binary"] license = "BSD-2-Clause" homepage = "https://lib.rs/bsdiff" repository = "https://github.com/space-wizards/bsdiff-rs" -edition = "2018" +edition = "2021" include = ["src/*.rs", "LICENSE", "README.md", "Cargo.toml"] diff --git a/README.md b/README.md index 31a529e..9981340 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,57 @@ # bsdiff-rs -[![Build status](https://github.com/space-wizards/bsdiff-rs/actions/workflows/rust.yml/badge.svg)](https://github.com/space-wizards/bsdiff-rs/actions/workflows/rust.yml) -[![Cargo Link](https://img.shields.io/crates/v/bsdiff.svg)](https://crates.rs/crates/bsdiff) +[![GitHub](https://img.shields.io/badge/github-bsdiff-8da0cb?logo=github)](https://github.com/space-wizards/bsdiff-rs) +[![crates.io version](https://img.shields.io/crates/v/bsdiff.svg)](https://crates.io/crates/bsdiff) +[![docs.rs docs](https://docs.rs/bsdiff/badge.svg)](https://docs.rs/bsdiff) +[![crates.io version](https://img.shields.io/crates/l/bsdiff.svg)](https://github.comspace-wizards/bsdiff-rss/blob/main/LICENSE-APACHE) +[![CI build](https://github.com/space-wizards/bsdiff-rs/actions/workflows/rust.yml/badge.svg)](https://github.com/space-wizards/bsdiff-rs/actions) -Rust port of a [bsdiff library](https://github.com/mendsley/bsdiff). High performance patching. All written in safe Rust. +Bsdiff is a method of diffing files. This crate is a port of a [bsdiff library](https://github.com/mendsley/bsdiff). +High performance patching. All written in safe +Rust. -## Diffing +It is usually a good idea to use bsdiff alongside a compression algorithm like bzip2. + +## Usage ```rust -let old = std::fs::read("old")?; -let new = std::fs::read("new")?; -let mut patch = Vec::new(); +fn main() { + let one = vec![1, 2, 3, 4, 5]; + let two = vec![1, 2, 4, 6]; + let mut patch = Vec::new(); + + bsdiff::diff(&one, &two, &mut patch).unwrap(); -bsdiff::diff(&old, &new, &mut patch)?; -// TODO: compress `patch` here -std::fs::write("patch", &patch)?; + let mut patched = Vec::with_capacity(two.len()); + bsdiff::patch(&one, &mut patch.as_slice(), &mut patched).unwrap(); + assert_eq!(patched, two); +} ``` -## Patching +## Diffing Files ```rust -let old = std::fs::read("old")?; -let patch = std::fs::read("patch")?; -// TODO: decompress `patch` here -let mut new = Vec::new(); +fn diff_files(file_a: &str, file_b: &str, patch_file: &str) -> std::io::Result<()> { + let old = std::fs::read(file_a)?; + let new = std::fs::read(file_b)?; + let mut patch = Vec::new(); + + bsdiff::diff(&old, &new, &mut patch)?; + // TODO: compress `patch` here + std::fs::write(patch_file, &patch) +} +``` -bsdiff::patch(&old, &mut patch.as_slice(), &mut new)?; -std::fs::write("new", &new)?; +## Patching Files + +```rust +fn patch_file(file_a: &str, patch_file: &str, file_b: &str) -> std::io::Result<()> { + let old = std::fs::read(file_a)?; + let patch = std::fs::read(patch_file)?; + // TODO: decompress `patch` here + let mut new = Vec::new(); + + bsdiff::patch(&old, &mut patch.as_slice(), &mut new)?; + std::fs::write(file_b, &new) +} ``` diff --git a/src/diff.rs b/src/diff.rs index a31add6..d584e6a 100644 --- a/src/diff.rs +++ b/src/diff.rs @@ -27,6 +27,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ +use std::cmp::Ordering; use std::io; use std::io::Write; @@ -101,15 +102,16 @@ fn split_internal( let mut k = 0; let mut i = start; while i < jj { - let v = V[usz(I[i] + h as isize)]; - if v < x { - i += 1; - } else if v == x { - I.swap(i, jj + j); - j += 1; - } else { - I.swap(i, kk + k); - k += 1; + match V[usz(I[i] + h as isize)].cmp(&x) { + Ordering::Less => i += 1, + Ordering::Equal => { + I.swap(i, jj + j); + j += 1; + } + Ordering::Greater => { + I.swap(i, kk + k); + k += 1; + } } } while jj + j < kk { @@ -210,14 +212,14 @@ fn matchlen(old: &[u8], new: &[u8]) -> usize { fn search(I: &[isize], old: &[u8], new: &[u8]) -> (isize, usize) { if I.len() < 3 { let x = matchlen(&old[usz(I[0])..], new); - let y = matchlen(&old[usz(I[I.len()-1])..], new); + let y = matchlen(&old[usz(I[I.len() - 1])..], new); if x > y { (I[0], x) } else { - (I[I.len()-1], y) + (I[I.len() - 1], y) } } else { - let mid = (I.len()-1) / 2; + let mid = (I.len() - 1) / 2; let left = &old[usz(I[mid])..]; let right = new; let len_to_check = left.len().min(right.len()); diff --git a/src/lib.rs b/src/lib.rs index 0d837ee..53744b3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,22 +1,4 @@ -//! Bsdiff is a method of diffing files. -//! This crate has been ported from C code. -//! The original code and more info can be found [here](https://github.com/mendsley/bsdiff). -//! -//! It is usually a good idea to use bsdiff alongside a compression algorithm like bzip2. -//! -//! # Example -//! -//! ``` -//! let one = vec![1, 2, 3, 4, 5]; -//! let two = vec![1, 2, 4, 6]; -//! let mut patch = Vec::new(); -//! -//! bsdiff::diff(&one, &two, &mut patch).unwrap(); -//! -//! let mut patched = Vec::with_capacity(two.len()); -//! bsdiff::patch(&one, &mut patch.as_slice(), &mut patched).unwrap(); -//! assert_eq!(patched, two); -//! ``` +#![doc = include_str!("../README.md")] mod diff; mod patch; diff --git a/src/patch.rs b/src/patch.rs index c993638..a49e4fd 100644 --- a/src/patch.rs +++ b/src/patch.rs @@ -32,7 +32,7 @@ use std::io; use std::io::Read; /// Apply a patch to an "old" file, returning the "new" file. -/// +/// /// `old` is the old file, `patch` will be read from with the patch,`new` is the buffer that will be written into. pub fn patch(old: &[u8], patch: &mut T, new: &mut Vec) -> io::Result<()> { let mut oldpos: usize = 0; @@ -40,7 +40,7 @@ pub fn patch(old: &[u8], patch: &mut T, new: &mut Vec) -> io::Resul // Read control data let mut buf = [0; 24]; if read_or_eof(patch, &mut buf)? { - return Ok(()) + return Ok(()); } // only seek can be negative @@ -99,7 +99,7 @@ fn read_or_eof(reader: &mut T, buf: &mut [u8; 24]) -> io::Result } else { Err(io::ErrorKind::UnexpectedEof.into()) } - }, + } Ok(n) => { if n >= tmp.len() { return Ok(false); @@ -116,9 +116,9 @@ fn read_or_eof(reader: &mut T, buf: &mut [u8; 24]) -> io::Result #[inline] fn offtin(buf: [u8; 8]) -> i64 { let y = i64::from_le_bytes(buf); - if 0 == y & (1<<63) { + if 0 == y & (1 << 63) { y } else { - -(y & !(1<<63)) + -(y & !(1 << 63)) } }