@@ -56,7 +56,9 @@ impl Pattern {
5656
5757 /// Check if a string matches this pattern
5858 pub fn matches ( & self , text : & str ) -> bool {
59- match_tokens ( & self . tokens , text)
59+ // Pre-collect chars once to avoid repeated allocations in recursive calls
60+ let text_chars: Vec < char > = text. chars ( ) . collect ( ) ;
61+ match_tokens_with_chars ( & self . tokens , & text_chars, 0 )
6062 }
6163}
6264
@@ -132,59 +134,54 @@ fn parse_char_class(chars: &mut std::iter::Peekable<std::str::Chars>) -> PaxResu
132134 Err ( PaxError :: PatternError ( "unclosed bracket" . to_string ( ) ) )
133135}
134136
135- /// Match tokens against text
136- fn match_tokens ( tokens : & [ Token ] , text : & str ) -> bool {
137- match_tokens_at ( tokens, text, 0 )
138- }
139-
140- /// Recursive matching with position tracking
141- fn match_tokens_at ( tokens : & [ Token ] , text : & str , pos : usize ) -> bool {
137+ /// Recursive matching with position tracking using pre-collected chars
138+ fn match_tokens_with_chars ( tokens : & [ Token ] , text_chars : & [ char ] , pos : usize ) -> bool {
142139 if tokens. is_empty ( ) {
143- return pos == text . len ( ) ;
140+ return pos == text_chars . len ( ) ;
144141 }
145142
146- let text_chars: Vec < char > = text. chars ( ) . collect ( ) ;
147-
148143 match & tokens[ 0 ] {
149144 Token :: Char ( c) => {
150145 if pos < text_chars. len ( ) && text_chars[ pos] == * c {
151- match_tokens_at ( & tokens[ 1 ..] , text , pos + 1 )
146+ match_tokens_with_chars ( & tokens[ 1 ..] , text_chars , pos + 1 )
152147 } else {
153148 false
154149 }
155150 }
156151 Token :: Any => {
157152 // ? matches any single character except /
158153 if pos < text_chars. len ( ) && text_chars[ pos] != '/' {
159- match_tokens_at ( & tokens[ 1 ..] , text , pos + 1 )
154+ match_tokens_with_chars ( & tokens[ 1 ..] , text_chars , pos + 1 )
160155 } else {
161156 false
162157 }
163158 }
164159 Token :: Star => {
165160 // * matches any sequence except /
166- match_star ( & tokens[ 1 ..] , text , pos)
161+ match_star_with_chars ( & tokens[ 1 ..] , text_chars , pos)
167162 }
168163 Token :: Class ( class) => {
169164 if pos < text_chars. len ( ) && class_matches ( class, text_chars[ pos] ) {
170- match_tokens_at ( & tokens[ 1 ..] , text , pos + 1 )
165+ match_tokens_with_chars ( & tokens[ 1 ..] , text_chars , pos + 1 )
171166 } else {
172167 false
173168 }
174169 }
175170 }
176171}
177172
178- /// Handle star matching (greedy with backtracking)
173+ /// Handle star matching (greedy with backtracking) using pre-collected chars
179174/// Star matches any sequence except /
180- #[ allow( clippy:: needless_range_loop) ]
181- fn match_star ( remaining_tokens : & [ Token ] , text : & str , start_pos : usize ) -> bool {
182- let text_chars: Vec < char > = text. chars ( ) . collect ( ) ;
175+ fn match_star_with_chars (
176+ remaining_tokens : & [ Token ] ,
177+ text_chars : & [ char ] ,
178+ start_pos : usize ,
179+ ) -> bool {
183180 let text_len = text_chars. len ( ) ;
184181
185182 // Try matching zero or more characters (but not /)
186183 for pos in start_pos..=text_len {
187- if match_tokens_at ( remaining_tokens, text , pos) {
184+ if match_tokens_with_chars ( remaining_tokens, text_chars , pos) {
188185 return true ;
189186 }
190187
0 commit comments