From e06189e654890235d97e20664aafd08acd102564 Mon Sep 17 00:00:00 2001 From: sharkdp Date: Sun, 11 Jun 2017 22:43:30 +0200 Subject: [PATCH] Initial version of parallel directory traversal see #32 --- src/main.rs | 70 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/src/main.rs b/src/main.rs index 301d5c7..e7320c8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -17,6 +17,7 @@ use std::ops::Deref; use std::os::unix::fs::PermissionsExt; use std::path::{Path, Component}; use std::process; +use std::sync::Arc; use clap::{App, AppSettings, Arg}; use atty::Stream; @@ -93,6 +94,9 @@ fn print_entry(base: &Path, entry: &Path, config: &FdOptions) { #[cfg(not(target_family = "unix"))] let is_executable = |p: &std::path::PathBuf| {false}; + let stdout = std::io::stdout(); + let mut handle = stdout.lock(); + if let Some(ref ls_colors) = config.ls_colors { let default_style = ansi_term::Style::default(); @@ -139,17 +143,17 @@ fn print_entry(base: &Path, entry: &Path, config: &FdOptions) { } }; - print!("{}", style.paint(comp_str)); + write!(handle, "{}", style.paint(comp_str)); if component_path.is_dir() && component_path != path_full { let sep = std::path::MAIN_SEPARATOR.to_string(); - print!("{}", style.paint(sep)); + write!(handle, "{}", style.paint(sep)); } } if config.null_separator { - print!("{}", '\0'); + writeln!(handle, "{}", '\0'); } else { - println!(); + writeln!(handle); } } else { // Uncolorized output @@ -157,7 +161,7 @@ fn print_entry(base: &Path, entry: &Path, config: &FdOptions) { let prefix = if config.path_display == PathDisplay::Absolute { ROOT_DIR } else { "" }; let separator = if config.null_separator { "\0" } else { "\n" }; - let r = write!(&mut std::io::stdout(), "{}{}{}", prefix, path_str, separator); + let r = writeln!(&mut std::io::stdout(), "{}{}{}", prefix, path_str, separator); if r.is_err() { // Probably a broken pipe. Exit gracefully. @@ -167,7 +171,7 @@ fn print_entry(base: &Path, entry: &Path, config: &FdOptions) { } /// Recursively scan the given search path and search for files / pathnames matching the pattern. -fn scan(root: &Path, pattern: &Regex, base: &Path, config: &FdOptions) { +fn scan(root: &Path, pattern: Arc, base: &Path, config: Arc) { let walker = WalkBuilder::new(root) .hidden(config.ignore_hidden) .ignore(config.read_ignore) @@ -177,31 +181,41 @@ fn scan(root: &Path, pattern: &Regex, base: &Path, config: &FdOptions) { .git_exclude(config.read_ignore) .follow_links(config.follow_links) .max_depth(config.max_depth) - .build() - .into_iter() - .filter_map(|e| e.ok()) - .filter(|e| e.path() != root); + .build_parallel(); - for entry in walker { - let path_rel_buf = match fshelper::path_relative_from(entry.path(), base) { - Some(p) => p, - None => error("Error: could not get relative path for directory entry.") - }; - let path_rel = path_rel_buf.as_path(); + walker.run(|| { + let base = base.to_owned(); + let config = config.clone(); + let pattern = pattern.clone(); - let search_str_o = - if config.search_full_path { - Some(path_rel.to_string_lossy()) - } else { - path_rel.file_name() - .map(|f| f.to_string_lossy()) + Box::new(move |entry_o| { + let entry = match entry_o { + Ok(e) => e, + Err(_) => return ignore::WalkState::Continue }; - if let Some(search_str) = search_str_o { - pattern.find(&*search_str) - .map(|_| print_entry(base, path_rel, config)); - } - } + let path_rel_buf = match fshelper::path_relative_from(entry.path(), &*base) { + Some(p) => p, + None => error("Error: could not get relative path for directory entry.") + }; + let path_rel = path_rel_buf.as_path(); + + let search_str_o = + if config.search_full_path { + Some(path_rel.to_string_lossy()) + } else { + path_rel.file_name() + .map(|f| f.to_string_lossy()) + }; + + if let Some(search_str) = search_str_o { + pattern.find(&*search_str) + .map(|_| print_entry(&base, path_rel, &config)); + } + + ignore::WalkState::Continue + }) + }); } /// Print error message to stderr and exit with status `1`. @@ -338,7 +352,7 @@ fn main() { match RegexBuilder::new(pattern) .case_insensitive(!config.case_sensitive) .build() { - Ok(re) => scan(root_dir, &re, base, &config), + Ok(re) => scan(root_dir, Arc::new(re), base, Arc::new(config)), Err(err) => error(err.description()) } }