From 1d66de713feb6aa701851db87cac01d541baad5e Mon Sep 17 00:00:00 2001 From: Cyandev Date: Sat, 2 Aug 2025 02:22:24 +0800 Subject: [PATCH 1/3] Optimize memory allocations --- src/lib.rs | 32 +++++++++++++++----------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 102cfca..673b141 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -258,6 +258,7 @@ pub fn glob_with(pattern: &str, options: MatchOptions) -> Result, is_recursive: bool, + is_regular: bool, } /// Show the original glob pattern. @@ -604,15 +606,19 @@ impl Pattern { let chars = pattern.chars().collect::>(); let mut tokens = Vec::new(); let mut is_recursive = false; + let mut is_regular = true; let mut i = 0; while i < chars.len() { match chars[i] { '?' => { + is_regular = false; tokens.push(AnyChar); i += 1; } '*' => { + is_regular = false; + let old = i; while i < chars.len() && chars[i] == '*' { @@ -674,6 +680,8 @@ impl Pattern { } } '[' => { + is_regular = false; + if i + 4 <= chars.len() && chars[i + 1] == '!' { match chars[i + 3..].iter().position(|x| *x == ']') { None => (), @@ -714,6 +722,7 @@ impl Pattern { tokens, original: pattern.to_string(), is_recursive, + is_regular, }) } @@ -877,19 +886,6 @@ fn fill_todo( path: &PathWrapper, options: MatchOptions, ) { - // convert a pattern that's just many Char(_) to a string - fn pattern_as_str(pattern: &Pattern) -> Option { - let mut s = String::new(); - for token in &pattern.tokens { - match *token { - Char(c) => s.push(c), - _ => return None, - } - } - - Some(s) - } - let add = |todo: &mut Vec<_>, next_path: PathWrapper| { if idx + 1 == patterns.len() { // We know it's good, so don't make the iterator match this path @@ -904,8 +900,10 @@ fn fill_todo( let pattern = &patterns[idx]; let is_dir = path.is_directory; let curdir = path.as_ref() == Path::new("."); - match pattern_as_str(pattern) { - Some(s) => { + match (pattern.is_regular, is_dir) { + (true, _) => { + let s = pattern.as_str(); + // This pattern component doesn't have any metacharacters, so we // don't need to read the current directory to know where to // continue. So instead of passing control back to the iterator, @@ -926,7 +924,7 @@ fn fill_todo( add(todo, next_path); } } - None if is_dir => { + (false, true) => { let dirs = fs::read_dir(path).and_then(|d| { d.map(|e| { e.map(|e| { @@ -970,7 +968,7 @@ fn fill_todo( } } } - None => { + _ => { // not a directory, nothing more to find } } From 257f2798c0ac86c86a33321599b74e02aaeb6a18 Mon Sep 17 00:00:00 2001 From: Cyandev Date: Sat, 2 Aug 2025 02:27:39 +0800 Subject: [PATCH 2/3] Fix clippy issues --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 673b141..d98b61e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -913,7 +913,7 @@ fn fill_todo( let next_path = if curdir { PathBuf::from(s) } else { - path.join(&s) + path.join(s) }; let next_path = PathWrapper::from_path(next_path); if (special && is_dir) From 23cf8752a3b1efb1a7e7c912ec3304c914a7b635 Mon Sep 17 00:00:00 2001 From: Cyandev Date: Mon, 11 Aug 2025 16:02:18 +0800 Subject: [PATCH 3/3] Fix code style --- src/lib.rs | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d98b61e..12d4c83 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -258,7 +258,7 @@ pub fn glob_with(pattern: &str, options: MatchOptions) -> Result, is_recursive: bool, - is_regular: bool, + /// A bool value that indicates whether the pattern contains any metacharacters. + /// We use this information for some fast path optimizations. + has_metachars: bool, } /// Show the original glob pattern. @@ -606,18 +608,18 @@ impl Pattern { let chars = pattern.chars().collect::>(); let mut tokens = Vec::new(); let mut is_recursive = false; - let mut is_regular = true; + let mut has_metachars = false; let mut i = 0; while i < chars.len() { match chars[i] { '?' => { - is_regular = false; + has_metachars = true; tokens.push(AnyChar); i += 1; } '*' => { - is_regular = false; + has_metachars = true; let old = i; @@ -680,7 +682,7 @@ impl Pattern { } } '[' => { - is_regular = false; + has_metachars = true; if i + 4 <= chars.len() && chars[i + 1] == '!' { match chars[i + 3..].iter().position(|x| *x == ']') { @@ -722,7 +724,7 @@ impl Pattern { tokens, original: pattern.to_string(), is_recursive, - is_regular, + has_metachars, }) } @@ -900,8 +902,15 @@ fn fill_todo( let pattern = &patterns[idx]; let is_dir = path.is_directory; let curdir = path.as_ref() == Path::new("."); - match (pattern.is_regular, is_dir) { - (true, _) => { + match (pattern.has_metachars, is_dir) { + (false, _) => { + debug_assert!( + pattern + .tokens + .iter() + .all(|tok| matches!(tok, PatternToken::Char(_))), + "broken invariant: pattern has metachars but shouldn't" + ); let s = pattern.as_str(); // This pattern component doesn't have any metacharacters, so we @@ -924,7 +933,7 @@ fn fill_todo( add(todo, next_path); } } - (false, true) => { + (true, true) => { let dirs = fs::read_dir(path).and_then(|d| { d.map(|e| { e.map(|e| { @@ -968,7 +977,7 @@ fn fill_todo( } } } - _ => { + (true, false) => { // not a directory, nothing more to find } }