Performance enhancements to delta process

This commit is contained in:
Caelan Sayler
2025-05-13 20:22:32 +01:00
committed by Caelan
parent 53bf55cace
commit 5d99b7213b
4 changed files with 781 additions and 54 deletions

762
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -35,7 +35,6 @@ glob = "0.3"
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0" }
zip = { version = "2.6", default-features = false, features = ["deflate"] }
zip-extensions = "0.8.2"
thiserror = "2.0"
lazy_static = "1.5"
regex = "1.10"
@@ -85,7 +84,8 @@ core-foundation = "0.10"
core-foundation-sys = "0.8"
uuid = { version = "1.13.1", features = ["v4", "fast-rng", "macro-diagnostics"] }
walkdir = "2.5"
substring = " 1.4"
mtzip = "=4.0.2"
ripunzip = "2.0.2"
# default to small, optimized workspace release binaries
[profile.release]

View File

@@ -65,10 +65,10 @@ enum-flags.workspace = true
log-panics.workspace = true
zstd.workspace = true
zip.workspace = true
zip-extensions.workspace = true
walkdir.workspace = true
sha1_smol.workspace = true
substring.workspace = true
mtzip.workspace = true
ripunzip.workspace = true
[target.'cfg(target_os="linux")'.dependencies]
waitpid-any.workspace = true

View File

@@ -1,4 +1,6 @@
use anyhow::{anyhow, bail, Result};
use mtzip::level::CompressionLevel;
use ripunzip::{NullProgressReporter, UnzipEngine, UnzipOptions};
use std::os::windows::fs::MetadataExt;
use std::{
collections::HashSet,
@@ -6,8 +8,6 @@ use std::{
path::{Path, PathBuf},
};
use walkdir::WalkDir;
use zip::{write::SimpleFileOptions, CompressionMethod};
use zip_extensions::{zip_create_from_directory_with_options, zip_extract};
pub fn zstd_patch_single<P1: AsRef<Path>, P2: AsRef<Path>, P3: AsRef<Path>>(old_file: P1, patch_file: P2, output_file: P3) -> Result<()> {
let old_file = old_file.as_ref();
@@ -54,6 +54,22 @@ fn fio_highbit64(v: u64) -> u32 {
return count;
}
fn zip_extract<P1: AsRef<Path>, P2: AsRef<Path>>(archive_file: P1, target_dir: P2) -> Result<()> {
let target_dir = target_dir.as_ref().to_path_buf();
let file = fs::File::open(archive_file)?;
let engine = UnzipEngine::for_file(file)?;
let null_progress = Box::new(NullProgressReporter {});
let options = UnzipOptions {
filename_filter: None,
progress_reporter: null_progress,
output_directory: Some(target_dir),
password: None,
single_threaded: false,
};
engine.unzip(options)?;
Ok(())
}
pub fn delta<P1: AsRef<Path>, P2: AsRef<Path>, P3: AsRef<Path>>(
old_file: P1,
delta_files: Vec<&PathBuf>,
@@ -93,15 +109,7 @@ pub fn delta<P1: AsRef<Path>, P2: AsRef<Path>, P3: AsRef<Path>>(
fs::create_dir_all(&delta_dir)?;
zip_extract(delta_file, &delta_dir)?;
let delta_relative_paths: Vec<PathBuf> = WalkDir::new(&delta_dir)
.follow_links(false)
.into_iter()
.filter_map(|entry| entry.ok())
.filter(|entry| entry.file_type().is_file())
.map(|entry| entry.path().strip_prefix(&delta_dir).map(|p| p.to_path_buf()))
.filter_map(|entry| entry.ok())
.collect();
let delta_relative_paths = enumerate_files_relative(&delta_dir);
let mut visited_paths = HashSet::new();
// apply all the zsdiff patches for files which exist in both the delta and the base package
@@ -153,15 +161,7 @@ pub fn delta<P1: AsRef<Path>, P2: AsRef<Path>, P3: AsRef<Path>>(
}
// anything in the work dir which was not visited is an old / deleted file and should be removed
let workdir_relative_paths: Vec<PathBuf> = WalkDir::new(&work_dir)
.follow_links(false)
.into_iter()
.filter_map(|entry| entry.ok())
.filter(|entry| entry.file_type().is_file())
.map(|entry| entry.path().strip_prefix(&work_dir).map(|p| p.to_path_buf()))
.filter_map(|entry| entry.ok())
.collect();
let workdir_relative_paths = enumerate_files_relative(&work_dir);
for relative_path in &workdir_relative_paths {
if !visited_paths.contains(relative_path) {
let file_to_delete = work_dir.join(relative_path);
@@ -173,15 +173,32 @@ pub fn delta<P1: AsRef<Path>, P2: AsRef<Path>, P3: AsRef<Path>>(
info!("All delta patches applied. Asembling output package at: {}", output_file.to_string_lossy());
// NOTE: zstd is not supported by older versions of Squirrel/Velopack, but
// it's assumed if someone is using this code, they are using a recent enough version
let options = SimpleFileOptions::default().compression_method(CompressionMethod::Zstd);
zip_create_from_directory_with_options(&output_file, &work_dir, |_| options)?;
let mut zipper = mtzip::ZipArchive::new();
let workdir_relative_paths = enumerate_files_relative(&work_dir);
for relative_path in &workdir_relative_paths {
zipper
.add_file_from_fs(work_dir.join(&relative_path), relative_path.to_string_lossy().to_string())
.compression_level(CompressionLevel::fast())
.done();
}
let mut file = fs::File::create(&output_file)?;
zipper.write(&mut file)?;
info!("Successfully applied {} delta patches in {}s.", delta_files.len(), time.s());
Ok(())
}
fn enumerate_files_relative<P: AsRef<Path>>(dir: P) -> Vec<PathBuf> {
WalkDir::new(&dir)
.follow_links(false)
.into_iter()
.filter_map(|entry| entry.ok())
.filter(|entry| entry.file_type().is_file())
.map(|entry| entry.path().strip_prefix(&dir).map(|p| p.to_path_buf()))
.filter_map(|entry| entry.ok())
.collect()
}
// NOTE: this is some code to do checksum verification, but it is not being used
// by the current implementation because zstd patching already has checksum verification
//