Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
fasterthanlime committed Feb 2, 2024
1 parent f0d34fe commit 58e92dd
Show file tree
Hide file tree
Showing 3 changed files with 124 additions and 4 deletions.
120 changes: 119 additions & 1 deletion rc-zip-sync/examples/jean.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use cfg_if::cfg_if;
use clap::{Parser, Subcommand};
use humansize::{format_size, BINARY};
use rc_zip::parse::{Archive, EntryContents, Method, Version};
use rc_zip_sync::ReadZip;
use rc_zip_sync::{ReadZip, ReadZipEntriesStreaming};

use std::{
borrow::Cow,
Expand Down Expand Up @@ -62,6 +62,12 @@ enum Commands {
Unzip {
zipfile: PathBuf,

#[arg(long)]
dir: Option<String>,
},
UnzipStreaming {
zipfile: PathBuf,

#[arg(long)]
dir: Option<String>,
},
Expand Down Expand Up @@ -294,6 +300,118 @@ fn do_main(cli: Cli) -> Result<(), Box<dyn std::error::Error>> {
let bps = (uncompressed_size as f64 / seconds) as u64;
println!("Overall extraction speed: {} / s", format_size(bps, BINARY));
}
Commands::UnzipStreaming { zipfile, dir } => {
let zipfile = File::open(zipfile)?;
let dir = PathBuf::from(dir.unwrap_or_else(|| ".".into()));

let mut entry = zipfile.read_first_zip_entry_streaming()?;

let mut num_dirs = 0;
let mut num_files = 0;
let mut num_symlinks = 0;
let mut done_bytes: u64 = 0;

use indicatif::{ProgressBar, ProgressStyle};
let pbar = ProgressBar::new(100);
pbar.set_style(
ProgressStyle::default_bar()
.template("{eta_precise} [{bar:20.cyan/blue}] {wide_msg}")
.unwrap()
.progress_chars("=>-"),
);

pbar.enable_steady_tick(Duration::from_millis(125));

let start_time = std::time::SystemTime::now();
loop {
let entry_name = entry.name().unwrap();
let entry_name = match sanitize_entry_name(entry_name) {
Some(name) => name,
None => continue,
};

pbar.set_message(entry_name.to_string());
match entry.contents() {
EntryContents::Symlink => {
num_symlinks += 1;

cfg_if! {
if #[cfg(windows)] {
let path = dir.join(entry_name);
std::fs::create_dir_all(
path.parent()
.expect("all full entry paths should have parent paths"),
)?;
let mut entry_writer = File::create(path)?;
let mut entry_reader = entry.reader();
std::io::copy(&mut entry_reader, &mut entry_writer)?;
} else {
let path = dir.join(entry_name);
std::fs::create_dir_all(
path.parent()
.expect("all full entry paths should have parent paths"),
)?;
if let Ok(metadata) = std::fs::symlink_metadata(&path) {
if metadata.is_file() {
std::fs::remove_file(&path)?;
}
}

let mut src = String::new();
entry.reader().read_to_string(&mut src)?;

// validate pointing path before creating a symbolic link
if src.contains("..") {
continue;
}
std::os::unix::fs::symlink(src, &path)?;
}
}
}
EntryContents::Directory => {
num_dirs += 1;
let path = dir.join(entry_name);
std::fs::create_dir_all(
path.parent()
.expect("all full entry paths should have parent paths"),
)?;
}
EntryContents::File => {
num_files += 1;
let path = dir.join(entry_name);
std::fs::create_dir_all(
path.parent()
.expect("all full entry paths should have parent paths"),
)?;
let mut entry_writer = File::create(path)?;
let entry_reader = entry.reader();
let before_entry_bytes = done_bytes;
let mut progress_reader = ProgressRead::new(
entry_reader,
entry.inner.uncompressed_size,
|prog| {
pbar.set_position(before_entry_bytes + prog.done);
},
);

let copied_bytes = std::io::copy(&mut progress_reader, &mut entry_writer)?;
done_bytes = before_entry_bytes + copied_bytes;
}
}
}
pbar.finish();
let duration = start_time.elapsed()?;
println!(
"Extracted {} (in {} files, {} dirs, {} symlinks)",
format_size(uncompressed_size, BINARY),
num_files,
num_dirs,
num_symlinks
);
let seconds = (duration.as_millis() as f64) / 1000.0;
let bps = (uncompressed_size as f64 / seconds) as u64;
println!("Overall extraction speed: {} / s", format_size(bps, BINARY));
}
}

Ok(())
Expand Down
4 changes: 3 additions & 1 deletion rc-zip-sync/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,6 @@ mod streaming_entry_reader;

// re-exports
pub use rc_zip;
pub use read_zip::{HasCursor, ReadZip, ReadZipWithSize, SyncArchive, SyncStoredEntry};
pub use read_zip::{
HasCursor, ReadZip, ReadZipEntriesStreaming, ReadZipWithSize, SyncArchive, SyncStoredEntry,
};
4 changes: 2 additions & 2 deletions rc-zip-sync/src/read_zip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,14 +226,14 @@ pub trait ReadZipEntriesStreaming<R>
where
R: Read,
{
fn first_entry(self) -> Result<StreamingEntryReader<R>, Error>;
fn read_first_zip_entry_streaming(self) -> Result<StreamingEntryReader<R>, Error>;
}

impl<R> ReadZipEntriesStreaming<R> for R
where
R: Read,
{
fn first_entry(mut self) -> Result<StreamingEntryReader<Self>, Error> {
fn read_first_zip_entry_streaming(mut self) -> Result<StreamingEntryReader<Self>, Error> {
// first, get enough data to read the first local file header
let mut buf = oval::Buffer::with_capacity(16 * 1024);

Expand Down

0 comments on commit 58e92dd

Please sign in to comment.