@@ -122,17 +122,30 @@ impl Atom {
122
122
normalize = false ;
123
123
}
124
124
let needle = if needle. is_ascii ( ) {
125
- let mut needle = if escape_whitespace {
126
- if let Some ( ( start, rem) ) = needle. split_once ( "\\ " ) {
127
- let mut needle = start. to_owned ( ) ;
128
- for rem in rem. split ( "\\ " ) {
129
- needle. push ( ' ' ) ;
130
- needle. push_str ( rem) ;
125
+ let mut needle_string = if escape_whitespace {
126
+ let mut needle_bytes = Vec :: with_capacity ( needle. len ( ) ) ;
127
+ let mut saw_backslash = false ;
128
+ for c in needle. bytes ( ) {
129
+ if saw_backslash {
130
+ if c. is_ascii_whitespace ( ) {
131
+ needle_bytes. push ( c) ;
132
+ saw_backslash = false ;
133
+ continue ;
134
+ } else {
135
+ needle_bytes. push ( b'\\' ) ;
136
+ }
131
137
}
132
- needle
133
- } else {
134
- needle. to_owned ( )
138
+ saw_backslash = c == b'\\' ;
139
+ if !saw_backslash {
140
+ needle_bytes. push ( c) ;
141
+ }
142
+ }
143
+ // push the potentially trailing backslash
144
+ if saw_backslash {
145
+ needle_bytes. push ( b'\\' ) ;
135
146
}
147
+ // SAFETY: we just checked that needle is ascii, so each `c` is a valid ASCII byte
148
+ unsafe { String :: from_utf8_unchecked ( needle_bytes) }
136
149
} else {
137
150
needle. to_owned ( )
138
151
} ;
@@ -141,18 +154,19 @@ impl Atom {
141
154
#[ cfg( feature = "unicode-casefold" ) ]
142
155
CaseMatching :: Ignore => {
143
156
ignore_case = true ;
144
- needle . make_ascii_lowercase ( )
157
+ needle_string . make_ascii_lowercase ( )
145
158
}
146
159
#[ cfg( feature = "unicode-casefold" ) ]
147
160
CaseMatching :: Smart => {
148
- ignore_case = !needle . bytes ( ) . any ( |b| b. is_ascii_uppercase ( ) )
161
+ ignore_case = !needle_string . bytes ( ) . any ( |b| b. is_ascii_uppercase ( ) )
149
162
}
150
163
CaseMatching :: Respect => ignore_case = false ,
151
164
}
165
+
152
166
if append_dollar {
153
- needle . push ( '$' ) ;
167
+ needle_string . push ( '$' ) ;
154
168
}
155
- Utf32String :: Ascii ( needle . into_boxed_str ( ) )
169
+ Utf32String :: Ascii ( needle_string . into_boxed_str ( ) )
156
170
} else {
157
171
let mut needle_ = Vec :: with_capacity ( needle. len ( ) ) ;
158
172
#[ cfg( feature = "unicode-casefold" ) ]
@@ -171,32 +185,38 @@ impl Atom {
171
185
let mut saw_backslash = false ;
172
186
for mut c in chars:: graphemes ( needle) {
173
187
if saw_backslash {
174
- if c == ' ' {
175
- needle_. push ( ' ' ) ;
188
+ if c. is_whitespace ( ) {
189
+ needle_. push ( c ) ;
176
190
saw_backslash = false ;
177
191
continue ;
178
192
} else {
179
193
needle_. push ( '\\' ) ;
180
194
}
181
195
}
182
196
saw_backslash = c == '\\' ;
183
- match case {
184
- #[ cfg( feature = "unicode-casefold" ) ]
185
- CaseMatching :: Ignore => c = chars:: to_lower_case ( c) ,
186
- #[ cfg( feature = "unicode-casefold" ) ]
187
- CaseMatching :: Smart => {
188
- ignore_case = ignore_case && !chars:: is_upper_case ( c)
197
+ if !saw_backslash {
198
+ match case {
199
+ #[ cfg( feature = "unicode-casefold" ) ]
200
+ CaseMatching :: Ignore => c = chars:: to_lower_case ( c) ,
201
+ #[ cfg( feature = "unicode-casefold" ) ]
202
+ CaseMatching :: Smart => {
203
+ ignore_case = ignore_case && !chars:: is_upper_case ( c)
204
+ }
205
+ CaseMatching :: Respect => ( ) ,
189
206
}
190
- CaseMatching :: Respect => ( ) ,
191
- }
192
- match normalization {
193
- # [ cfg ( feature = "unicode-normalization" ) ]
194
- Normalization :: Smart => {
195
- normalize = normalize && chars :: normalize ( c ) == c ;
207
+ match normalization {
208
+ # [ cfg ( feature = "unicode-normalization" ) ]
209
+ Normalization :: Smart => {
210
+ normalize = normalize && chars :: normalize ( c ) == c ;
211
+ }
212
+ Normalization :: Never => ( ) ,
196
213
}
197
- Normalization :: Never => ( ) ,
214
+ needle_ . push ( c ) ;
198
215
}
199
- needle_. push ( c) ;
216
+ }
217
+ // push the potentially trailing backslash
218
+ if saw_backslash {
219
+ needle_. push ( '\\' ) ;
200
220
}
201
221
} else {
202
222
let chars = chars:: graphemes ( needle) . map ( |mut c| {
0 commit comments