Skip to content

Commit bfcd9a4

Browse files
authored
Optimize memory allocations (#147)
Remove the redundant pattern joining, which can reduce memory allocations during the iteration.
1 parent e78862d commit bfcd9a4

File tree

1 file changed

+25
-18
lines changed

1 file changed

+25
-18
lines changed

src/lib.rs

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ pub fn glob_with(pattern: &str, options: MatchOptions) -> Result<Paths, PatternE
259259
original: "".to_string(),
260260
tokens: Vec::new(),
261261
is_recursive: false,
262+
has_metachars: false,
262263
});
263264
}
264265

@@ -552,6 +553,9 @@ pub struct Pattern {
552553
original: String,
553554
tokens: Vec<PatternToken>,
554555
is_recursive: bool,
556+
/// A bool value that indicates whether the pattern contains any metacharacters.
557+
/// We use this information for some fast path optimizations.
558+
has_metachars: bool,
555559
}
556560

557561
/// Show the original glob pattern.
@@ -605,15 +609,19 @@ impl Pattern {
605609
let chars = pattern.chars().collect::<Vec<_>>();
606610
let mut tokens = Vec::new();
607611
let mut is_recursive = false;
612+
let mut has_metachars = false;
608613
let mut i = 0;
609614

610615
while i < chars.len() {
611616
match chars[i] {
612617
'?' => {
618+
has_metachars = true;
613619
tokens.push(AnyChar);
614620
i += 1;
615621
}
616622
'*' => {
623+
has_metachars = true;
624+
617625
let old = i;
618626

619627
while i < chars.len() && chars[i] == '*' {
@@ -675,6 +683,8 @@ impl Pattern {
675683
}
676684
}
677685
'[' => {
686+
has_metachars = true;
687+
678688
if i + 4 <= chars.len() && chars[i + 1] == '!' {
679689
match chars[i + 3..].iter().position(|x| *x == ']') {
680690
None => (),
@@ -715,6 +725,7 @@ impl Pattern {
715725
tokens,
716726
original: pattern.to_string(),
717727
is_recursive,
728+
has_metachars,
718729
})
719730
}
720731

@@ -878,19 +889,6 @@ fn fill_todo(
878889
path: &PathWrapper,
879890
options: MatchOptions,
880891
) {
881-
// convert a pattern that's just many Char(_) to a string
882-
fn pattern_as_str(pattern: &Pattern) -> Option<String> {
883-
let mut s = String::new();
884-
for token in &pattern.tokens {
885-
match *token {
886-
Char(c) => s.push(c),
887-
_ => return None,
888-
}
889-
}
890-
891-
Some(s)
892-
}
893-
894892
let add = |todo: &mut Vec<_>, next_path: PathWrapper| {
895893
if idx + 1 == patterns.len() {
896894
// We know it's good, so don't make the iterator match this path
@@ -905,8 +903,17 @@ fn fill_todo(
905903
let pattern = &patterns[idx];
906904
let is_dir = path.is_directory;
907905
let curdir = path.as_ref() == Path::new(".");
908-
match pattern_as_str(pattern) {
909-
Some(s) => {
906+
match (pattern.has_metachars, is_dir) {
907+
(false, _) => {
908+
debug_assert!(
909+
pattern
910+
.tokens
911+
.iter()
912+
.all(|tok| matches!(tok, PatternToken::Char(_))),
913+
"broken invariant: pattern has metachars but shouldn't"
914+
);
915+
let s = pattern.as_str();
916+
910917
// This pattern component doesn't have any metacharacters, so we
911918
// don't need to read the current directory to know where to
912919
// continue. So instead of passing control back to the iterator,
@@ -916,7 +923,7 @@ fn fill_todo(
916923
let next_path = if curdir {
917924
PathBuf::from(s)
918925
} else {
919-
path.join(&s)
926+
path.join(s)
920927
};
921928
let next_path = PathWrapper::from_path(next_path);
922929
if (special && is_dir)
@@ -927,7 +934,7 @@ fn fill_todo(
927934
add(todo, next_path);
928935
}
929936
}
930-
None if is_dir => {
937+
(true, true) => {
931938
let dirs = fs::read_dir(path).and_then(|d| {
932939
d.map(|e| {
933940
e.map(|e| {
@@ -971,7 +978,7 @@ fn fill_todo(
971978
}
972979
}
973980
}
974-
None => {
981+
(true, false) => {
975982
// not a directory, nothing more to find
976983
}
977984
}

0 commit comments

Comments
 (0)