From 342954cda0b88ccae2d0680d68abe32bd58bf4ff Mon Sep 17 00:00:00 2001 From: Ajeet D'Souza <98ajeet@gmail.com> Date: Sun, 5 Apr 2020 20:44:23 +0530 Subject: [PATCH] Performance improvements --- Cargo.lock | 14 +++++++------- Cargo.toml | 2 +- src/db.rs | 21 ++++++++++++--------- src/dir.rs | 2 +- src/subcommand/add.rs | 21 +++++++++++++-------- src/subcommand/init.rs | 2 -- src/subcommand/query.rs | 27 ++++++++++++++++----------- src/util.rs | 39 ++++++++++++++++++--------------------- 8 files changed, 68 insertions(+), 60 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a2e6934..67655d4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -49,7 +49,7 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -295,15 +295,15 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.105" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde_derive 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_derive" -version = "1.0.105" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", @@ -432,7 +432,7 @@ dependencies = [ "bstr 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", "dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", "structopt 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", "uuid 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -475,8 +475,8 @@ dependencies = [ "checksum redox_users 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431" "checksum regex-automata 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "ae1ded71d66a4a97f5e961fd0cb25a5f366a42a41570d16a763a69c092c26ae4" "checksum rust-argon2 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2bc8af4bda8e1ff4932523b94d3dd20ee30a87232323eda55903ffd71d2fb017" -"checksum serde 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)" = "e707fbbf255b8fc8c3b99abb91e7257a622caeb20a9818cbadbeeede4e0932ff" -"checksum serde_derive 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)" = "ac5d00fc561ba2724df6758a17de23df5914f20e41cb00f94d5b7ae42fffaff8" +"checksum serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)" = "36df6ac6412072f67cf767ebbde4133a5b2e88e76dc6187fa7104cd16f783399" +"checksum serde_derive 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)" = "9e549e3abf4fb8621bd1609f11dfc9f5e50320802273b12f3811a67e6716ea6c" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" "checksum structopt 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)" = "c8faa2719539bbe9d77869bfb15d4ee769f99525e707931452c97b693b3f159d" "checksum structopt-derive 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "3f88b8e18c69496aad6f9ddf4630dd7d585bcaf765786cb415b9aec2fe5a0430" diff --git a/Cargo.toml b/Cargo.toml index 8a04a67..a731381 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,7 +17,7 @@ anyhow = "1.0.28" bincode = "1.2.1" clap = "2.33.0" dirs = "2.0.2" -serde = { version = "1.0.105", features = ["derive"] } +serde = { version = "1.0.106", features = ["derive"] } structopt = "0.3.12" uuid = { version = "0.8.1", features = ["v4"] } diff --git a/src/db.rs b/src/db.rs index 9e7c126..e017fc8 100644 --- a/src/db.rs +++ b/src/db.rs @@ -121,7 +121,7 @@ impl DB { continue; }; - let split_line = line.rsplitn(3, '|').collect::>(); + let split_line = line.rsplitn(3, '|').collect::>(); match split_line.as_slice() { [epoch_str, rank_str, path_str] => { @@ -215,7 +215,7 @@ impl DB { Ok(()) } - pub fn query(&mut self, keywords: &[String], now: Epoch) -> Option { + pub fn query(&mut self, keywords: &[String], now: Epoch) -> Option<&Dir> { let (idx, dir, _) = self .data .dirs @@ -228,7 +228,8 @@ impl DB { })?; if dir.is_dir() { - Some(dir.to_owned()) + // FIXME: change this to Some(dir) once the MIR borrow checker comes to stable Rust + Some(&self.data.dirs[idx]) } else { self.data.dirs.swap_remove(idx); self.modified = true; @@ -236,7 +237,7 @@ impl DB { } } - pub fn query_all(&mut self, keywords: &[String]) -> Vec { + pub fn query_all<'a>(&'a mut self, keywords: &'a [String]) -> impl Iterator { let orig_len = self.data.dirs.len(); self.data.dirs.retain(Dir::is_dir); @@ -247,15 +248,17 @@ impl DB { self.data .dirs .iter() - .filter(|dir| dir.is_match(&keywords)) - .cloned() - .collect() + .filter(move |dir| dir.is_match(keywords)) } pub fn remove>(&mut self, path: P) -> Result<()> { + let path_canonicalized; let path_abs = match path.as_ref().canonicalize() { - Ok(path_abs) => path_abs, - Err(_) => path.as_ref().to_path_buf(), + Ok(path_abs) => { + path_canonicalized = path_abs; + &path_canonicalized + } + Err(_) => path.as_ref(), }; if let Some(idx) = self.data.dirs.iter().position(|dir| dir.path == path_abs) { diff --git a/src/dir.rs b/src/dir.rs index 96bfe81..efb4378 100644 --- a/src/dir.rs +++ b/src/dir.rs @@ -5,7 +5,7 @@ use std::path::{Path, PathBuf}; pub use f64 as Rank; pub use i64 as Epoch; // use a signed integer so subtraction can be performed on it -#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[derive(Debug, Default, Deserialize, Serialize)] pub struct Dir { pub path: PathBuf, pub rank: Rank, diff --git a/src/subcommand/add.rs b/src/subcommand/add.rs index c3c4b22..da46d38 100644 --- a/src/subcommand/add.rs +++ b/src/subcommand/add.rs @@ -15,20 +15,25 @@ pub struct Add { impl Add { pub fn run(&self) -> Result<()> { - let mut db = util::get_db()?; - let now = util::get_current_time()?; - let maxage = config::zo_maxage()?; - let excluded_dirs = config::zo_exclude_dirs(); - + let current_dir; let path = match &self.path { - Some(path) => path.clone(), - None => env::current_dir().context("unable to fetch current directory")?, + Some(path) => path, + None => { + current_dir = env::current_dir().context("unable to fetch current directory")?; + ¤t_dir + } }; - if excluded_dirs.contains(&path) { + let excluded_dirs = config::zo_exclude_dirs(); + if excluded_dirs.contains(path) { return Ok(()); } + let mut db = util::get_db()?; + + let maxage = config::zo_maxage()?; + let now = util::get_current_time()?; + db.add(path, maxage, now) } } diff --git a/src/subcommand/init.rs b/src/subcommand/init.rs index 1cd51d3..d51d16e 100644 --- a/src/subcommand/init.rs +++ b/src/subcommand/init.rs @@ -45,8 +45,6 @@ impl Init { let stdout = io::stdout(); let mut handle = stdout.lock(); - // If any `writeln!` call fails to write to stdout, we assume the user's - // computer is on fire and panic. let z = config.z; writeln!(handle, "{}", z(&self.z_cmd)).unwrap(); diff --git a/src/subcommand/query.rs b/src/subcommand/query.rs index 06e0713..ac4c5bc 100644 --- a/src/subcommand/query.rs +++ b/src/subcommand/query.rs @@ -15,7 +15,7 @@ pub struct Query { } impl Query { - pub fn run(mut self) -> Result<()> { + pub fn run(&self) -> Result<()> { let path_opt = if self.interactive { self.query_interactive()? } else { @@ -35,7 +35,7 @@ impl Query { Ok(()) } - fn query(&mut self) -> Result>> { + fn query(&self) -> Result>> { if let [path] = self.keywords.as_slice() { if Path::new(path).is_dir() { return Ok(Some(path.as_bytes().to_vec())); @@ -44,11 +44,13 @@ impl Query { let now = util::get_current_time()?; - for keyword in &mut self.keywords { - *keyword = keyword.to_lowercase(); - } + let keywords = self + .keywords + .iter() + .map(|keyword| keyword.to_lowercase()) + .collect::>(); - let path_opt = util::get_db()?.query(&self.keywords, now).map(|dir| { + let path_opt = util::get_db()?.query(&keywords, now).map(|dir| { // `path_to_bytes` is guaranteed to succeed here since // the path has already been queried successfully let path_bytes = util::path_to_bytes(&dir.path).unwrap(); @@ -58,14 +60,17 @@ impl Query { Ok(path_opt) } - fn query_interactive(&mut self) -> Result>> { + fn query_interactive(&self) -> Result>> { let now = util::get_current_time()?; - for keyword in &mut self.keywords { - *keyword = keyword.to_lowercase(); - } + let keywords = self + .keywords + .iter() + .map(|keyword| keyword.to_lowercase()) + .collect::>(); - let dirs = util::get_db()?.query_all(&self.keywords); + let mut db = util::get_db()?; + let dirs = db.query_all(&keywords); util::fzf_helper(now, dirs) } } diff --git a/src/util.rs b/src/util.rs index 74614d5..ade9f43 100644 --- a/src/util.rs +++ b/src/util.rs @@ -4,7 +4,7 @@ use crate::dir::{Dir, Epoch}; use anyhow::{anyhow, bail, Context, Result}; -use std::cmp::{Ordering, PartialOrd}; +use std::cmp::Reverse; use std::io::{Read, Write}; use std::path::Path; use std::process::{Command, Stdio}; @@ -49,9 +49,12 @@ pub fn get_current_time() -> Result { Ok(current_time as Epoch) } -pub fn fzf_helper(now: Epoch, mut dirs: Vec) -> Result>> { +pub fn fzf_helper<'a, I>(now: Epoch, dirs: I) -> Result>> +where + I: IntoIterator, +{ let mut fzf = Command::new("fzf") - .arg("-n2..") + .args(&["-n2..", "--no-sort"]) .stdin(Stdio::piped()) .stdout(Stdio::piped()) .spawn() @@ -62,26 +65,20 @@ pub fn fzf_helper(now: Epoch, mut dirs: Vec) -> Result>> { .as_mut() .ok_or_else(|| anyhow!("could not connect to fzf stdin"))?; - for dir in dirs.iter_mut() { - dir.rank = dir.get_frecency(now); - } + let mut dir_frecencies = dirs + .into_iter() + .map(|dir| (dir, clamp(dir.get_frecency(now), 0.0, 9999.0) as i32)) + .collect::>(); - dirs.sort_unstable_by(|dir1, dir2| { - dir1.rank - .partial_cmp(&dir2.rank) - .unwrap_or(Ordering::Equal) - .reverse() - }); + dir_frecencies.sort_unstable_by_key(|&(dir, frecency)| Reverse((frecency, &dir.path))); - for dir in dirs.iter() { + for &(dir, frecency) in dir_frecencies.iter() { // ensure that frecency fits in 4 characters - let frecency = clamp(dir.rank, 0.0, 9999.0); - if let Some(path_bytes) = path_to_bytes(&dir.path) { (|| { - write!(fzf_stdin, "{:>4.0} ", frecency)?; + write!(fzf_stdin, "{:>4} ", frecency)?; fzf_stdin.write_all(path_bytes)?; - fzf_stdin.write_all(b"\n") + writeln!(fzf_stdin) })() .context("could not write into fzf stdin")?; } @@ -125,11 +122,11 @@ pub fn fzf_helper(now: Epoch, mut dirs: Vec) -> Result>> { pub fn clamp(val: f64, min: f64, max: f64) -> f64 { assert!(min <= max); - if val < min { - min - } else if val > max { + if val > max { max - } else { + } else if val > min { val + } else { + min } }