1#![allow(unused)]
8use crate::State;
9use regex::Regex;
10
11pub trait MatchTarget<'a> {
16 fn get_input(&self) -> &'a str;
18 fn clear_ranges(&mut self);
20 fn push_range(&mut self, start: usize, end: usize);
22}
23
24impl<'a> MatchTarget<'a> for &'a str {
26 #[inline]
27 fn get_input(&self) -> &'a str {
28 self
29 }
30 #[inline]
31 fn clear_ranges(&mut self) {}
32 #[inline]
33 fn push_range(&mut self, _start: usize, _end: usize) {}
34}
35
36impl<'a> MatchTarget<'a> for &mut State<'a> {
38 #[inline]
39 fn get_input(&self) -> &'a str {
40 self.input
41 }
42 #[inline]
43 fn clear_ranges(&mut self) {
44 self.ranges.clear();
45 }
46 #[inline]
47 fn push_range(&mut self, start: usize, end: usize) {
48 self.ranges.push((start, end));
49 }
50}
51
52#[inline]
61pub fn has_keyword<'a>(mut target: impl MatchTarget<'a>, keyword: &str, ignore_case: bool) -> bool {
62 target.clear_ranges();
63 let input = target.get_input();
64 let k_len = keyword.len();
65 if k_len == 0 || input.len() < k_len {
66 return false;
67 }
68
69 let mut found = false;
70
71 if !ignore_case {
72 let mut start = 0;
73 while let Some(idx) = input[start..].find(keyword) {
74 let abs_idx = start + idx;
75 target.push_range(abs_idx, abs_idx + k_len);
76 found = true;
77 start = abs_idx + k_len;
78 }
79 } else {
80 let input_bytes = input.as_bytes();
82 let keyword_bytes = keyword.as_bytes();
83 let mut i = 0;
84 while i <= input_bytes.len() - k_len {
85 let window = &input_bytes[i..i + k_len];
86 if window.eq_ignore_ascii_case(keyword_bytes) {
87 if input.is_char_boundary(i) && input.is_char_boundary(i + k_len) {
89 target.push_range(i, i + k_len);
90 found = true;
91 }
92 i += k_len;
93 } else {
94 i += 1;
95 }
96 }
97 }
98 found
99}
100
101#[inline]
109pub fn has_chars<'a>(
110 mut target: impl MatchTarget<'a>,
111 chars: &[char],
112 any: bool,
113 ignore_case: bool,
114) -> bool {
115 target.clear_ranges();
116 let input = target.get_input();
117
118 if chars.is_empty() || input.is_empty() {
119 return false;
120 }
121
122 let mut target_map = [false; 256];
123 let mut target_count = 0;
124
125 for &c in chars {
127 if c.is_ascii() {
128 let b = if ignore_case {
129 c.to_ascii_lowercase() as usize
130 } else {
131 c as usize
132 };
133 if !target_map[b] {
134 target_map[b] = true;
135 target_count += 1; }
137 }
138 }
139
140 if target_count == 0 {
141 return false;
142 }
143
144 let mut found_map = [false; 256];
145 let mut distinct_found = 0;
146 let mut any_found = false;
147
148 for (i, b) in input.bytes().enumerate() {
150 if !b.is_ascii() {
151 continue;
152 }
153 let val = if ignore_case {
154 b.to_ascii_lowercase() as usize
155 } else {
156 b as usize
157 };
158
159 if target_map[val] {
160 target.push_range(i, i + 1);
162 any_found = true;
163
164 if !any && !found_map[val] {
166 found_map[val] = true;
167 distinct_found += 1;
168 }
169 }
170 }
171
172 if any {
174 any_found
175 } else {
176 let all_found = distinct_found == target_count;
177 if !all_found {
178 target.clear_ranges();
180 }
181 all_found
182 }
183}
184
185#[inline]
187pub fn has_regex<'a>(mut target: impl MatchTarget<'a>, re: &Regex) -> bool {
188 target.clear_ranges();
189 let input = target.get_input();
190 let mut found = false;
191
192 for mat in re.find_iter(input) {
193 target.push_range(mat.start(), mat.end());
194 found = true;
195 }
196 found
197}
198
199#[inline]
203pub fn upper_prob(input: &str) -> f64 {
204 let mut upper_count = 0;
205 let mut letter_count = 0;
206
207 for b in input.bytes() {
208 if b.is_ascii_uppercase() {
209 upper_count += 1;
210 letter_count += 1;
211 } else if b.is_ascii_lowercase() {
212 letter_count += 1;
213 }
214 }
215
216 if letter_count == 0 {
217 return 0.0;
218 }
219 (upper_count as f64) / (letter_count as f64)
220}
221
222#[inline]
224pub fn has_upper(input: &str) -> bool {
225 input.bytes().any(|b| b.is_ascii_uppercase())
226}
227
228#[inline]
230pub fn has_lower(input: &str) -> bool {
231 input.bytes().any(|b| b.is_ascii_lowercase())
232}
233
234pub fn is_base64(s: &str) -> bool {
245 let len = s.len();
246
247 if len <= 8 {
249 return false;
250 }
251
252 let trimmed = s.trim_end_matches('=');
254 let padding_count = len - trimmed.len();
255
256 if padding_count > 2 {
257 return false;
258 }
259
260 if trimmed.len() % 4 == 1 {
263 return false;
264 }
265
266 if trimmed.ends_with('/')
269 || trimmed.ends_with('+')
270 || trimmed.ends_with('-')
271 || trimmed.ends_with('_')
272 {
273 return false;
274 }
275
276 let mut upper_count = 0;
277 let mut letter_count = 0;
278
279 let mut has_digit = false;
280 let mut has_special = padding_count > 0;
281
282 let mut consecutive_upper = 0;
283 let mut consecutive_lower = 0;
284
285 for b in trimmed.bytes() {
287 if b.is_ascii_uppercase() {
288 upper_count += 1;
289 letter_count += 1;
290 consecutive_upper += 1;
291 consecutive_lower = 0;
292
293 if consecutive_upper >= 8 {
294 return false;
295 }
296 } else if b.is_ascii_lowercase() {
297 letter_count += 1;
298 consecutive_lower += 1;
299 consecutive_upper = 0;
300
301 if consecutive_lower >= 8 {
302 return false;
303 }
304 } else {
305 consecutive_upper = 0;
307 consecutive_lower = 0;
308
309 if b.is_ascii_digit() {
310 has_digit = true;
311 } else if b == b'+' || b == b'/' || b == b'-' || b == b'_' {
312 has_special = true;
314 }
315 }
316 }
317
318 if !has_digit && !has_special {
320 return false;
321 }
322
323 if letter_count == 0 {
325 return false;
326 }
327
328 let prob = (upper_count as f64) / (letter_count as f64);
330 if prob <= 0.25 || prob >= 0.75 {
331 return false;
332 }
333
334 true
335}
336
337#[inline]
342pub fn sort_ascii_counts(input: &str, ignore_case: bool) -> Vec<(char, usize)> {
343 let mut counts = [0usize; 256];
344
345 for b in input.bytes() {
346 if b.is_ascii() {
347 let val = if ignore_case {
348 b.to_ascii_lowercase() as usize
349 } else {
350 b as usize
351 };
352 counts[val] += 1;
353 }
354 }
355
356 let mut result: Vec<(char, usize)> = counts
357 .iter()
358 .enumerate()
359 .filter(|&(_, &count)| count > 0)
360 .map(|(b, &count)| (b as u8 as char, count))
361 .collect();
362
363 result.sort_unstable_by(|a, b| b.1.cmp(&a.1).then_with(|| a.0.cmp(&b.0)));
365
366 result
367}
368
369pub fn is_hex(input: &str) -> bool {
380 let mut s = input.trim();
381 if s.is_empty() || !s.is_ascii() || s.len() <= 10 {
382 return false;
383 }
384
385 if (s.starts_with("0x") || s.starts_with("0X") || s.starts_with("\\x") || s.starts_with("\\X"))
387 && s[2..].find(&s[0..2]).is_none()
388 {
389 s = &s[2..];
390 }
391
392 let prefix = if s.starts_with("0x") {
394 "0x"
395 } else if s.starts_with("0X") {
396 "0X"
397 } else if s.starts_with("\\x") {
398 "\\x"
399 } else if s.starts_with("\\X") {
400 "\\X"
401 } else if s.starts_with("%") {
402 "%"
403 } else {
404 ""
405 };
406
407 let mut idx = prefix.len();
408 let bytes = s.as_bytes();
409
410 if idx + 2 > bytes.len() {
411 return false;
412 }
413
414 let mut upper_hex = false;
415 let mut lower_hex = false;
416
417 #[inline]
419 fn check_hex(b: u8, upper: &mut bool, lower: &mut bool) -> bool {
420 if b.is_ascii_digit() {
421 return true;
422 }
423 if (b'a'..=b'f').contains(&b) {
424 *lower = true;
425 return true;
426 }
427 if (b'A'..=b'F').contains(&b) {
428 *upper = true;
429 return true;
430 }
431 false
432 }
433
434 if !check_hex(bytes[idx], &mut upper_hex, &mut lower_hex)
435 || !check_hex(bytes[idx + 1], &mut upper_hex, &mut lower_hex)
436 {
437 return false;
438 }
439 idx += 2;
440
441 let separator = if idx == bytes.len() {
443 ""
444 } else {
445 if !prefix.is_empty() {
446 if let Some(next_prefix_idx) = s[idx..].find(prefix) {
447 &s[idx..idx + next_prefix_idx]
448 } else {
449 return false;
450 }
451 } else {
452 let mut sep_len = 0;
453 while idx + sep_len < bytes.len() {
454 let b = bytes[idx + sep_len];
455 if b.is_ascii_hexdigit() {
456 break;
457 }
458 sep_len += 1;
459 }
460 &s[idx..idx + sep_len]
461 }
462 };
463
464 if !separator.is_empty() && separator.bytes().any(|b| b.is_ascii_alphanumeric()) {
466 return false;
467 }
468
469 let mut curr_idx = 0;
470 let mut count = 0;
471
472 while curr_idx < bytes.len() {
474 if !s[curr_idx..].starts_with(prefix) {
475 return false;
476 }
477 curr_idx += prefix.len();
478
479 if curr_idx + 2 > bytes.len() {
480 return false;
481 }
482
483 if !check_hex(bytes[curr_idx], &mut upper_hex, &mut lower_hex)
484 || !check_hex(bytes[curr_idx + 1], &mut upper_hex, &mut lower_hex)
485 {
486 return false;
487 }
488
489 if upper_hex && lower_hex {
491 return false;
492 }
493
494 curr_idx += 2;
495 count += 1;
496
497 if curr_idx == bytes.len() {
498 break;
499 }
500
501 if !s[curr_idx..].starts_with(separator) {
502 return false;
503 }
504 curr_idx += separator.len();
505
506 if curr_idx == bytes.len() {
507 return false; }
509 }
510 count > 0
511}
512
513#[cfg(test)]
514mod tests {
515 use super::*;
516
517 #[test]
518 fn test() {
519 has_keyword("", "", true);
520 has_keyword(&mut State::new(""), "", true);
521 }
522}