|
REAL
Regular Expression Algorithmic Library — constexpr C++20 regex
|
The Pike VM, generic over the scratch-state container policy. More...
#include <pike.hpp>
Classes | |
| struct | alternation_hit |
A match the pair scan found (start == npos: none), and where the block scans stopped. More... | |
| struct | cp_hi_cache_entry |
Cache entry for cp_hi_cached (thread-local, not on basic_pike_state). Keyed by a content fingerprint, never a pointer into a program: programs die while the cache lives, and a recycled cp_ranges address would return another class's table (false membership, e.g. emoji matching [\w€]). More... | |
| struct | cp_span |
| One buffered class-loop match: its whole-match span (fill_span_slots mirrors a wrap). More... | |
| struct | scan_set_reset |
| Clears scan_set_ when an inner-literal scan returns, before its lease ends. More... | |
| struct | slot_pair |
| A two-slot sink, for a filler that must call a route function expecting a slot container. More... | |
Public Member Functions | |
| constexpr | pike_vm (const program_view &prog, State &state) |
| Binds the VM to a program and caller-owned scratch state. | |
| template<bool Cascade = false, typename OutSlots > | |
| constexpr bool | run (std::string_view text, std::size_t start, run_mode mode, OutSlots &out_slots, std::size_t forbid_empty_until=0, match_semantics sem=match_semantics::first) |
Runs the VM over text starting at start. | |
| template<bool Cascade = false, bool Probe = false, typename OutSlots > | |
| constexpr bool | run_general (std::string_view text, std::size_t start, run_mode mode, OutSlots &out_slots, std::size_t *forward_stop=nullptr) |
The general Pike VM loop, also run by the lazy-DFA route on the [s, e] window its two passes located. | |
| template<typename OutSlots > | |
| bool | confirm_at (std::string_view text, std::size_t s, OutSlots &out_slots, std::size_t &stop) |
Confirm a match anchored at s: the forward DFA finds its end and the one-pass table fills the captures, as on the lazy-DFA route. Falls back to the anchored Pike when the pattern is not DFA/one-pass eligible, or when the DFA's leftmost match does not begin at s. | |
| constexpr std::size_t | find_on_subject (std::string_view text, std::size_t pos, std::string_view lit, std::size_t rare, bool inner) const |
The next occurrence of a literal of the pattern's hints at or after pos, by find_literal_adaptive with a density kept for the whole subject. | |
| constexpr void | il_reset_on_new_haystack (std::string_view text) |
| Re-enables the inner-literal route and clears its density counters on a new haystack. | |
| template<typename OutSlots > | |
| bool | run_inner_literal (std::string_view text, std::size_t start, OutSlots &out_slots, bool &abandon, bool density_gate=true) |
The inner-literal search: memmem a required literal, reverse-match the prefix to the match start, forward-confirm — the reverse-inner protocol (regex-automata's ReverseInner). | |
| lookaround_scratch & | lookaround_state () |
| The lookaround sub-scratch, built on first use. | |
| template<bool Cascade, typename OutSlots > | |
| bool | run_class_loop_trailing_la (std::string_view text, std::size_t start, run_mode mode, OutSlots &out_slots) |
| Trailing-lookaround class+: body scan + longest end where lookaround holds. | |
| template<bool Cascade, bool Cp, typename OutSlots > | |
| bool | trailing_la_walk (std::string_view text, std::size_t start, run_mode mode, OutSlots &out_slots) |
| The body of run_class_loop_trailing_la for one body kind. | |
| template<bool Cascade, bool WbEdge, bool WbKept> | |
| constexpr std::size_t | fill_class_spans (std::string_view text, std::size_t start, cp_span *out, std::size_t cap) |
Fills up to cap class_loop matches from start without leaving the route. | |
| constexpr std::size_t | fill_single_class_spans (std::string_view text, std::size_t start, cp_span *out, std::size_t cap) |
Fills up to cap bare single byte-class matches from start without leaving the route. | |
| template<bool WbEdge> | |
| constexpr std::size_t | fill_cp_class_spans (std::string_view text, std::size_t start, cp_span *out, std::size_t cap) |
Fills up to cap cp_class_loop matches from start without leaving the route. | |
| template<bool WbEdge> | |
| constexpr std::size_t | fill_cp_class_spans_wrapped (std::string_view text, std::size_t start, cp_span *out, std::size_t cap) |
fill_cp_class_spans for a pattern with a kept \b/\B wrap: its spans, less those whose wrap does not hold. | |
| template<typename OutSlots > | |
| constexpr void | write_cp_span_slots (OutSlots &out_slots, std::size_t s, std::size_t e) |
| Writes a buffered span into a caller's slots exactly as the per-match path would. | |
| template<typename OutSlots > | |
| constexpr bool | run_cp_class_loop (std::string_view text, std::size_t start, run_mode mode, OutSlots &out_slots) |
Fast path for a whole-pattern code-point class klass_cp, optionally a greedy +. | |
| template<typename OutSlots , typename InClass , typename ScanEnd , typename LastWidth > | |
| constexpr bool | run_possessive_loop_generic (std::string_view text, std::size_t start, run_mode mode, OutSlots &out_slots, const InClass &in_class, const ScanEnd &scan_end, const LastWidth &last_width) |
Shared driver: a possessive class+/++ loop, bare/suffixed (pattern_hints::possessive_prefix_size == 0) or delimited/"quoted" (non-zero); the body's membership comes from in_class / scan_end, so it serves byte- and code-point classes. | |
| template<typename OutSlots > | |
| constexpr bool | run_possessive_byte_loop (std::string_view text, std::size_t start, run_mode mode, OutSlots &out_slots) |
Possessive literal-byte +/++ loop (byte_loop_possessive, e.g. a++), on the shared algorithm of run_possessive_loop_generic. | |
| template<typename OutSlots > | |
| constexpr bool | run_possessive_class_loop (std::string_view text, std::size_t start, run_mode mode, OutSlots &out_slots) |
Possessive class+/++ loop over a BYTE class (klass_loop_possessive). See run_possessive_loop_generic for the shared algorithm. | |
| template<typename OutSlots > | |
| constexpr bool | run_possessive_cp_class_loop (std::string_view text, std::size_t start, run_mode mode, OutSlots &out_slots) |
Possessive class+/++ loop over a CODE-POINT class (klass_cp_loop_possessive), on the decode/membership primitives of run_cp_class_loop (the scan predicate differs per compiler, see in_class). See run_possessive_loop_generic for the shared algorithm. | |
| template<bool SkipSaves = false> | |
| constexpr std::size_t | match_byte_klass_run (std::string_view text, std::size_t pc, std::size_t s) const |
Matches the run of byte/klass instructions starting at pc, one text byte each, up to the first non-consuming op. Shared by the fixed-shape and alternation fast paths. | |
| constexpr bool | wb_boundaries_ok (std::size_t s, std::size_t e) const |
O(1) lead/trail \b/\B check at match bounds [s, e), for every wb-wrapping fast path (hints 0/1/2 from pattern_hints::wb_lead / pattern_hints::wb_trail). | |
| template<bool SkipSaves> | |
| constexpr std::size_t | match_fixed_body_wb (std::string_view text, std::size_t s) const |
Fixed-shape body match from pattern_hints::body_pc, then B1 \b/\B wrap. | |
| template<typename MatchAt , typename OutSlots > | |
| constexpr bool | fast_search (std::string_view text, std::size_t start, MatchAt match_at, OutSlots &out_slots) |
Leftmost search over the candidate starts of next_candidate, for the first one match_at accepts. Shared by the fast paths that verify a fixed shape at a position. | |
| template<typename OutSlots > | |
| bool | run_pair_filtered_shape (std::string_view text, std::size_t start, OutSlots &out_slots) |
| Search route for a HETEROGENEOUS fixed shape: vector-prefilter two positions, verify each survivor with the ordinary fixed-body walk, hand the sub-block tail to fast_search. | |
| template<typename OutSlots > | |
| constexpr bool | run_fixed_shape (std::string_view text, std::size_t start, run_mode mode, OutSlots &out_slots) |
| Fast path for a whole-pattern fixed-width byte/klass sequence. | |
| template<typename OutSlots > | |
| constexpr void | fill_fixed_saves (std::size_t match_start, OutSlots &out_slots) const |
| Fills the capturing-group slots of a fixed-shape match. Every consuming op is one byte wide, so each save sits at a constant offset from the match start: one linear pass, no re-match. | |
| constexpr bool | run_shape_atom (std::string_view text, std::size_t pc, std::size_t &at, std::size_t e) const |
Consumes the atom at pc at at, within e. | |
| template<typename OutSlots > | |
| constexpr bool | match_run_shape (std::string_view text, std::size_t s, std::size_t e, OutSlots &out_slots) const |
Fills the groups of a match the DFAs found at [s, e) for a program of run shape, by one walk that takes every loop as far as it goes. | |
| template<typename OutSlots > | |
| constexpr std::size_t | match_cp_shape (std::string_view text, std::size_t s, OutSlots &out_slots) const |
Verifies a fixed code-point shape forward from s, filling capture slots as it goes. | |
| template<typename OutSlots > | |
| constexpr bool | fail_slots (OutSlots &out_slots, std::size_t count) const |
| Clears the capture slots for a search that found nothing, and says so. | |
| template<typename OutSlots > | |
| constexpr bool | fail_slots (OutSlots &out_slots) const |
| fail_slots for every slot of the program. | |
| constexpr std::size_t | prefix_run_end (std::string_view text, std::size_t from, std::size_t limit) const |
Where the two-run shape's prefix class run, continued forward from from, stops. | |
| template<typename OutSlots > | |
| constexpr void | fill_two_run_saves (std::string_view text, std::size_t s, std::size_t h, std::size_t lit_end, std::size_t e, OutSlots &out_slots) const |
Fills capture slots for a class+ <literal> class+ match, by anchor rather than by offset. | |
| template<bool Cascade> | |
| constexpr std::size_t | fill_codepoint_class_spans (std::string_view text, std::size_t start, cp_span *out, std::size_t cap) |
Batched twin of run_codepoint_class, filling up to cap maximal spans in ONE call. | |
| template<bool Cascade, typename OutSlots > | |
| constexpr bool | run_codepoint_class (std::string_view text, std::size_t start, run_mode mode, OutSlots &out_slots) |
Fast path for . / a negated class, optionally a greedy +. | |
| template<typename OutSlots > | |
| bool | run_aho_corasick (std::string_view text, std::size_t start, OutSlots &out_slots) |
| Multi-literal search via the automaton ac_ready hands back, cached per regex in detail::regex_immutables. | |
| alternation_density & | alternation_density_for (std::string_view text) const |
| This subject's alternation density, judged anew when the state last sampled another subject. | |
| const alternation_density * | alternation_density_seen (std::string_view text) const |
| The alternation density the state holds for this subject, without sampling it. | |
| const alternation_pairs * | alternation_plan (std::string_view text, std::size_t pos, std::array< std::uint8_t, 8 > mem, std::size_t cnt) const |
| The alternation's probe pairs when this subject's first bytes are dense, for the block scans of run_alternation and fill_alternation_spans, else null (a compile-time storage keeps no state for them, or no plan fits). | |
| bool | alternation_filter_takes (std::string_view text, std::size_t start) const |
| Whether run_alternation will mask this subject's blocks by its pairs or fingerprint: a dense subject, where the Aho-Corasick gate (calibrated against the first-byte scan) would pick the automaton. The filtered scan beats the automaton there. | |
| const alternation_pairs * | alternation_plan_decide (std::string_view text, std::size_t pos, std::array< std::uint8_t, 8 > mem, std::size_t cnt) const |
| Out of line, the half of alternation_plan that resets the density on a new subject, samples it, and builds the plan. | |
| bool | alternation_wide_may_take (std::string_view text) const |
| Whether run_alternation_wide may take this search: an alternation with more first bytes than the small set holds and no single first byte, within the fingerprint plan's branches, on a subject its sample has not refused. The cheap half, inline, so that a refused subject costs no call. | |
| template<typename OutSlots > | |
| std::optional< bool > | run_alternation_wide (std::string_view text, std::size_t start, OutSlots &out_slots, cp_span *spans=nullptr, std::size_t cap=0, std::size_t *filled=nullptr) |
| Search route for an alternation of literals with more first bytes than the small set holds: the blocks the nibble fingerprint marks, verified in branch order (priority unchanged), then the last bytes by the first-byte table. Taken per subject on a sample: where false candidates are dense enough that verifying them costs more than the automaton's walk, it declines to the automaton's gate. Out of line: run is shared by every route. | |
| const alternation_pairs * | variant_plan (std::string_view text, std::size_t start) |
The variants' fingerprint for a program that is not a fixed alternation (branches holding a case-folded i, s or k, folding to non-ASCII), when this subject's sample finds its first bytes dense and the fingerprint's candidates among them sparse. Taken only where next_candidate would scan by the first bytes; decided once per subject. The fingerprint admits every start a match can have and a few more, which the confirming anchored walk rejects. No candidate lands inside a code point: a continuation byte's high nibble is no first byte's. | |
| std::size_t | variant_candidate (std::string_view text, std::size_t pos, const alternation_pairs &plan) const |
The next start at or after pos the variants' fingerprint admits (the first bytes, in the last blocks it cannot read past). | |
| constexpr void | add_branch_nibbles (alternation_pairs &plan, std::size_t branch) const |
Adds the branch at branch to plan's nibble fingerprint, in bucket plan.count % 8: each of its first three positions admits its byte, or every member of its class; a position past the branch's end admits anything (only that bucket loses selectivity). A branch one byte wide leaves the plan without a fingerprint: its bucket would mark every start. | |
| bool | add_variant_nibbles (alternation_pairs &plan, std::size_t branch) const |
Adds the branch at branch to plan's fingerprint, following each byte offset 0 to 2 a path through it can reach: a byte or class admits its members at every offset reached and moves one on; a code-point class admits its ASCII members, and the UTF-8 encoding of each other member laid from each offset reached, and moves on by every width it has; where the straight line ends, every byte is admitted from each offset reached on. A superset of the bytes a match can start with. | |
| alternation_pairs | build_cp_alternation_plan () const |
| The fingerprint of a program laid out as an alternation of straight-line branches (after leading position assertions, which only narrow where a match starts) that is not a fixed alternation: one of its branches holds a code-point class. No pairs: those need a byte at every head. | |
| alternation_pairs | build_alternation_pairs () const |
Each branch's first byte and its farthest byte within 15 of it (the pairs, when every branch opens on a byte), and the branches' nibble fingerprint, read from the split chain in source order as the scans' match_at reads it. A branch that opens on a class leaves the plan without pairs; the fingerprint then carries it alone, whatever its minimum of branches against the pairs. | |
| template<typename OutSlots > | |
| constexpr bool | run_alternation (std::string_view text, std::size_t start, run_mode mode, OutSlots &out_slots) |
| Fast path for an alternation of straight-line branches. | |
| std::size_t | fill_alternation_wide_spans (std::string_view text, std::size_t start, cp_span *out, std::size_t cap, bool &partial, bool &disarm) |
Fills up to cap matches of an alternation with more first bytes than the small set holds, from start, by the scan of run_alternation_wide run from each match's end, without re-entering run(). | |
| bool | alternation_automaton_claims (std::string_view text, std::size_t start) |
Whether run()'s cascade would hand this subject's search at start to the Aho-Corasick automaton rather than to run_alternation: the gate's conditions in the same order, on the same per-subject state, whose verdicts are sticky, so that a batched walk (fill_alternation_spans) asks once and never overrules a routing decision that was measured. The fingerprint run() tries first needs more first bytes than the small set holds, which that walk's eligibility excludes. | |
| constexpr std::size_t | fill_alternation_spans (std::string_view text, std::size_t start, cp_span *out, std::size_t cap) |
Fills up to cap fixed_alternation matches from start without leaving the route. | |
| std::size_t | fill_fixed_shape_spans (std::string_view text, std::size_t start, cp_span *out, std::size_t cap) |
Fills up to cap fixed-shape matches from start without re-entering the route gate. | |
| std::size_t | fill_exact_literal_spans (std::string_view text, std::size_t start, cp_span *out, std::size_t cap) |
Fills up to cap exact-literal matches from start without re-entering the route gate. | |
| std::size_t | fill_inner_literal_spans (std::string_view text, std::size_t start, cp_span *out, std::size_t cap, bool &partial, bool &disarm) |
Fills up to cap inner-literal matches from start without re-entering the route gate. | |
| std::size_t | fill_lazy_dfa_spans (std::string_view text, std::size_t start, cp_span *out, std::size_t cap, bool &partial) |
Fills up to cap lazy-DFA matches from start without re-entering the route gate. | |
| constexpr bool | literal_at (std::string_view text, std::size_t cand, std::size_t len) const |
Tests whether the fixed literal prefix occurs at cand. | |
| template<typename OutSlots > | |
| constexpr bool | replay_literal (std::size_t cand, std::size_t len, OutSlots &out_slots) const |
Fills capture slots for a literal match at cand: replays save instructions at their consumed offsets and checks the chain's zero-width assertions there. | |
| template<typename OutSlots > | |
| bool | run_literal_one_search (std::string_view text, std::size_t start, std::size_t len, OutSlots &out_slots) |
| The whole exact-literal search in one find_prefix, for a pattern_hints::literal_one_search program (called from run_exact_literal). | |
| template<typename OutSlots > | |
| constexpr bool | run_exact_literal (std::string_view text, std::size_t start, run_mode mode, OutSlots &out_slots) |
| Fast path for a pure-literal pattern. | |
| constexpr std::size_t | next_candidate (std::string_view text, std::size_t pos, std::size_t start) const |
First position >= pos that could start a match, per the hints: the prefilter step (literal prefix, rare or unique byte, line start, first-byte set); pos itself when nothing skips. | |
| constexpr bool | seed_viable (std::string_view text, std::size_t pos, std::size_t start) const |
Cheap pre-check before seeding a new thread at pos: live threads may drag the loop through positions the prefilter would skip. Also enforces code-point alignment in text mode. | |
| constexpr bool | assertion_holds (assert_kind kind, std::size_t pos, bool word_ness_flipped) const |
Evaluates a zero-width assertion at pos in the current text. | |
| template<typename OutSlots > | |
| bool | run_bounded_backtrack (std::string_view text, std::size_t start, run_mode mode, OutSlots &out_slots) |
| The general loop's answer, by backtracking under a bit per (instruction, position). | |
| template<typename OutSlots > | |
| bool | backtrack_from (backtrack_frame &frame, std::size_t seed, std::size_t start, run_mode mode, bool cf, OutSlots &out_slots) |
Every branch from pc 0 at seed, in priority order – one start of run_bounded_backtrack. | |
| std::int32_t | backtrack_possessive (backtrack_frame &frame, const instr &instruction, std::int32_t pc, std::size_t &pos, bool cf) |
| A possessive loop's step in backtrack_from. The VM decides it when the thread arrives, so a match consumes (writing the loop's capture, if it has one) and a miss leaves by the exit at the same position. | |
| template<typename OutSlots > | |
| bool | extends_past_end (std::string_view text, std::size_t start, OutSlots &out_slots) |
Whether a match anchored at start could come out differently if text continued past its end: what a caller lexing text that arrives in pieces must know before committing a token. | |
| constexpr bool | cut_short (std::size_t pos) const |
Whether the code point at pos is not all there: past the end of the text, or a sequence the end cuts short – what a class test or a word boundary at pos would read more text to decide. | |
| constexpr void | probe_step (const instr &instruction, std::size_t pos) |
Probe of extends_past_end on a thread about to consume at pos: one alive at the end of the text, or at a code point the end cuts short, would read what comes next. | |
| constexpr bool | reads_right (const lookaround_sub &sub) const |
Whether sub holds an assertion that reads what follows where it stands: $, \Z, \z, \b, \B, \<, \>. | |
| constexpr void | probe_closure (const instr &instruction, std::size_t pos) |
Probe of extends_past_end on an epsilon step at pos: an assertion that looks right, a lookahead, or a possessive test whose answer the end of the text decides. | |
| template<bool Probe = false, typename OutSlots > | |
| constexpr void | step (list_type &clist, list_type &nlist, std::size_t pos, run_mode mode, bool &matched, OutSlots &out_slots) |
Advances every thread of clist by the byte at pos; survivors land in nlist. A thread reaching match records its slots and cuts all lower-priority threads (leftmost-greedy order). | |
| constexpr void | tier1_capture_on_match (list_type &clist, std::size_t i, std::int32_t capture_start_slot, std::size_t start, std::size_t end) |
Tier 1's on-match capture write: if capture_start_slot is not -1, records [start, end) into thread i's capture block, in place. | |
| template<bool Probe = false> | |
| constexpr void | advance_thread (list_type &clist, list_type &nlist, std::size_t i, std::int32_t next_pc, std::size_t next_pos) |
Advances thread i of clist by one consumed byte, seeding its continuation's closure into nlist with its own reference on the thread's capture block (shared until a save copies it). | |
| constexpr const std::size_t * | thread_slots (list_type &clist, std::size_t i) |
Pointer to thread i's slot_count capture values (its COW block), read by match. | |
| constexpr bool | cp_class_matches (const detail::cp_class &cc, char32_t cp) const |
Tests a decoded code point against a klass_cp class: ASCII bitmap below 0x80, binary search of the class's range slice above (constexpr / const paths; cp_class_matches_idx uses the cached tables). The class is already the effective set: a plain positive membership test. | |
| constexpr bool | cp_class_matches_idx (std::size_t cp_index, char32_t cp) |
| Membership by class index (ASCII + European page + sparse hi / bsearch). | |
| template<bool Probe = false> | |
| constexpr void | add_thread (list_type &list, std::int32_t pc0, std::size_t pos, std::size_t initial) |
Adds pc0 and its whole epsilon closure to list — the one closure walk (COW). Each DFS frame carries a capture-block index (in eps_entry::block) rather than mutating a shared working array, so capture state is copy-on-write and there are no slot-restore entries: | |
| constexpr void | cow_release_blocks (list_type &list) |
| Releases the block references a list's threads hold, before the list is reset or the run returns: the one decref site paired with each step→closure incref (keep it single). | |
| constexpr bool | lookaround_holds (std::uint16_t sub_id, std::size_t pos) |
Evaluates a bounded lookaround at pos (true if the thread should proceed). | |
| constexpr bool | single_class_ahead (const instr &body, std::size_t pos) |
L1 peephole — does the single consuming op body match the code point / byte AT pos (ahead)? Mirrors the per-op logic of lookahead_matches for a one-instruction sub-program. | |
| constexpr const instr * | single_atom_body (const lookaround_sub &sub) const |
| The one consuming instruction of a lookaround body that is a single atom, or null. | |
| constexpr bool | single_class_behind (const instr &body, std::size_t pos) |
L1 peephole — does body match the code point / byte ending EXACTLY at pos (behind)? The defining lookbehind trap: the match must END at pos, so the code point is the one whose aligned start s gives s + length == pos (byte mode: pos - 1). | |
| constexpr bool | lookahead_matches (const lookaround_sub &sub, std::size_t pos) |
Lookahead: does the sub-pattern match a prefix starting at pos? A forward Pike simulation bounded to l_max bytes, stopping at the first match (capture-free: any match is a witness). | |
| constexpr bool | unbounded_lookahead_matches (std::uint16_t sub_id, const lookaround_sub &sub, std::size_t pos) |
Unbounded lookahead: does the sub-pattern match a prefix of the text from pos? | |
| constexpr void | fill_ahead_table (const lookaround_sub &sub, lookaround_scratch::ahead_table &table) |
Fills table with every position's answer, for unbounded_lookahead_matches to read. | |
| constexpr bool | lookbehind_matches (std::uint16_t sub_id, const lookaround_sub &sub, std::size_t pos) |
Lookbehind: does the sub-pattern match a window ENDING EXACTLY at pos? | |
| constexpr void | sub_add_thread (thread_list &list, std::int32_t pc0, std::size_t pos, bool &matched) |
| Epsilon-closure for the lookaround sub-VM, on the isolated sub-scratch. | |
Static Public Member Functions | |
| static constexpr std::size_t | run_shape_loop_atom (const program_view &prog, std::size_t begin, std::size_t end) |
The one atom of a loop body [begin, end) that holds nothing else but saves – a group around one atom, repeated: ([aeiou])+. | |
| static constexpr bool | is_run_shape (const program_view &prog) |
Whether prog is saves, atoms (a byte, a byte class, a code-point class) and greedy atom+ and atom* loops, then match: nothing else, no alternation, no lazy loop, no assertion. | |
| static std::uint8_t | nibble3_buckets (const alternation_pairs &plan, std::string_view text, std::size_t at) |
The fingerprint buckets the three bytes at at admit, as the vector scans compute them for a block: a bucket's bit survives where both nibbles of each byte carry it. Every bucket where fewer than three bytes remain, as the scans leave those starts to the first-byte table. | |
| static constexpr bool | no_class_loop_above (const pattern_hints &hints) noexcept |
| Whether no class loop takes the pattern first: the byte-class loop, the code-point one and the three possessive loops sit above every literal, shape and alternation route in the cascade. | |
| static constexpr bool | exact_literal_is_the_route (const pattern_hints &hints) noexcept |
Is the exact-literal route the one run() would take, in its one-search subset? | |
| static constexpr bool | inner_literal_is_the_route (const program_view &prog) noexcept |
Is the inner-literal route the one run() would take for this program? | |
| static constexpr bool | fixed_shape_is_the_route (const program_view &prog) noexcept |
Is the fixed-shape route the one run() would take for this program, in a groupless search? | |
| static constexpr bool | lazy_dfa_is_the_route (const pattern_hints &hints) noexcept |
Is the lazy-DFA route the one run() would actually take for this program? | |
| static constexpr bool | window_cut_before (std::string_view text, std::size_t start, std::size_t candidate) |
Whether no whole code point lies between start and candidate: the candidate IS start, or only continuation bytes separate them. | |
Static Public Attributes | |
| static constexpr std::size_t | lazy_dfa_min_input {512} |
| Below this input length the lazy-DFA route is skipped (the two-pass setup does not amortise). Public: real::basic_match_iterator reads it to decide whether to batch this route. | |
Private Types | |
| using | list_type = std::remove_reference_t< decltype(std::declval< State & >().list_a)> |
The concrete thread-list type taken from the bound State. | |
| using | pool_type = std::remove_reference_t< decltype(std::declval< State & >().pool)> |
The capture-block pool type of the bound State (COW): heap-backed or static_vec. | |
Private Member Functions | |
| bool | ac_candidate_completes (std::string_view text, std::size_t at) |
Does a branch of the alternation COMPLETE at at? | |
| template<typename Dummy = void> | |
| bool | ac_density_favours_automaton (std::string_view text, std::size_t start) |
| Decides ONCE PER HAYSTACK whether the Aho-Corasick automaton should take this alternation's searches, by sampling candidate density at the search start. | |
| void | ensure_immutables () |
Build (or rebuild) the per-regex immutables, race-free: the Tier-A byte program the DFAs run over and its alphabet. Invalidated by program identity (regex_immutables::built_for == prog_.code.data()); the hot path is one atomic load. Not a once_flag: a spent one never rebuilds after assign-onto-warmed (silent 0 matches). Needs no DFA, so the anchored path can call it without the DFA build. | |
| void | ensure_op_table () |
| Build (or rebuild) the one-pass capture extractor, on top of ensure_immutables. | |
| void | ensure_set_search_dfas (detail::regex_immutables &immut, shared_dfa_set &set) |
Builds the search DFAs for immut into this thread's leased set, once. | |
| void | ensure_set_il_prefix_rev (detail::regex_immutables &immut, shared_dfa_set &set) |
Builds the IL-prefix reverse DFA for immut into this thread's leased set, once. | |
| template<typename Fn > | |
| bool | with_search_dfas (Fn &&fn) |
Run fn with this thread's search DFAs for the regex (see dfa_lease), taking no lock. | |
| template<typename Walk > | |
| bool | walk_on_scan_set (const Walk &walk) |
| The forward walk of with_search_dfas, on the set an inner-literal scan already leased. | |
| template<bool Cascade, typename OutSlots > | |
| std::optional< bool > | try_shared_lazy_dfa_search (std::string_view text, std::size_t start, run_mode mode, OutSlots &out_slots) |
Lazy-DFA search route on the shared confirm DFAs. noinline: inlined, its body inflates the x86 class-loop codegen of run (as ac_ready). | |
| template<typename Dummy = void> | |
| const ac_automaton * | ac_ready () |
Build (or rebuild, on a program change) the Aho-Corasick automaton for a fixed_alternation program whose branch count has reached ac_branch_threshold. | |
| const alternation_pairs * | alternation_pairs_ready () const |
| The regex's alternation probe pairs, built once per regex in its immutables, or null when there is no per-regex cache: the caller then scans by the first bytes. Same identity discipline as ac_ready. | |
| constexpr bool | row_key_stale (std::int32_t have, std::int32_t want) const |
Is the state's cached row key stale for want? | |
| void | verify_class_row (detail::regex_immutables &cache, std::size_t class_index) |
Verifies (and if needed fills) the byte row for class_index, then caches it in the state. | |
| constexpr const std::uint8_t * | derive_class_table (std::size_t class_index) |
| Derives the byte row into the VM state: the constant-evaluation path, where no per-regex cache exists. | |
| void | ensure_membership_rows (detail::regex_immutables &cache) const |
| Sizes the per-regex membership rows for this program, if not already. Cold: once per regex, behind an acquire load on the hot path. | |
| void | fill_class_row (detail::regex_immutables &cache, std::size_t class_index) const |
| Fills one byte-class row of the per-regex cache, once. | |
| void | fill_cp_ascii_row (detail::regex_immutables &cache, std::size_t cp_index) const |
| Fills one code-point-class ASCII row of the per-regex cache, once. | |
| void | fill_cp_page_row (detail::regex_immutables &cache, std::size_t cp_index) const |
| Fills one U+0080..U+07FF membership bitmap of the per-regex cache, once. | |
| constexpr const std::uint8_t * | class_table (std::size_t class_index) |
Returns a flat 256-byte membership table for class class_index (one load per byte). | |
| template<bool Cascade, typename OutSlots > | |
| constexpr bool | run_class_loop_anchored (std::string_view text, std::size_t start, run_mode mode, OutSlots &out_slots) |
Cold half of the class-loop route: everything a \A/^ or \Z/$ implies. | |
| template<typename OutSlots > | |
| constexpr bool | run_class_loop_end_anchored (std::string_view text, std::size_t start, run_mode mode, OutSlots &out_slots) |
X+$ / ^X+$ in search mode: the run that ENDS at the anchor, found by walking back. | |
| constexpr const std::uint8_t * | resolve_class_table (std::size_t class_index) |
| Cold half of class_table (the storage-mode resolution), and the only path that writes the state's row cache for a byte class. | |
| constexpr const std::uint8_t * | cp_ascii_table (std::size_t cp_index) |
Byte-indexed membership table for the ASCII bitmap of a cp_class, as class_table for the klass_cp scan loop, keyed negatively so it never collides with a byte class. | |
| constexpr bool | cp_class_holds (const cp_class &cc, char32_t cp) const |
Stateless membership of cp in cc: no VM-state cache touched. | |
| constexpr const std::uint64_t * | cp_page_table (std::size_t cp_index) |
Builds (once, cached) the cp_class's membership bitmap over [U+0080, U+07FF]: one load instead of a range search on two-byte code points (see basic_pike_state::cp_page). | |
| constexpr bool | cp_member_page (std::size_t cp_index, char32_t cp) |
Page-bitmap membership for U+0080..U+07FF. Kept separate so class-loop lambdas can inline it without pulling the sparse-hi path into the European hot stream (\p{N}, accented). | |
| constexpr bool | cp_member_high (std::size_t cp_index, char32_t cp) |
| Membership for cp > U+07FF: sparse 2-stage hi table, else bsearch (small classes / constexpr). The table build is cold-outlined; the per-cp probe is last-hit + bit test. | |
| bool | cp_member_high_unshared (std::size_t cp_index, char32_t cp) |
| cp_member_high for the trailing-lookaround walk, written out rather than called. | |
| void | resolve_hi (std::size_t cp_index) |
Fills the state's sparse-hi memo for cp_index, the cold half of cp_member_high, outlined so the per-code-point path stays a class-key compare and a bit test. | |
| constexpr bool | cp_member_hi (std::size_t cp_index, char32_t cp) |
| Non-ASCII membership: European page bitmap, then sparse 2-stage hi / bsearch. | |
| template<typename OutSlots > | |
| constexpr void | fill_span_slots (OutSlots &out_slots, std::size_t match_start, std::size_t match_end) const |
Writes a class-loop fast-path result into out_slots: the whole-match span in slots 0/1, mirrored into the group's slots for a pattern wrapped in one capturing group ((\w+), ([a-z]+)): the group's span equals the whole match by construction. | |
| constexpr std::size_t | run_cascade_stop (std::string_view text, std::size_t from) const |
The memchr-cascade run tail: the next stop byte at or after from, or the text end. Its own function so the cascade never inlines into the per-byte loop of run_class_loop (that bloat slowed stop-dense short runs); reached only past cascade_run_threshold bytes. | |
| template<bool Cascade> | |
| constexpr std::size_t | class_run_end (std::string_view text, const std::uint8_t *tbl, std::size_t match_start) const |
The end of the maximal run of tbl's members that begins with the member at match_start. | |
| template<bool Cascade, typename OutSlots > | |
| constexpr bool | run_class_loop (std::string_view text, std::size_t start, run_mode mode, OutSlots &out_slots) |
| Fast path for a whole-pattern "class+": a maximal run of class bytes in one scan loop, exactly the VM's greedy result, with no thread lists. | |
Static Private Member Functions | |
| static constexpr run_mode | window_mode (run_mode mode) noexcept |
| The mode the VM fills a window's groups in once the DFAs proved where the match starts. | |
| static void | fill_byte_row (detail::regex_immutables &cache, std::size_t ready_index, const char_class &klass, std::uint8_t *row) |
Expands klass into one flat 256-byte membership row of the per-regex cache, once. | |
| static const cp_hi_table * | cp_hi_build (const program_view &prog, std::size_t cp_index, std::uint64_t key_fp, std::array< cp_hi_cache_entry, 8 > &cache, const cp_hi_table *&last_tab, std::uint64_t &last_fp) |
| Cold path: build a sparse hi table and install it in the thread-local cache. Outlined so the hot membership check never inlines the range-walk builder. | |
| static const cp_hi_table * | cp_hi_cached (const program_view &prog, std::size_t cp_index) |
Thread-local sparse hi tables, keyed by cp_class::fingerprint (set once at intern), so basic_pike_state keeps its size. Hot path: a uint64 load and a sticky compare. | |
| template<typename OutSlots > | |
| static constexpr void | ensure_slot_size (OutSlots &out, std::size_t n) |
Size out without a full npos fill when already sized: ensure_size, or a grow-only resize for the seam tests' std::vector. | |
Private Attributes | |
| const program_view & | prog_ |
| The program being executed (borrowed; a stable lvalue that outlives the VM). | |
| State & | state_ |
| Borrowed reusable scratch state. | |
| shared_dfa_set * | scan_set_ {nullptr} |
| The set an inner-literal scan leased for its prefix reverse, or null. | |
| std::string_view | text_ |
| The subject text for the current run. | |
| std::size_t | forbid_empty_until_ {} |
| Reject an empty match starting below this offset (CPython 3.7+ rule); 0 = none. | |
| match_semantics | sem_ {match_semantics::first} |
| Match semantics for the current run; match_semantics::longest forces the general loop. | |
| bool | extends_ {false} |
| Set by a probing run (extends_past_end) when more text could change the answer. | |
Static Private Attributes | |
| static constexpr std::uint32_t | il_density_probe_candidates {8} |
| Inner-literal density gate: candidates sampled across the haystack before the verdict. | |
| static constexpr std::size_t | il_density_milli_threshold {60} |
| Candidate density, in candidates per 1000 bytes, at or above which the IL route yields to the DFA. | |
| static constexpr std::size_t | ac_density_sample_bytes {256} |
| AC routing: sample window, and the candidate-work product at or above which the automaton beats the memchr cascade. | |
| static constexpr std::size_t | ac_density_min_span {64} |
| Shortest span an early verdict may rest on. | |
| static constexpr std::size_t | ac_density_work_threshold {550} |
Product (candidates per 1000 bytes) * branch_count at or above which the automaton wins, at or above ac_branch_threshold branches. | |
| static constexpr std::uint16_t | ac_branch_floor {4} |
| Fewest branches the automaton is ever considered for; below this nothing is measured. | |
| static constexpr std::size_t | ac_completion_pct {15} |
| Percentage of sampled candidates that may COMPLETE a branch and still leave the automaton ahead. Above it the cascade wins whatever the candidate density says. | |
| static constexpr std::size_t | ac_density_work_threshold_low {1400} |
| The same product for ac_branch_floor .. ac_branch_threshold branches, where the safe direction is reversed. | |
| static constexpr std::size_t | ac_completion_walk_budget {1024} |
| Branch walks the completion half of the sample may spend per decision: each verified candidate walks every branch (a thousand-word alternation spent ~2 M instructions deciding a ~4 k search). Past it, candidates count for density only; twelve branches still verify 85. | |
| static constexpr std::uint16_t | ac_branch_threshold {12} |
| Branch count of a pattern_hints::fixed_alternation at or above which one Aho-Corasick walk beats the memchr cascade of pattern_hints::small_set (none past 8 first bytes). Just below, the automaton loses on prose; 12, not 11: AC must beat the VM-branch path here. | |
| static constexpr std::uint32_t | cp_page_max {0x7FFU} |
Highest code point covered by the cp_page bitmap (the 2-byte UTF-8 range). | |
| static constexpr std::size_t | cascade_run_threshold {32} |
Accepted-byte count after which a class+ run switches from the per-byte advance to a memchr-cascade to the next stop byte, so stop-dense short runs stay at baseline. | |
| static constexpr std::uint32_t | cp_hi_range_threshold {20U} |
| Below this many total ranges, high-cp membership stays on bsearch (small scripts). The classes straddling the crossover pull opposite ways on both ISAs; this value sits in that gap, and moving it past either costs that class. | |
The Pike VM, generic over the scratch-state container policy.
| State | A basic_pike_state instantiation (vector- or static-backed). |
| StateBoundToProgram | The caller guarantees this state never serves a second program (fresh per search, or owned by a walk over one regex), so the membership-row accessors skip the per-run() program-identity compare (2.9 points of a [a-z]+ walk). Defaults to false: an embedder holding a state across regexes (Python binding, meta-seam harness) needs it. |
|
inlineconstexpr |
Binds the VM to a program and caller-owned scratch state.
| [in] | prog | The compiled program to execute. |
| [in,out] | state | Reusable scratch (borrowed; must outlive the VM). |
|
inlineprivate |
Does a branch of the alternation COMPLETE at at?
Walks the split chain in source order, asking match_byte_klass_run per branch, as run_alternation and fill_alternation_spans do: no thread lists, no capture work. A copy, not a shared call: relocating those hot bodies risks a regression costing more than this gate wins. The copies agree because they ask the same primitive in the same order.
| [in] | text | The subject. |
| [in] | at | A candidate position (a branch head byte occurs there). |
at.
|
inlineprivate |
Decides ONCE PER HAYSTACK whether the Aho-Corasick automaton should take this alternation's searches, by sampling candidate density at the search start.
Sticky per subject data pointer (as pike_state::il_abandoned), since find_iter re-enters search() per match, and short per-match searches would never see the density (and would pay the sample each time). Reuses next_candidate, so "candidate" means what it means to the cascade.
| [in] | text | Subject. |
| [in] | start | Where this search begins; the window is measured from here. |
true when the automaton should take over. Storages without the guard fields (the compile-time scratch) answer true unconditionally, preserving their behaviour.
|
inlineprivate |
Build (or rebuild, on a program change) the Aho-Corasick automaton for a fixed_alternation program whose branch count has reached ac_branch_threshold.
noinline, NOT cold: inlined into the dispatch chain of run() it regresses run_class_loop on x86 by presence alone, and cold would deoptimize a function called on every AC-eligible search.
search() (a per-state automaton would be rebuilt per search). Its identity atomic is its own, never folded into built_for: only this route consults it.| Dummy | Never named by a caller: a member template with one if constexpr-gated call site is not emitted (nor counted uncovered) for instantiations that never take the route. |
nullptr when this program has none (not a fixed alternation past the threshold, or a branch's icase-fold expansion would exceed ac_max_branch_expansion); the caller then falls back to run_alternation.
|
inlineconstexpr |
Adds the branch at branch to plan's nibble fingerprint, in bucket plan.count % 8: each of its first three positions admits its byte, or every member of its class; a position past the branch's end admits anything (only that bucket loses selectivity). A branch one byte wide leaves the plan without a fingerprint: its bucket would mark every start.
| [in,out] | plan | The plan being built; plan.count is this branch's index. |
| [in] | branch | The branch's first instruction. |
|
inlineconstexpr |
Adds pc0 and its whole epsilon closure to list — the one closure walk (COW). Each DFS frame carries a capture-block index (in eps_entry::block) rather than mutating a shared working array, so capture state is copy-on-write and there are no slot-restore entries:
split shares (incref: one ref → the two pushed frames), jump transfers it;save — the ONLY write — copies-on-write first if the block is shared (capture_pool::cow_write);| [in,out] | list | The thread list to populate (its slots hold one block index per pc). |
| [in] | pc0 | The program counter to seed from. |
| [in] | pos | The current input position. |
| [in] | initial | Capture-free: group 0's START (full width, hence std::size_t rather than an eps_entry field). Otherwise the starting block, whose ref the caller already owns. |
|
inline |
Adds the branch at branch to plan's fingerprint, following each byte offset 0 to 2 a path through it can reach: a byte or class admits its members at every offset reached and moves one on; a code-point class admits its ASCII members, and the UTF-8 encoding of each other member laid from each offset reached, and moves on by every width it has; where the straight line ends, every byte is admitted from each offset reached on. A superset of the bytes a match can start with.
| [in,out] | plan | The plan being built; plan.count is this branch's bucket. |
| [in] | branch | The branch's first instruction. |
|
inlineconstexpr |
Advances thread i of clist by one consumed byte, seeding its continuation's closure into nlist with its own reference on the thread's capture block (shared until a save copies it).
| [in] | clist | Current list, holding the thread to advance. |
| [in,out] | nlist | Next list, receiving the continuation's closure. |
| [in] | i | Thread index within clist. |
| [in] | next_pc | Program counter the thread continues at. |
| [in] | next_pos | Text position the thread continues at. |
|
inline |
Whether run()'s cascade would hand this subject's search at start to the Aho-Corasick automaton rather than to run_alternation: the gate's conditions in the same order, on the same per-subject state, whose verdicts are sticky, so that a batched walk (fill_alternation_spans) asks once and never overrules a routing decision that was measured. The fingerprint run() tries first needs more first bytes than the small set holds, which that walk's eligibility excludes.
| [in] | text | The subject. |
| [in] | start | Where the walk begins. |
|
inline |
This subject's alternation density, judged anew when the state last sampled another subject.
| [in] | text | The subject. |
|
inline |
The alternation density the state holds for this subject, without sampling it.
| [in] | text | The subject. |
|
inline |
Whether run_alternation will mask this subject's blocks by its pairs or fingerprint: a dense subject, where the Aho-Corasick gate (calibrated against the first-byte scan) would pick the automaton. The filtered scan beats the automaton there.
| [in] | text | The subject. |
| [in] | start | Where the search starts. |
|
inlineprivate |
The regex's alternation probe pairs, built once per regex in its immutables, or null when there is no per-regex cache: the caller then scans by the first bytes. Same identity discipline as ac_ready.
|
inline |
The alternation's probe pairs when this subject's first bytes are dense, for the block scans of run_alternation and fill_alternation_spans, else null (a compile-time storage keeps no state for them, or no plan fits).
Decided once per subject, from alternation_sample_bytes bytes at pos, and only when at least alternation_sample_min remain: a short subject is scanned by the first bytes, unsampled. The plan is built once per program from its branches, read as the scans' match_at reads them. Deciding is out of line (alternation_plan_decide), so the callers' first-byte loops keep their code.
| [in] | text | The subject. |
| [in] | pos | Where the scan starts. |
| [in] | mem | The branches' first bytes. |
| [in] | cnt | How many of mem are valid. |
|
inline |
Out of line, the half of alternation_plan that resets the density on a new subject, samples it, and builds the plan.
| [in] | text | The subject. |
| [in] | pos | Where the scan starts. |
| [in] | mem | The branches' first bytes, padded. |
| [in] | cnt | How many of mem are valid. |
|
inline |
Whether run_alternation_wide may take this search: an alternation with more first bytes than the small set holds and no single first byte, within the fingerprint plan's branches, on a subject its sample has not refused. The cheap half, inline, so that a refused subject costs no call.
| [in] | text | The subject. |
|
inlineconstexpr |
Evaluates a zero-width assertion at pos in the current text.
| [in] | kind | The assertion to evaluate. |
| [in] | pos | The position at which to evaluate it. |
| [in] | word_ness_flipped | For a word assert (\b \B \< \>), whether this instruction flips the program's default word-ness — set for a scoped (?a:...) / (?-a:...) island. |
true if the assertion holds there.
|
inline |
Every branch from pc 0 at seed, in priority order – one start of run_bounded_backtrack.
| [in,out] | frame | The search's marks, leaf flags, slots and pending branches. |
| [in] | seed | The start. |
| [in] | start | The search's start (row 0). |
| [in] | mode | Anchoring (a full match must end at the text's end). |
| [in] | cf | The program's capture-free walk: only group 0's start is carried. |
| [out] | out_slots | Capture slots, filled on a match. |
|
inline |
A possessive loop's step in backtrack_from. The VM decides it when the thread arrives, so a match consumes (writing the loop's capture, if it has one) and a miss leaves by the exit at the same position.
| [in,out] | frame | The search's frame (slots, pending restores). |
| [in] | instruction | The possessive instruction. |
| [in] | pc | Its program counter. |
| [in,out] | pos | The position; advanced by one on a match. |
| [in] | cf | The program's capture-free walk: the loop's capture is not recorded. |
|
inline |
Each branch's first byte and its farthest byte within 15 of it (the pairs, when every branch opens on a byte), and the branches' nibble fingerprint, read from the split chain in source order as the scans' match_at reads it. A branch that opens on a class leaves the plan without pairs; the fingerprint then carries it alone, whatever its minimum of branches against the pairs.
count == 0 when a branch opens on neither a byte nor a class, the branches outnumber it, or neither filter holds.
|
inline |
The fingerprint of a program laid out as an alternation of straight-line branches (after leading position assertions, which only narrow where a match starts) that is not a fixed alternation: one of its branches holds a code-point class. No pairs: those need a byte at every head.
count == 0 when the layout is not that, a branch declines (add_variant_nibbles), the branches outnumber the buckets' capacity, or there is no fingerprint on this target.
|
inlineconstexprprivate |
The end of the maximal run of tbl's members that begins with the member at match_start.
| Cascade | Past cascade_run_threshold bytes, hand the rest to run_cascade_stop, which is sound because a byte-class run never validates UTF-8. |
| [in] | text | Subject. |
| [in] | tbl | The class's byte membership table. |
| [in] | match_start | A member's offset. |
|
inlineconstexprprivate |
Returns a flat 256-byte membership table for class class_index (one load per byte).
always_inline is load-bearing: out of line, the call frame costs more than the inlined accessor (6.2 M instructions against 0.85 M on a 64 KiB [a-z]+ walk). It fits only with derive_class_table kept out of it.
| [in] | class_index | Index into the program's interned classes. |
|
inline |
Confirm a match anchored at s: the forward DFA finds its end and the one-pass table fills the captures, as on the lazy-DFA route. Falls back to the anchored Pike when the pattern is not DFA/one-pass eligible, or when the DFA's leftmost match does not begin at s.
| [in] | text | Subject. |
| [in] | s | Candidate match start to confirm. |
| [out] | out_slots | Capture slots, filled on a match. |
| [out] | stop | How far the confirm reached, for the linearity backstop. |
s.
|
inlineconstexpr |
Releases the block references a list's threads hold, before the list is reset or the run returns: the one decref site paired with each step→closure incref (keep it single).
| [in] | list | List whose threads' block references are dropped. |
|
inlineconstexprprivate |
Byte-indexed membership table for the ASCII bitmap of a cp_class, as class_table for the klass_cp scan loop, keyed negatively so it never collides with a byte class.
| [in] | cp_index | Index into the program's cp_classes. |
|
inlineconstexprprivate |
Stateless membership of cp in cc: no VM-state cache touched.
The cached paths (cp_member_page, cp_member_high) hold ONE class each; the inner-literal reverse alternates with the confirm's classes per candidate, so it reads the class directly (ASCII bitmap, else a binary search of its ranges).
| [in] | cc | The code-point class. |
| [in] | cp | The code point. |
true if cp is a member.
|
inlineconstexpr |
Tests a decoded code point against a klass_cp class: ASCII bitmap below 0x80, binary search of the class's range slice above (constexpr / const paths; cp_class_matches_idx uses the cached tables). The class is already the effective set: a plain positive membership test.
| [in] | cc | The code-point class (from prog_.cp_classes). |
| [in] | cp | The decoded code point. |
cp is a member.
|
inlineconstexpr |
Membership by class index (ASCII + European page + sparse hi / bsearch).
| [in] | cp_index | Index of the code-point class. |
| [in] | cp | The decoded code point. |
cp is a member.
|
inlinestaticprivate |
Cold path: build a sparse hi table and install it in the thread-local cache. Outlined so the hot membership check never inlines the range-walk builder.
| [in] | prog | Program owning the class. |
| [in] | cp_index | Index of the code-point class in prog.cp_classes. |
| [in] | key_fp | The class's content fingerprint, the cache key. |
| [in,out] | cache | Thread-local entries, one of which is overwritten. |
| [out] | last_tab | Sticky last-hit table pointer, set to the built table. |
| [out] | last_fp | Sticky last-hit fingerprint, set to key_fp. |
cache.
|
inlinestaticprivate |
Thread-local sparse hi tables, keyed by cp_class::fingerprint (set once at intern), so basic_pike_state keeps its size. Hot path: a uint64 load and a sticky compare.
| [in] | prog | Program owning the class. |
| [in] | cp_index | Index of the code-point class in prog.cp_classes. |
|
inlineconstexprprivate |
Non-ASCII membership: European page bitmap, then sparse 2-stage hi / bsearch.
| [in] | cp_index | Index of the code-point class. |
| [in] | cp | Code point at or above U+0080. |
cp is a member.
|
inlineconstexprprivate |
Membership for cp > U+07FF: sparse 2-stage hi table, else bsearch (small classes / constexpr). The table build is cold-outlined; the per-cp probe is last-hit + bit test.
| [in] | cp_index | Index of the code-point class. |
| [in] | cp | Code point above cp_page_max. |
cp is a member.
|
inlineprivate |
cp_member_high for the trailing-lookaround walk, written out rather than called.
A second call site of cp_member_high, even from this cold walk, makes GCC stop inlining it into fill_cp_class_spans, which charges the code-point class scans. Runtime only (the walk is dynamic-only).
| [in] | cp_index | Index of the code-point class. |
| [in] | cp | Code point above cp_page_max. |
cp is a member.
|
inlineconstexprprivate |
Page-bitmap membership for U+0080..U+07FF. Kept separate so class-loop lambdas can inline it without pulling the sparse-hi path into the European hot stream (\p{N}, accented).
| [in] | cp_index | Index of the code-point class. |
| [in] | cp | Code point in U+0080..U+07FF; outside that range the bit index is meaningless. |
cp is a member.
|
inlineconstexprprivate |
Builds (once, cached) the cp_class's membership bitmap over [U+0080, U+07FF]: one load instead of a range search on two-byte code points (see basic_pike_state::cp_page).
| [in] | cp_index | Index into the program's cp_classes. |
cp - 0x80).
|
inlineconstexpr |
Whether the code point at pos is not all there: past the end of the text, or a sequence the end cuts short – what a class test or a word boundary at pos would read more text to decide.
| [in] | pos | The position. |
pos.
|
inlineconstexprprivate |
Derives the byte row into the VM state: the constant-evaluation path, where no per-regex cache exists.
noinline is load-bearing: inlined, this 256-iteration loop keeps class_table out of basic_match_iterator::advance (figures at class_table).
| [in] | class_index | Index into the program's interned byte classes. |
|
inlineprivate |
Sizes the per-regex membership rows for this program, if not already. Cold: once per regex, behind an acquire load on the hot path.
| [in,out] | cache | The per-regex immutables. |
|
inlineprivate |
Build (or rebuild) the one-pass capture extractor, on top of ensure_immutables.
Split out: it is the expensive half (on a first capture search, more than the byte program and the lazy DFA together) and only some routes consult it. Guarded by its own regex_immutables::op_table_for, which ensure_immutables clears on rebuild, so a reassigned regex never reads a stale extractor.
|
inlineprivate |
Builds the IL-prefix reverse DFA for immut into this thread's leased set, once.
| [in] | immut | Per-regex immutables naming the program to build for. |
| [in,out] | set | The leased DFA set to populate. |
|
inlineprivate |
Builds the search DFAs for immut into this thread's leased set, once.
| [in] | immut | Per-regex immutables naming the program to build for. |
| [in,out] | set | The leased DFA set to populate. |
|
inlinestaticconstexprprivate |
Size out without a full npos fill when already sized: ensure_size, or a grow-only resize for the seam tests' std::vector.
| [in,out] | out | Slot storage to grow. |
| [in] | n | Minimum size required; out is never shrunk. |
|
inlinestaticconstexprnoexcept |
Is the exact-literal route the one run() would take, in its one-search subset?
Mirrors run()'s cascade, like lazy_dfa_is_the_route: only the class loops sit above this route. literal_one_search carries the rest (no capture, assertion or anchor, a literal of >= 2 bytes, prefix_size == exact_literal_len), so the answer is find_prefix plus two stores.
| [in] | hints | The program's shape hints. |
|
inline |
Whether a match anchored at start could come out differently if text continued past its end: what a caller lexing text that arrives in pieces must know before committing a token.
Runs the general loop in prefix mode with probes (probe_step, probe_closure). A thread still alive when the text runs out outranks the match found (prefix mode cuts lower priorities), and so can anything that read the end as an end: an assertion looking right ($, \Z, \b, ...), a lookahead window reaching it, a code point it cuts short. Conservative: may say true where a closer look says false, never the reverse (a waiting caller loses time, not tokens).
| [in] | text | The text available so far. |
| [in] | start | Where the match is anchored. |
| [out] | out_slots | The match on text as it stands (prefix mode). |
|
inlineconstexpr |
fail_slots for every slot of the program.
| [out] | out_slots | The slots. |
|
inlineconstexpr |
Clears the capture slots for a search that found nothing, and says so.
| [out] | out_slots | The slots, count of them set to real::npos. |
| [in] | count | How many: the program's slots, or the two a groupless route writes. |
|
inlineconstexpr |
Leftmost search over the candidate starts of next_candidate, for the first one match_at accepts. Shared by the fast paths that verify a fixed shape at a position.
| MatchAt | Callable std::size_t(std::size_t pos): the match end at pos, or npos. |
| OutSlots | Output slot container (already sized to two). |
| [in] | text | The subject text. |
| [in] | start | Index to begin at. |
| [in] | match_at | The per-position matcher. |
| [out] | out_slots | Receives the (start, end) span on success. |
true if a match was found.
|
inlineconstexpr |
Fills table with every position's answer, for unbounded_lookahead_matches to read.
| [in] | sub | The lookaround sub-program. |
| [in,out] | table | The table to fill for the current subject. |
|
inlineconstexpr |
Fills up to cap fixed_alternation matches from start without leaving the route.
The route is return-dominated (see run_alternation). Small-set shape only (2..8 distinct first bytes, pattern_hints::small_set_size), which the mask scan needs; other alternations are not batched, rather than growing a second scan body here (docs/MEASUREMENT.md §5.4).
The scan is a COPY of run_alternation's, not a shared call: relocating that measured hot body risks a regression worse than this filler's gain.
| [in] | text | The subject. |
| [in] | start | Where to begin. |
| [out] | out | Buffer for the spans found. |
| [in] | cap | Capacity of out; the walk stops there and resumes from the last end. |
|
inline |
Fills up to cap matches of an alternation with more first bytes than the small set holds, from start, by the scan of run_alternation_wide run from each match's end, without re-entering run().
run() hands such an alternation to that route where alternation_wide_may_take says so and the route takes the subject on its sample (a verdict sticky per subject); otherwise the automaton's gate decides. The filler asks the same questions, and where either declines it writes nothing and says so through disarm: the walk leaves every later search to run().
| [in] | text | The subject. |
| [in] | start | Where to begin. |
| [out] | out | Buffer for the spans found. |
| [in] | cap | Capacity of out. |
| [out] | partial | The fill stopped without proving the subject spent. |
| [out] | disarm | The route declined this subject: batch no more of it. |
|
inlinestaticprivate |
Expands klass into one flat 256-byte membership row of the per-regex cache, once.
Shared body of fill_class_row and fill_cp_ascii_row (cold, once per class). fill_cp_page_row is not folded in: it builds a 30-word bitmap from range pairs.
| [in,out] | cache | The per-regex immutables. |
| [in] | ready_index | Index of this row's ready bit. |
| [in] | klass | The membership set to expand. |
| [out] | row | Destination, 256 bytes. |
|
inlineprivate |
Fills one byte-class row of the per-regex cache, once.
| [in,out] | cache | The per-regex immutables. |
| [in] | class_index | Index into the program's interned byte classes. |
|
inlineconstexpr |
Fills up to cap class_loop matches from start without leaving the route.
The byte-class twin of fill_cp_class_spans. On word text this route emits a match every few bytes and the scan is a table lookup per byte, so the per-match return dominates.
| Cascade | Whether the memchr stop-tail applies, chosen once per walk by the caller. |
| [in] | text | The subject. |
| [in] | start | Where to begin. |
| [out] | out | Buffer for the spans found. |
| [in] | cap | Capacity of out. |
refill_batch must be re-measured on BOTH ISAs against rows that never touch it: fillers move the translation unit's inline budget (a ./negated-class filler took back most of this one's gain; docs/design.dox §10.1).
|
inlineconstexpr |
Batched twin of run_codepoint_class, filling up to cap maximal spans in ONE call.
Otherwise the ./negated-class shape pays a full route entry per match, the other class routes one per sixteen. Not a flag on the existing function: widening a shared scan lambda by one branch charges the property-class rows that never use it.
Search semantics only: basic_match_iterator excludes anchored shapes from batching, and run_mode::full keeps run_codepoint_class.
| Cascade | Select the memchr-cascade run scan, chosen once per walk. |
| [in] | text | The subject. |
| [in] | start | Byte offset to begin at. |
| [out] | out | Receives the spans. |
| [in] | cap | Capacity of out. |
|
inlineprivate |
Fills one code-point-class ASCII row of the per-regex cache, once.
| [in,out] | cache | The per-regex immutables. |
| [in] | cp_index | Index into the program's code-point classes. |
|
inlineconstexpr |
Fills up to cap cp_class_loop matches from start without leaving the route.
Most of a single-code-point row is the per-match return (run()'s dispatch, fill_span_slots, the iterator's re-entry), not the scan: a buffer amortises it over cap matches and hoists asc.
The caller (basic_match_iterator) guards search semantics and no \b/\B wrap (a kept one goes through fill_cp_class_spans_wrapped).
| [in] | text | The subject. |
| [in] | start | Where to begin. |
| [out] | out | Buffer for the spans found. |
| [in] | cap | Capacity of out; the walk stops there and resumes from the last end. |
|
inlineconstexpr |
fill_cp_class_spans for a pattern with a kept \b/\B wrap: its spans, less those whose wrap does not hold.
Filters the plain filler's batches (the per-match route skips a failing run whole) and refills until a span survives or the runs are spent: never an empty batch while runs remain (the iterator reads one as the end). Kept apart and cold: a template parameter on the plain filler changes GCC's inlining of its code-point lookup.
| [in] | text | The subject. |
| [in] | start | Where to begin. |
| [out] | out | Buffer for the spans found. |
| [in] | cap | Capacity of out. |
|
inlineprivate |
Fills one U+0080..U+07FF membership bitmap of the per-regex cache, once.
| [in,out] | cache | The per-regex immutables. |
| [in] | cp_index | Index into the program's code-point classes. |
|
inline |
Fills up to cap exact-literal matches from start without re-entering the route gate.
Enlarging refill_batch charges no other row; one extra comparison on the hot path of advance does (see run_literal_one_search).
No partial state: find_prefix scans to the subject's end, so an empty return proves exhaustion.
| [in] | text | The subject. |
| [in] | start | Where to begin. |
| [out] | out | Buffer for the spans found. |
| [in] | cap | Capacity of out; the walk stops there and resumes from the last end. |
|
inlineconstexpr |
Fills the capturing-group slots of a fixed-shape match. Every consuming op is one byte wide, so each save sits at a constant offset from the match start: one linear pass, no re-match.
Starts at pattern_hints::body_pc, not 1: a leading \b/\B sits at pc 1 and the walk would break on it at once, filling no group (\B(\w){2}).
| [in] | match_start | Byte offset where the match begins. |
| [out] | out_slots | Receives the group slots. |
|
inline |
Fills up to cap fixed-shape matches from start without re-entering the route gate.
Calls run_fixed_shape rather than copying it, as fill_inner_literal_spans calls its route: the scan and verify are the per-match walk's own, so the two cannot disagree, and what goes is the walk's return through the iterator and run()'s cascade per match – about a quarter of a dense date row.
| [in] | text | The subject. |
| [in] | start | Where the walk resumes. |
| [out] | out | The spans found. |
| [in] | cap | Capacity of out. |
|
inline |
Fills up to cap inner-literal matches from start without re-entering the route gate.
The per-match route bills one engine entry per match, flat across densities: the return is the cost. Calls run_inner_literal rather than copying it (unlike fill_alternation_spans): its linearity backstop, density guard, size floor and reverse confirm are state whose duplication would make the batched and per-match walks disagree. The per-haystack reset is shared through il_reset_on_new_haystack.
partial follows the lazy-DFA filler's contract: every abandonment (density, linearity, size floor, unplaceable start) leaves matches for another route; only memmem running out proves exhaustion.
| [in] | text | The subject. |
| [in] | start | Where to begin. |
| [out] | out | Buffer for the spans found. |
| [in] | cap | Capacity of out; the walk stops there and resumes from the last end. |
| [out] | partial | True unless the subject was proven spent; see above. |
| [out] | disarm | Set when the route has ABANDONED this haystack: the caller must stop calling this filler for the rest of the walk, or it pays a failed refill per match (3634 attempts against 7 on a dense date cell, slower than the core). |
|
inline |
Fills up to cap lazy-DFA matches from start without re-entering the route gate.
Patterns no shape recognizer claims ([a-z]+|[0-9]+) land here; per match, the return, not the DFA scan, is the cost. The anchored walks from candidates give way to one forward pass and reverse, as in try_shared_lazy_dfa_search.
partial: an empty return ends the walk in basic_match_iterator::advance, sound only where the scan covers the whole subject. This route can stop with matches still ahead (a DFA quit, under lazy_dfa_min_input bytes left, no shared DFAs yet), so partial stays set unless exhaustion is PROVEN, and the caller resumes on the per-match path.
| [in] | text | The subject. |
| [in] | start | Where to begin. |
| [out] | out | Buffer for the spans found. |
| [in] | cap | Capacity of out; the walk stops there and resumes from the last end. |
| [out] | partial | True unless the subject was proven spent; see above. |
|
inlineconstexpr |
Fills up to cap bare single byte-class matches from start without leaving the route.
Each accepted byte is one match, otherwise a full route entry (several times slower per byte than [a-z]+); no Cascade variant. The caller (real::basic_match_iterator) guards search semantics, no anchor and no \b/\B wrap; the 4-opcode shape rules out groups and a {k,} minimum.
| [in] | text | The subject. |
| [in] | start | Where to begin. |
| [out] | out | Buffer for the spans found. |
| [in] | cap | Capacity of out; the walk stops there and resumes from the last end. |
|
inlineconstexprprivate |
Writes a class-loop fast-path result into out_slots: the whole-match span in slots 0/1, mirrored into the group's slots for a pattern wrapped in one capturing group ((\w+), ([a-z]+)): the group's span equals the whole match by construction.
No npos fill: this writer covers every slot such shapes have.
| [out] | out_slots | Capture slots to write. |
| [in] | match_start | Whole-match start offset. |
| [in] | match_end | Whole-match end offset. |
|
inlineconstexpr |
Fills capture slots for a class+ <literal> class+ match, by anchor rather than by offset.
No fixed widths, but every save lands on one of four positions, decided by where it sits relative to the two loops and the literal (one walk of a dozen instructions per match). When the literal can occur inside the prefix run (pattern_hints::il_fwd_last), the greedy prefix gives back only to the LAST occurrence that leaves the suffix a member, so the literal is moved there first.
| [in] | text | The subject. |
| [in] | s | Match start (the prefix run's beginning). |
| [in] | h | The candidate literal's start. |
| [in] | lit_end | One past the candidate literal. |
| [in] | e | Match end (the suffix run's end). |
| [out] | out_slots | Slots to fill. |
|
inlineconstexpr |
The next occurrence of a literal of the pattern's hints at or after pos, by find_literal_adaptive with a density kept for the whole subject.
A subject whose rarest literal byte proved common stays on the two-byte filter for later searches. A storage without the fields keeps the density per call (correct, re-learns); constant evaluation takes the plain searches.
| [in] | text | The subject. |
| [in] | pos | Index to start from. |
| [in] | lit | The literal (the prefix or the inner literal). |
| [in] | rare | Offset of its rarest byte, from the hints. |
| [in] | inner | Whether lit is the inner literal (each literal keeps its own density). |
|
inlinestaticconstexprnoexcept |
Is the fixed-shape route the one run() would take for this program, in a groupless search?
Mirrors the cascade above run_fixed_shape, one clause per earlier route, for the same reason lazy_dfa_is_the_route states: a batched walk bypasses run(), so batching a shape an earlier route claims takes it off that route. The inner-literal route declines fixed shapes itself.
| [in] | prog | The program. |
|
inlineconstexpr |
Re-enables the inner-literal route and clears its density counters on a new haystack.
One mechanism: run()'s gate and fill_inner_literal_spans must observe the same reset of the sticky per-haystack guards, or a batched walk and a per-match walk silently diverge.
| [in] | text | The subject being scanned. |
|
inlinestaticconstexprnoexcept |
Is the inner-literal route the one run() would take for this program?
Mirrors that route's gate and only the routes with their own run_* body above it (class loops, possessive loops, exact literal): a scan strategy is not a route. prefix_code is required unconditionally, conservatively (the gate needs it only for reverse-confirming storages), so a static regex without a prefix program keeps the per-match walk.
| [in] | prog | The compiled program view. |
|
inlinestaticconstexpr |
Whether prog is saves, atoms (a byte, a byte class, a code-point class) and greedy atom+ and atom* loops, then match: nothing else, no alternation, no lazy loop, no assertion.
For such a program the walk that takes every loop as far as its atom matches, never backing up, is the highest-priority path: at each loop it chose the preferred branch whenever that branch could be taken. So when that walk reaches match its groups are the VM's (match_run_shape), and when an atom fails it, the VM decides.
| [in] | prog | The program. |
|
inlinestaticconstexprnoexcept |
Is the lazy-DFA route the one run() would actually take for this program?
Mirrors run()'s cascade: a batched walk bypasses run(), so batching a shape an EARLIER route claims takes it off that faster route (a plain literal would lose its memmem). The conditions are stated positively, one per route above the lazy DFA, never as a residue of the shape recognizers.
run() above the lazy DFA means adding its hint here. Nothing enforces it; the failure is a silent slowdown on exactly the new route's shape.| [in] | hints | The program's shape hints. |
|
inlineconstexpr |
Tests whether the fixed literal prefix occurs at cand.
| [in] | text | The subject text. |
| [in] | cand | Candidate start offset. |
| [in] | len | Length of the literal (hints.exact_literal_len). |
true if text[cand : cand+len] equals the literal.
|
inlineconstexpr |
Lookahead: does the sub-pattern match a prefix starting at pos? A forward Pike simulation bounded to l_max bytes, stopping at the first match (capture-free: any match is a witness).
| [in] | sub | The lookaround sub-program. |
| [in] | pos | Position the lookaround is evaluated at. |
|
inlineconstexpr |
Evaluates a bounded lookaround at pos (true if the thread should proceed).
Runs a capture-free Pike simulation of the sub-program on the isolated sub-scratch (state_.lookaround): the main state_ is never touched, so an in-flight match is unaffected. Bounded to l_max bytes (linear per position); (?! / (?<! negate the result.
| [in] | sub_id | Index into prog_.lookarounds. |
| [in] | pos | The text position the assertion is evaluated at. |
true if the (possibly negated) assertion holds, so the thread proceeds.
|
inline |
The lookaround sub-scratch, built on first use.
Lazy: search() builds a fresh state, and an eager scratch (two thread lists and a stack) would be built and destroyed on every search of every pattern.
|
inlineconstexpr |
Lookbehind: does the sub-pattern match a window ENDING EXACTLY at pos?
The match must finish precisely at pos (the defining lookbehind trap); a start may lie anywhere in [pos - l_max, pos], pos itself being the empty window. Outside byte mode a start inside a code point can only match the empty window (no sub-program consumes from a continuation byte: code-point ops decode strictly, literals begin with a lead byte, \C forces byte mode), so a thread started at every position is equivalent.
One forward walk per lookbehind (lookaround_scratch::behind_walk) starts a thread at each position and steps them together, so each byte is stepped once per search (per-start windows cost O(l_max^2) per position). A query that moves backward or leaps more than l_max ahead restarts it at pos - l_max.
| [in] | sub_id | Index of the lookaround in prog_.lookarounds. |
| [in] | sub | The lookaround sub-program. |
| [in] | pos | Position the sub must end exactly at. |
pos.
|
inlineconstexpr |
Matches the run of byte/klass instructions starting at pc, one text byte each, up to the first non-consuming op. Shared by the fixed-shape and alternation fast paths.
| [in] | text | The subject text. |
| [in] | pc | Index of the first instruction of the run. |
| [in] | s | Text offset to match from. |
|
inlineconstexpr |
Verifies a fixed code-point shape forward from s, filling capture slots as it goes.
The shape is a sequence of code-point atoms and literal bytes with no loop (pattern_hints::il_cp_shape_eligible), so one linear walk decides the whole match and every save lands on the position the walk has reached — no engine, and no separate capture pass.
| [in] | text | Subject. |
| [in] | s | Candidate match start. |
| [out] | out_slots | Receives the slots (untouched unless the walk succeeds). |
s.
|
inlineconstexpr |
Fixed-shape body match from pattern_hints::body_pc, then B1 \b/\B wrap.
| SkipSaves | Step over interleaved save instructions (a grouped shape, whose slots the caller fills from constant offsets). |
| [in] | text | Subject. |
| [in] | s | Candidate match start. |
|
inlineconstexpr |
Fills the groups of a match the DFAs found at [s, e) for a program of run shape, by one walk that takes every loop as far as it goes.
| [in] | text | The subject. |
| [in] | s | Match start. |
| [in] | e | Match end. |
| [out] | out_slots | Slots to fill. |
match (its groups are the VM's, and it ends at e, where the VM's path ends); false leaves the answer to the VM.
|
inlineconstexpr |
First position >= pos that could start a match, per the hints: the prefilter step (literal prefix, rare or unique byte, line start, first-byte set); pos itself when nothing skips.
| [in] | text | The subject text. |
| [in] | pos | Current position. |
| [in] | start | The run's start offset (for one-shot anchored patterns). |
|
inlinestatic |
The fingerprint buckets the three bytes at at admit, as the vector scans compute them for a block: a bucket's bit survives where both nibbles of each byte carry it. Every bucket where fewer than three bytes remain, as the scans leave those starts to the first-byte table.
| [in] | plan | The fingerprint (its nibbles valid). |
| [in] | text | The subject. |
| [in] | at | The start. |
|
inlinestaticconstexprnoexcept |
Whether no class loop takes the pattern first: the byte-class loop, the code-point one and the three possessive loops sit above every literal, shape and alternation route in the cascade.
| [in] | hints | The program's hints. |
|
inlineconstexpr |
Where the two-run shape's prefix class run, continued forward from from, stops.
| [in] | text | The subject. |
| [in] | from | A position inside or at the end of the run. |
| [in] | limit | Where to stop at the latest. |
from that is not a member, or limit.
|
inlineconstexpr |
Probe of extends_past_end on an epsilon step at pos: an assertion that looks right, a lookahead, or a possessive test whose answer the end of the text decides.
| [in] | instruction | The instruction the closure walk is at. |
| [in] | pos | The position. |
|
inlineconstexpr |
Probe of extends_past_end on a thread about to consume at pos: one alive at the end of the text, or at a code point the end cuts short, would read what comes next.
| [in] | instruction | The thread's instruction. |
| [in] | pos | The position it consumes at. |
|
inlineconstexpr |
Whether sub holds an assertion that reads what follows where it stands: $, \Z, \z, \b, \B, \<, \>.
| [in] | sub | The lookaround (they do not nest, so its code is all its own). |
|
inlineconstexpr |
Fills capture slots for a literal match at cand: replays save instructions at their consumed offsets and checks the chain's zero-width assertions there.
| OutSlots | Output slot container. |
| [in] | cand | Start offset of the literal match. |
| [in] | len | Length of the literal. |
| [out] | out_slots | Receives the capture slots. |
false (and clears out_slots) if an assertion fails here, so the caller tries the next occurrence; true otherwise.
|
inlineconstexprprivate |
Cold half of class_table (the storage-mode resolution), and the only path that writes the state's row cache for a byte class.
Outlined: every byte beside the row-key compare competes for the budget that lets the accessor inline into basic_match_iterator::advance (inlined, it charged the class-scan rows).
| [in] | class_index | Index into the program's interned byte classes. |
|
inlineprivate |
Fills the state's sparse-hi memo for cp_index, the cold half of cp_member_high, outlined so the per-code-point path stays a class-key compare and a bit test.
| [in] | cp_index | Index of the code-point class to resolve. |
|
inlineconstexprprivate |
Is the state's cached row key stale for want?
With StateBoundToProgram a matching key is proof on its own, and the program-identity compare (a pointer chase per run(), so per match on a walk) compiles away. Without it the compare is required: a state carried across regexes would answer from the previous program's rows.
| [in] | have | The key the state last verified (table_class or cp_page_class). |
| [in] | want | The key wanted now. |
true if the row must be re-verified.
|
inlineconstexpr |
Runs the VM over text starting at start.
On success fills out_slots with byte offsets (npos for unset capture slots; slots 0/1 are the whole match).
| Cascade | Select the memchr-cascade class-run variant (chosen once by the caller from stop_set_size, never per match). Off = the plain hot path, byte for byte. |
| OutSlots | Output slot container (resized to the program's slot count). |
| [in] | text | The subject text. |
| [in] | start | Index to begin matching/searching from. |
| [in] | mode | Anchoring mode (run_mode). |
| [out] | out_slots | Receives the capture slots on success. |
| [in] | forbid_empty_until | Reject an empty match starting below this offset (the iterator sets it to the next codepoint boundary, CPython 3.7+ rule). 0 means no restriction. |
| [in] | sem | Match semantics: match_semantics::first (default, leftmost-first) or the experimental match_semantics::longest (which forces the general loop, off every fast path). |
true if a match was found.align-loops only moves the regression between class-scan routes. Accepted.
|
inline |
Multi-literal search via the automaton ac_ready hands back, cached per regex in detail::regex_immutables.
Search mode only: the automaton's leftmost-first scan IS the candidate search. Runtime only, never instantiated for the static storage's State. noinline, NOT cold (as ac_ready), since inside the body of run() it charges the negated-class rows on x86.
| OutSlots | Output slot container. |
| [in] | text | The subject text. |
| [in] | start | Index to begin at. |
| [out] | out_slots | Receives the matched span on success. |
true if some branch matched.
|
inlineconstexpr |
Fast path for an alternation of straight-line branches.
Each branch is a fixed-width byte/klass sequence: at a candidate the branches are tried in source order (read from the split chain) and the first that matches wins, as the VM's thread priority.
| OutSlots | Output slot container. |
| [in] | text | The subject text. |
| [in] | start | Index to begin at. |
| [in] | mode | Anchoring mode. |
| [out] | out_slots | Receives the matched span on success. |
true if some branch matched.
|
inline |
Search route for an alternation of literals with more first bytes than the small set holds: the blocks the nibble fingerprint marks, verified in branch order (priority unchanged), then the last bytes by the first-byte table. Taken per subject on a sample: where false candidates are dense enough that verifying them costs more than the automaton's walk, it declines to the automaton's gate. Out of line: run is shared by every route.
| OutSlots | Output slot container. |
| [in] | text | The subject. |
| [in] | start | Where the search starts. |
| [out] | out_slots | The span, on a match (a single search). |
| [out] | spans | Non-null for a batched walk: the matches from start, each from the previous one's end, instead of one search's span. |
| [in] | cap | Capacity of spans. |
| [out] | filled | With spans: how many were written, fewer than cap only at the end of the subject. |
spans: whether any was written); empty when it declined.
|
inline |
The general loop's answer, by backtracking under a bit per (instruction, position).
Walks the program depth first in the VM's priority order (a split's preferred branch first, each start in turn), marking every (instruction, position) entered; a marked pair prunes the branch, as the VM's list drops a present thread. The first match reached is the VM's answer. A jump into a loop head already entered at this position takes the loop's exit, as in the VM: this position's marks are the VM's seen set there, since only the walk at a position marks it.
Marks persist across starts, and starts the prefilter rules out are skipped. Neither changes the answer: pairs an exploration marked without matching are closed under every transition (a split holds both branches; a jump exits a loop only when its head and body were entered), so none reaches a match. Each pair is entered at most once: O(n x m), the caller holding n x m under bounded_backtrack_bits.
| [in] | text | Subject (already in text_). |
| [in] | start | Byte offset to begin at. |
| [in] | mode | Anchoring: full, prefix or search. |
| [out] | out_slots | Capture slots, filled on a match. |
|
inlineconstexprprivate |
The memchr-cascade run tail: the next stop byte at or after from, or the text end. Its own function so the cascade never inlines into the per-byte loop of run_class_loop (that bloat slowed stop-dense short runs); reached only past cascade_run_threshold bytes.
| [in] | text | Subject. |
| [in] | from | Offset to search from. |
text.size() when none remains.
|
inlineconstexprprivate |
Fast path for a whole-pattern "class+": a maximal run of class bytes in one scan loop, exactly the VM's greedy result, with no thread lists.
No-lookaround path only: trailing-lookaround class+ goes to run_class_loop_trailing_la from outside run. always_inline: on x86 an out-of-line call costs a double-digit share of a match-dense find_iter walk.
| Cascade | Take the memchr-cascade run tail (chosen once per walk from stop_set_size). |
| OutSlots | Output slot container. |
| [in] | text | The subject text. |
| [in] | start | Index to begin at. |
| [in] | mode | Anchoring mode. |
| [out] | out_slots | Receives the (start, end) span on success. |
true if a non-empty run was found.
|
inlineconstexprprivate |
Cold half of the class-loop route: everything a \A/^ or \Z/$ implies.
Outlined so the unanchored path pays exactly one branch. \A/^ is a MODE (search becomes prefix; a region past 0 cannot match); \Z/$ is a LIMIT (run_class_loop_end_anchored). Both were peeled out of the program, so this is all that enforces them.
| Cascade | Whether the memchr stop-tail applies. |
| OutSlots | Output slot container. |
| [in] | text | The subject. |
| [in] | start | Region start. |
| [in] | mode | Anchoring mode as the caller asked for it. |
| [out] | out_slots | Receives the span on success. |
true on a match.
|
inlineconstexprprivate |
X+$ / ^X+$ in search mode: the run that ENDS at the anchor, found by walking back.
A trailing \Z/$ pins the end, so the leftmost match is the maximal class run finishing there: one backward walk from the limit.
\Z (kind 1) is the strict end; $ (kind 2) also matches before ONE final newline. A class holding \n (\s+$) consumes it and ends at the true end (\s$ over "ab\n" is (2, 3)), so the newline is stripped only when the class cannot hold it.
Soundness of picking one limit: this route arms only an UNBOUNDED greedy run (+, {k,}), which over a class holding \n always reaches the true end; a bounded one would not ([ \t\n]{1,2}$ over " \n\n" is (0, 2)). The route-vs-general product test catches a wrong choice.
| OutSlots | Output slot container. |
| [in] | text | The subject. |
| [in] | start | Region start; the match may not begin before it. |
| [in] | mode | Anchoring mode: search, prefix or full. |
| [out] | out_slots | Receives the span on success. |
true when a run ends at the anchor.
|
inline |
Trailing-lookaround class+: body scan + longest end where lookaround holds.
Called from real.hpp / find_iter outside run, once per match. Only this selector inlines into the caller; each walk is out of line, since it must not share a body or inlining unit with run_class_loop (the hot [a-z]+ path). A cold, out-of-line selector makes every match two calls, since clang keeps the walk out of it. Dynamic-only. A code-point body (pattern_hints::trailing_la_cp) walks whole code points: a run holds only valid ones, so its candidate ends are the bytes that are not UTF-8 continuations.
| [in] | text | Subject. |
| [in] | start | Byte offset to begin at. |
| [in] | mode | Anchoring: full, prefix or search. |
| [out] | out_slots | Capture slots, filled on a match. |
|
inlineconstexpr |
Fast path for . / a negated class, optionally a greedy +.
Scans code points as the VM's byte-level expansion would: an ASCII byte matches the ASCII set; a valid 2–4 byte UTF-8 sequence always matches (a negated ASCII class excludes only ASCII); anything else stops, as the VM's lead/continuation branches fail. Covers .+, [^,]+, ., [^,].
| OutSlots | Output slot container. |
| [in] | text | The subject text. |
| [in] | start | Index to begin at. |
| [in] | mode | Anchoring mode. |
| [out] | out_slots | Receives the matched span on success. |
true if at least one codepoint matched.
|
inlineconstexpr |
Fast path for a whole-pattern code-point class klass_cp, optionally a greedy +.
Scans code points directly against the class predicate (ASCII bitmap below 0x80, range binary search above), advancing by the code point's byte width, with no thread lists — the analog of run_class_loop for a Unicode shorthand (\w+, \d+, \s+). A malformed sequence stops the run, exactly as the VM's klass_cp fails on it.
| OutSlots | Output slot container. |
| [in] | text | The subject text. |
| [in] | start | Index to begin at. |
| [in] | mode | Anchoring mode. |
| [out] | out_slots | Receives the matched span on success. |
true if a non-empty run was found. Whether a class member starts at i: membership only, no width.
A strict decode of a byte below 0x80 is {lead, 1, valid}, so asc[lead] is the answer, like in_class in run_class_loop. Kept apart from width, which extend_run needs for the length (asking width for the bit made this scan cost several times the byte-class route's).
|
inlineconstexpr |
Fast path for a pure-literal pattern.
The prefilter locates the bytes; this replays saves directly, with no thread lists. A leading or trailing zero-width assertion (\b, ^, $ …) may fail an occurrence, so search mode scans successive occurrences until they hold (\B2 on "220").
| OutSlots | Output slot container. |
| [in] | text | The subject text. |
| [in] | start | Index to begin at. |
| [in] | mode | Anchoring mode. |
| [out] | out_slots | Receives the capture slots on success. |
true if a match was found.memchr. That needs a density gate (like ac_density_favours_automaton), not a recognition-time redirect.
|
inlineconstexpr |
Fast path for a whole-pattern fixed-width byte/klass sequence.
A straight-line program (no branches/assertions) has one thread: each byte/klass instruction consumes one byte, verified by a single walk. Covers class{n} and \d{4}-\d{2}-\d{2}.
| OutSlots | Output slot container. |
| [in] | text | The subject text. |
| [in] | start | Index to begin at. |
| [in] | mode | Anchoring mode. |
| [out] | out_slots | Receives the matched span on success. |
true if the sequence matched.
|
inlineconstexpr |
The general Pike VM loop, also run by the lazy-DFA route on the [s, e] window its two passes located.
| [in] | text | Subject. |
| [in] | start | Byte offset to begin at. |
| [in] | mode | Anchoring: full, prefix or search. |
| [out] | out_slots | Capture slots, filled on a match. |
| [out] | forward_stop | When non-null, receives how far the forward scan reached — the inner-literal route's linearity backstop. |
|
inline |
The inner-literal search: memmem a required literal, reverse-match the prefix to the match start, forward-confirm — the reverse-inner protocol (regex-automata's ReverseInner).
Search mode only, runtime only (the reverse DFA is not constexpr). Two guards keep it linear: the reverse is bounded below by min_match_start (the previous literal's end), and a literal starting before min_pre_start (the last confirm's forward reach) abandons the scan.
| [in] | text | Subject. |
| [in] | start | Byte offset to begin the scan at. |
| [out] | out_slots | Capture slots, filled on a match. |
| [out] | abandon | Set when a linearity guard trips, so the caller retries the whole search on the core VM. |
| [in] | density_gate | Whether to consult the candidate-density gate. False only for the batched filler, whose candidates mostly complete: the gate counts before confirming. |
abandon set when the route gave up.
|
inline |
The whole exact-literal search in one find_prefix, for a pattern_hints::literal_one_search program (called from run_exact_literal).
noinline on a HOT path: inside run_exact_literal it grows a function sharing an inlining unit with run and the class loops, and the growth alone charges run_codepoint_class (the hazard documented on run). Out of line it costs a short literal one call.
| OutSlots | Output slot container. |
| [in] | text | The subject text. |
| [in] | start | Index to begin searching at. |
| [in] | len | The literal's length (hints.exact_literal_len, >= 2 by the hint). |
| [out] | out_slots | Receives [cand, cand + len] on success. |
true if the literal occurs at or after start.count_matches unchanged: recompiling that shared entry point (every row measures through it) charges other rows. Judge a filler change on machine code first (function sizes in the consumer unit), then on layout.
|
inline |
Search route for a HETEROGENEOUS fixed shape: vector-prefilter two positions, verify each survivor with the ordinary fixed-body walk, hand the sub-block tail to fast_search.
Its own route: hosted inside run_fixed_shape (inline or noinline), it cost instructions on shapes that never enter it ([0-9]{2}:[0-9]{2}). noinline so the body of run does not grow. Search mode only.
| OutSlots | Output slot container. |
| [in] | text | The subject text. |
| [in] | start | Index to begin searching at. |
| [out] | out_slots | Receives the matched span on success, npos on failure (seam parity with run_fixed_shape, through fail_slots). |
true if the sequence matched.
|
inlineconstexpr |
Possessive literal-byte +/++ loop (byte_loop_possessive, e.g. a++), on the shared algorithm of run_possessive_loop_generic.
| [in] | text | Subject. |
| [in] | start | Byte offset to begin at. |
| [in] | mode | Anchoring: full, prefix or search. |
| [out] | out_slots | Capture slots, filled on a match. |
|
inlineconstexpr |
Possessive class+/++ loop over a BYTE class (klass_loop_possessive). See run_possessive_loop_generic for the shared algorithm.
| [in] | text | Subject. |
| [in] | start | Byte offset to begin at. |
| [in] | mode | Anchoring: full, prefix or search. |
| [out] | out_slots | Capture slots, filled on a match. |
|
inlineconstexpr |
Possessive class+/++ loop over a CODE-POINT class (klass_cp_loop_possessive), on the decode/membership primitives of run_cp_class_loop (the scan predicate differs per compiler, see in_class). See run_possessive_loop_generic for the shared algorithm.
| [in] | text | Subject. |
| [in] | start | Byte offset to begin at. |
| [in] | mode | Anchoring: full, prefix or search. |
| [out] | out_slots | Capture slots, filled on a match. |
|
inlineconstexpr |
Shared driver: a possessive class+/++ loop, bare/suffixed (pattern_hints::possessive_prefix_size == 0) or delimited/"quoted" (non-zero); the body's membership comes from in_class / scan_end, so it serves byte- and code-point classes.
A possessive run never gives back, so a required literal SUFFIX (or, delimited, the closing SUFFIX) may fail to follow, with nothing to retry within the attempt. In search mode the retry skips to the failed attempt's body end: sound and linear PROVIDED the eligibility pattern_hints documents held at recognition (prefilter.hpp), since every candidate strictly inside the run fails identically.
| InClass | bool(std::size_t) -> true if the body's class/cp-class accepts the byte/code point starting at that offset. |
| ScanEnd | std::size_t(std::size_t from) -> end of the maximal body run starting at from (from itself when no run starts there). |
| LastWidth | std::size_t(std::size_t end) -> width (in bytes) of the LAST atom of a non-empty run ending at end: 1 for a byte or byte-class body, a backward UTF-8 decode (codepoint_retreat) for a code-point class. Called only with end past the run's start. |
| [in] | text | Subject. |
| [in] | start | Byte offset to begin at. |
| [in] | mode | Anchoring: full, prefix or search. |
| [out] | out_slots | Capture slots, filled on a match. |
| [in] | in_class | Membership test, per InClass. |
| [in] | scan_end | Maximal-run scanner, per ScanEnd. |
| [in] | last_width | Last-atom width, per LastWidth. |
|
inlineconstexpr |
Consumes the atom at pc at at, within e.
| [in] | text | The subject. |
| [in] | pc | The atom's instruction. |
| [in,out] | at | The position; advanced past the atom when it matches. |
| [in] | e | The window's end. |
|
inlinestaticconstexpr |
The one atom of a loop body [begin, end) that holds nothing else but saves – a group around one atom, repeated: ([aeiou])+.
| [in] | prog | The program. |
| [in] | begin | The body's first instruction (the loop split's preferred target). |
| [in] | end | The loop split. |
|
inlineconstexpr |
Cheap pre-check before seeding a new thread at pos: live threads may drag the loop through positions the prefilter would skip. Also enforces code-point alignment in text mode.
| [in] | text | The subject text. |
| [in] | pos | The candidate seed position. |
| [in] | start | The run's start offset. |
true if a fresh thread should be seeded at pos.
|
inlineconstexpr |
The one consuming instruction of a lookaround body that is a single atom, or null.
Such a body is [byte | klass; match] (two slots) or, in text mode, [klass_cp; three continuation slots; match] (five: the compiler always emits the chain), so a code-point class is matched by klass_cp alone.
| [in] | sub | The lookaround sub-program. |
|
inlineconstexpr |
L1 peephole — does the single consuming op body match the code point / byte AT pos (ahead)? Mirrors the per-op logic of lookahead_matches for a one-instruction sub-program.
| [in] | body | The sub-program's single consuming instruction. |
| [in] | pos | Position the lookaround is evaluated at. |
body accepts what starts at pos; false at the text end.
|
inlineconstexpr |
L1 peephole — does body match the code point / byte ending EXACTLY at pos (behind)? The defining lookbehind trap: the match must END at pos, so the code point is the one whose aligned start s gives s + length == pos (byte mode: pos - 1).
| [in] | body | The sub-program's single consuming instruction. |
| [in] | pos | Position the lookaround is evaluated at. |
body accepts the atom ending at pos; false at the text start.
|
inlineconstexpr |
Advances every thread of clist by the byte at pos; survivors land in nlist. A thread reaching match records its slots and cuts all lower-priority threads (leftmost-greedy order).
| OutSlots | Output slot container. |
| [in,out] | clist | The current thread list (consumed). |
| [in,out] | nlist | The next thread list (receives survivors). |
| [in] | pos | The current input position. |
| [in] | mode | Anchoring mode (affects match acceptance). |
| [in,out] | matched | Set to true when a match is recorded. |
| [out] | out_slots | Receives the slots of an accepted match. |
|
inlineconstexpr |
Epsilon-closure for the lookaround sub-VM, on the isolated sub-scratch.
Parks consuming pcs in list and sets matched on reaching the sub's match. A capture-free sub emits no save and no assert_lookaround (nesting is rejected at compile time). Touches only state_.lookaround->stack. Linear: mark_seen dedups within a generation, so each assert_lookaround is evaluated at most once per position (a lookahead costs O(L) there; a lookbehind advances its walk by the bytes since its last query).
| [in,out] | list | The sub thread list to populate. |
| [in] | pc0 | The sub-program counter to seed from. |
| [in] | pos | The current input position (for assertions). |
| [in,out] | matched | Set to true if the sub's match is reachable here. |
|
inlineconstexpr |
Pointer to thread i's slot_count capture values (its COW block), read by match.
| [in] | clist | List holding the thread. |
| [in] | i | Thread index within clist. |
|
inlineconstexpr |
Tier 1's on-match capture write: if capture_start_slot is not -1, records [start, end) into thread i's capture block, in place.
Called ONLY on a confirmed atom match, never before the test: a possessive loop always attempts one more repetition, so an early save would tear the capture into [next attempt's start, this iteration's end). See program.hpp's opcode-family note.
| [in,out] | clist | The current thread list (whose slot this thread owns is updated). |
| [in] | i | Index of the thread in clist. |
| [in] | capture_start_slot | The start slot, or -1 for an uncaptured Tier 1 loop (a no-op). |
| [in] | start | Position before the atom was consumed. |
| [in] | end | Position after the atom was consumed. |
|
inline |
The body of run_class_loop_trailing_la for one body kind.
Out of line but not cold: optimized for size, the walk runs more instructions on GCC.
| Cascade | Whether the byte walk may take its memchr-cascade tail. |
| Cp | The body is a klass_cp (whole code points) rather than a klass. |
| [in] | text | Subject. |
| [in] | start | Byte offset to begin at. |
| [in] | mode | Anchoring: full, prefix or search. |
| [out] | out_slots | Capture slots, filled on a match. |
|
inlineprivate |
Lazy-DFA search route on the shared confirm DFAs. noinline: inlined, its body inflates the x86 class-loop codegen of run (as ac_ready).
| [in] | text | Subject. |
| [in] | start | Byte offset to begin at. |
| [in] | mode | Anchoring: full, prefix or search. |
| [out] | out_slots | Capture slots, filled on a match. |
|
inlineconstexpr |
Unbounded lookahead: does the sub-pattern match a prefix of the text from pos?
A sub-pattern with no bound (.*, +, {n,}) run forward from every position is quadratic. Whether it matches from a position depends only on the text after it, so one pass from the end answers every position: row pos says, per sub-program instruction, whether match is reachable from it at pos. A consuming instruction (a code-point test steps its continuation chain byte by byte) depends only on row pos + 1; match holds; epsilon instructions (jump, split, a position assertion at pos) propagate within the row. Once per subject, O(n x m), into lookaround_scratch::ahead_table.
| [in] | sub_id | Index of the lookaround in prog_.lookarounds. |
| [in] | sub | The lookaround sub-program (l_max < 0). |
| [in] | pos | Position the lookahead is evaluated at. |
pos.
|
inline |
The next start at or after pos the variants' fingerprint admits (the first bytes, in the last blocks it cannot read past).
| [in] | text | The subject. |
| [in] | pos | Where to look from. |
| [in] | plan | The fingerprint (variant_plan). |
|
inline |
The variants' fingerprint for a program that is not a fixed alternation (branches holding a case-folded i, s or k, folding to non-ASCII), when this subject's sample finds its first bytes dense and the fingerprint's candidates among them sparse. Taken only where next_candidate would scan by the first bytes; decided once per subject. The fingerprint admits every start a match can have and a few more, which the confirming anchored walk rejects. No candidate lands inside a code point: a continuation byte's high nibble is no first byte's.
| [in] | text | The subject. |
| [in] | start | Where the search starts. |
|
inlineprivate |
Verifies (and if needed fills) the byte row for class_index, then caches it in the state.
Must stay outlined: inlined, it pushes class_table past what inlines into basic_match_iterator::advance (out of line, class_table costs a tenth of a class-loop walk).
| [in,out] | cache | The per-regex immutables. |
| [in] | class_index | Index into the program's interned byte classes. |
|
inlineprivate |
The forward walk of with_search_dfas, on the set an inner-literal scan already leased.
| [in] | walk | Callable taking (lazy_dfa& fwd). |
walk ran; false when the route must stay on the Pike VM.
|
inlineconstexpr |
O(1) lead/trail \b/\B check at match bounds [s, e), for every wb-wrapping fast path (hints 0/1/2 from pattern_hints::wb_lead / pattern_hints::wb_trail).
| [in] | s | Match start (lead assert position). |
| [in] | e | Match end (trail assert position). |
|
inlinestaticconstexpr |
Whether no whole code point lies between start and candidate: the candidate IS start, or only continuation bytes separate them.
The DROP rule's window-edge guard asks it of a search's first candidate: one reached past a whole non-class character satisfies a dropped leading \b, but when the window begins inside a code point the scan crosses only its tail, and the character before the candidate (unseen, it started before the window) may be a word character.
| [in] | text | The subject. |
| [in] | start | The window's start. |
| [in] | candidate | The first candidate, at or after start. |
candidate is not one the scan crossed.
|
inlinestaticconstexprprivatenoexcept |
The mode the VM fills a window's groups in once the DFAs proved where the match starts.
| [in] | mode | The search's own mode. |
|
inlineprivate |
Run fn with this thread's search DFAs for the regex (see dfa_lease), taking no lock.
| [in] | fn | Callable taking (lazy_dfa& fwd, reverse_dfa& rev). |
fn ran; false when the route must stay on the Pike VM (no immut / ineligible).
|
inlineconstexpr |
Writes a buffered span into a caller's slots exactly as the per-match path would.
Not fill_span_slots called directly: that writer is always_inline, and expanded into the batched walk's emission it charges Python's sub under Apple clang, which instruction counts do not show.
| [out] | out_slots | Slots to fill. |
| [in] | s | Match start. |
| [in] | e | Match end. |
|
staticconstexprprivate |
Percentage of sampled candidates that may COMPLETE a branch and still leave the automaton ahead. Above it the cascade wins whatever the candidate density says.
A false start costs the cascade (verify, reject, resume), not the automaton; a match favours the cascade (it stops there) and charges the automaton a per-match return: at fixed density the completed fraction alone flips the verdict (benchmarks/ac_regime.cpp). The more conservative ISA's balance point: it may decline a win, never take a loss.
|
staticconstexprprivate |
AC routing: sample window, and the candidate-work product at or above which the automaton beats the memchr cascade.
The automaton scans at a flat rate while the cascade spans two orders of magnitude on the same pattern and length, and the crossover moves with branch count (the cascade tries branches in order): the rule is the product (candidates per 1000 bytes) * branch_count (benchmarks/ac_regime.cpp). Density cannot tell a false start from a completed match, which pull in opposite directions, hence ac_completion_pct too. Do not retune these constants against that sweep (it moves the error).
The constant is under the lowest crossover measured over both ISAs: at or above ac_branch_threshold branches, switching early is the safe error, switching late forfeits a win.
|
staticconstexprprivate |
Inner-literal density gate: candidates sampled across the haystack before the verdict.
Above the crossover every candidate is a failed confirm and the core scan wins by a growing margin; the threshold sits conservatively above it, so sparse IL wins (≪ 10/1000) stay. Capture-free only (slot_count ≤ 2): with groups, IL still beat the forced DFA on dense input.
(?:\w+)_(?:\w+)): the crossover moves with the fallback's cost, so one threshold cannot fit every fallback (the fixed-shape one is excluded by route condition). A fix needs that cost as a second variable.