1use crate::ast::Pattern;
28use crate::token::Token;
29
30pub struct PatternSet {
32 pats: Vec<Pattern>,
33 names: Vec<String>,
37 shapes: crate::custom::ShapeSet,
41 as_one: bool,
44 probed: bool,
47 together: std::sync::OnceLock<Together>,
49}
50
51impl std::fmt::Debug for PatternSet {
52 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54 f.debug_struct("PatternSet").field("pats", &self.pats).field("names", &self.names).finish_non_exhaustive()
55 }
56}
57
58impl Clone for PatternSet {
59 fn clone(&self) -> Self {
62 PatternSet {
63 pats: self.pats.clone(),
64 names: self.names.clone(),
65 shapes: self.shapes.clone(),
66 as_one: self.as_one,
67 probed: self.probed,
68 together: std::sync::OnceLock::new(),
69 }
70 }
71}
72
73struct Together {
76 union: Option<crate::nfa::Union>,
77 place: Vec<Option<u32>>,
78 required_literals: usize,
81}
82
83enum Lexed<'t> {
87 Bytes,
88 Shared(&'t [crate::token::Token]),
89 Own,
90}
91
92struct Probe {
97 filter: Option<crate::prefilter::BloomFilter>,
98}
99
100impl Probe {
101 fn refuses_all(&self, lits: &[&str]) -> bool {
103 self.filter.as_ref().is_some_and(|f| crate::prefilter::all_absent_by(f, lits))
104 }
105}
106
107#[derive(Clone, Debug, Default, PartialEq, Eq)]
113pub struct SetMatches {
114 bits: Vec<bool>,
115}
116
117impl SetMatches {
118 #[must_use]
120 pub fn matched(&self, i: usize) -> bool {
121 self.bits.get(i).copied().unwrap_or(false)
122 }
123
124 #[must_use]
126 pub fn matched_any(&self) -> bool {
127 self.bits.iter().any(|b| *b)
128 }
129
130 #[must_use]
132 pub fn matched_all(&self) -> bool {
133 self.bits.iter().all(|b| *b)
134 }
135
136 #[must_use]
138 pub fn len(&self) -> usize {
139 self.bits.len()
140 }
141
142 #[must_use]
144 pub fn is_empty(&self) -> bool {
145 self.bits.is_empty()
146 }
147
148 pub fn iter(&self) -> impl Iterator<Item = usize> + '_ {
150 self.bits.iter().enumerate().filter_map(|(i, b)| b.then_some(i))
151 }
152}
153
154enum Plan {
156 Settled(bool),
158 Alone,
161 Shared(crate::nfa::Compiled, usize),
164 Together(u32, usize),
167}
168
169impl PatternSet {
170 #[must_use]
173 pub fn new(pats: Vec<Pattern>) -> Self {
174 PatternSet {
175 pats,
176 names: Vec::new(),
177 shapes: crate::custom::ShapeSet::new(),
178 as_one: true,
179 probed: false,
180 together: std::sync::OnceLock::new(),
181 }
182 }
183
184 #[must_use]
187 pub fn named(pats: Vec<Pattern>, mut names: Vec<String>) -> Self {
188 let count = pats.len();
189 names.truncate(count);
190 while names.len() < count {
191 names.push(names.len().to_string());
192 }
193 let mut set = PatternSet::new(pats);
194 set.names = names;
195 set
196 }
197
198 pub fn from_text(
212 text: &str,
213 shapes: &mut crate::custom::ShapeSet,
214 ) -> Result<PatternSet, crate::custom::ShapeError> {
215 let mut members = Vec::new();
216 shapes.declare_lines(text, Some(&mut members))?;
217 Ok(PatternSet::of_members(members, shapes))
218 }
219
220 pub fn from_file(
227 path: &std::path::Path,
228 shapes: &mut crate::custom::ShapeSet,
229 ) -> Result<PatternSet, crate::custom::ShapeError> {
230 let members = shapes.declare_file_members(path)?;
231 Ok(PatternSet::of_members(members, shapes))
232 }
233
234 fn of_members(members: Vec<(String, Pattern)>, shapes: &crate::custom::ShapeSet) -> PatternSet {
236 let (names, pats): (Vec<String>, Vec<Pattern>) = members.into_iter().unzip();
237 PatternSet::named(pats, names).under(shapes.clone())
238 }
239
240 #[must_use]
243 pub fn under(mut self, shapes: crate::custom::ShapeSet) -> Self {
244 self.shapes = shapes;
245 self
246 }
247
248 #[must_use]
251 pub fn names(&self) -> &[String] {
252 &self.names
253 }
254
255 #[must_use]
258 pub fn name(&self, i: usize) -> String {
259 self.names.get(i).cloned().unwrap_or_else(|| i.to_string())
260 }
261
262 #[must_use]
264 pub fn shapes(&self) -> &crate::custom::ShapeSet {
265 &self.shapes
266 }
267
268 #[must_use]
276 pub fn scan(&self, input: &[u8]) -> Vec<(usize, crate::engine::Span)> {
277 self.scan_from(input, 0)
278 }
279
280 #[must_use]
283 pub fn scan_from(&self, input: &[u8], at: usize) -> Vec<(usize, crate::engine::Span)> {
284 let mut out: Vec<(usize, crate::engine::Span)> = Vec::new();
285 self.each_member(input, at, false, None, |i, spans, _| {
286 out.extend(spans.into_iter().map(|s| (i, s)));
287 });
288 out.sort_unstable_by_key(|&(i, s)| (s.start(), s.end(), i));
289 out
290 }
291
292 #[must_use]
304 pub fn scan_from_over(
305 &self,
306 input: &[u8],
307 at: usize,
308 toks: &[Token],
309 ) -> Vec<(usize, crate::engine::Span)> {
310 let mut out: Vec<(usize, crate::engine::Span)> = Vec::new();
311 self.each_member(input, at, false, Some(toks), |i, spans, _| {
312 out.extend(spans.into_iter().map(|s| (i, s)));
313 });
314 out.sort_unstable_by_key(|&(i, s)| (s.start(), s.end(), i));
315 out
316 }
317
318 #[must_use]
324 pub fn scan_matches(&self, input: &[u8], lists: bool) -> Vec<(usize, crate::engine::Match)> {
325 self.resolved(input, false, lists)
326 }
327
328 #[must_use]
336 pub fn first_matches(&self, input: &[u8], lists: bool) -> Vec<(usize, crate::engine::Match)> {
337 let shaped = !self.shapes.is_empty();
338 let spans = if shaped { self.first_spans_shaped(input, 0, false) } else { self.first_spans(input) };
339 let mut out = Vec::with_capacity(spans.len());
340 for (i, s) in spans {
341 let p = &self.pats[i];
342 let resolved = match (lists, shaped) {
343 (true, false) => crate::engine::captures_with_lists(p, input, &[s]),
344 (false, false) => crate::engine::captures(p, input, &[s]),
345 (true, true) => crate::engine::captures_with_shapes_and_lists(p, input, &self.shapes, &[s]),
346 (false, true) => crate::engine::captures_with_shapes(p, input, &self.shapes, &[s]),
347 };
348 out.extend(resolved.into_iter().map(|m| (i, m)));
349 }
350 out.sort_by_key(|(i, m)| (m.start, m.end, *i));
351 out
352 }
353
354 fn first_spans(&self, input: &[u8]) -> Vec<(usize, crate::engine::Span)> {
360 let mut out = Vec::new();
361 let mut shared = Vec::new();
362 let mut together = Vec::new();
363 let probe = self.probe(input);
364 for (i, p) in self.pats.iter().enumerate() {
365 match self.plan(i, p, input, &probe) {
366 Plan::Settled(false) => {}
367 Plan::Settled(true) => {
370 let first = match crate::engine::routed_first(p, input) {
371 Some(first) => first,
372 None => crate::cursor::find(p, input),
373 };
374 if let Some(s) = first {
375 out.push((i, s));
376 }
377 }
378 Plan::Alone => {
379 if let Some(s) = crate::cursor::find(p, input) {
380 out.push((i, s));
381 }
382 }
383 Plan::Shared(c, max_len) => shared.push((i, c, max_len)),
384 Plan::Together(j, max_len) => together.push((i, j, max_len)),
385 }
386 }
387 out.extend(self.over_a_shared_prefix(input, &shared, &together, false));
388 out
389 }
390
391 fn resolved(&self, input: &[u8], first: bool, lists: bool) -> Vec<(usize, crate::engine::Match)> {
394 let mut out: Vec<(usize, crate::engine::Match)> = Vec::new();
395 self.each_member(input, 0, first, None, |i, spans, lexed| {
396 let p = &self.pats[i];
397 let matches = match lexed {
398 Lexed::Bytes if lists => crate::engine::captures_with_lists(p, input, &spans),
399 Lexed::Bytes => crate::engine::captures(p, input, &spans),
400 Lexed::Shared(toks) if lists => crate::engine::captures_over_with_lists(p, input, toks, &spans),
401 Lexed::Shared(toks) => crate::engine::captures_over(p, input, toks, &spans),
402 Lexed::Own if lists => crate::engine::captures_with_shapes_and_lists(p, input, &self.shapes, &spans),
403 Lexed::Own => crate::engine::captures_with_shapes(p, input, &self.shapes, &spans),
404 };
405 out.extend(matches.into_iter().map(|m| (i, m)));
406 });
407 out.sort_by_key(|(i, m)| (m.start, m.end, *i));
408 out
409 }
410
411 fn each_member<F>(
421 &self,
422 input: &[u8],
423 at: usize,
424 first: bool,
425 reuse: Option<&[Token]>,
426 mut emit: F,
427 ) where
428 F: FnMut(usize, Vec<crate::engine::Span>, Lexed<'_>),
429 {
430 let shaped = !self.shapes.is_empty();
431 let mut shared = Vec::new();
432 let mut own = Vec::new();
433 let probe = self.probe(input);
434 for (i, p) in self.pats.iter().enumerate() {
435 if !p.library_kinds().is_empty() {
436 own.push(i);
437 continue;
438 }
439 if shaped {
440 shared.push(i);
441 continue;
442 }
443 if crate::prefilter::requires_absent_with(p, input, probe.filter.as_ref()) {
444 continue;
445 }
446 let routed = if first {
449 crate::engine::routed_first(p, input).map(|s| s.into_iter().collect())
450 } else if at == 0 {
451 crate::engine::routed_spans(p, input)
452 } else {
453 crate::engine::routed_spans_positional(p, input)
454 };
455 match routed {
456 Some(spans) => emit(i, spans.into_iter().filter(|s| s.start() >= at).collect(), Lexed::Bytes),
457 None => shared.push(i),
458 }
459 }
460 if !shared.is_empty() {
461 let lexed;
470 let toks: &[Token] = match (shaped, reuse) {
471 (true, _) => {
472 let blobs = crate::lexer::blob_runs(input);
473 lexed = crate::lexer::lex_with_shapes(input, &blobs, &self.shapes, 0);
474 &lexed
475 }
476 (false, Some(lent)) => lent,
477 (false, None) => {
478 lexed = crate::parallel_lex::lex_parallel(input);
479 &lexed
480 }
481 };
482 let start = toks.partition_point(|t| t.start() < at);
483 let sig = crate::nfa::Stitched::significant_of(toks);
484 for i in shared {
485 let p = &self.pats[i];
486 let stream = crate::nfa::Stitched::new(toks, &sig);
487 let spans = if let Some(mut w) = crate::nfa::SerialWalk::over_stream(p, input, stream) {
488 w.seek(at);
489 let mut spans = Vec::new();
490 while let Some(s) = w.next_span(input) {
491 spans.push(s);
492 if first {
493 break;
494 }
495 }
496 spans
497 } else {
498 let mut spans = crate::engine::scan_tokens_from(p, input, toks, start);
499 if first {
500 spans.truncate(1);
501 }
502 spans
503 };
504 emit(i, spans, Lexed::Shared(toks));
505 }
506 }
507 for i in own {
508 let mut spans = crate::engine::scan_with_shapes_from(&self.pats[i], input, &self.shapes, at);
509 if first {
510 spans.truncate(1);
511 }
512 emit(i, spans, Lexed::Own);
513 }
514 }
515
516 fn first_spans_shaped(&self, input: &[u8], at: usize, any: bool) -> Vec<(usize, crate::engine::Span)> {
520 let mut out = Vec::new();
521 self.each_member(input, at, true, None, |i, spans, _| {
522 if let Some(&s) = spans.first() {
523 out.push((i, s));
524 }
525 });
526 out.sort_unstable_by_key(|&(i, _)| i);
527 if any {
528 out.truncate(1);
529 }
530 out
531 }
532
533 #[must_use]
536 pub fn max_tokens(&self) -> Option<usize> {
537 self.pats.iter().map(Pattern::max_tokens).try_fold(0usize, |best, m| m.map(|m| best.max(m)))
538 }
539
540 #[must_use]
543 pub fn depends_on_whole_input(&self) -> bool {
544 self.pats.iter().any(Pattern::depends_on_whole_input)
545 }
546
547 #[must_use]
551 pub fn depends_on_more_than_its_lines(&self) -> bool {
552 self.pats.iter().any(Pattern::depends_on_more_than_its_lines)
553 }
554
555 #[must_use]
558 pub fn reads_whitespace(&self) -> bool {
559 self.pats.iter().any(Pattern::reads_whitespace)
560 }
561
562 #[must_use]
570 pub fn probed(mut self, on: bool) -> Self {
571 self.probed = on;
572 self
573 }
574
575 #[must_use]
580 pub fn walked_as_one(mut self, on: bool) -> Self {
581 self.as_one = on;
582 self.together = std::sync::OnceLock::new();
583 self
584 }
585
586 #[must_use]
588 pub fn with_names(mut self, names: Vec<String>) -> Self {
589 let count = self.pats.len();
590 self.names = names;
591 self.names.truncate(count);
592 while self.names.len() < count {
593 self.names.push(self.names.len().to_string());
594 }
595 self
596 }
597
598 fn together(&self) -> &Together {
600 self.together.get_or_init(|| {
601 let mut place = vec![None; self.pats.len()];
602 let mut members: Vec<&Pattern> = Vec::new();
603 if self.as_one {
604 for (i, p) in self.pats.iter().enumerate() {
605 if crate::nfa::union_eligible(p) {
606 place[i] = Some(u32::try_from(members.len()).expect("a set holds fewer than four billion patterns"));
607 members.push(p);
608 }
609 }
610 }
611 let union = (!members.is_empty()).then(|| crate::nfa::Union::of(&members));
612 let required_literals =
613 self.pats.iter().map(crate::prefilter::required_literal_count).sum();
614 Together { union, place, required_literals }
615 })
616 }
617
618 fn probe(&self, input: &[u8]) -> Probe {
622 let many = self.probed
623 && self.together().required_literals > crate::prefilter::direct_search_max_literals();
624 Probe { filter: many.then(|| crate::prefilter::BloomFilter::build(input)) }
625 }
626
627 fn absent_for(&self, indices: impl Iterator<Item = usize>, input: &[u8]) -> std::collections::HashSet<Vec<u8>> {
630 let mut absent = std::collections::HashSet::new();
631 for i in indices {
632 absent.extend(crate::prefilter::absent_guard_literals(&self.pats[i], input));
633 }
634 absent
635 }
636
637 fn first_spans_together(
640 &self,
641 asked: &[(usize, u32)],
642 input: &[u8],
643 toks: &[crate::token::Token],
644 from: usize,
645 ) -> Vec<(usize, Option<crate::engine::Span>)> {
646 if asked.is_empty() {
647 return Vec::new();
648 }
649 let Some(u) = self.together().union.as_ref() else {
650 return Vec::new();
651 };
652 let mut active = vec![false; u.len()];
653 for &(_, j) in asked {
654 active[j as usize] = true;
655 }
656 let absent = self.absent_for(asked.iter().map(|&(i, _)| i), input);
657 let firsts = crate::nfa::first_spans_union(u, &active, input, toks, from, &absent);
658 asked.iter().map(|&(i, j)| (i, firsts[j as usize])).collect()
659 }
660
661 #[must_use]
663 pub fn len(&self) -> usize {
664 self.pats.len()
665 }
666
667 #[must_use]
669 pub fn is_empty(&self) -> bool {
670 self.pats.is_empty()
671 }
672
673 #[must_use]
675 pub fn patterns(&self) -> &[Pattern] {
676 &self.pats
677 }
678
679 #[must_use]
684 pub fn matches(&self, input: &[u8]) -> Vec<usize> {
685 if !self.shapes.is_empty() {
686 return self.first_spans_shaped(input, 0, false).into_iter().map(|(i, _)| i).collect();
687 }
688 let mut out = Vec::new();
689 let mut shared = Vec::new();
690 let mut together = Vec::new();
691 let probe = self.probe(input);
692 for (i, p) in self.pats.iter().enumerate() {
693 match self.plan(i, p, input, &probe) {
694 Plan::Settled(true) => out.push(i),
695 Plan::Settled(false) => {}
696 Plan::Alone => {
697 if crate::engine::is_match(p, input) {
698 out.push(i);
699 }
700 }
701 Plan::Shared(c, max_len) => shared.push((i, c, max_len)),
702 Plan::Together(j, max_len) => together.push((i, j, max_len)),
703 }
704 }
705 out.extend(self.over_a_shared_prefix(input, &shared, &together, false).into_iter().map(|(i, _)| i));
706 out.sort_unstable();
707 out
708 }
709
710 #[must_use]
717 pub fn is_match(&self, input: &[u8]) -> bool {
718 if !self.shapes.is_empty() {
719 return !self.first_spans_shaped(input, 0, true).is_empty();
720 }
721 let mut shared = Vec::new();
722 let mut together = Vec::new();
723 let mut alone = Vec::new();
724 let probe = self.probe(input);
725 for (i, p) in self.pats.iter().enumerate() {
726 match self.plan(i, p, input, &probe) {
727 Plan::Settled(true) => return true,
728 Plan::Settled(false) => {}
729 Plan::Alone => alone.push(p),
730 Plan::Shared(c, max_len) => shared.push((i, c, max_len)),
731 Plan::Together(j, max_len) => together.push((i, j, max_len)),
732 }
733 }
734 if alone.iter().any(|p| crate::engine::is_match(p, input)) {
738 return true;
739 }
740 !self.over_a_shared_prefix(input, &shared, &together, true).is_empty()
741 }
742
743 #[must_use]
750 pub fn matched(&self, input: &[u8]) -> SetMatches {
751 let mut bits = vec![false; self.pats.len()];
752 for i in self.matches(input) {
753 bits[i] = true;
754 }
755 SetMatches { bits }
756 }
757
758 #[must_use]
768 pub fn matches_at(&self, input: &[u8], at: usize) -> SetMatches {
769 let mut bits = vec![false; self.pats.len()];
770 if !self.shapes.is_empty() {
771 for (i, _) in self.first_spans_shaped(input, at, false) {
772 bits[i] = true;
773 }
774 return SetMatches { bits };
775 }
776 let mut open = Vec::new();
777 let probe = self.probe(input);
778 for (i, p) in self.pats.iter().enumerate() {
779 if !p.library_kinds().is_empty() {
780 open.push(i);
781 continue;
782 }
783 if crate::prefilter::requires_absent_with(p, input, probe.filter.as_ref()) {
784 continue;
785 }
786 if let Some(spans) = crate::engine::routed_spans_positional(p, input) {
789 bits[i] = spans.iter().any(|s| s.start() >= at);
790 } else {
791 open.push(i);
792 }
793 }
794 if open.is_empty() {
795 return SetMatches { bits };
796 }
797 let toks = crate::parallel_lex::lex_parallel(input);
798 let start = toks.partition_point(|t| t.start() < at);
799 let sig = crate::nfa::Stitched::significant_of(&toks);
803 let (asked, apart) = self.split_together(open);
804 for (i, first) in self.first_spans_together(&asked, input, &toks, start) {
805 bits[i] = first.is_some();
806 }
807 for i in apart {
808 let p = &self.pats[i];
809 let stream = crate::nfa::Stitched::new(&toks, &sig);
810 if !p.library_kinds().is_empty() {
811 bits[i] = crate::cursor::find_at(p, input, at).is_some();
812 } else if let Some(mut w) = crate::nfa::SerialWalk::over_stream(p, input, stream) {
813 w.seek(at);
814 bits[i] = w.next_span(input).is_some();
815 } else {
816 bits[i] = !crate::engine::scan_tokens_from(p, input, &toks, start).is_empty();
817 }
818 }
819 SetMatches { bits }
820 }
821
822 fn split_together(&self, open: Vec<usize>) -> (Vec<(usize, u32)>, Vec<usize>) {
825 let place = &self.together().place;
826 let mut asked = Vec::new();
827 let mut apart = Vec::new();
828 for i in open {
829 match place[i] {
830 Some(j) => asked.push((i, j)),
831 None => apart.push(i),
832 }
833 }
834 (asked, apart)
835 }
836
837 #[must_use]
839 pub fn is_match_at(&self, input: &[u8], at: usize) -> bool {
840 self.matches_at(input, at).matched_any()
841 }
842
843 #[must_use]
854 pub fn matches_with_spans(&self, input: &[u8]) -> Vec<(usize, crate::engine::Span)> {
855 if !self.shapes.is_empty() {
856 return self.first_spans_shaped(input, 0, false);
857 }
858 let mut out = Vec::new();
859 let mut open = Vec::new();
860 let probe = self.probe(input);
861 for (i, p) in self.pats.iter().enumerate() {
862 if !p.library_kinds().is_empty() {
863 open.push(i);
864 continue;
865 }
866 if crate::prefilter::requires_absent_with(p, input, probe.filter.as_ref()) {
867 continue;
868 }
869 match crate::engine::routed_first(p, input) {
874 Some(first) => {
875 if let Some(s) = first {
876 out.push((i, s));
877 }
878 }
879 None => open.push(i),
880 }
881 }
882 if !open.is_empty() {
883 let toks = crate::parallel_lex::lex_parallel(input);
884 let sig = crate::nfa::Stitched::significant_of(&toks);
886 let (asked, apart) = self.split_together(open);
887 for (i, first) in self.first_spans_together(&asked, input, &toks, 0) {
888 if let Some(s) = first {
889 out.push((i, s));
890 }
891 }
892 for i in apart {
893 let p = &self.pats[i];
894 let stream = crate::nfa::Stitched::new(&toks, &sig);
895 let found = if !p.library_kinds().is_empty() {
896 crate::cursor::find(p, input)
897 } else if let Some(mut w) =
898 crate::nfa::SerialWalk::over_stream(p, input, stream)
899 {
900 w.next_span(input)
901 } else {
902 crate::engine::scan_tokens_from(p, input, &toks, 0).into_iter().next()
903 };
904 if let Some(s) = found {
905 out.push((i, s));
906 }
907 }
908 }
909 out.sort_unstable_by_key(|&(i, _)| i);
910 out
911 }
912
913 fn plan(&self, index: usize, pattern: &Pattern, input: &[u8], probe: &Probe) -> Plan {
916 if !pattern.library_kinds().is_empty() {
919 return Plan::Alone;
920 }
921 if let Some(settled) = self.without_a_lex(pattern, input, probe) {
922 return Plan::Settled(settled);
923 }
924 if !crate::prefilter::settles_from_a_prefix(pattern) {
925 return Plan::Alone;
926 }
927 if let Some(j) = self.together().place[index] {
928 return Plan::Together(j, crate::nfa::bounded_max_len(pattern).unwrap_or(1).max(1));
929 }
930 match crate::nfa::compile_pattern(pattern) {
931 Some(c) => {
932 Plan::Shared(c, crate::nfa::bounded_max_len(pattern).unwrap_or(1).max(1))
933 }
934 None => Plan::Alone,
937 }
938 }
939
940 fn over_a_shared_prefix(
956 &self,
957 input: &[u8],
958 shared: &[(usize, crate::nfa::Compiled, usize)],
959 together: &[(usize, u32, usize)],
960 stop_at_the_first: bool,
961 ) -> Vec<(usize, crate::engine::Span)> {
962 let mut found: Vec<(usize, crate::engine::Span)> = Vec::new();
963 if shared.is_empty() && together.is_empty() {
964 return found;
965 }
966 let seen = |found: &[(usize, crate::engine::Span)], i: usize| found.iter().any(|&(k, _)| k == i);
967 let asked: Vec<(usize, u32)> = together.iter().map(|&(i, j, _)| (i, j)).collect();
968 let bound: std::collections::HashMap<usize, usize> =
969 together.iter().map(|&(i, _, max_len)| (i, max_len)).collect();
970 crate::prefilter::over_widening_prefixes(input, |toks, whole| {
971 let still: Vec<(usize, u32)> =
974 asked.iter().filter(|(i, _)| !seen(&found, *i)).copied().collect();
975 for (i, first) in self.first_spans_together(&still, input, toks, 0) {
976 let hit = if whole {
977 first
978 } else {
979 first.and_then(|s| {
980 crate::prefilter::settled_clear_of_the_cut(toks, &[s], bound[&i])
981 })
982 };
983 if let Some(s) = hit {
984 found.push((i, s));
985 if stop_at_the_first {
986 return false;
987 }
988 }
989 }
990 for (i, c, max_len) in shared {
991 if seen(&found, *i) {
992 continue;
993 }
994 let spans = crate::nfa::scan_nfa_over_compiled(c, &self.pats[*i], input, toks);
995 let hit = if whole {
999 spans.first().copied()
1000 } else {
1001 crate::prefilter::settled_clear_of_the_cut(toks, &spans, *max_len)
1002 };
1003 if let Some(s) = hit {
1004 found.push((*i, s));
1005 if stop_at_the_first {
1006 return false;
1007 }
1008 }
1009 }
1010 found.len() < shared.len() + together.len()
1011 });
1012 found
1013 }
1014
1015 #[must_use]
1017 pub fn matched_all(&self, input: &[u8]) -> bool {
1018 self.matches(input).len() == self.pats.len()
1019 }
1020
1021 fn without_a_lex(&self, pattern: &Pattern, input: &[u8], probe: &Probe) -> Option<bool> {
1028 if crate::prefilter::requires_absent_with(pattern, input, probe.filter.as_ref()) {
1029 return Some(false);
1030 }
1031 if let Some(lits) = crate::prefilter::byte_routable_literals(pattern) {
1032 if probe.refuses_all(&lits) {
1033 return Some(false);
1034 }
1035 if let Some(found) = crate::prefilter::byte_route_any_word_literal(&lits, input) {
1036 return Some(found);
1037 }
1038 }
1039 if let Some(punct) = crate::prefilter::byte_routable_word_then_punct(pattern)
1040 && let Some(found) = crate::prefilter::byte_route_any_word_then_punct(punct, input)
1041 {
1042 return Some(found);
1043 }
1044 if let Some((bp, prefix)) = crate::prefilter::byte_routable_byte_pattern(pattern)
1045 && let Some(found) = crate::prefilter::byte_route_any_byte_pattern(bp, &prefix, input)
1046 {
1047 return Some(found);
1048 }
1049 None
1050 }
1051}
1052
1053#[cfg(test)]
1054mod tests {
1055 use super::*;
1056
1057 const SOURCES: &[&str] = &[
1062 "\"alpha\"",
1063 "\"zzzqqq\"",
1064 "\\W",
1065 "\\N",
1066 "\\W \"=\"",
1067 "`cond_[0-9]+`",
1068 "\"let\" \\W \"=\"",
1069 "\\W:x \"=\" =x",
1070 "\\B(\\W)",
1071 "\"nowhere_at_all\" \"=\" \\N",
1072 ];
1073
1074 fn corpus() -> Vec<u8> {
1075 let mut s = String::new();
1076 for i in 0..300 {
1077 match i % 4 {
1078 0 => s.push_str(&format!("let value_{i} = {} ;\n", i * 37)),
1079 1 => s.push_str(&format!("call_{i}(alpha, beta, {i}) ;\n")),
1080 2 => s.push_str(&format!("key_{i}: item_{i}, item_{} ;\n", i + 1)),
1081 _ => s.push_str(&format!("if (cond_{i}) {{ do_{i}(x) ; }}\n")),
1082 }
1083 }
1084 s.into_bytes()
1085 }
1086
1087 fn built() -> PatternSet {
1088 PatternSet::new(
1089 SOURCES.iter().map(|s| crate::parse(s).expect("pattern parses")).collect(),
1090 )
1091 }
1092
1093 #[test]
1094 fn the_set_reports_what_each_pattern_reports_alone() {
1095 let input = corpus();
1098 let set = built();
1099 let want: Vec<usize> = SOURCES
1100 .iter()
1101 .enumerate()
1102 .filter(|(_, s)| {
1103 let p = crate::parse(s).expect("pattern parses");
1104 crate::is_match(&p, &input)
1105 })
1106 .map(|(i, _)| i)
1107 .collect();
1108 assert_eq!(set.matches(&input), want);
1109 assert_eq!(set.is_match(&input), !want.is_empty());
1110 assert_eq!(set.matched_all(&input), want.len() == SOURCES.len());
1111 }
1112
1113 #[test]
1114 fn the_position_row_reports_where_each_pattern_first_matches_alone() {
1115 let input = corpus();
1121 let set = built();
1122 let want: Vec<(usize, crate::engine::Span)> = SOURCES
1123 .iter()
1124 .enumerate()
1125 .filter_map(|(i, s)| {
1126 let p = crate::parse(s).expect("pattern parses");
1127 crate::find(&p, &input).map(|span| (i, span))
1128 })
1129 .collect();
1130 assert_eq!(set.matches_with_spans(&input), want);
1131 let mut firsts: Vec<(usize, crate::engine::Span)> = set
1134 .first_matches(&input, false)
1135 .into_iter()
1136 .map(|(i, m)| {
1137 let at = |o: usize| u32::try_from(o).expect("a corpus offset fits a span");
1138 (i, crate::engine::Span { start: at(m.start), end: at(m.end) })
1139 })
1140 .collect();
1141 firsts.sort_unstable_by_key(|&(i, _)| i);
1142 assert_eq!(firsts, want);
1143 }
1144
1145 fn generated(n: usize) -> Vec<Pattern> {
1150 (0..n)
1151 .map(|i| {
1152 let src = match i % 8 {
1153 0 => format!("\"value_{i}\" \"=\" \\N"),
1154 1 => format!("\"call_{i}\" \\B(\\W \",\" \\W \",\" \\N)"),
1155 2 => format!("\"key_{i}\" \":\" \\W"),
1156 3 => format!("\"cond_{i}\" \")\" \"{{\""),
1157 4 => format!("\\W \"=\" \\N{{={}}}", i * 37),
1158 5 => format!("\"item_{i}\" ~\"alpha\""),
1159 6 => format!("\\N{{>={i}}} \";\""),
1160 _ => format!("\"do_{i}\" \\B(\\W)"),
1161 };
1162 crate::parse(&src).expect("pattern parses")
1163 })
1164 .collect()
1165 }
1166
1167 #[test]
1168 fn walked_as_one_agrees_with_walked_apart_at_every_size() {
1169 let input = corpus();
1174 for n in [10usize, 100, 1000] {
1175 let together = PatternSet::new(generated(n));
1176 let apart = PatternSet::new(generated(n)).walked_as_one(false);
1177 assert_eq!(together.matches(&input), apart.matches(&input), "matches, {n} patterns");
1178 assert!(!together.matches(&input).is_empty(), "the generated set has members that match");
1179 assert_eq!(
1180 together.matches_with_spans(&input),
1181 apart.matches_with_spans(&input),
1182 "first spans, {n} patterns"
1183 );
1184 assert_eq!(together.is_match(&input), apart.is_match(&input), "is_match, {n} patterns");
1185 for at in [0usize, 1000, input.len() / 2, input.len()] {
1186 assert_eq!(together.matches_at(&input, at), apart.matches_at(&input, at), "at {at}, {n} patterns");
1187 }
1188 }
1189 }
1190
1191 #[test]
1192 fn an_empty_set_matches_nothing_and_matches_all_of_it() {
1193 let set = PatternSet::new(Vec::new());
1196 assert!(set.is_empty());
1197 assert_eq!(set.len(), 0);
1198 assert_eq!(set.matches(b"anything"), Vec::<usize>::new());
1199 assert!(!set.is_match(b"anything"));
1200 assert!(set.matched_all(b"anything"));
1201 }
1202
1203 #[test]
1204 fn a_set_of_only_absent_patterns_matches_none() {
1205 let input = corpus();
1206 let set = PatternSet::new(
1207 ["\"zzzqqq\"", "\"nowhere_at_all\"", "\"absent_word\" \"=\""]
1208 .iter()
1209 .map(|s| crate::parse(s).expect("pattern parses"))
1210 .collect(),
1211 );
1212 assert_eq!(set.matches(&input), Vec::<usize>::new());
1213 assert!(!set.is_match(&input));
1214 assert!(!set.matched_all(&input));
1215 }
1216
1217 #[test]
1218 fn the_index_follows_the_order_the_set_was_built_in() {
1219 let input = corpus();
1224 let set = PatternSet::new(
1225 ["\\B(\\W)", "\"alpha\"", "\"zzzqqq\"", "\\W \"=\""]
1226 .iter()
1227 .map(|s| crate::parse(s).expect("pattern parses"))
1228 .collect(),
1229 );
1230 assert_eq!(set.matches(&input), vec![0, 1, 3]);
1231 assert_eq!(set.patterns().len(), 4);
1232 }
1233
1234 #[test]
1235 fn a_shared_prefix_that_widens_answers_what_a_single_ask_answers() {
1236 let mut input = corpus();
1241 while input.len() < 400_000 {
1242 let more = corpus();
1243 input.extend_from_slice(&more);
1244 }
1245 input.extend_from_slice(b"\nonly_at_the_very_end = 7 ;\n");
1246 let sources = [
1247 "\"alpha\"",
1248 "\"nowhere_at_all\"",
1249 "\\W \"=\" \\N",
1250 "\"only_at_the_very_end\" \"=\" \\N",
1251 "\\B(\\W)",
1252 "\"zzzqqq\" \"=\"",
1253 ];
1254 let set = PatternSet::new(
1255 sources.iter().map(|s| crate::parse(s).expect("pattern parses")).collect(),
1256 );
1257 let want: Vec<usize> = sources
1258 .iter()
1259 .enumerate()
1260 .filter(|(_, s)| {
1261 let p = crate::parse(s).expect("pattern parses");
1262 crate::is_match(&p, &input)
1263 })
1264 .map(|(i, _)| i)
1265 .collect();
1266 assert_eq!(set.matches(&input), want);
1267 assert_eq!(set.is_match(&input), !want.is_empty());
1268 assert!(want.contains(&3), "the member that matches only at the end is found");
1269 assert!(!want.contains(&1), "the member that matches nowhere is not");
1270 }
1271
1272 #[test]
1273 fn an_empty_input_matches_nothing() {
1274 let set = built();
1275 assert_eq!(set.matches(b""), Vec::<usize>::new());
1276 assert!(!set.is_match(b""));
1277 }
1278}