diff --git a/CONTEXT.md b/CONTEXT.md
index 3d57211..4d4d423 100644
--- a/CONTEXT.md
+++ b/CONTEXT.md
@@ -56,26 +56,26 @@ substring engine — and it is absent from every caseless benchmark: no
dedicated caseless substring engine exists in that suite at all; the
caseless columns are contested only by general regex engines.
-**Direct rebar audit (2026-08-17):** `casei` was wired into every caseless
-literal or finite-alternation definition at rebar commit `463d00f`: 18
+**Direct Rebar audit (2026-08-24):** `casei` was wired into every caseless
+literal or finite-alternation definition at Rebar commit `463d00f`: 18
performance workloads and three semantic checks. Rebar's pinned
[performance models](https://github.com/BurntSushi/rebar/blob/463d00f31887e84c38467805b9e3122c314b9521/MODELS.md)
-enumerate every non-overlapping match, rather than return the first. Five rows
-enable Unicode folding and therefore share this repository's folding contract;
-against the selected current leaders, the loop-over-`Find` adapter wins two and
-loses three on both Ice Lake and Sapphire Rapids, with the five-pattern Russian
-row losing by roughly 9× to Hyperscan. Thirteen additional performance rows
-request ASCII-only case matching; they were run and output-verified, but
-`casei` retains stronger Unicode semantics. [`REBAR.md`](REBAR.md) records
-every row, both-host ratios,
-the incompatible `s`/`ſ` behavior check, and the causal diagnosis. Instrumented
-reproduction showed that a stateful one-pass enumerator did not improve the 9×
-row: the five-pattern plan filters on common Cyrillic roots, admits 21.7% of the
-corpus into rune decoding and token-map verification, and never gains the rare
-interior pair-pair anchors selected for a single pattern. The missing iterator
-is therefore an API gap, not the primary performance gap. These measurements
-bound the result here to the 33-row arena contract; Rebar's non-overlapping
-enumeration numbers cannot be borrowed in support of it.
+enumerate every non-overlapping match. Five rows enable Unicode folding and
+share this repository's contract. The first audit won two and lost three on
+both Ice Lake and Sapphire Rapids; its five-pattern Russian row was roughly 9×
+behind Hyperscan. Instrumentation put the loss in a shared filter over common
+Cyrillic starts and decoded confirmation, rather than in repeated API setup.
+
+That result opened the missing cells. The retained follow-up combines tagged
+interior anchors, exact pair replay, one/two/three-byte raw confirmation with
+source-width return, and an exact common-byte origin gate. The current adapter
+uses `Matcher.Each` and wins all five same-contract rows on both hosts. Its
+worst ratios are 0.8794 on Ice Lake and 0.8999 on Sapphire Rapids. Thirteen
+additional rows request ASCII-only matching; they remain visible as
+different-contract stress data, with `casei` winning 9 of all 18 rows on each
+host. [`REBAR.md`](REBAR.md) records the before/after mechanisms, complete
+table, incompatible `s`/`ſ` behavior check, and raw receipts. The arena now
+contains three focused rows derived from the gap and has 36 rows in total.
**Correction (v2, after a three-way prior-art sweep):** dedicated engines
DO exist, with different contracts:
diff --git a/HOW_IT_WORKS.md b/HOW_IT_WORKS.md
index 63f6f61..a1c951a 100644
--- a/HOW_IT_WORKS.md
+++ b/HOW_IT_WORKS.md
@@ -1,213 +1,287 @@
# How casei works
-`casei` gets most of its speed by using a few raw-byte tests before it decodes
-Unicode.
-
## Ten seconds
-It compiles each pattern set into two parts that agree:
+`casei` does cheap byte tests before it pays for Unicode.
+
+It compiles a pattern set into one exact search plan and a set of conservative
+byte filters. The filters reject impossible starts in blocks. The exact plan
+checks every survivor.
```text
exact fold-token plan
/ \
-patterns -> compile once -> first correct match
+patterns -> compile once -> leftmost correct match
\ /
- cheap raw-byte sieve
- (64 starts per block)
+ conservative byte sieve
+```
+
+The sieve may say "maybe" too often. It may never skip a real match. That one
+rule lets the fast path use compact byte tables while the plan keeps complete
+Unicode semantics.
+
+## One minute
+
+Suppose the text is one megabyte long and the pattern is `fatal panic`.
+Checking every byte as a possible start repeats almost the same failure over
+and over.
+
+At compile time, `casei` chooses byte positions that are useful for this exact
+literal. On AVX-512, one comparison covers 64 possible starts. A few comparison
+masks are intersected:
+
+```text
+candidate start 0 1 2 3 4 5 ... 63
+probe at offset A 0 0 1 0 0 0 ... 0
+probe at offset B 0 0 1 0 1 0 ... 0
+probe at offset C 0 0 0 0 1 0 ... 0
+ -------------------- AND
+survivors 0 0 0 0 0 0 ... 0
```
-The sieve rejects impossible starting positions without declaring a match. It
-usually rejects all 64 positions in a block. The complete Unicode plan checks
-any survivors. Every real start reaches that plan, along with false positives
-that cost an extra check.
+No survivor means the whole block is done. A survivor is replayed through the
+exact plan. That plan decides the match, source byte offset, leftmost order,
+and pattern-ID tie.
+
+The compiler can choose adjacent pairs, dispersed bytes, triples, Shufti-style
+tables, pair-pair filters, or tagged multi-pattern anchors. The choice comes
+from the compiled pattern shape. It never comes from the benchmark name.
+
+## Why the advantage exists
+
+The advantage has three layers.
+
+### 1. A smaller question
+
+The input is a literal or finite set of literals under Unicode simple folding.
+The answer is the first leftmost match, or a stream of non-overlapping matches.
-## One minute: searching one block
+A regex engine must preserve classes, captures, repetition, lookarounds, and
+the rest of its language. `casei` can spend compilation on literal-specific
+byte positions and a literal-specific confirmation plan. Arena adapters make
+every entrant answer the same result contract and charge any adaptation to the
+entrant being adapted.
-Imagine looking for `fatal panic` without case sensitivity.
+### 2. Less work
-Checking every possible start spends time on positions that cannot match. Fast
-search engines use filters to skip that work. `casei` specializes its filters
-for literal strings under Unicode simple folding. The caller asks for one
-leftmost answer.
+Most bytes never reach the Unicode executor. A block filter proves that no
+match can start at any of 64 positions. Multi-pattern filters also return an
+eight-bit pattern tag, so the exact replay checks only the literals named by
+the surviving lane.
-`casei` knows at compile time that this is a literal. It chooses useful byte
-positions far enough apart to make accidental alignment rare when the pattern
-permits it. On AVX-512 it loads those positions for 64 possible starts at once,
-compares their case-normalized bytes, and intersects the resulting 64-bit masks.
+The current focused paths add two more pieces:
+
+- A single Unicode literal can compile its one-, two-, and three-byte fold
+ spellings into a bounded raw confirmation. The confirmer advances by the
+ spelling that matched and returns the source width it proved.
+- An eligible multi-pattern plan can choose an exact ASCII byte present in
+ every spelling of every literal. The first occurrence bounds where a match
+ could begin. A dedicated kernel scans eight 64-byte blocks before it tests
+ the ordered masks.
+
+Both are gates into the same plan. Neither is a second matcher.
+
+The [two-host ablations](audit/acceptance/ablations/README.md) remove the origin
+gate, variable confirmation, and returned pattern tags one at a time. Each
+removal loses a required focused field or Rebar row on at least one host.
+
+### 3. Hardware-shaped kernels
+
+AVX-512 BW compares 64 bytes at a time. VBMI performs byte-table lookup in a
+register. Mask registers hold candidate sets without converting them back to
+ordinary vectors.
+
+Instruction latency still matters. On Ice Lake, OpcodeX reports three-cycle
+latency and one-per-cycle reciprocal throughput for the unmasked ZMM `VPERMB`
+used by the hot table loops. A loop that starts one block and waits leaves issue
+slots unused. The hand-written kernels carry independent blocks through those
+three cycles.
+
+The exact common-byte gate is a simple example:
```text
-candidate starts 0 1 2 3 4 5 ... 63
-probe at offset A 0 0 1 0 0 0 ... 0
-probe at offset B 0 0 1 0 1 0 ... 0
-probe at offset C 0 0 0 0 1 0 ... 0
- -------------------- AND
-survivors 0 0 0 0 0 0 ... 0
+load+compare block 0 -> k0
+load+compare block 1 -> k1
+...
+load+compare block 7 -> k7
+ordered pair tests find the first non-empty mask
```
-When no bit survives, the search advances by a block. Otherwise `casei` replays
-the complete pattern at each surviving byte position.
+OpcodeX reports the memory-source `VPCMPEQB` form at three-cycle latency and
+one-per-cycle throughput on Ice Lake. Its current uops.info catalog has no
+Sapphire Rapids column, so the instruction table is used only to explain the
+Ice Lake schedule. Eight independent compares give the core work while earlier
+masks are in flight. The Sapphire Rapids result comes from direct execution and
+the complete field measurements.
-The compiler chooses among dispersed single-byte probes, adjacent pairs,
-pair-pair anchors, triples, and bounded Shufti/Teddy-style tables. The choice is
-made from facts proved about the compiled patterns.
+The tagged multi-anchor kernel uses the same idea with `VPERMB` tables. Its
+common sparse path proves eight blocks empty. A hit returns to a four-block
+dispatcher, preserves byte order, extracts the first lane's tag byte, and lets
+Go perform exact pair and plan checks only for those tags.
-## Why Unicode does not break the sieve
+
+Unicode coverage and the shared multi-pattern plan
-Lowercasing both strings cannot implement Unicode simple folding. Each pattern
-position represents an orbit of equivalent runes, whose UTF-8 encodings may
-have different lengths:
+## Unicode without hand-waving
+
+Lowercasing both strings is a different operation. Simple folding groups runes
+into orbits:
```text
k K K one byte, one byte, three bytes
s S ſ one byte, one byte, two bytes
-σ ς Σ three different runes in one orbit
+σ ς Σ three runes in one orbit
```
-`casei` first compiles the complete relation into tokens. Valid runes map to
-their fold-orbit token. Invalid UTF-8 bytes map to separate opaque tokens. The
-shared state machine advances over those tokens and owns all semantic decisions.
-
-Raw-byte filters are then derived only where they are safe:
+`casei` compiles each orbit to one token. Valid haystack runes map to those
+tokens. Invalid UTF-8 bytes map to opaque one-byte tokens. The shared state
+machine advances over tokens and owns the answer.
-- A fixed ASCII probe is used only when its offsets remain valid for every
- relevant rendering.
-- Pair and triple tables contain every raw UTF-8 form that could begin the
- corresponding token sequence.
-- Low-bit table aliases and normalized Shufti buckets are allowed to add
- survivors, because the exact plan follows them.
-- A route is disabled when the compiler cannot prove that its filter covers
- every possible start.
+Raw filters are derived from that plan under coverage proofs:
-Every filter must cover each encoding of every real start. Extra survivors are
-checked by the exact plan.
+- Fixed offsets are used only while every earlier fold spelling has the same
+ width.
+- Pair and triple tables contain every possible raw spelling for the token
+ window they represent.
+- A low-six-bit `VPERMB` alias may create a false survivor. Exact replay removes
+ it.
+- Variable confirmation stores complete raw forms and advances by the form
+ that matched.
+- A filter route is disabled when the compiler cannot prove coverage.
-## Many patterns in one scan
+This makes false positives a performance cost. False negatives are a
+correctness bug.
-`NewMatcher` compiles every pattern into one trie with failure transitions over
-fold tokens. Depending on its size, the same state machine uses a dense
-transition table or sparse edges.
+## One pattern and many patterns
-The filters summarize possible starts across the whole pattern set. One pass
-over the haystack therefore serves one pattern or hundreds:
+`NewMatcher` compiles the whole set into a trie with failure transitions over
+fold tokens. Small plans use sparse edges. Larger plans can materialize dense
+transitions.
```text
-N=1 one compiled plan, one filter, one scan
-N=512 one shared plan, shared filters, one scan
+N=1 one plan, one traversal
+N=8 one shared plan, tagged filters, one traversal
+N=512 one shared plan, shared filters, one traversal
```
-Terminals record pattern IDs. The plan delays its answer only as far as needed
-to prove the leftmost byte position; ties go to the lowest original pattern
-index.
+Terminals carry the original pattern ID. The plan waits only long enough to
+prove the leftmost source byte. Equal starts choose the lowest ID.
-## Where the advantage comes from
+The tagged Unicode route is important for enumeration. Each literal contributes
+selective interior pairs and their possible source-start offsets. The vector
+screen intersects primary and confirmation pairs while carrying the owning
+pattern bits. Exact pair checks remove table aliases. The existing raw token
+plan then proves the match and its width.
-`casei` works under a narrower contract than the general regex engines in the
-field. The compiler receives literals under Unicode simple folding. It also
-knows that the caller wants the first leftmost answer, which lets it design the
-raw-byte sieve and exact verifier together for the whole pattern set.
+
-Four measured layers contribute to the result:
+
+Competitor implementations and the assembly A/B
-| layer | what it buys | evidence that it matters |
-|---|---|---|
-| Work avoidance | Whole blocks are rejected without decoding or advancing the exact plan at every byte. | Bypassing the shape-selected routes made the median row 3.88× slower on Ice Lake and 4.28× slower on Sapphire Rapids. Three rows were unchanged within 1%; the worst Unicode multi-pattern rows were 401× and 424× slower. |
-| Shared construction | One pattern set becomes one transition plan and one traversal. | Ten paired measurements compared the shared-plan candidate with the earlier per-pattern `IndexFold` loop. The median maximum `x_vs_best` across the 33 rows fell from 6.718 to 0.9123 while the semantic suite stayed green. |
-| Wider native transition | AVX-512 BW handles 64 candidate starts and keeps set arithmetic in mask registers; VBMI performs byte-table lookup in registers. | Masking AVX-512 off while retaining the same plans reduced median throughput by 1.72× on Ice Lake and 1.89× on Sapphire Rapids. One Ice Lake row and three Sapphire Rapids rows favored AVX2, mostly short or Unicode verification-heavy cases. |
-| Kernel scheduling | Fewer dependent operations keep the wide sieve fed. | Fusing one four-way Shufti reduction improved its 64 KiB kernel by 21.6% and the field row using it by 21.8%. Replacing the complete assembly backend with Go's experimental SIMD package then regressed a required 1 MiB row. |
-
-The AVX2 backend keeps the same plan and semantics. The published field lead is
-specifically the AVX-512 implementation.
-
-### Why the hand-written kernels matter
-
-A 512-bit register gives the kernel 64 lanes, while each instruction still has
-latency. On Ice Lake, the `VPERMB` lookup used by the hot long-literal filter
-takes three cycles to produce an answer. The core can start one lookup each
-cycle. The generated Go SIMD loop starts one 64-byte block and waits for its
-answer. The assembly loop keeps four independent blocks in flight, filling the
-lookup unit while earlier answers arrive.
-
-The assembly also sends lookup results straight into AVX-512 mask registers
-and asks whether any of four masks survived. The generated loop builds another
-vector, converts it to a mask, and moves that mask to a general register on
-every 64-byte block. In larger Shufti kernels, the generated code spills lookup
-tables to 144- and 512-byte stack frames; the assembly keeps them in vector
-registers.
-
-The complete-backend A/B passed correctness. Six alternating-order Ice Lake
-runs put the experimental backend at 20.8–23.3 µs/op on
-`single/log_miss_1mb`, versus 18.4–20.8 µs/op for assembly. The public
-[archsimd Case](https://app.perfloop.ai/t/oss/case_37sjyc8f94) and the
-[negative-result record](NOVELTY.md#complete-experimental-go-simd-backend-negative-result)
-contain the acceptance decision and falsifier.
-
-## Where it differs from the field
-
-Every scoring implementation was built from source and profiled. I then read
-the source, generated code, or hot disassembly where its behavior lived. The
-detailed prior-art record is in [`CONTEXT.md`](CONTEXT.md). [`NOVELTY.md`](NOVELTY.md)
-records the provenance of each adopted technique.
-
-| entrant | its strength | the verified distinction |
+## What the competitors do
+
+Every scoring entrant is open source. The audit read the level where its hot
+answer lives: source, intrinsics, generated assembly, or built-object
+disassembly.
+
+| entrant | strong path in this field | where casei differs |
|---|---|---|
-| Vectorscan | A mature multi-regex engine with Teddy/FDR-style literal machinery and a 512-bit VBMI target. | It ran at the same vector width on the same CPUs. The full-scan miss rows therefore compare two 512-bit engines. Its all-match API is covered separately in [`REBAR.md`](REBAR.md). |
-| PCRE2-JIT | Strong prefix analysis and native JIT code for a broad regex language. | `casei` spends its compile budget on the narrower literal-set contract and can derive filters that need not preserve general regex behavior. |
-| rust/regex | Byte-oriented automata plus strong literal extraction and Teddy prefilters. | It carries a general regex representation; `casei` shares one fold-token literal plan and specializes raw filters around its exact start/tie contract. |
-| StringZilla | Dedicated SIMD UTF-8 search, including full-fold expansions. | Full folding is a different relation. The arena times the verification needed to reduce its candidates to simple-fold semantics; `casei` represents that relation directly. |
-| veloz | Excellent hand-written AVX2 ASCII single-literal search. | Its contract covers one ASCII literal. The comparison with `casei` includes both specialization and the wider ISA. |
-| Rust Aho-Corasick | Strong ASCII multi-pattern DFA with a Teddy prefilter. | `casei` extends the shared-plan shape to Unicode fold orbits, variable UTF-8 widths, opaque invalid bytes, and 512-bit filters. |
-
-The measured result comes from a complete Unicode plan behind shape-specific
-raw-byte rejection, shared across the pattern set and accelerated by AVX-512
-kernels. Shufti, Teddy, rare anchors, tries, failure links, `VPERMB`, and
-confirmation after a candidate are known techniques. Their combination under
-this contract is what produced the new result.
-
-## How the claims are checked
-
-1. Seeded single- and multi-pattern differentials and exhaustive
- byte-pair filter checks compare each dispatch mode with Go `regexp (?i)` on
- valid UTF-8 and the opaque-byte oracle on invalid input. Separate fuzz
- targets exercise the same oracles.
-2. The filter-route and ISA ablations measure work avoidance and
- vector width separately; the Shufti case isolates one assembly change. The
- [raw two-host audit](audit/publication/README.md) includes every sample, the
- exact ablation, file hashes, and a recomputation script.
-3. Every native entrant is rebuilt from pinned source and
- checked for its actual dispatched width before it can enter `x_vs_best`.
-4. The 33 arena rows, including five overlap-allowed single-needle counts, run
- on both Ice Lake and Sapphire Rapids.
-5. The direct Rebar integration exposes the non-overlapping enumeration rows
- where the current API loses and keeps those results in the record.
-
-The verified measurements are the [engine Case](https://app.perfloop.ai/t/oss/case_9r9ntnxjd1)
-and the [Shufti refinement](https://app.perfloop.ai/t/oss/case_hqryrfd6j4).
-The full local field is reproduced by [`scripts/reproduce.sh`](scripts/reproduce.sh).
+| Vectorscan 5.4.12 | Mature Teddy, FDR, and vermicelli machinery with a 512-bit VBMI database. The inspected Sapphire Rapids hot miss path entered `avx512vbmi_vermicelliExec`. | `casei` compiles only the simple-fold literal-set question and joins its filter to the same plan that owns leftmost and pattern ties. The arena compares both at 512 bits. |
+| PCRE2 10.47 JIT | Native JIT with strong prefix analysis for a general regex language. It reported a 128-bit path here. | `casei` can select several literal-specific byte positions and raw fold spellings without preserving general regex behavior. |
+| rust/regex | Automata plus memchr and Teddy prefilters. Eligible observed paths reported AVX2. | `casei` shares one fold-token plan across the literal set and uses AVX-512 masks and VBMI tables around it. |
+| StringZilla 5.1.2 | Dedicated SIMD Unicode search with full-fold expansions. | Full folding is a different relation. The arena charges the verification needed to reduce its candidates to simple-fold results. |
+| veloz | Hand-written AVX2 search for one ASCII literal. | `casei` answers Unicode and multi-pattern questions and uses 512-bit kernels on the measured hosts. |
+| Rust Aho-Corasick | Strong ASCII multi-pattern DFA with a Teddy prefilter. | `casei` extends the shared-plan shape to simple-fold orbits, variable UTF-8 widths, opaque invalid bytes, and AVX-512 filters. |
+| Go regexp | Correct simple folding through the standard scalar regexp engine. | It is the semantic floor. `casei` is a compiled literal engine. |
+
+Shufti, Teddy, rare anchors, tries, failure links, byte-table lookup, and
+confirmation after a candidate are known techniques. The result comes from
+combining them under this contract and driving the combination to a field
+position no entrant held. [`NOVELTY.md`](NOVELTY.md) makes that claim and its
+falsifiers explicit.
+
+## Does the assembly matter?
+
+Yes. It was measured.
+
+The complete amd64 backend was re-expressed with Go's experimental
+`simd/archsimd` package. It passed direct tests and differential fuzzing. On
+the required Ice Lake `single/log_miss_1mb` row, six alternating-order runs put
+the generated backend at 20.8 to 23.3 µs/op and `0.878` to `0.913 x_vs_best`.
+The assembly control measured 18.4 to 20.8 µs/op and `0.785` to `0.797`.
+
+The disassembly explains the gap. The generated hot loop handles one block,
+materializes a vector intersection, converts it to a mask, and crosses that
+mask to a general register on each iteration. The assembly loop interleaves
+four independent blocks and produces k-masks directly with `VPTESTMB`. Larger
+generated Shufti functions also spill wide tables to 144-byte and 512-byte
+stack frames. Their assembly controls keep the tables in vector registers.
+
+That experiment is public in the
+[Go SIMD backend Case](https://app.perfloop.ai/t/oss/case_37sjyc8f94).
+
+The assembly audit also killed changes that looked attractive on paper:
+
+- Combining k-masks with seven extra `KORQ` instructions did not reduce the
+ tested dependency cost.
+- `PREFETCHT0` at 512, 1024, 2048, and 4096 bytes was neutral or slower in the
+ sparse tagged loop.
+- Boolean fusion, target alignment, and a BITALG scout failed their paired
+ controls.
+
+Removed experiments and their falsifiers live in [`NOVELTY.md`](NOVELTY.md).
+
+
+
+
+Verification, limits, and code map
+
+## How the result is checked
+
+1. Deterministic differentials compare single and multi results with Go
+ `regexp (?i)` on valid UTF-8 and an opaque-byte oracle on invalid input.
+2. `FuzzIndexFold` and `FuzzMatcher` run those same contracts. The current
+ publication source was fuzzed on the portable ARM path and both AVX-512
+ hosts.
+3. Direct assembly models cover randomized lengths, every 64-byte boundary,
+ tails, dense and sparse epochs, false table aliases, and exact source widths.
+4. GDB breakpoints on both CPUs proved that the three new native kernels were
+ reached by their direct tests.
+5. Every field entrant is rebuilt from pinned source and checked for actual
+ dispatch before timing.
+6. Three complete paired passes require all 36 arena rows below 1.0 on Ice Lake
+ and Sapphire Rapids.
+7. Three Rebar passes require all five same-contract external rows below 1.0 on
+ both CPUs.
+
+The current evidence is in [`audit/acceptance/`](audit/acceptance/README.md)
+and [`audit/rebar/`](audit/rebar/README.md).
## Limits
-- `Find` returns one leftmost match. Rebar's worst count-all row exposes a
- multi-pattern plan that filters on common Cyrillic starts. The current plan
- cannot combine rare interior pairs from several patterns. A measured one-pass
- enumerator left that cost in place. See
- [`REBAR.md`](REBAR.md) for the counters, profiles, and controls.
-- Compiling a plan costs more than `strings.Index` on a single short lookup.
-- AVX2 and scalar paths run the same correctness suite. The published lead
- covers AVX-512, and there is no NEON kernel yet.
-- Full-fold expansions such as `ß -> ss` are outside the relation.
-- Adversarial data can leave many filter survivors. The arena includes
- periodic, same-byte, and torture rows. Rebar contributes a Russian
- multi-pattern example from a real corpus.
-
-## Map from the model to the code
-
-- [`plan.go`](plan.go) compiles fold tokens, the shared state machine, and every
- conservative filter; it also contains the exact fallback transitions.
-- [`matcher.go`](matcher.go) exposes the one-plan `Matcher` API.
-- [`root_amd64.go`](root_amd64.go) performs runtime dispatch and connects plan
- shapes to block transitions.
+- The measured result covers Intel x86-64 with AVX-512F/BW/VBMI.
+- AVX2 and scalar paths preserve semantics. They do not carry the published
+ speed claim.
+- ARM64 uses the portable scalar path. There is no NEON kernel.
+- The search relation is Unicode simple folding. Full-fold expansions are out
+ of scope.
+- The API accepts literals and finite literal sets.
+- Adversarial text can create many filter survivors. The arena includes
+ same-byte, periodic, torture, width-changing, dense-hit, and late-hit rows.
+
+## Map to the code
+
+- [`plan.go`](plan.go) compiles fold tokens, the shared state machine, and
+ route selection.
+- [`unicode_confirm.go`](unicode_confirm.go) packs and checks fixed- and
+ variable-width raw confirmations.
+- [`raw_byte.go`](raw_byte.go) builds tagged multi-pattern anchors and the
+ common-byte origin gate.
+- [`matcher.go`](matcher.go) exposes `Find` and `Each` over the same plan.
+- [`root_amd64.go`](root_amd64.go) performs runtime feature dispatch.
- [`root_amd64.s`](root_amd64.s) contains the AVX2 and AVX-512 kernels.
-- [`root_other.go`](root_other.go) is the portable implementation of the same
- filter contracts.
-- [`arena/field.yaml`](arena/field.yaml) defines who is allowed into the field
- and what semantics and ISA each entrant ran.
+- [`root_other.go`](root_other.go) implements the portable filter contracts.
+- [`arena/field.yaml`](arena/field.yaml) defines the field, semantics, and
+ dispatch requirements.
+
+
diff --git a/NOVELTY.md b/NOVELTY.md
index 25c480e..ffbafe9 100644
--- a/NOVELTY.md
+++ b/NOVELTY.md
@@ -1372,6 +1372,60 @@ the same compiled plan confirms its complete literal. The mixed-fold hit and
miss arena rows, warmed per-pattern control, and Unicode / invalid-byte
differentials are the operational falsifiers for these filters.
+### Tagged interior-pair multi-anchor enumeration
+
+For an eligible multi-literal plan, the compiler assigns one pattern-tag bit to
+each literal, chooses a stable interior UTF-8 pair as that literal's primary
+anchor, and records a second pair at every possible bounded byte displacement
+caused by earlier simple-fold spellings. A third pair is retained as an exact
+scalar guard. The immutable plan owns a 64-entry first-byte table and
+second-byte table for the primary tags, plus up to three corresponding
+confirmation table pairs and their displacements. It is built with the search
+plan, not from a caller, haystack sample, benchmark name, or first-use history.
+
+On a VBMI host, the block loop uses the low six bits of source bytes to look up
+all primary and confirmation tag masks, intersects the masks for each compiled
+displacement, and returns the first conservative survivor with its pattern-tag
+byte. Exact full-byte primary, confirmation, and guard pairs are checked only
+for those tags; low-six-bit aliases therefore cannot create a match. The shared
+plan then replays the candidate and supplies byte start, source width, terminal
+selection, leftmost order, and lowest-ID ties. Wider forms, malformed bytes,
+unsupported plans, scalar tails, and feature-disabled hosts remain on the
+existing decoded/raw-plan authority. This is a conservative enumeration filter,
+not a separate recognizer or a byte-only fold definition.
+
+The components are known art: fixed rare anchors and candidate verification are
+catalogued in `CONTEXT.md` §§3 and 8, while Vectorscan's Teddy implementation
+uses compiled byte-class tables, `VPERMB`, and mask combination for the same
+block-filter role. The repository-specific composition is a pattern-tagged,
+variable-displacement set of UTF-8 interior-pair filters that retains the
+shared fold-transition replay as the sole match authority. It makes no claim
+that this state representation is new. Its operational claim is falsified by
+any missed canonical reference occurrence, any disagreement between the
+assembly predicate and its scalar table model, a tail-alignment failure, or a
+full native field run that does not improve the contested result without
+regressing another required row.
+
+### Variable-width raw confirmation
+
+An eligible N=1 Unicode plan now packs every one-, two-, or three-byte raw
+spelling of each fold token into a bounded confirmation sequence. The pair-pair
+screen fixes the candidate start only before the first width-changing orbit.
+Confirmation then advances its cursor by the spelling that actually matched
+and returns the resulting source width. `Matcher.Each` consumes that proved
+width instead of decoding the same occurrence again. A malformed pattern,
+four-byte spelling, orbit with more than three raw forms, oversized plan, or
+unproved anchor retains the decoded executor.
+
+Raw candidate confirmation and finite fold-variant expansion are known art in
+StringZilla, ClickHouse, Sneller, and the regex engines catalogued in
+`CONTEXT.md`. The repository-specific packed layout and assembly loop encode
+the existing plan's finite raw forms; they are not claimed as a new matching
+state. Their operational result is new only if the same-contract field rows
+move below 1.0 while differentials, exact source width, tails, and the complete
+arena remain green. A bare shortest spelling at the end of a haystack is an
+explicit regression case because it falsified the first maximum-width bound.
+
### VBMI table and mask-scheduling follow-up
The native Vectorscan 5.4.12 source prepared by `arena/vectorscan/prepare.sh`
@@ -1400,13 +1454,219 @@ materializing a separate vector AND. The general pair-root loop separately
unrolls two independent blocks after its first one-block probe, which is
ordinary dependency/branch scheduling rather than a different matcher.
-This is a negative novelty assessment. Byte-class table lookup, Teddy/Shufti
-candidate masks, confirmation after a survivor, `VPERMB`/`VPERMT2B` selection,
-and loop unrolling are established techniques. The package-specific table
-layouts merely encode predicates the existing plan already owns, and the
-common plan remains the sole match authority for N=1 and multi-pattern calls.
-The only falsifiable claim is operational and belongs to the arena and semantic
-differentials, not to a new search construction.
+The tagged multi-anchor miss loop likewise carries four independent primary
+blocks. Its retained schedule applies `VPTESTMB` directly to each pair of
+pattern-tag vectors, retains the four k-masks, and crosses only the earliest
+nonzero block into the scalar guard and shared-plan replay. Two aggregate
+variants materialized all four products, OR-reduced them, and used one
+`VPTESTMB` before reconstructing a block mask on a hit. The second also kept
+the products across a false confirmation and checked all four blocks before
+advancing, so it did not discard already computed primary work. Two pair-
+retention variants instead kept the original primary pairs and k-masks,
+materializing only the selected product after a false confirmation; one kept
+pairs high and confirmation scratch low, while the other did the reverse. They
+retained the direct no-hit instruction mix but increased live vector state
+through the shared confirmation. All four regressed the real dense enumeration;
+the product form also lost the raw N=5 miss A/B. They were removed. This
+scheduling cell is reopened only by an alternating-order field run that
+improves both the sparse synthetic rows and the real tagged enumeration without
+changing the common-plan replay.
+
+The Vectorscan-derived software-prefetch detour is also closed. An
+otherwise-identical `zero512` kernel issued one `PREFETCHT0` per 512-byte sparse
+batch and was compared in same-process shuffled A/B runs at each lead distance:
+
+| `PREFETCHT0` lead | Result | Decision |
+| --- | --- | --- |
+| 512 bytes | Repeatedly slower | Removed |
+| 1024 bytes | Neutral | Removed |
+| 2048 bytes | Repeatedly slower | Removed |
+| 4096 bytes | Repeatedly slower | Removed |
+
+The sweep targeted the common zero-only raw N=5 loop; no distance showed a
+repeatable win, so no prefetch instruction remains and this dimension must not
+be reopened without new evidence that changes the memory-latency premise.
+
+An AVX-512 BITALG union-scout variant was also rejected. It derived the union
+of nonzero `second`-table classes once, broadcast its 64-bit membership mask,
+and used eight memory-source `VPSHUFBITQMB` operations on the second-byte
+windows at `1(AX), 65(AX), …, 449(AX)`. A zero union mask safely advanced 512
+bytes; a nonzero mask replayed the unchanged four-block tagged dispatcher. The
+scalar model checked every low-six class and all four byte aliases, and the
+assembly agreed with the vector table model across randomized horizons and
+tails. The raw N=5 rows had a zero union mask in all 10,239 full chunks, while
+Rebar always fell back, so the construction was reached on precisely the
+intended sparse shape.
+
+Despite removing the explicit zero-scout loads, table lookups, products, and
+vector reduction, four shuffled same-process Ice Lake A/B runs rejected it:
+
+| Workload | BITALG / retained median range | Result |
+| --- | --- | --- |
+| raw N=5 miss | 1.0014--1.0114 | Slower in every run |
+| raw N=5 late hit | 1.0024--1.0157 | Slower in every run |
+| dense-prefix/sparse-suffix | 1.0056--1.0181 | Slower in every run |
+| uniform dense/no-confirm | 0.9834--1.0002 | Mixed, not a compensating gate |
+| Rebar N=5 iterator | 0.9712--0.9818 | Faster, but insufficient against the raw regressions |
+
+The direct memory form assembled as intended and used no spills, but a
+throughput reduction in one schedule does not establish a field improvement.
+It was removed before a native field run because it failed the required
+same-source raw and heterogeneous controls. Reopen it only with a materially
+different membership/scheduling proof, not another tuning of this union scout.
+
+A separate zero512 Boolean-fusion cell was also rejected. The base materializes
+eight primary tag products with `VPANDQ`, then OR-reduces them. The temporary
+form used two four-block accumulators: one initial `VPANDQ` per accumulator,
+three `VPTERNLOGD $0xf8` folds per accumulator, and one final `VPORQ`.
+`0xf8` was modeled bit-for-bit as `dst | (srcA & srcB)` and the copied base and
+fused kernels both matched the scalar vector-table model on randomized lengths,
+tails, dense prefixes, and the Rebar enumeration checksum. Opcodex confirms
+that the native Go-assembler `VPTERNLOGD` ZMM form needs only AVX-512F and is a
+one-cycle, 0.5 reciprocal-throughput instruction on Ice Lake.
+
+Four pinned, shuffled same-process runs produced these fused/base paired-median
+ranges:
+
+| Workload | Fused / base paired-median range | Result |
+| --- | --- | --- |
+| raw N=5 miss | 0.9957--1.0038 | Slower in three of four runs; no repeatable win |
+| raw N=5 late hit | 0.9939--1.0011 | Mixed |
+| dense-prefix/sparse-suffix | 0.9969--1.0037 | Mixed |
+| Rebar N=5 iterator | 0.9976--1.0014 | Mixed |
+
+The lower Boolean instruction count did not yield repeatable sparse headroom
+and did not clear the raw and heterogeneous gates. The fused source and copied
+A/B kernel were removed without a native field run. Do not retry this exact
+`0xf8` grouping; a future zero512 change needs a different bottleneck proof.
+
+Code-target alignment was assessed separately after that fusion result. The
+unmodified zero512 target had low six address bits `0x13` in the test binary.
+Temporary otherwise-identical kernels put `PCALIGN $32` or `PCALIGN
+$64` immediately before that target; disassembly put each target at a
+64-byte boundary. Both retained the predicate, confirmation horizon, density
+switch, and raw-plan replay, and each agreed with the scalar table model and
+Rebar's 971-result checksum.
+
+Four pinned, shuffled same-process runs gave these paired-median ranges versus
+the unaligned kernel:
+
+| Workload | `PCALIGN $32` / base | `PCALIGN $64` / base |
+| --- | --- | --- |
+| raw N=5 miss | 0.9947--1.0032 | 0.9997--1.0049 |
+| raw N=5 late hit | 0.9942--1.0056 | 0.9962--1.0021 |
+| dense-prefix/sparse-suffix | 0.9986--1.0024 | 0.9977--1.0007 |
+| Rebar N=5 iterator | 1.0179--1.0240 | 1.0201--1.0308 |
+
+Neither target had repeatable raw headroom, and both consistently regressed the
+heterogeneous enumeration. The alignment copies were removed without a native
+field run; do not reopen these target alignments absent a changed instruction
+layout or a new bottleneck proof.
+
+A compiled universal-byte scout was also rejected after a source-level
+min/max-width model. It intersected bytes invariant across every simple-fold
+rendering of every eligible literal, selected space (`0x20`) for the five raw
+N=5 literals, and converted each selected occurrence into the actual
+primary-pair coordinate. The resulting package-owned interval was `[1,20]`;
+it covered every compiled primary-start width and was admitted only when its
+span was at most 64 bytes. A scalar combined model, randomized/tail checks,
+width-changing forms, malformed bytes, endpoint lanes, and a deliberately
+constructed low-six-bit tagged-table false positive all agreed with the
+assembly and the existing exact replay. Disassembly showed the sparse path as
+one early `VPCMPEQB` probe followed by eight memory-source `VPCMPEQB` probes
+and k-mask OR reduction; a nonzero byte mask re-entered the unchanged tagged
+four-block dispatcher.
+
+Four pinned, shuffled same-process A/B runs compared that scout with the same
+compiled plan after only its valid bit was cleared:
+
+| Workload | Scout / no-scout paired-median range | Result |
+| --- | --- | --- |
+| raw N=5 miss | 0.9981--1.0095 | No repeatable gain |
+| raw N=5 late hit | 1.0051--1.0099 | Slower in every run |
+| dense-prefix/sparse-suffix | 1.0000--1.0139 | No gain; slower in three runs |
+| Rebar N=5 iterator | 0.9561--0.9605 | Faster, but not a compensating gate |
+
+The sparse raw rows and heterogeneous control did not clear their gate, so the
+universal compiler, vector path, model, and A/B harness were removed before a
+native field run. Reopen this route only with a different instruction schedule
+that improves both raw rows without losing the dense control; do not reuse the
+same nine-probe layout.
+
+Changing level from the zero512 body to Find's origin also did not clear the
+floor. A temporary compiler chose an ASCII byte fixed by `SimpleFold` that was
+present in every literal, proved the maximum folded-source prefix width before
+that byte, and used the existing `literalSkipASCII` scan to begin the unchanged
+raw plan no later than that bound before the first occurrence. `literalSkipASCII`
+was necessary rather than `rootSkipASCII`: the latter intentionally stops at a
+high byte, while the exact fixed-byte predicate may skip UTF-8 and malformed
+bytes. Enumerating every fold spelling before the selected byte, malformed
+inputs, width-changing prefixes, unrelated earlier bytes, vector lanes/tails,
+and tie cases all agreed with the reference matcher.
+
+Four pinned, shuffled same-process runs compared only this origin gate with the
+same plan after its gate was disabled:
+
+| Workload | Origin gate / no-gate paired-median range | Result |
+| --- | --- | --- |
+| raw N=5 miss | 0.9957--1.0131 | Mixed; no repeatable gain |
+| raw N=5 late hit | 0.9858--1.0037 | Mixed; no repeatable gain |
+
+The no-space miss still traversed the corpus once and reached the same
+bandwidth/loop floor as zero512; the late-hit path likewise had no stable
+advantage. The compiler, Find branch, and A/B tests were removed before a full
+field run. Do not retry this exact origin gate without a different measured
+reason it can beat that floor.
+
+### Retained origin proof with a dedicated exact scan
+
+The later retained path reuses the safe compiler fact from the rejected origin
+experiment, but it does not reuse that transition. The rejected path called the
+generic folded `literalSkipASCII` loop and did not move the raw miss floor. The
+retained `literalSkipExact64` kernel exploits the stronger predicate: the
+selected ASCII byte is invariant under `SimpleFold`, so it needs no fold vector.
+It issues eight independent memory-source `VPCMPEQB` operations across 512
+bytes, retains their k-masks, and tests ordered mask pairs before extracting the
+first lane. OpcodeX reports the relevant compare at three-cycle latency and
+one-per-cycle reciprocal throughput on Ice Lake. Its uops.info catalog has no
+Sapphire Rapids column, so the second host is supported by direct execution and
+field measurement rather than that offline table. The scan then starts the
+unchanged tagged plan no later than the maximum proved prefix before that first
+exact byte.
+
+The gate ships only as part of the combined result with tagged survivor bits,
+variable-width raw confirmation, and the wider sparse tagged schedule. No
+isolated novelty claim is made for it. The combined source passed every sample
+of the 36-row paired field on both hosts: worst medians were 0.9624 on Ice Lake
+and 0.9716 on Sapphire Rapids. It also moved all five same-contract Rebar rows
+below 1.0 on both hosts, with worst ratios 0.8794 and 0.8999. Those external
+rows are the result the prior construction did not hold.
+
+The retained route is falsified by a missed fold spelling, a start earlier than
+its lookback bound, a malformed-byte disagreement, a first-lane ordering error,
+an assembly/model mismatch at any 64-byte boundary, any Rebar same-contract row
+at or above 1.0, or any arena sample at or above 1.0.
+
+The checked-in [load-bearing ablations](audit/acceptance/ablations/README.md)
+exercise those falsifiers. Removing the origin gate loses the focused N=5
+field row on both hosts. Removing variable confirmation loses two of five
+same-contract Rebar rows on both hosts. Ignoring the returned tag byte loses
+the five-pattern Rebar row on Ice Lake.
+
+### Novelty decision for the retained combination
+
+Byte-class table lookup, Teddy/Shufti candidate masks, fixed rare anchors,
+confirmation after a survivor, `VPERMB`/`VPERMT2B` selection, finite fold-form
+expansion, and loop unrolling are established techniques. The package-specific
+tables encode predicates the existing plan already owns, and the common plan
+remains the sole match authority for N=1 and multi-pattern calls.
+
+The construction is therefore a negative novelty finding at the component
+level. The claimed advance is the result: correct simple-fold UTF-8 literal-set
+search, including non-overlapping enumeration, that leads the complete pinned
+field on all 36 arena rows and all five same-contract Rebar rows on both target
+microarchitectures. A published implementation with the same semantics and a
+better measured position would falsify that result claim.
### Complete experimental Go SIMD backend: negative result
@@ -1445,7 +1705,7 @@ compiler already emits.
This negative would be falsified by a later compiler/backend that preserves the
four-way independent schedule, keeps mask arithmetic in k-registers, and avoids
the wide spills, followed by a complete-backend result that passes the same
-correctness checks and all 33 field rows on both qualifying processors. Until
+correctness checks and all 36 field rows on both qualifying processors. Until
then, the assembly backend remains the accepted implementation.
### Rejected cells
@@ -1465,15 +1725,24 @@ Unicode/invalid-byte differential evidence.
## Provenance
-This contribution contains novelty assessments and implementation provenance in this file. The
-original orbit-quotient, raw-byte, fixed-width projection, rolling-fingerprint,
-and prefix-invariant-anchor assessments, plus the five follow-up construction
-sweeps above, were written for this repository from the current `AGENTS.md`,
-`README.md`, `CONTEXT.md`, source and test files, and the cited source
-locations. They contain no copied implementation code and make no external
-performance claim. The follow-up sweep checked current upstream source via
-`git ls-remote` and immutable raw-source revisions for GNU libc, .NET,
-rust-lang/regex, Hyperscan, and StringZilla; semantic differences are stated
-where those engines are used only as mechanical prior art. If implementation
-files are added later, each non-trivial file will identify its authorship and
-source provenance here.
+This contribution contains novelty assessments and implementation provenance
+in this file. The original orbit-quotient, raw-byte, fixed-width projection,
+rolling-fingerprint, and prefix-invariant-anchor assessments, plus the follow-up
+construction sweeps above, were written for this repository from the current
+`AGENTS.md`, `README.md`, `CONTEXT.md`, source and test files, and the cited
+source locations.
+
+The retained follow-up in `unicode_confirm.go`, `raw_byte.go`, `plan.go`,
+`matcher.go`, `root_amd64.go`, `root_amd64.s`, `root_other.go`, the focused
+arena rows, and their tests was produced in user-directed coding-agent sessions.
+The public Perfloop Cases supplied experiment history and rejected hypotheses;
+the final implementation was independently inspected, disassembled, modeled,
+and measured in the repository workspace. OpcodeX from Perfloop commit
+`ff4c454c6563852711156e7104f076529517c6c0` supplied offline instruction
+latency and throughput data for the queried Intel targets.
+
+The implementation was written from the techniques, not copied from field
+source. The follow-up read immutable source or built-object disassembly for
+Vectorscan, PCRE2, rust/regex, StringZilla, veloz, and Rust Aho-Corasick. No
+field implementation is imported, linked, executed, or embedded by the
+candidate module; `scripts/check-baseline-isolation.sh` enforces that boundary.
diff --git a/README.md b/README.md
index 6372ee4..a8f681c 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,89 @@
# casei
-`casei` searches UTF-8 text without lowercasing it first. `IndexFold` finds one
-literal. A compiled `Matcher` finds the leftmost of many literals in one scan.
-Both use Unicode simple case folding, the same relation as Go's `regexp (?i)`
-on valid UTF-8. Matches keep their original byte offsets, and the search never
-builds a lowercased copy of the input.
+Fast case-insensitive UTF-8 substring search for one literal or a whole set.
+
+`IndexFold` returns the byte offset of one literal. A compiled `Matcher`
+returns the leftmost match from many literals in one scan. Both use Unicode
+simple folding, the same case relation as Go's `regexp (?i)` on valid UTF-8.
+
+## The result
+
+On Intel Ice Lake and Sapphire Rapids with AVX-512F, BW, and VBMI, `casei`
+finished first on every row of its 36-row arena. Each row includes the fastest
+eligible result from Go regexp, PCRE2-JIT, rust/regex, Vectorscan, StringZilla,
+veloz, and Rust Aho-Corasick where their contracts apply.
+
+| host | rows won | worst median `x_vs_best` | worst sample | median speedup |
+|---|---:|---:|---:|---:|
+| Ice Lake | 36/36 | 0.9624 | 0.9736 | 1.80× |
+| Sapphire Rapids | 36/36 | 0.9716 | 0.9799 | 1.56× |
+
+`x_vs_best` is casei time divided by the fastest other implementation on the
+same workload. Lower is better. Every one of the 216 measured row samples was
+below 1.0. Every row had 5 to 7 entrants. `casei` reported 512-bit dispatch,
+and Vectorscan reported a 512-bit VBMI database.
+
+Rebar provides a useful independent check. It asks engines to enumerate every
+non-overlapping match instead of returning the first one. `casei` now wins all
+five Rebar rows that request the same Unicode folding relation on both CPUs.
+
+| Rebar selection | Ice Lake | Sapphire Rapids |
+|---|---:|---:|
+| same Unicode contract | 5/5 wins, worst 0.8794 | 5/5 wins, worst 0.8999 |
+| all 18 representable stress rows | 9/18 wins | 9/18 wins |
+
+The other 13 Rebar rows request ASCII-only case matching. `casei` keeps Unicode
+simple-fold semantics on them, so those timings are published as stress data
+rather than folded into the product claim. The complete table and raw receipts
+are in [REBAR.md](REBAR.md).
+
+The speed claim is for the AVX-512 implementation. The portable implementation
+is correct and scalar. There is no NEON kernel yet.
+
+## The idea
+
+Most of search is proving that a match does not start here.
+
+Imagine 64 possible starting positions in a block of text. `casei` checks a few
+useful bytes for all 64 positions at once. The result is a 64-bit mask:
+
+```text
+possible starts 0 1 2 3 4 5 ... 63
+first useful byte 0 0 1 0 0 0 ... 0
+second useful byte 0 0 1 0 1 0 ... 0
+ -------------------- AND
+survivors 0 0 1 0 0 0 ... 0
+```
+
+An empty mask skips the whole block. A surviving bit means "check this one."
+The exact Unicode plan checks it before `casei` can report a match.
+
+Compilation builds those two parts together:
+
+```text
+patterns
+ |
+ +--> conservative byte sieve --> reject impossible starts in blocks
+ |
+ +--> exact simple-fold plan ----> decide matches, offsets, order, and ties
+```
+
+That is where the advantage comes from:
+
+1. The compiler knows the question is literal search under simple folding. It
+ can choose byte tests that a general regex engine cannot assume.
+2. One pattern and 512 patterns use one compiled plan and one traversal. The
+ many-pattern sieve carries pattern tags, so exact replay checks only the
+ literals that survived.
+3. Unicode decoding happens at survivors instead of at every byte. Width-changing
+ folds such as `k`/`K`/`K` are represented explicitly.
+4. The AVX-512 kernels keep several independent blocks in flight and keep set
+ arithmetic in mask registers. The hand-written schedule matters: a complete
+ port to Go's experimental SIMD package stayed correct and lost the required
+ field row it was tested on.
+
+The [one-page explanation](HOW_IT_WORKS.md) follows this model into the actual
+filters, assembly, competitor implementations, and measurements.
## Use it
@@ -13,360 +92,143 @@ go get github.com/tsenart/casei
```
```go
-// One needle. Cache hits allocate nothing.
+// One needle.
if casei.ContainsFold(line, "payment declined") {
- alert(line)
+ alert(line)
}
-// Byte offset instead of a bool.
-at := casei.IndexFold(line, "payment declined") // -1 when absent
+// Byte offset, or -1 when absent.
+at := casei.IndexFold(line, "payment declined")
-// Many needles, one pass. Leftmost match wins; ties go to the lowest
-// pattern index.
+// Many needles, one compiled plan. Leftmost match wins. A tie goes to the
+// lowest pattern index.
m := casei.NewMatcher([]string{"fatal panic", "oom killed", "segfault"})
if match, ok := m.Find(line); ok {
- fmt.Println(m.Patterns()[match.Pattern], match.Start)
+ fmt.Println(m.Patterns()[match.Pattern], match.Start)
}
+
+// Enumerate non-overlapping matches. Width is the number of source bytes
+// consumed by this occurrence, which can differ across a Unicode fold orbit.
+m.Each(log, func(match casei.Match, width int) bool {
+ fmt.Println(match.Pattern, match.Start, width)
+ return true
+})
```
-`NewMatcher` compiles the pattern set once. Reuse the `*Matcher` across searches
-and share it freely; `Find` is safe for concurrent use. Every published
-benchmark path allocates nothing after compilation, including cache-hit
-`IndexFold`. Compiling a plan can allocate. During a search, the generic Unicode
-plan allocates an offset ring only when its longest pattern needs more than the
-256 entries kept inline.
+`NewMatcher` compiles once. Reuse the matcher across searches. `Find` is safe
+for concurrent use. Search is allocation-free on the published paths after
+compilation. A generic plan with a longest pattern above the 256-entry inline
+ring may allocate an offset ring during search.
-On valid UTF-8, matching is Unicode **simple** case folding, identical to Go's
-`regexp` with `(?i)`: `k` matches the Kelvin sign U+212A, `ſ` matches `s`,
-`σ`/`ς`/`Σ` all match, and `ß` matches `ẞ` but never `ss`. Invalid bytes are
-matched as opaque one-byte units. Lowercasing both sides does not have these
-semantics. [Here is why](HOW_IT_WORKS.md#why-unicode-does-not-break-the-sieve).
+The library requires Go 1.22 or newer. Rebuilding the native benchmark field
+requires Go 1.24 or newer.
-Requires Go 1.22+. The AVX-512 and AVX2 paths are chosen at runtime on x86-64;
-every other platform runs the portable path, which returns identical results
-(see [Limitations](#limitations) for what that costs).
+## Semantics
-## Where it stands
+On valid UTF-8, matching follows Unicode simple folding:
-On Intel Ice Lake and Sapphire Rapids with AVX-512F/BW/VBMI, `casei` finished
-first on all 33 rows of its open arena. The median speedup over the fastest
-correct alternative was 1.9x on Ice Lake and 1.6x on Sapphire Rapids. Those
-rows cover first-match search and five single-needle count workloads.
+- `k`, `K`, and Kelvin sign `K` match.
+- `s`, `S`, and long s `ſ` match.
+- `σ`, `ς`, and `Σ` match.
+- `ß` and `ẞ` match. `ß` and `ss` do not.
-Rebar asks a broader question: enumerate every non-overlapping match. On its
-five rows with the same Unicode folding contract, `casei` wins two and loses
-three on both hosts.
+Invalid UTF-8 bytes are opaque one-byte units. Results use source byte offsets.
+`Matcher.Find` returns the leftmost start, with ties resolved by the lowest
+pattern index. `Matcher.Each` emits non-overlapping matches in that order and
+returns the exact source width of each occurrence.
-| measured question | Ice Lake | Sapphire Rapids |
-|---|---:|---:|
-| casei arena, 33 rows | 33/33 wins; 1.9x median lead | 33/33 wins; 1.6x median lead |
-| Unicode-equivalent Rebar rows | 2/5 wins; worst loss 9.86x | 2/5 wins; worst loss 9.18x |
+Correctness is checked against Go `regexp (?i)` by deterministic differential
+tests and two fuzz targets. The portable path, AVX2 path, and AVX-512 path run
+the same contract suite. Filter tests include exhaustive byte-pair projections,
+randomized tails, malformed input, width-changing folds, and ordering ties.
-This is the current boundary. The speed result covers x86-64 AVX-512 and the
-arena's first-match and single-needle count contract. It does not claim
-leadership for non-overlapping enumeration. The [Rebar audit](REBAR.md) records
-every applicable workload, all six raw measurement passes, and the three
-losses.
+## How the field was measured
-The Rebar result also found missing coverage in the original gym. Its
-competitive bar had no multi-pattern enumeration row and none of Rebar's real
-count/count-spans workloads. Perfloop optimized the board I supplied, so those
-paths were not part of the original target.
+The arena builds every native entrant from pinned source. It rejects an entrant
+that silently dispatches below the width promised for that tier. Each row
+reports active entrants and their observed vector widths.
-### Work in progress
+`BenchmarkBar` measures `casei` beside each eligible competitor six times.
+The order alternates, so each operation goes first in three pairs. The median
+paired ratio is computed for each competitor, and the largest ratio names the
+fastest field result. The checked-in acceptance run repeats the complete board
+three times on each host, pinned to one core.
-Two newer public Cases carry the gap work forward:
+The current raw transcripts are here:
-- [Keep N=1 confirmation inside the AVX-512 scan](https://app.perfloop.ai/t/oss/case_s8c41a1per)
- moved a new false-survivor row from `x_vs_best=4.547` to `0.7328` in ten
- randomized pairs. [PR #10](https://github.com/tsenart/casei/pull/10) is open;
- that targeted result has not yet passed the full two-host board.
-- [Replace the decoded transition loop with one raw-byte plan](https://app.perfloop.ai/t/oss/case_rmg4fdm3me)
- is the open general path for the remaining decoded-confirmation cost.
+- [Ice Lake BenchmarkBar](audit/acceptance/results/ice/benchmarkbar.txt)
+- [Sapphire Rapids BenchmarkBar](audit/acceptance/results/spr/benchmarkbar.txt)
+- [Receipt verifier and summary](audit/acceptance/README.md)
-The older hypotheses and their stopped results remain in the
-[Rebar audit](REBAR.md#public-work-on-the-losses). A change is accepted only if
-all five comparable Rebar rows win on both processors and every current
-`BenchmarkBar` row remains below `x_vs_best=1.0` against the full-strength
-field.
+The repository also keeps the earlier sequential-window runs that exposed
+measurement drift near parity. They failed the publication bar and are part of
+the methodology record.
-I built `casei` as a hard, self-contained test for
-[Perfloop](https://app.perfloop.ai). I supplied the problem, constraints,
-hypotheses, and reviews. Perfloop generated and measured the candidates. A
-separate verifier tried to break each survivor. The
-[full engine Case](https://app.perfloop.ai/t/oss/case_9r9ntnxjd1) is public.
+To rebuild the field and rerun all 36 rows on a qualifying Linux host:
-## How it gets its speed
+```sh
+./scripts/reproduce.sh
+```
-A literal could start at every byte of the haystack. Checking the complete
-Unicode relation at every byte is expensive, so `casei` first eliminates
-starts with cheaper byte tests.
+The script requires x86-64 with AVX2 and AVX-512F/BW/VBMI. It builds PCRE2,
+Vectorscan, rust/regex, Rust Aho-Corasick, and StringZilla from their pinned
+sources before it runs the board.
-Compilation produces an exact plan and a set of cheaper byte tests:
+## What changed after Rebar found the gap
-```text
-patterns -> complete simple-fold plan -> exact answer
- \-> conservative byte filters -> 64 starts at once -> survivors only
-```
+The original arena covered first-match search and single-needle counting. It
+did not contain multi-pattern enumeration or the two focused shapes that made
+Rebar's Russian rows hard. Perfloop optimized the board it was given. Rebar
+showed what the board had omitted.
-The AVX-512 sieve tests 64 possible starts together. On sparse workloads, most
-blocks produce no survivors. A surviving bit means only “maybe,” so the exact
-plan still decides Unicode equivalence, byte offsets, leftmost order, and
-pattern ties. A filter may admit extra work, but it may never reject a real
-match.
-
-The compiler chooses selective byte positions for the actual pattern set, and
-shape-specific kernels evaluate them with 512-bit VBMI tables and mask
-registers. One package-owned fold-token state machine handles both one needle
-and many, so the fast path does not delegate to a second matcher.
-
-The assembly matters, but it amplifies the plan rather than replacing it. One
-Shufti scheduling change improved its contested row by 21.8%. Bypassing the
-shape-selected filters made the median row 3.88x slower on Ice Lake and 4.28x
-slower on Sapphire Rapids. Replacing the complete backend with Go's
-experimental SIMD package passed correctness and made a required field row
-slower.
-
-[The one-page explanation](HOW_IT_WORKS.md) walks from that mental model to the
-actual plan, kernels, competitor differences, causal measurements, and limits.
-
-## Results
-
-`BenchmarkBar` timed `casei` and every eligible competitor built from pinned
-source in the same benchmark process, with each entrant dispatching its widest
-eligible path. The complete tables below come from three fresh passes on each
-GCP host, one exposing Ice Lake and one exposing Sapphire Rapids. Perfloop also
-ran ten co-measured pairs of the pre-engine and final source, choosing the two
-source arms' order randomly inside each pair; that public Case used the worst
-`x_vs_best` across all 33 rows as its acceptance metric.
-
-Perfloop's verified runs put **casei first on every one of 33 rows, on both
-microarchitectures**. The median speedup over the fastest correct alternative
-was **1.9x** on Ice Lake and **1.6x** on Sapphire Rapids, using the median
-`1 / x_vs_best` across the 33 acceptance rows. The narrowest median lead was
-1.08x and the widest was 25.7x. The short table below comes from the separate
-per-engine throughput lanes on Sapphire Rapids; both complete displays follow.
-Throughput is in GB/s. Values are rounded to one decimal, so `0.0` means below
-0.05 GB/s. `casei vs #2` uses those display lanes.
-
-| row | casei | Vectorscan | veloz | PCRE2-JIT | StringZilla | rust/regex | casei vs #2 |
-|---|---|---|---|---|---|---|---|
-| `log_miss_1mb` | **56.4** | 51.3 | 8.3 | 23.3 | 12.3 | 9.0 | **1.10×** |
-| `code_miss_256kb` | **56.1** | 29.1 | 8.3 | 23.3 | 11.5 | 9.1 | **1.93×** |
-| `prose_miss_1mb` | **56.3** | 19.5 | 8.3 | 23.2 | 12.1 | 9.0 | **2.43×** |
-| `ru_miss_1mb` | **27.5** | 16.5 | – | 22.8 | 6.5 | 9.0 | **1.21×** |
-| `multi_N512_miss_log_64kb` | **27.7** | 6.8 | – | 19.5 | 0.0 | 0.5 | **1.42×** |
-| `multi_N512_miss_hazard_64kb` | **9.8** | 4.6 | – | 0.0 | 0.0 | 0.5 | **2.14×** |
-| `latency_match_start_1kb` | **118.1** | 2.9 | 70.1 | 4.6 | 4.4 | 3.3 | **1.68×** |
-| `samechar_miss_64kb` | **67.6** | 44.7 | 8.3 | 22.3 | 11.0 | 0.5 | **1.51×** |
-| `periodic_miss_64kb` | **35.5** | 0.6 | 8.3 | 28.4 | 11.0 | 0.5 | **1.25×** |
-| `torture_miss_64kb` | **13.1** | 0.1 | 0.5 | 0.3 | 0.1 | 0.3 | **25.76×** |
-| `log_hit_sparse_1mb` | **32.1** | 1.5 | 8.0 | 7.2 | 10.3 | 6.6 | **3.11×** |
-
-
-Full 33-row tables for both CPUs
-
-The visible columns show the six engines with lanes on all or most rows;
-`rust/regex` is the rure adapter. Go `regexp` and Rust Aho-Corasick are omitted
-from this display. Both still enter the acceptance row's `x_vs_best` wherever
-eligible. The display ratio and acceptance score are kept separate because
-they come from different benchmark surfaces.
-
-#### Sapphire Rapids (Xeon 8481C), GB/s (higher is better; **bold** = casei)
-
-| row | casei | Vectorscan | veloz | PCRE2-JIT | StringZilla | rust/regex | casei vs #2 |
-|---|---|---|---|---|---|---|---|
-| `latency_match_start_1kb` | **118.1** | 2.9 | 70.1 | 4.6 | 4.4 | 3.3 | **1.68×** |
-| `samechar_miss_64kb` | **67.6** | 44.7 | 8.3 | 22.3 | 11.0 | 0.5 | **1.51×** |
-| `log_miss_1mb` | **56.4** | 51.3 | 8.3 | 23.3 | 12.3 | 9.0 | **1.10×** |
-| `prose_miss_1mb` | **56.3** | 19.5 | 8.3 | 23.2 | 12.1 | 9.0 | **2.43×** |
-| `code_miss_256kb` | **56.1** | 29.1 | 8.3 | 23.3 | 11.5 | 9.1 | **1.93×** |
-| `log_miss_64kb` | **53.4** | 45.2 | 8.3 | 22.3 | 12.3 | 8.9 | **1.18×** |
-| `log_needle3_64kb` | **53.3** | 45.0 | 8.3 | 22.1 | 18.0 | 13.8 | **1.19×** |
-| `log_needle32_64kb` | **53.3** | 6.8 | 8.3 | 21.0 | 11.0 | 8.9 | **2.54×** |
-| `log_needle16_64kb` | **53.3** | 36.0 | 8.3 | 22.1 | 11.8 | 8.9 | **1.48×** |
-| `log_needle8_64kb` | **53.0** | 6.8 | 8.3 | 20.9 | 18.0 | 8.9 | **2.53×** |
-| `multi_N8_miss_ru_1mb` | **38.6** | 5.7 | – | 23.3 | 0.8 | 9.0 | **1.66×** |
-| `multi_N64_miss_ru_64kb` | **37.0** | 7.2 | – | 21.8 | 0.1 | 0.5 | **1.70×** |
-| `multi_N8_hazard_hit_1mb` | **35.5** | 6.7 | – | 2.7 | 0.9 | 31.6 | **1.13×** |
-| `periodic_miss_64kb` | **35.5** | 0.6 | 8.3 | 28.4 | 11.0 | 0.5 | **1.25×** |
-| `log_hit_sparse_1mb` | **32.1** | 1.5 | 8.0 | 7.2 | 10.3 | 6.6 | **3.11×** |
-| `multi_N8_miss_log_1mb` | **29.3** | 6.8 | – | 14.3 | 1.6 | 9.0 | **2.04×** |
-| `multi_N64_miss_log_64kb` | **27.7** | 6.8 | – | 22.0 | 0.2 | 0.5 | **1.26×** |
-| `multi_N512_miss_log_64kb` | **27.7** | 6.8 | – | 19.5 | 0.0 | 0.5 | **1.42×** |
-| `ru_miss_1mb` | **27.5** | 16.5 | – | 22.8 | 6.5 | 9.0 | **1.21×** |
-| `ru_hit_sparse_1mb` | **24.5** | 0.8 | – | 19.3 | 6.5 | 8.5 | **1.27×** |
-| `latency_match_mid_1kb` | **22.7** | 2.4 | 14.5 | 2.6 | 3.8 | 2.5 | **1.57×** |
-| `kelvin_hazard_1mb` | **20.3** | 1.8 | – | 1.2 | 12.8 | 8.5 | **1.58×** |
-| `multi_N8_miss_hazard_1mb` | **18.3** | 6.8 | – | 0.3 | 0.9 | 2.8 | **2.67×** |
-| `multi_N2_miss_log_1mb` | **15.2** | 11.5 | – | 0.7 | 5.8 | 5.5 | **1.32×** |
-| `log_miss_1kb` | **13.7** | 5.5 | 7.9 | 5.4 | 5.5 | 4.0 | **1.74×** |
-| `latency_match_end_1kb` | **13.5** | 2.4 | 7.5 | 1.7 | 3.2 | 2.0 | **1.80×** |
-| `latency_miss_1kb` | **13.4** | 4.6 | 7.9 | 4.9 | 5.4 | 3.9 | **1.69×** |
-| `prose_hit_dense_1mb` | **13.1** | 0.0 | 6.8 | 1.0 | 4.2 | 2.9 | **1.93×** |
-| `torture_miss_64kb` | **13.1** | 0.1 | 0.5 | 0.3 | 0.1 | 0.3 | **25.76×** |
-| `code_hit_brackets_256kb` | **11.1** | 0.0 | 6.0 | 1.1 | 1.3 | 0.9 | **1.86×** |
-| `multi_N8_hit_log_1mb` | **10.2** | 5.7 | – | 2.0 | 1.8 | 2.5 | **1.78×** |
-| `multi_N512_miss_hazard_64kb` | **9.8** | 4.6 | – | 0.0 | 0.0 | 0.5 | **2.14×** |
-| `ru_latency_miss_1kb` | **8.5** | 3.6 | – | 5.1 | 3.9 | 3.6 | **1.65×** |
-
-#### Ice Lake (Xeon @ 2.6 GHz), GB/s (higher is better; **bold** = casei)
-
-| row | casei | Vectorscan | veloz | PCRE2-JIT | StringZilla | rust/regex | casei vs #2 |
-|---|---|---|---|---|---|---|---|
-| `latency_match_start_1kb` | **118.2** | 2.5 | 63.6 | 4.2 | 3.7 | 3.1 | **1.86×** |
-| `samechar_miss_64kb` | **71.7** | 39.0 | 6.9 | 22.7 | 11.0 | 0.6 | **1.84×** |
-| `prose_miss_1mb` | **57.2** | 16.7 | 6.8 | 16.4 | 12.2 | 9.5 | **3.43×** |
-| `log_miss_1mb` | **57.1** | 45.0 | 6.8 | 21.8 | 12.2 | 9.4 | **1.27×** |
-| `code_miss_256kb` | **56.9** | 23.1 | 6.9 | 19.1 | 11.5 | 9.6 | **2.47×** |
-| `log_miss_64kb` | **54.5** | 38.9 | 6.8 | 19.8 | 11.9 | 9.3 | **1.40×** |
-| `log_needle32_64kb` | **52.8** | 6.9 | 6.9 | 16.0 | 11.0 | 9.4 | **3.31×** |
-| `log_needle8_64kb` | **52.8** | 6.9 | 6.9 | 16.1 | 15.5 | 9.2 | **3.28×** |
-| `log_needle16_64kb` | **52.8** | 28.2 | 6.9 | 15.8 | 11.7 | 9.3 | **1.87×** |
-| `log_needle3_64kb` | **52.6** | 38.9 | 6.9 | 16.5 | 15.3 | 14.3 | **1.35×** |
-| `multi_N8_miss_ru_1mb` | **37.0** | 6.1 | – | 16.5 | 0.8 | 9.5 | **2.24×** |
-| `multi_N8_hazard_hit_1mb` | **35.4** | 7.7 | – | 3.0 | 1.0 | 33.0 | **1.07×** |
-| `multi_N64_miss_ru_64kb` | **35.2** | 5.8 | – | 15.7 | 0.1 | 0.5 | **2.24×** |
-| `multi_N8_miss_log_1mb` | **31.1** | 7.0 | – | 13.3 | 1.6 | 9.5 | **2.33×** |
-| `periodic_miss_64kb` | **30.9** | 0.5 | 6.9 | 23.5 | 11.0 | 0.6 | **1.32×** |
-| `multi_N64_miss_log_64kb` | **29.5** | 6.9 | – | 20.4 | 0.2 | 0.5 | **1.45×** |
-| `multi_N512_miss_log_64kb` | **29.5** | 6.9 | – | 13.8 | 0.0 | 0.5 | **2.13×** |
-| `log_hit_sparse_1mb` | **27.6** | 1.5 | 6.7 | 6.9 | 10.3 | 6.8 | **2.67×** |
-| `ru_miss_1mb` | **21.7** | 17.4 | – | 16.4 | 6.4 | 9.6 | **1.25×** |
-| `kelvin_hazard_1mb` | **21.0** | 1.9 | – | 1.1 | 12.3 | 8.9 | **1.70×** |
-| `multi_N8_miss_hazard_1mb` | **18.5** | 7.5 | – | 0.3 | 0.9 | 3.1 | **2.46×** |
-| `latency_match_mid_1kb` | **18.5** | 2.0 | 12.0 | 2.3 | 3.2 | 2.4 | **1.54×** |
-| `ru_hit_sparse_1mb` | **18.3** | 0.9 | – | 16.0 | 6.2 | 8.8 | **1.15×** |
-| `multi_N2_miss_log_1mb` | **15.0** | 11.6 | – | 0.7 | 5.9 | 5.8 | **1.29×** |
-| `log_miss_1kb` | **13.3** | 4.4 | 6.6 | 4.6 | 4.8 | 3.6 | **2.03×** |
-| `latency_miss_1kb` | **12.8** | 3.8 | 6.6 | 4.2 | 4.7 | 3.6 | **1.95×** |
-| `prose_hit_dense_1mb` | **12.0** | 0.0 | 5.8 | 1.0 | 3.6 | 2.8 | **2.06×** |
-| `latency_match_end_1kb` | **11.0** | 2.0 | 6.2 | 1.6 | 2.9 | 2.0 | **1.79×** |
-| `torture_miss_64kb` | **10.2** | 0.1 | 0.4 | 0.2 | 0.1 | 0.3 | **25.51×** |
-| `multi_N8_hit_log_1mb` | **10.1** | 5.9 | – | 1.9 | 1.7 | 2.8 | **1.71×** |
-| `multi_N512_miss_hazard_64kb` | **9.8** | 3.9 | – | 0.0 | 0.0 | 0.5 | **2.53×** |
-| `code_hit_brackets_256kb` | **9.1** | 0.0 | 5.0 | 1.0 | 1.1 | 0.7 | **1.82×** |
-| `ru_latency_miss_1kb` | **8.0** | 3.1 | – | 4.6 | 3.5 | 3.4 | **1.74×** |
-
-Diagnostic baselines (`ToLower`+`Index`, the Go Aho-Corasick port, and the
-exact-match `ceiling`) are omitted from the “fastest” comparison. The
-[methodology](#benchmark-method) explains why. Rebuild the field and rerun the
-local board with `./scripts/reproduce.sh`.
-
-
-The 33 rows include 28 first-match operations and five overlap-allowed
-single-needle count operations. They cover ASCII and UTF-8 workloads, with one
-needle or many, on both processors. Vectorscan used its 512-bit AVX-512 VBMI
-path on the same machines. This gives the field an equal-width control alongside
-narrower engines such as AVX2 veloz and 128-bit PCRE2-JIT. Every benchmark row
-reports the width each entrant used.
-
-Rebar measures non-overlapping `count` and `count-spans`. On the five
-performance rows that share `casei`'s Unicode contract, the loop-over-`Find`
-adapter wins two and loses three on both hosts. The worst row spends its time
-behind a weak shared filter choice; a stateful enumerator left that cost in
-place. The [complete Rebar audit](REBAR.md) lists every applicable row, the
-original benchmark coverage gap, and the controls used to trace the losses.
-
-On valid UTF-8, correctness is pinned to Go `regexp` `(?i)` by deterministic
-single- and multi-pattern differentials. The suite runs under both x86 vector
-paths. The portable scalar dispatch runs it too. It includes tens of thousands
-of randomized searches and two exhaustive 65,536-pair filter checks.
-`FuzzIndexFold` and `FuzzMatcher` run separately. Invalid-byte inputs are checked
-against the opaque-unit contract.
-
-## Reproduce it
-
-With Go 1.24+ on an x86-64 Linux host **with AVX2 and AVX-512F/BW/VBMI** (pin a
-GCP `n2` to Ice Lake, use `c3` for Sapphire Rapids, or use equivalent recent
-Intel hardware), one script builds the entire competitor field from source and
-runs the scoreboard. Apple Silicon does not meet this performance-host
-contract. CI rebuilds and checks the same pinned field for correctness on every
-push.
+The follow-up added three focused arena rows and kept the Rebar rows as an
+external gate. The surviving construction combines:
-```sh
-git clone https://github.com/tsenart/casei && cd casei
-./scripts/reproduce.sh # ~15 min: builds pcre2, vectorscan (VBMI), rure,
- # rust-regex, stringzilla, then runs the benchmark
-```
+- tagged interior anchors for several Unicode literals;
+- raw confirmation that follows one-, two-, and three-byte fold spellings and
+ returns the source width it proved;
+- an exact common-byte origin gate for eligible multi-pattern plans; and
+- wider assembly schedules that scan several cache lines before testing masks.
+
+The result closes all five same-contract Rebar rows while preserving all 36
+arena wins. [REBAR.md](REBAR.md) contains the before/after account and every
+external row.
-It prints, for all 33 rows, every entrant's local throughput and the vector
-width it dispatched, plus `x_vs_best` (`casei`'s time ÷ the fastest *correct*
-competitor). It then fails unless all three samples of every row are below 1,
-every row has at least two entrants, and both `casei` and Vectorscan report
-512-bit dispatch with Vectorscan's VBMI path active. Perfloop's public Case
-separately records ten co-measured pre-engine/final-source pairs, with random
-source-arm order, for the board's worst `x_vs_best`.
-
-The [publication audit](audit/publication/README.md) records a fresh three-pass
-acceptance run on both CPU models, the work-avoidance and AVX-512 ablations,
-raw samples, and the script that recomputes their summaries.
-
-## Benchmark method
-
-
-Read the field, scoring, and measurement rules
-
-The arena applies the following rules:
-
-- A baseline enters `x_vs_best` after its output passes the agreement tests for
- that tier. `ToLower` plus `Index` and the Go Aho-Corasick port remain
- diagnostic lanes.
-- Each row is scored against its fastest eligible competitor.
-- Entrants report the ISA and vector width they dispatched. Vectorscan is built
- with `BUILD_AVX512VBMI`, and the arena checks that its 512-bit path ran.
-- The workload set includes `periodic`, `samechar`, and `torture` inputs that
- expose data-dependent cliffs.
-- Twenty-eight rows measure a first byte offset or a leftmost match with ties
- resolved by pattern order. Five single-needle rows repeatedly request the
- first offset and count every overlap-allowed match. An entrant with an
- enumeration API performs the required reduction inside its timed operation.
-- [`arena/field.yaml`](arena/field.yaml) pins nine engines to source versions
- and build flags. Perfloop's engine Case records ten co-measured source pairs.
- `reproduce.sh` rebuilds the field and runs the local board.
-
-I wrote the arena alongside `casei`. Its source, workloads, field manifest, and
-measurements are open for independent runs.
-
-
-
-## Limitations
-
-- The measured performance result covers AVX-512. Other x86 machines use the
- AVX2 path. ARM uses the portable scalar path, with no NEON kernel yet. All
- three dispatch modes run the correctness suite.
-- `NewMatcher` is meant to be reused. On a tiny one-shot lookup, plan setup
- gives `strings.Index` the advantage.
-- The contract is Unicode simple folding. Full-fold expansions such as
- `ß` to `ss` are outside it.
-- The public API returns the first match. The rebar adapter enumerates by
- calling `Find` again on each suffix and loses three of five comparable rows
- on both measured hosts. [`REBAR.md`](REBAR.md) includes those results and the
- ASCII-only rows that ask for weaker folding semantics.
-
-## How it was built
-
-I used `casei` as an operator-directed Perfloop case. I supplied the hypotheses
-and audited the field and host ISA; Perfloop generated candidates and killed or
-kept them by measurement. The public trails cover the
-[engine](https://app.perfloop.ai/t/oss/case_9r9ntnxjd1) and a later
-[kernel-scheduling refinement](https://app.perfloop.ai/t/oss/case_hqryrfd6j4).
-The Rebar audit then widened the gym and exposed the three open losses above.
-The repository contains the resulting source, field manifest, correctness
-tests, measurements, and reproduction scripts.
-
-## Details
-
-- [`HOW_IT_WORKS.md`](HOW_IT_WORKS.md): the short mental model first, followed
- by the exact plan, assembly contribution, competitor comparison, and
- evidence.
-- [`REBAR.md`](REBAR.md): every applicable third-party rebar workload, the
- semantic map, both-host measurements, real losses, and their diagnosis.
-- [`arena/field.yaml`](arena/field.yaml): the field, versions, build flags,
- ISA, corpus hashes, semantic status.
-- [`CONTEXT.md`](CONTEXT.md): every technique known to this problem, with
- sources and measured numbers (including rebar's published results).
-- [`NOVELTY.md`](NOVELTY.md): the construction and prior-art assessment; the fold-orbit
- representation is *not* claimed as novel, and says why.
-- [`AGENTS.md`](AGENTS.md): the arena's rules of engagement, baseline isolation,
- single-engine identity, and the acceptance bar a candidate must clear.
+## Perfloop record
+
+I built `casei` as a hard, self-contained test for
+[Perfloop](https://app.perfloop.ai). I supplied the problem, field, and
+constraints. Perfloop proposed implementations, measured them against the
+field, and sent survivors to an independent verifier.
+
+- [Original full-engine Case](https://app.perfloop.ai/t/oss/case_9r9ntnxjd1)
+- [Shared interior-anchor Case](https://app.perfloop.ai/t/oss/case_jws72csfa9)
+- [Dispersed Unicode-probe Case](https://app.perfloop.ai/t/oss/case_b2m0dmh5wa)
+- [Raw-confirmation Case](https://app.perfloop.ai/t/oss/case_tgkp9bs0r6)
+- [Complete Go SIMD backend, rejected](https://app.perfloop.ai/t/oss/case_37sjyc8f94)
+
+The public Cases are the experiment log. [`NOVELTY.md`](NOVELTY.md) records
+the constructions that failed on paper or in measurements. Negative results
+stay in the repo so the next attempt starts from evidence.
+
+## Limits
+
+- Published speed numbers cover x86-64 AVX-512F/BW/VBMI on Intel Ice Lake and
+ Sapphire Rapids.
+- The portable path is scalar. ARM64 is correct, with no NEON speed claim.
+- The API searches literals and finite literal sets.
+- Folding is Unicode simple folding. Full-fold expansions such as `ß -> ss`
+ are outside the contract.
+- Plan compilation has a cost. Cache a matcher for repeated searches.
+- The 36-row arena belongs to this repository. Its sources, field, dispatch,
+ failed measurements, and verifier are open and pinned. Rebar is the external
+ cross-check.
+
+## Read next
+
+- [How it works](HOW_IT_WORKS.md)
+- [Direct Rebar audit](REBAR.md)
+- [Known field and prior art](CONTEXT.md)
+- [Novelty and negative results](NOVELTY.md)
+- [Current acceptance receipts](audit/acceptance/README.md)
+- [Rebar receipts](audit/rebar/README.md)
diff --git a/REBAR.md b/REBAR.md
index 2d16810..a01f9bc 100644
--- a/REBAR.md
+++ b/REBAR.md
@@ -1,251 +1,205 @@
# Direct Rebar audit
-`casei` loses three of the five Rebar workloads that ask the same Unicode
-folding question. Rebar counts every non-overlapping match. The 33-row arena
-contains 28 first-match operations and five overlap-allowed single-needle
-counts. It has no multi-pattern enumeration row. On Rebar's worst row, a weak
-multi-pattern filter sends too much text into the exact Unicode plan. A measured
-streaming version left that cost in place.
-
-The audit below includes every caseless literal or finite-alternation workload
-that `casei` can represent.
+Rebar found the hole in the original `casei` benchmark. It also provides the
+external check that the hole is now closed.
## The answer in 30 seconds
At Rebar commit
[`463d00f`](https://github.com/BurntSushi/rebar/commit/463d00f31887e84c38467805b9e3122c314b9521),
-the inventory contains 18 performance workloads and three behavior checks.
-`casei` passed the two checks that use compatible Unicode semantics. The third
-asks `s` to miss `ſ`, which conflicts with Unicode simple folding.
-
-Only five performance rows enable Unicode semantics and therefore ask the same
-folding question as `casei`. Against Rebar's three recorded leaders (Hyperscan,
-PCRE2-JIT, and rust/regex), `casei` wins **2/5** and loses **3/5** on both Ice Lake
-and Sapphire Rapids. Its median `time / best-other-time` is 1.27 and 1.28; the
-worst row is roughly 9× slower.
-
-The remaining 13 performance rows request ASCII-only case insensitivity.
-`casei` produced the expected answers on those corpora while retaining its
-stronger Unicode relation. Their timings remain useful as stress tests under
-that contract.
-
-## Why the original gym missed these losses
-
-The arena's scenario list had five single-needle count rows during the original
-engine build. Its competitive bar mistakenly ignored their `count` flag and
-timed only the first match. Commit
-[`c4392e7`](https://github.com/tsenart/casei/commit/c4392e7e6bbdaa8cd263059d5a041b29bd57e9ae)
-corrected that wiring before the publication runs. `casei` won those five rows
-in the corrected 33-row result.
-
-The corrected arena still counts by repeatedly calling a single-needle search.
-It has no row that enumerates a compiled multi-pattern plan, and its synthetic
-Unicode count rows do not cover the dense Russian corpus or the Rebar prefilter
-shape measured here. The original Perfloop objective never asked it to win on
-these paths.
-
-The missing coverage let the losses survive. The measurements below identify
-their direct causes: weak shared filtering on the five-pattern row and costly
-native confirmation on the two single-pattern rows. The 33 arena rows remain
-regression gates, and the five Unicode-equivalent Rebar rows are the acceptance
-target for the open work.
-
-## Why this is a different benchmark contract
-
-| | casei arena | Rebar rows on this page |
-|---|---|---|
-| answer | first byte offset or leftmost match on 28 rows; overlap-allowed count on 5 single-needle rows | count or total span of every non-overlapping match |
-| search state | one `IndexFold`/`Find` call, repeated from the next byte on the 5 count rows | one compiled engine repeatedly enumerates from the end of each match |
-| pattern sets | multi-pattern rows stop at the first leftmost answer | both single-pattern and multi-pattern rows enumerate to the end |
-| folding | Unicode simple folding on every row | Unicode on 5 rows, ASCII-only on 13 |
-| measurement | in-process field timing, with three publication passes on each pinned host; Perfloop separately co-measured the engine's source revisions | [Rebar's sequential runner protocol](https://github.com/BurntSushi/rebar/blob/463d00f31887e84c38467805b9e3122c314b9521/METHODOLOGY.md), three independent passes here |
+18 performance workloads can be represented as one literal or a finite set of
+literals. Five request Unicode-aware case folding and ask the same semantic
+question as `casei`.
-The audit adapter compiles `NewMatcher` outside the timed region. Each iteration
-calls `Find` on successive suffixes until it reaches the end. The adapter checks
-the matched byte width under simple folding before advancing. It supports
-rebar's `count` and `count-spans` models, with the same compiled plan reused for
-every hit. A future iterator could retain scan state and vector continuity. The
-diagnosis below measures how much that would change the worst row.
+`casei` now wins all five on both measured CPUs:
-## Results
+| host | wins | median `casei / best` | worst row |
+|---|---:|---:|---:|
+| Ice Lake | 5/5 | 0.6441 | 0.8794 |
+| Sapphire Rapids | 5/5 | 0.6716 | 0.8999 |
-`casei / best` is median casei time divided by the fastest selected competitor
-on the same row and pass, then the median across three passes. Values below 1.0
-are wins. The named competitor is the fastest by its three-pass median.
+Values below 1.0 are wins. The selected field is Hyperscan 5.4.2, PCRE2 10.47
+JIT, and rust/regex 1.12.4.
-| rebar row | requested folding | Ice Lake `casei / best` | Sapphire Rapids `casei / best` |
-|---|---|---:|---:|
-| `curated/01-literal/sherlock-casei-en` | ASCII-only* | 7.57× (Hyperscan) | 8.88× (Hyperscan) |
-| `curated/01-literal/sherlock-casei-ru` | Unicode | 2.57× (PCRE2-JIT) | 2.47× (PCRE2-JIT) |
-| `curated/02-literal-alternate/sherlock-casei-en` | ASCII-only* | 7.02× (Hyperscan) | 7.02× (Hyperscan) |
-| `curated/02-literal-alternate/sherlock-casei-ru` | Unicode | 9.86× (Hyperscan) | 9.18× (Hyperscan) |
-| `hyperscan/literal-casei-english-nosom` | ASCII-only* | 4.95× (Hyperscan) | 6.40× (Hyperscan) |
-| `hyperscan/literal-casei-english-som` | ASCII-only* | 4.96× (Hyperscan) | 6.52× (Hyperscan) |
-| `hyperscan/literal-casei-russian-nosom` | Unicode | **0.71×** (rust/regex) | **0.58×** (rust/regex) |
-| `hyperscan/literal-casei-russian-som` | Unicode | **0.72×** (rust/regex) | **0.56×** (rust/regex) |
-| `imported/leipzig/tom-sawyer-huckle-fin-insensitive` | ASCII-only* | 2.70× (Hyperscan) | 3.10× (Hyperscan) |
-| `imported/leipzig/twain-insensitive` | ASCII-only* | 1.35× (Hyperscan) | 1.18× (Hyperscan) |
-| `imported/sherlock/name-alt3-casei` | ASCII-only* | **0.78×** (rust/regex) | **0.69×** (rust/regex) |
-| `imported/sherlock/name-alt5-casei` | ASCII-only* | 1.04× (rust/regex) | **0.94×** (rust/regex) |
-| `imported/sherlock/name-holmes-casei` | ASCII-only* | **0.79×** (PCRE2-JIT) | **0.75×** (PCRE2-JIT) |
-| `imported/sherlock/name-sherlock-casei` | ASCII-only* | 1.57× (PCRE2-JIT) | 1.82× (PCRE2-JIT) |
-| `imported/sherlock/name-sherlock-holmes-casei` | ASCII-only* | 2.64× (PCRE2-JIT) | 3.04× (PCRE2-JIT) |
-| `imported/sherlock/the-casei` | ASCII-only* | 1.08× (PCRE2-JIT) | **0.90×** (PCRE2-JIT) |
-| `opt/prefilter/literal-casei-english` | ASCII-only* | 1.85× (PCRE2-JIT) | 2.22× (PCRE2-JIT) |
-| `opt/prefilter/literal-casei-russian` | Unicode | 1.27× (PCRE2-JIT) | 1.28× (PCRE2-JIT) |
-
-`*` Rebar disables Unicode-aware case folding. `casei` cannot disable it, so it
-does more work and would also match Unicode fold mates not present in these
-particular corpora. These rows passed output verification on the recorded text.
-Their requested relation is ASCII-only, so they stay outside the
-contract-equivalent result.
-
-Across all 18 stress rows, including those ASCII-only rows, `casei` wins 4/18
-on Ice Lake and 6/18 on Sapphire Rapids. The product comparison uses the five
-rows with the same Unicode contract.
-
-## Why the worst row is 9× slower
-
-The bad row searches a 1,570,556-byte Russian Sherlock Holmes corpus for five
-names and counts 971 matches:
+The remaining 13 rows request ASCII-only case matching. `casei` keeps its
+Unicode relation and does more work. Across all 18 stress rows it wins 9/18 on
+each host. All rows and losses appear below.
+
+## Before and after
+
+The first Rebar audit was bad for `casei`:
+
+```text
+before
+
+five Russian patterns
+ |
+ v
+common first-byte filter -> too many survivors -> decoded Unicode replay
+ |
+ +----------------------------------------> about 9x behind Hyperscan
+
+one Russian pattern
+ |
+ v
+good interior anchor -> survivor -> decoded Unicode replay
+ |
+ +----------> behind native confirmation
+```
+
+The accepted follow-up keeps one plan and changes the work before replay:
```text
-one Russian pattern rare interior byte pairs -> about 2,100 candidates
-five Russian patterns common starting letters -> about 80,000 filter stops
+after
+
+five Russian patterns
+ |
+ +-> exact shared-byte origin gate
+ +-> tagged interior pairs for each literal
+ +-> exact pair checks for surviving tags
+ +-> the same raw fold-token plan ----------------------> 0.84x to 0.88x
+
+one Russian pattern
+ |
+ +-> pair-pair screen
+ +-> raw one/two/three-byte fold confirmation
+ +-> confirmed source width ----------------------------> 0.29x to 0.90x
```
-For one pattern, the compiler selects a fused pair-pair anchor from inside the
-literal. The AVX-512 VBMI kernel skips 1,552,007 bytes and asks the exact
-matcher to check 2,134 candidate positions.
-
-For all five patterns, the current compiler cannot combine those interior
-anchors into one shared filter. It falls back to a nine-pair Shufti filter over
-the patterns' first UTF-8 bytes. Several of those starts are common Cyrillic
-letters. Across the full count, the filter is invoked 79,950 times and admits
-193,449 runes, or 21.7% of the corpus, into the exact plan. The plan performs
-175,860 dense state transitions. The CPU profile attributes most of the time to
-UTF-8 decoding and fold-token map lookup. The AVX-512 filter is a smaller part
-of the row.
-
-A one-pass diagnostic enumerator took 4.69 ms, compared with 4.65 to 4.99 ms for
-repeated `Find`. Both returned 971 matches. The API restart cost is within the
-noise on this row.
-
-Replacing Shufti with the exact nine-pair AVX-512 filter made the row slower.
-Disabling AVX-512 added roughly 25% to 33%. Those measurements put the cost in
-the selectivity of the compiled question reaching the kernel.
-
-Rebar's Hyperscan runner was rebuilt and checked independently. It returned 971
-matches with an AVX-512 VBMI database. Start-of-match tracking changed its time
-by about 2%. Hyperscan expands the folds at compile time into a byte-level
-database and scans the five literals continuously. Its verification path avoids
-the Go rune decoder and token map used here.
-
-The smaller two losses have the same general shape at lower severity. On the
-single Russian literal, `casei`'s rare interior anchor is effective. Each
-survivor still enters a Go rune decoder and token map. PCRE2-JIT verifies in
-native code and leads by about 2.5×. On the sparse one-match Russian prefilter
-row, that residual verification gap is about 1.28×. The two wins compare the
-same sparse shape with rust/regex.
+The multi-pattern screen returns pattern tags, not matches. The existing plan
+still decides every result. The variable-width confirmer returns the number of
+source bytes it proved, so `Matcher.Each` can continue without decoding that
+match again.
+
+The common-byte gate applies only when every simple-fold spelling of every
+literal contains the same exact ASCII byte. It proves the latest possible
+start before the first occurrence. Its AVX-512 kernel compares eight cache
+lines before testing the ordered masks. If the proof cannot be compiled, the
+route is absent.
+
+## Why the original gym missed it
+
+The original arena had first-match rows and five single-needle count rows. The
+competitive bar once ignored the count flag on those five rows and timed only
+the first match. That wiring was corrected before the earlier publication run,
+and `casei` won the corrected rows.
+
+Two gaps remained:
+
+1. No row enumerated a compiled multi-pattern Unicode plan to the end of a
+ corpus.
+2. No focused row forced variable-width confirmation or the five-pattern raw
+ transition shape from Rebar.
+
+Perfloop optimized the board it was given. Rebar exposed the omitted shapes.
+The arena now has three focused rows:
+
+- `multi/multi_N1_unicode_pair_miss_1_5mb`
+- `multi/multi_N5_raw_transition_miss_5mb`
+- `multi/multi_N5_raw_transition_late_hit_5mb`
+
+Those rows bring the board to 36. All 36 remain below 1.0 `x_vs_best` on both
+hosts after the Rebar work.
+
+## The benchmark contracts
+
+| | casei arena | Rebar audit |
+|---|---|---|
+| answer | first byte offset, leftmost match, or overlap-allowed single-needle count | count or total span of every non-overlapping match |
+| compilation | included in `IndexFold`; outside repeated `Matcher` searches | matcher compiled before timing, like the other Rebar engines |
+| pattern sets | one and many | one and finite alternations |
+| folding | Unicode simple folding on every row | Unicode on 5 rows, ASCII-only on 13 |
+| timing | in-process paired windows, alternating order, three complete passes | Rebar runner protocol, three passes, pinned to one core |
+
+The audit adapter validates complete enumeration against an independent
+simple-fold source scan before warmup. The timed iteration calls `Matcher.Each`
+with only a count or span sink.
+
+## All 18 rows
+
+`casei / best` is the median of three per-pass ratios. The named competitor is
+the fastest by its three-pass median. Bold values are wins.
+
+| Rebar row | requested folding | Ice Lake | Sapphire Rapids |
+|---|---|---:|---:|
+| `curated/01-literal/sherlock-casei-en` | ASCII-only* | 4.48× (Hyperscan) | 4.93× (Hyperscan) |
+| `curated/01-literal/sherlock-casei-ru` | Unicode | **0.87×** (PCRE2-JIT) | **0.90×** (PCRE2-JIT) |
+| `curated/02-literal-alternate/sherlock-casei-en` | ASCII-only* | 4.69× (Hyperscan) | 4.91× (Hyperscan) |
+| `curated/02-literal-alternate/sherlock-casei-ru` | Unicode | **0.88×** (Hyperscan) | **0.84×** (Hyperscan) |
+| `hyperscan/literal-casei-english-nosom` | ASCII-only* | 3.55× (Hyperscan) | 4.32× (Hyperscan) |
+| `hyperscan/literal-casei-english-som` | ASCII-only* | 3.53× (Hyperscan) | 4.39× (Hyperscan) |
+| `hyperscan/literal-casei-russian-nosom` | Unicode | **0.36×** (rust/regex) | **0.29×** (Hyperscan) |
+| `hyperscan/literal-casei-russian-som` | Unicode | **0.36×** (rust/regex) | **0.29×** (Hyperscan) |
+| `imported/leipzig/tom-sawyer-huckle-fin-insensitive` | ASCII-only* | 2.37× (Hyperscan) | 2.10× (Hyperscan) |
+| `imported/leipzig/twain-insensitive` | ASCII-only* | 1.11× (Hyperscan) | 1.08× (Hyperscan) |
+| `imported/sherlock/name-alt3-casei` | ASCII-only* | **0.70×** (rust/regex) | **0.63×** (rust/regex) |
+| `imported/sherlock/name-alt5-casei` | ASCII-only* | **0.89×** (rust/regex) | **0.85×** (rust/regex) |
+| `imported/sherlock/name-holmes-casei` | ASCII-only* | **0.57×** (PCRE2-JIT) | **0.55×** (PCRE2-JIT) |
+| `imported/sherlock/name-sherlock-casei` | ASCII-only* | 1.54× (PCRE2-JIT) | 1.84× (PCRE2-JIT) |
+| `imported/sherlock/name-sherlock-holmes-casei` | ASCII-only* | 2.50× (PCRE2-JIT) | 3.01× (PCRE2-JIT) |
+| `imported/sherlock/the-casei` | ASCII-only* | **0.78×** (PCRE2-JIT) | **0.66×** (PCRE2-JIT) |
+| `opt/prefilter/literal-casei-english` | ASCII-only* | 1.90× (PCRE2-JIT) | 2.34× (PCRE2-JIT) |
+| `opt/prefilter/literal-casei-russian` | Unicode | **0.64×** (PCRE2-JIT) | **0.67×** (PCRE2-JIT) |
+
+`*` Rebar disables Unicode-aware folding. `casei` would match Unicode fold
+mates that these definitions exclude. The recorded corpora happen to produce
+the expected outputs, so their timings are useful stress data. Their semantic
+contract stays outside the 5/5 result.
## Correctness and inventory closure
-Every recorded runner invocation returned rebar's expected result on each of
-the 18 performance workloads it entered. On both hosts, `casei` also passed the
-two compatible behavior checks. The incompatible behavior row is:
+Every recorded invocation returned Rebar's expected answer on the 18
+performance workloads. `casei` also passed the two compatible behavior checks.
+The third behavior check is intentionally incompatible:
```text
-test/unicode/case/ascii-only: pattern "s", haystack "ſ"
-rebar with unicode=false: 0 matches
-casei Unicode simple fold: 1 match
+test/unicode/case/ascii-only
+pattern: "s"
+haystack: "ſ"
+Rebar with unicode=false: 0 matches
+casei simple folding: 1 match
```
-The audit also inspected the remaining case-insensitive rebar definitions:
-
-- `curated/03-date/*` and `wild/url/*` are general regex grammars.
-- `curated/13-noseyparker/*` loads a large rule file whose caseless rules also
- use classes, boundaries, captures, and bounded repetition; its search and
- compile workloads are general-regex workloads, not finite literal sets.
-- `imported/sherlock/name-alt4-casei` contains a character class and `+`.
-- `wild/ruff`, `reported/p893-hir-case-folding`, and
- `reported/i988-cloudflare-compile` exercise inline flags, classes,
- repetition, captures, or compile time.
-
-Those are outside a literal/finite-alternation API. Every caseless rebar row
-that can be represented as one literal or a finite set of literals is accounted
-for above.
+The remaining case-insensitive Rebar definitions use general regex features:
+classes, captures, repetition, boundaries, inline flags, or compile-time rule
+sets. They cannot be represented by a literal or finite literal-set API. The
+18 rows above account for every caseless Rebar workload that can.
## Measurement record
-The exact adapter, pinned integration script, six raw CSVs, and an independent
-ratio calculator are checked in under
-[`audit/rebar/`](audit/rebar/README.md). Running
-`python3 audit/rebar/summarize.py` reproduces this page's table and summaries
-from those receipts.
-
| item | recorded value |
|---|---|
-| casei source | tree `d1f73802d35c29009a433eaaf9c2b51113ab5c95`, merged as [`3954dbe`](https://github.com/tsenart/casei/commit/3954dbe40e8e21c4c7b2e2716f22647dd7cd880c) |
-| rebar source | `463d00f31887e84c38467805b9e3122c314b9521` |
+| Rebar source | `463d00f31887e84c38467805b9e3122c314b9521` |
| selected field | Hyperscan 5.4.2, PCRE2 10.47 JIT, rust/regex 1.12.4 |
| hosts | GenuineIntel family 6/model 106 Ice Lake and family 6/model 143 Sapphire Rapids, both with AVX-512F/BW/VBMI |
-| passes | three per host, with up to 100 warmups/200 ms and 1,000 measured iterations/500 ms per benchmark |
-| plan setup | outside rebar's timed iteration, matching the other engines |
-| entrants | three or four per row including `casei`, as selected by each upstream definition |
-
-The selected field contains the three leaders in rebar's published records for
-these literal rows. A displayed win is scoped to that leader set. Any unmeasured
-entrant could only make the ratio worse.
-
-One upstream build detail is recorded for reproducibility: rebar's vendored
-PCRE2 10.47 snapshot contains `pcre2posix.c` but not its unused
-`pcre2posix.h`. The audit omitted that POSIX wrapper from the build; rebar's
-runner uses the native PCRE2 API, so no compiled search or JIT code changed.
-
-## Public work on the losses
-
-The first four Perfloop experiments are public and stopped:
-
-- [Compile shared interior UTF-8 anchors for multi-pattern plans](https://app.perfloop.ai/t/oss/case_jws72csfa9)
- explored the roughly 9x multi-pattern loss.
-- [Compile dispersed width-stable Unicode byte probes](https://app.perfloop.ai/t/oss/case_b2m0dmh5wa)
- explored the two single-pattern losses.
-- [Compile width-stable Unicode byte confirmations](https://app.perfloop.ai/t/oss/case_tgkp9bs0r6)
- explored compiled raw-byte confirmation.
-- [Carry the confirmed end into repeated matching](https://app.perfloop.ai/t/oss/case_1jg4we7k3s)
- tested the narrower repeated-call explanation. The one-pass control and
- repeated `Find` remained effectively tied on the worst row.
-
-Two later Cases carry the surviving work:
-
-- [Keep N=1 confirmation inside the AVX-512 scan](https://app.perfloop.ai/t/oss/case_s8c41a1per)
- is verified on one targeted false-survivor row. It moved
- `x_vs_best` from `4.547` to `0.7328` over ten randomized pairs.
- [PR #10](https://github.com/tsenart/casei/pull/10) remains open because that
- result has not been reproduced across the complete board on both hosts.
-- [Replace the decoded transition loop with one raw-byte plan](https://app.perfloop.ai/t/oss/case_rmg4fdm3me)
- is open. It targets the shared cost that remains after filtering: decoding a
- surviving position, mapping it into the fold-token alphabet, and then
- advancing the exact plan.
-
-Any change accepted into `casei` must beat all five Unicode-equivalent Rebar
-rows on Ice Lake and Sapphire Rapids, preserve the one-engine design and full
-correctness contract, and keep every current `BenchmarkBar` row below 1.0
-`x_vs_best`.
-
-## Required next work
-
-The next construction must:
-
-1. compile once and keep one package-owned plan;
-2. combine selective interior anchors from several patterns into one shared
- byte-level filter;
-3. replace hot-path rune-to-token map lookups with compiled raw-byte
- transitions where the plan permits it;
-4. preserve exact non-overlapping enumeration through the same state machine;
- and
-5. beat the fastest eligible entrant on all five Unicode-equivalent rows before
- any count-all performance claim is made.
-
-The public performance claim therefore covers the 33-row arena contract. It
-does not claim leadership for non-overlapping enumeration.
+| passes | three per host |
+| warmup bound | 100 iterations or 200 ms |
+| measurement bound | 1,000 iterations or 500 ms |
+| CPU placement | runner and child engines pinned to core 2 |
+| plan setup | outside the timed iteration |
+
+The adapter, pinned integration script, six raw CSVs, receipt hashes, and ratio
+calculator are checked in under [`audit/rebar/`](audit/rebar/README.md).
+
+```sh
+(cd audit/rebar/results && sha256sum -c SHA256SUMS)
+python3 audit/rebar/summarize.py
+```
+
+One upstream build detail is preserved in the reproducer. Rebar's vendored
+PCRE2 snapshot contains `pcre2posix.c` without its unused header. The audit
+omits that POSIX wrapper. Rebar uses the native PCRE2 API, so its JIT search
+code is unchanged.
+
+## Experiment history
+
+The gaps were explored in public Perfloop Cases:
+
+- [Shared interior UTF-8 anchors](https://app.perfloop.ai/t/oss/case_jws72csfa9)
+- [Dispersed width-stable probes](https://app.perfloop.ai/t/oss/case_b2m0dmh5wa)
+- [Raw byte confirmation](https://app.perfloop.ai/t/oss/case_tgkp9bs0r6)
+- [Carry confirmed ends into repeated matching](https://app.perfloop.ai/t/oss/case_1jg4we7k3s)
+
+The last case rejected the idea that API restart cost was the main problem.
+The selective filter and native confirmation work survived. The current result
+combines those pieces with the exact origin gate and wider assembly schedules.
diff --git a/arena/bar_test.go b/arena/bar_test.go
index 537750a..db15991 100644
--- a/arena/bar_test.go
+++ b/arena/bar_test.go
@@ -8,6 +8,7 @@ package arena_test
import (
"fmt"
+ "sort"
"testing"
"time"
@@ -21,26 +22,40 @@ import (
vectorscan "github.com/tsenart/casei/arena/vectorscan"
)
-// timeOp returns ns/op for one operation. It times manually rather than
-// through testing.Benchmark, which cannot be nested inside a running
-// benchmark, and takes the best of three samples to reduce sensitivity to
-// transient host load.
-func timeOp(op func()) float64 {
+// timeWindow returns ns/op for one manually timed window. testing.Benchmark
+// cannot be nested inside a running benchmark.
+func timeWindow(op func()) float64 {
const budget = 25 * time.Millisecond
- best := 0.0
- for sample := 0; sample < 3; sample++ {
- n := 0
- start := time.Now()
- for time.Since(start) < budget {
- op()
- n++
- }
- ns := float64(time.Since(start).Nanoseconds()) / float64(n)
- if best == 0 || ns < best {
- best = ns
+ n := 0
+ start := time.Now()
+ for time.Since(start) < budget {
+ op()
+ n++
+ }
+ return float64(time.Since(start).Nanoseconds()) / float64(n)
+}
+
+// pairedRatio measures the candidate beside one competitor six times, with
+// each operation going first in three pairs. The median paired ratio reduces
+// drift from CPU migration, frequency changes, and host load between timing
+// windows.
+func pairedRatio(candidate, competitor func()) float64 {
+ candidate()
+ competitor()
+ var ratios [6]float64
+ for round := range ratios {
+ var candidateNS, competitorNS float64
+ if round%2 == 0 {
+ candidateNS = timeWindow(candidate)
+ competitorNS = timeWindow(competitor)
+ } else {
+ competitorNS = timeWindow(competitor)
+ candidateNS = timeWindow(candidate)
}
+ ratios[round] = candidateNS / competitorNS
}
- return best
+ sort.Float64s(ratios[:])
+ return (ratios[2] + ratios[3]) / 2
}
// velozVectorBits is deliberately strict: Veloz has an SSE/scalar fallback,
@@ -132,45 +147,44 @@ func BenchmarkBar(b *testing.B) {
for _, s := range scenarios {
s := s
b.Run("single/"+s.name, func(b *testing.B) {
- cand := timeOp(func() { sink = runSingleScenario(casei.IndexFold, s) })
-
- best := timeOp(func() { sink = runSingleScenario(indexRegexp, s) })
+ candidate := func() { sink = runSingleScenario(casei.IndexFold, s) }
+ best := pairedRatio(candidate, func() { sink = runSingleScenario(indexRegexp, s) })
competitors := 1
- if v := timeOp(func() { sink = runSingleScenario(indexPCRE2, s) }); v < best {
- best = v
+ if ratio := pairedRatio(candidate, func() { sink = runSingleScenario(indexPCRE2, s) }); ratio > best {
+ best = ratio
}
competitors++
rure := rureSingles[s.needle]
- rureTime := timeOp(func() { sink = runSingleScenario(indexRure, s) })
+ rureRatio := pairedRatio(candidate, func() { sink = runSingleScenario(indexRure, s) })
// The Rust adapter records the backend reached by this exact query.
// A query that did not reach memchr AVX2 is diagnostic only; it must
// not race a target-width field entrant under a CPU-flag label.
if rure.VectorBits() == 256 {
- if rureTime < best {
- best = rureTime
+ if rureRatio > best {
+ best = rureRatio
}
competitors++
}
- if v := timeOp(func() { sink = runSingleScenario(indexVectorscan, s) }); v < best {
- best = v
+ if ratio := pairedRatio(candidate, func() { sink = runSingleScenario(indexVectorscan, s) }); ratio > best {
+ best = ratio
}
competitors++
if stringZillaAvailable {
- if v := timeOp(func() { sink = runSingleScenario(indexStringZilla, s) }); v < best {
- best = v
+ if ratio := pairedRatio(candidate, func() { sink = runSingleScenario(indexStringZilla, s) }); ratio > best {
+ best = ratio
}
competitors++
}
if !s.utf8 && velozVectorBits() == 256 {
- if v := timeOp(func() { sink = runSingleScenario(veloz.IndexFold, s) }); v < best {
- best = v
+ if ratio := pairedRatio(candidate, func() { sink = runSingleScenario(veloz.IndexFold, s) }); ratio > best {
+ best = ratio
}
competitors++
}
for b.Loop() {
- sink = runSingleScenario(casei.IndexFold, s)
+ candidate()
}
- b.ReportMetric(cand/best, "x_vs_best")
+ b.ReportMetric(best, "x_vs_best")
b.ReportMetric(float64(competitors), "competitors")
b.ReportMetric(float64(competitors+1), "entrants")
reportSingleDispatch(b, s)
@@ -182,58 +196,58 @@ func BenchmarkBar(b *testing.B) {
scenarioIndex := scenarioIndex
b.Run("multi/"+s.name, func(b *testing.B) {
m := casei.NewMatcher(s.patterns)
- cand := timeOp(func() { _, matcherFound = m.Find(s.haystack) })
+ candidate := func() { _, matcherFound = m.Find(s.haystack) }
re := regexpAltFor(s.patterns)
- best := timeOp(func() { matcherSink = len(re.FindStringIndex(s.haystack)) })
+ best := pairedRatio(candidate, func() { matcherSink = len(re.FindStringIndex(s.haystack)) })
competitors := 1
pcre := pcre2Alts[scenarioIndex]
- if v := timeOp(func() { _, _, matcherFound = pcre.Find(s.haystack) }); v < best {
- best = v
+ if ratio := pairedRatio(candidate, func() { _, _, matcherFound = pcre.Find(s.haystack) }); ratio > best {
+ best = ratio
}
competitors++
rure := rureAlts[scenarioIndex]
- rureTime := timeOp(func() { _, _, matcherFound = rure.Find(s.haystack) })
+ rureRatio := pairedRatio(candidate, func() { _, _, matcherFound = rure.Find(s.haystack) })
if rure.VectorBits() == 256 {
- if rureTime < best {
- best = rureTime
+ if rureRatio > best {
+ best = rureRatio
}
competitors++
}
vscan := vectorscanAlts[scenarioIndex]
- if v := timeOp(func() { _, _, matcherFound = vscan.Find(s.haystack) }); v < best {
- best = v
+ if ratio := pairedRatio(candidate, func() { _, _, matcherFound = vscan.Find(s.haystack) }); ratio > best {
+ best = ratio
}
competitors++
if stringZillaAvailable {
stringzilla := stringZillaAlts[scenarioIndex]
- if v := timeOp(func() { _, _, matcherFound = stringzilla.Find(s.haystack) }); v < best {
- best = v
+ if ratio := pairedRatio(candidate, func() { _, _, matcherFound = stringzilla.Find(s.haystack) }); ratio > best {
+ best = ratio
}
competitors++
}
supplemental := 0
rust := rustACAlts[scenarioIndex]
if !s.utf8 {
- rustTime := timeOp(func() { _, _, matcherFound = rust.Find(s.haystack) })
+ rustRatio := pairedRatio(candidate, func() { _, _, matcherFound = rust.Find(s.haystack) })
// The direct Rust DFA exposes the memchr backend reached by this
// exact prefilter query. Do not call an unobserved scalar/SSE path
// an AVX2 field entrant merely because this process has AVX2.
if rust.VectorBits() == 256 {
- if rustTime < best {
- best = rustTime
+ if rustRatio > best {
+ best = rustRatio
}
competitors++
}
goAC := acBuild(s.patterns, true)
- _ = timeOp(func() { _, matcherFound = acFirst(&goAC, s.haystack) })
+ _ = pairedRatio(candidate, func() { _, matcherFound = acFirst(&goAC, s.haystack) })
supplemental++
}
for b.Loop() {
- _, matcherFound = m.Find(s.haystack)
+ candidate()
}
- b.ReportMetric(cand/best, "x_vs_best")
+ b.ReportMetric(best, "x_vs_best")
b.ReportMetric(float64(competitors), "competitors")
b.ReportMetric(float64(competitors+1+supplemental), "entrants")
reportMultiDispatch(b, s, m.VectorBits(), rure, rust, vscan)
diff --git a/arena/matcher_bench_test.go b/arena/matcher_bench_test.go
index 9c36ca3..abcc9f8 100644
--- a/arena/matcher_bench_test.go
+++ b/arena/matcher_bench_test.go
@@ -52,6 +52,56 @@ func genNeedles(n int, format string) []string {
return out
}
+// unicodePairConfirmMissCorpus repeats a near-full-width false match at the
+// pair-pair anchor density measured on the Russian corpus. It exercises the
+// N=1 Matcher path without giving any engine a true-match early exit.
+func unicodePairConfirmMissCorpus() string {
+ const needle = "приключения лилий"
+ const haystackBytes = 1_570_556
+ const survivors = 2_134
+
+ falseLiteral := needle[:len(needle)-len("й")] + "я"
+ bytes := []byte(strings.Repeat("x", haystackBytes))
+ step := len(bytes) / survivors
+ inserted := 0
+ for at := 0; at+len(falseLiteral) <= len(bytes) && inserted < survivors; at += step {
+ copy(bytes[at:], falseLiteral)
+ inserted++
+ }
+ if inserted != survivors {
+ panic(fmt.Sprintf("inserted %d false survivors, want %d", inserted, survivors))
+ }
+ return string(bytes)
+}
+
+// rawTransitionPatterns are ordinary Unicode simple-fold literals with distinct
+// Cyrillic roots. The companion rows keep an admitted root followed by an ASCII
+// mismatch at a fixed density, and add one late full literal for the hit case.
+// They exercise a shared multi-pattern plan against every native field entrant;
+// no production path observes these fixture names or values.
+var rawTransitionPatterns = []string{
+ "Шерлок Холмс",
+ "Джон Уотсон",
+ "Ирен Адлер",
+ "инспектор Лестрейд",
+ "профессор Мориарти",
+}
+
+func rawTransitionCorpus(bytes int, lateMatch bool) string {
+ const period = 256
+ if bytes < period {
+ panic("raw transition corpus is shorter than its period")
+ }
+ out := []byte(strings.Repeat("x", ((bytes+period-1)/period)*period))
+ for at := 0; at+len("Дx") <= len(out); at += period {
+ copy(out[at:], "Дx")
+ }
+ if lateMatch {
+ copy(out[len(out)-period:], rawTransitionPatterns[0])
+ }
+ return string(out)
+}
+
var multiScenarios = func() []multiScenario {
logs1m := buildLogCorpus(1 << 20)
prose1m := buildProseCorpus(1 << 20)
@@ -79,6 +129,9 @@ var multiScenarios = func() []multiScenario {
// about the tier this repository exists for.
{"multi_N512_miss_hazard_64kb", cyr1m[:64<<10], genHazardNeedles(512), true},
{"multi_N8_hit_log_1mb", plant(logs1m, "Payment Declined", 4), hit8, false},
+ {"multi_N1_unicode_pair_miss_1_5mb", unicodePairConfirmMissCorpus(), []string{"приключения лилий"}, true},
+ {"multi_N5_raw_transition_miss_5mb", rawTransitionCorpus(5<<20, false), rawTransitionPatterns, true},
+ {"multi_N5_raw_transition_late_hit_5mb", rawTransitionCorpus(5<<20, true), rawTransitionPatterns, true},
{"multi_N8_miss_ru_1mb", cyr1m, genNeedles(8, "щупальце%d"), true},
{"multi_N64_miss_ru_64kb", cyr1m[:64<<10], genNeedles(64, "щупальце%d"), true},
// This is the all-ASCII half of the mixed-fold hazard set. It keeps the
diff --git a/ascii_partition_test.go b/ascii_partition_test.go
new file mode 100644
index 0000000..f243b0c
--- /dev/null
+++ b/ascii_partition_test.go
@@ -0,0 +1,352 @@
+package casei
+
+import (
+ "math/rand/v2"
+ "strings"
+ "testing"
+)
+
+// asciiPartitionPatterns deliberately leave the root filter too broad while
+// retaining a complete, bounded ASCII triple projection. Nine ASCII literals
+// disable the eight-entry pair projection; the Cyrillic roots make the regular
+// mixed-byte filter unusable. This is the shape where one high byte otherwise
+// poisons the remainder of an otherwise block-friendly scan.
+func asciiPartitionPatterns() []string {
+ return []string{
+ "abc0", "abc1", "def0", "def1", "ghi0", "ghi1", "jkl0", "jkl1", "mno0",
+ "абв0", "где1", "жзи2", "йкл3", "мно4", "прс5", "туф6", "хцч7", "шщъ8", "ыьэ9",
+ }
+}
+
+func decodedPlanFind(p *searchPlan, haystack string) (Match, bool) {
+ var inlineStarts [256]int
+ starts := inlineStarts[:]
+ if p.maxUnits > len(starts) {
+ starts = make([]int, p.maxUnits)
+ }
+ return p.findUnfilteredWithStarts(haystack, starts)
+}
+
+func TestASCIIPartitionPlanAdmission(t *testing.T) {
+ plan := newSearchPlan(asciiPartitionPatterns())
+ if runtimeVectorBits() != 512 {
+ t.Skipf("ASCII partition is runtime-gated to AVX-512; vector width %d", runtimeVectorBits())
+ }
+ if !plan.asciiPartitionUsable() {
+ t.Fatalf("plan did not retain the partition route: triples=%d complete=%t shufti=%t pair=%t filter=%t",
+ plan.asciiTriples.n, plan.asciiTriplesComplete, plan.asciiTriples.shufti.usable(),
+ plan.asciiPairAnchors.usable(), plan.filter.usable())
+ }
+ if plan.maxBytes == 0 || plan.maxBytes < plan.maxUnits {
+ t.Fatalf("maximum source width = %d for %d units", plan.maxBytes, plan.maxUnits)
+ }
+ if plan.filter.usable() || plan.asciiPairAnchors.usable() || plan.rawByteMulti.usable() {
+ t.Fatalf("fixture entered a route outside the intended gap: filter=%t pair=%t raw=%t", plan.filter.usable(), plan.asciiPairAnchors.usable(), plan.rawByteMulti.usable())
+ }
+}
+
+func TestASCIIPartitionDifferential(t *testing.T) {
+ patterns := asciiPartitionPatterns()
+ plan := newSearchPlan(patterns)
+ matcher := NewMatcher(patterns)
+ rng := rand.New(rand.NewPCG(20260829, 17))
+ units := []string{
+ "x", " ", "a", "A", "b", "c", "0", "Д", "д", "Ж", "ж", "€", "ᲁ",
+ "\x00", "\xff", "\x80", "\xc2\x80", "\xe2\x84\xaa",
+ }
+ for iteration := 0; iteration < 2000; iteration++ {
+ var input strings.Builder
+ for range 1 + rng.IntN(160) {
+ input.WriteString(units[rng.IntN(len(units))])
+ }
+ if iteration%3 == 0 {
+ input.WriteString(patterns[rng.IntN(len(patterns))])
+ }
+ haystack := input.String()
+ want, wantOK := decodedPlanFind(plan, haystack)
+ got, gotOK := matcher.Find(haystack)
+ if gotOK != wantOK || gotOK && got != want {
+ t.Fatalf("iteration %d Find(%x) = %+v,%t; decoded = %+v,%t", iteration, haystack, got, gotOK, want, wantOK)
+ }
+ ref, refOK := refFind(haystack, patterns)
+ if gotOK != refOK || gotOK && got != ref {
+ t.Fatalf("iteration %d Find(%x) = %+v,%t; reference = %+v,%t", iteration, haystack, got, gotOK, ref, refOK)
+ }
+ }
+}
+
+func TestASCIIPartitionPositionDensityAndBoundaries(t *testing.T) {
+ patterns := asciiPartitionPatterns()
+ plan := newSearchPlan(patterns)
+ matcher := NewMatcher(patterns)
+ for _, size := range []int{1, 63, 64, 127, 1024, 8192} {
+ for _, gap := range []int{1, 7, 31, 127, 511} {
+ var input strings.Builder
+ for at := 0; at < size; at += gap {
+ input.WriteString(strings.Repeat("x", min(gap, size-at)))
+ input.WriteString("€")
+ }
+ haystack := input.String()
+ want, wantOK := refFind(haystack, patterns)
+ if got, gotOK := matcher.Find(haystack); gotOK != wantOK || gotOK && got != want {
+ t.Fatalf("size %d gap %d Find = %+v,%t; want %+v,%t", size, gap, got, gotOK, want, wantOK)
+ }
+ }
+ }
+
+ highAt := 64
+ spanEnd := highAt + len("€")
+ boundaryStart := spanEnd + plan.maxBytes - 1
+ boundary := []byte(strings.Repeat("x", boundaryStart+len("abc0")+32))
+ copy(boundary[highAt:], "€")
+ copy(boundary[boundaryStart:], "abc0")
+ secondHighAt := 128
+ interWindowStart := secondHighAt - plan.maxBytes - 1
+ interWindow := []byte(strings.Repeat("x", 256))
+ copy(interWindow[highAt:], "€")
+ copy(interWindow[secondHighAt:], "€")
+ copy(interWindow[interWindowStart:], "abc0")
+
+ for _, haystack := range []string{
+ "abc0" + "€" + strings.Repeat("x", 64),
+ strings.Repeat("x", 64) + "€" + "abc0",
+ strings.Repeat("x", 64) + "ᲁ" + "abc0",
+ strings.Repeat("x", 64) + "\xff" + "abc0",
+ strings.Repeat("x", 64) + "\x80" + "abc0",
+ strings.Repeat("x", 64) + "\xc2\x80" + "abc0",
+ strings.Repeat("x", 64) + "\x00" + "abc0",
+ strings.Repeat("x", 64) + "abc0" + "€" + "def1",
+ strings.Repeat("x", 64) + "€" + strings.Repeat("x", 64) + "\xff" + "ghi2",
+ strings.Repeat("x", 76) + "jkl0" + strings.Repeat("x", 5) + "\xff" + strings.Repeat("x", 512),
+ string(boundary),
+ string(interWindow),
+ } {
+ want, wantOK := refFind(haystack, patterns)
+ if got, gotOK := matcher.Find(haystack); gotOK != wantOK || gotOK && got != want {
+ t.Fatalf("boundary Find(%x) = %+v,%t; want %+v,%t", haystack, got, gotOK, want, wantOK)
+ }
+ }
+}
+
+func TestASCIIPartitionWidthChangingBoundaries(t *testing.T) {
+ patterns := append(asciiPartitionPatterns(), "абвkK0", "гдеsſ1")
+ plan := newSearchPlan(patterns)
+ if !plan.asciiPartitionUsable() {
+ t.Skipf("width-changing fixture is not admitted on vector width %d", runtimeVectorBits())
+ }
+ if plan.maxBytes <= plan.maxUnits {
+ t.Fatalf("width-changing fixture did not widen windows: maxBytes=%d maxUnits=%d", plan.maxBytes, plan.maxUnits)
+ }
+ matcher := NewMatcher(patterns)
+ for _, haystack := range []string{
+ strings.Repeat("x", 73) + "абвKK0" + strings.Repeat("x", 73),
+ strings.Repeat("x", 73) + "гдеSſ1" + strings.Repeat("x", 73),
+ strings.Repeat("x", 73) + "абвkK0" + "€" + "гдеsſ1",
+ } {
+ want, wantOK := refFind(haystack, patterns)
+ if got, gotOK := matcher.Find(haystack); gotOK != wantOK || gotOK && got != want {
+ t.Fatalf("width-changing Find(%x) = %+v,%t; want %+v,%t; maxBytes=%d", haystack, got, gotOK, want, wantOK, plan.maxBytes)
+ }
+ }
+}
+
+func TestASCIIPartitionEachContract(t *testing.T) {
+ patterns := append(asciiPartitionPatterns(), "abc0")
+ matcher := NewMatcher(patterns)
+ for _, haystack := range []string{
+ "abc0xx€ABC1xxdef0",
+ strings.Repeat("x", 91) + "€" + "ghi1" + strings.Repeat("x", 73) + "\xff" + "jkl0",
+ strings.Repeat("x", 512) + "ᲁ" + "mno0" + strings.Repeat("x", 512) + "abc1",
+ } {
+ want := refEach(haystack, patterns)
+ var got []refEachResult
+ if complete := matcher.Each(haystack, func(match Match, width int) bool {
+ got = append(got, refEachResult{match: match, width: width})
+ return true
+ }); !complete {
+ t.Fatalf("Each(%x) stopped early", haystack)
+ }
+ if len(got) != len(want) {
+ t.Fatalf("Each(%x) returned %d matches, want %d: got=%+v want=%+v", haystack, len(got), len(want), got, want)
+ }
+ for i := range want {
+ if got[i] != want[i] {
+ t.Fatalf("Each(%x) match %d = %+v, want %+v", haystack, i, got[i], want[i])
+ }
+ }
+ }
+}
+
+func TestASCIIPartitionReportsWorkSplit(t *testing.T) {
+ patterns := asciiPartitionPatterns()
+ plan := newSearchPlan(patterns)
+ if !plan.asciiPartitionUsable() {
+ t.Skipf("ASCII partition is runtime-gated to AVX-512; vector width %d", runtimeVectorBits())
+ }
+ var input strings.Builder
+ for input.Len()+512+len("€") <= 1<<16 {
+ input.WriteString(strings.Repeat("x", 512))
+ input.WriteString("€")
+ }
+ input.WriteString(strings.Repeat("x", 1<<16-input.Len()))
+ haystack := input.String()
+ var stats asciiPartitionStats
+ if _, ok := plan.findUnfilteredWithStats(haystack, &stats); ok {
+ t.Fatal("setup unexpectedly matched")
+ }
+ if stats.fallbackEntries != 0 || stats.decodedWindows == 0 || stats.decodedWindowBytes == 0 {
+ t.Fatalf("partition diagnostics = %+v", stats)
+ }
+ if stats.highBytes == 0 || stats.firstExceptional <= 0 || stats.asciiCandidateBytes <= stats.decodedWindowBytes {
+ t.Fatalf("partition did not expose sparse ASCII work: %+v", stats)
+ }
+
+ fallbackHaystack := strings.Repeat("x", 256) + "€"
+ stats = asciiPartitionStats{}
+ if _, ok := plan.findUnfilteredWithStats(fallbackHaystack, &stats); ok {
+ t.Fatal("fallback setup unexpectedly matched")
+ }
+ if stats.fallbackEntries != 1 || stats.firstExceptional < 0 || stats.decodedWindows != 0 {
+ t.Fatalf("tail fallback diagnostics = %+v", stats)
+ }
+}
+
+func TestASCIIPartitionRejectsDenseExceptionalInput(t *testing.T) {
+ if runtimeVectorBits() != 512 {
+ t.Skipf("ASCII partition is runtime-gated to AVX-512; vector width %d", runtimeVectorBits())
+ }
+ plan := newSearchPlan(asciiPartitionPatterns())
+ for _, tc := range []struct {
+ name string
+ interval int
+ value byte
+ }{
+ {name: "high", interval: 32, value: 0xe2},
+ {name: "nul", interval: 128, value: 0},
+ } {
+ input := []byte(strings.Repeat("x", 1<<20))
+ for at := 0; at < len(input); at += tc.interval {
+ input[at] = tc.value
+ }
+ var stats asciiPartitionStats
+ if _, ok := plan.findUnfilteredWithStats(string(input), &stats); ok {
+ t.Fatalf("%s setup unexpectedly matched", tc.name)
+ }
+ if stats.fallbackEntries != 1 || stats.decodedWindows != 0 {
+ t.Fatalf("%s dense input was partitioned: %+v", tc.name, stats)
+ }
+ }
+
+ lateDense := []byte(strings.Repeat("x", 1<<20))
+ copy(lateDense[0:3], "€")
+ for at := 4096; at < len(lateDense)-2; at += 32 {
+ copy(lateDense[at:at+3], "€")
+ }
+ var stats asciiPartitionStats
+ if _, ok := plan.findUnfilteredWithStats(string(lateDense), &stats); ok {
+ t.Fatal("late dense setup unexpectedly matched")
+ }
+ if stats.fallbackEntries != 1 {
+ t.Fatalf("late dense input was not rejected while discovering spans: %+v", stats)
+ }
+
+ contiguous := []byte(strings.Repeat("x", 1<<20))
+ copy(contiguous[0:3], "€")
+ copy(contiguous[4096:], strings.Repeat("€", 1<<16))
+ stats = asciiPartitionStats{}
+ if _, ok := plan.findUnfilteredWithStats(string(contiguous), &stats); ok {
+ t.Fatal("contiguous dense setup unexpectedly matched")
+ }
+ if stats.fallbackEntries != 1 {
+ t.Fatalf("contiguous dense input was not rejected early: %+v", stats)
+ }
+
+ // A sparse first span must not make the partitioner spend a decoded window
+ // before it discovers a later contiguous valid-UTF-8 region is dense.
+ sparsePrefixDenseSuffix := []byte(strings.Repeat("x", 1<<20))
+ copy(sparsePrefixDenseSuffix[64:], "€")
+ copy(sparsePrefixDenseSuffix[4096:], strings.Repeat("€", 1<<16))
+ stats = asciiPartitionStats{}
+ if _, ok := plan.findUnfilteredWithStats(string(sparsePrefixDenseSuffix), &stats); ok {
+ t.Fatal("sparse-prefix setup unexpectedly matched")
+ }
+ if stats.fallbackEntries != 1 || stats.decodedWindows != 0 {
+ t.Fatalf("sparse-prefix dense input was partitioned before fallback: %+v", stats)
+ }
+
+ sampledDenseRegion := []byte(strings.Repeat("x", 1<<20))
+ copy(sampledDenseRegion[64:], "€")
+ for at := 200000; at+3 < 700000; at += 8 {
+ copy(sampledDenseRegion[at:], "€")
+ }
+ if asciiPartitionSparseEnough(string(sampledDenseRegion), 64) {
+ t.Fatal("sampled dense region passed admission")
+ }
+ stats = asciiPartitionStats{}
+ if _, ok := plan.findUnfilteredWithStats(string(sampledDenseRegion), &stats); ok {
+ t.Fatal("sampled dense setup unexpectedly matched")
+ }
+ if stats.fallbackEntries != 1 || stats.decodedWindows != 0 {
+ t.Fatalf("sampled dense input was admitted before fallback: %+v", stats)
+ }
+
+ widePatterns := asciiPartitionPatterns()
+ widePatterns[0] = strings.Repeat("q", 100000) + "0"
+ widePlan := newSearchPlan(widePatterns)
+ if !widePlan.asciiPartitionUsable() || widePlan.maxBytes < 100000 {
+ t.Fatalf("wide pattern did not retain the partition shape: usable=%t maxBytes=%d", widePlan.asciiPartitionUsable(), widePlan.maxBytes)
+ }
+ for _, tc := range []struct {
+ length int
+ highAt int
+ }{{length: 256, highAt: 189}, {length: 131072, highAt: 64}} {
+ wideHaystack := []byte(strings.Repeat("x", tc.length))
+ copy(wideHaystack[tc.highAt:], "€")
+ stats = asciiPartitionStats{}
+ if _, ok := widePlan.findUnfilteredWithStats(string(wideHaystack), &stats); ok {
+ t.Fatalf("wide-pattern setup unexpectedly matched at length %d", tc.length)
+ }
+ if stats.fallbackEntries != 1 || stats.decodedWindows != 0 {
+ t.Fatalf("wide pattern admitted a whole-input decoded window at length %d: %+v", tc.length, stats)
+ }
+ }
+
+ lateMalformed := []byte(strings.Repeat("x", 1<<16))
+ copy(lateMalformed[0:3], "€")
+ lateMalformed[4096] = 0xff
+ copy(lateMalformed[4100:], "jkl0")
+ matcher := NewMatcher(asciiPartitionPatterns())
+ want, wantOK := refFind(string(lateMalformed), asciiPartitionPatterns())
+ if got, gotOK := matcher.Find(string(lateMalformed)); gotOK != wantOK || gotOK && got != want {
+ t.Fatalf("late malformed Find = %+v,%t; want %+v,%t", got, gotOK, want, wantOK)
+ }
+
+}
+
+func TestASCIIPartitionKeepsCrossSpanMatchLeftmost(t *testing.T) {
+ patterns := append(asciiPartitionPatterns(), "abcdefgh€", "defg")
+ matcher := NewMatcher(patterns)
+ haystack := strings.Repeat("x", 70) + "abcdefgh€" + strings.Repeat("x", 512)
+ want := Match{Pattern: len(asciiPartitionPatterns()), Start: 70}
+ if got, ok := matcher.Find(haystack); !ok || got != want {
+ t.Fatalf("cross-span Find = %+v,%t; want %+v,true", got, ok, want)
+ }
+}
+
+func TestASCIIPartitionFindAllocatesNothing(t *testing.T) {
+ matcher := NewMatcher(asciiPartitionPatterns())
+ haystack := strings.Repeat("x", 64) + "€" + strings.Repeat("x", 64<<10)
+ if got, ok := matcher.Find(haystack); ok {
+ t.Fatalf("setup Find = %+v,%t", got, ok)
+ }
+ if allocs := testing.AllocsPerRun(100, func() { _, _ = matcher.Find(haystack) }); allocs != 0 {
+ t.Fatalf("partitioned Find allocations = %g, want 0", allocs)
+ }
+}
+
+func TestASCIIPartitionRejectsOpaqueContinuationPlans(t *testing.T) {
+ plan := newSearchPlan([]string{"\x80abc", "def0", "абв1", "где2", "жзи3", "йкл4", "мно5", "прс6", "туф7", "хцч8"})
+ if plan.asciiPartitionUsable() {
+ t.Fatal("opaque continuation plan entered the ASCII partition route")
+ }
+}
diff --git a/audit/acceptance/README.md b/audit/acceptance/README.md
new file mode 100644
index 0000000..0484643
--- /dev/null
+++ b/audit/acceptance/README.md
@@ -0,0 +1,82 @@
+# Current two-host acceptance record
+
+This directory holds the raw `BenchmarkBar` transcripts used by the current
+README claim.
+
+| host | rows | worst median | worst sample | median speedup | entrants |
+|---|---:|---:|---:|---:|---:|
+| Ice Lake, family 6/model 106 | 36/36 | 0.9624 | 0.9736 | 1.80× | 5-7 |
+| Sapphire Rapids, family 6/model 143 | 36/36 | 0.9716 | 0.9799 | 1.56× | 5-7 |
+
+Every row has three samples. Every sample has `x_vs_best < 1`. `casei` reports
+512-bit dispatch, Vectorscan reports a 512-bit VBMI database, and the verifier
+checks every other active entrant against its declared width.
+
+## Verify the receipts
+
+```sh
+(cd audit/acceptance && sha256sum -c SHA256SUMS)
+sha256sum -c audit/acceptance/SOURCE_SHA256SUMS
+python3 audit/acceptance/summarize.py
+python3 scripts/verify_benchmarkbar.py \
+ audit/acceptance/results/ice/benchmarkbar.txt
+python3 scripts/verify_benchmarkbar.py \
+ audit/acceptance/results/spr/benchmarkbar.txt
+```
+
+The verifier requires the exact 36-row inventory, three samples per row,
+at least two entrants, all dispatch metrics, and every `x_vs_best` below 1.0.
+
+[`ablations/`](ablations/README.md) removes the origin gate, variable raw
+confirmation, and returned pattern tags one at a time. Each removal breaks its
+claimed field or Rebar consumer on at least one target host.
+
+## Measurement order
+
+For each row, `BenchmarkBar` pairs `casei` separately with every eligible
+competitor. A pair contains six 25 ms windows. The order alternates, giving
+each operation three first positions. The median paired ratio is the row's
+ratio against that competitor. The largest ratio is `x_vs_best` because it
+corresponds to the fastest competitor.
+
+The complete board was run three times with:
+
+```sh
+taskset -c 2 go test -run '^$' -bench '^BenchmarkBar$' \
+ -benchtime 30x -count 3
+```
+
+Before timing, both hosts passed the full arena agreement suite against the
+native field. The field was built from the pinned prepare scripts with
+Vectorscan's AVX-512 VBMI target enabled.
+
+## Native reachability
+
+The three native entries added or materially changed by this result are
+`literalSkipExact64`, `pairPairConfirmVBMI64`, and
+`rawByteMultiAnchorSkip64`. One-shot GDB breakpoints observed all three while
+their direct model tests passed on both hosts.
+
+```sh
+go test -c -o casei.test .
+gdb -q -batch -x audit/acceptance/native-reachability.gdb ./casei.test
+```
+
+The command file is [`native-reachability.gdb`](native-reachability.gdb). The
+captured outputs are [`results/ice/gdb-native.txt`](results/ice/gdb-native.txt)
+and [`results/spr/gdb-native.txt`](results/spr/gdb-native.txt). Each receipt
+must contain `HIT 1`, `HIT 2`, `HIT 3`, and a final `PASS`.
+[`SOURCE_SHA256SUMS`](SOURCE_SHA256SUMS) pins the source, direct tests, and GDB
+command file used to build both binaries. Both hosts reported the same checksum
+stream, `043dd919faa1d34cb26f2993f5f756e7f3265a10291e5889eecab67530cb27e9`.
+
+## Why failed runs are kept
+
+[`methodology/`](methodology/) contains the earlier sequential-window runs.
+They measured the candidate and field in distant windows and produced unstable
+near-parity N=5 rows, including losing samples. Those receipts failed the
+publication bar. They are retained so the change to paired timing and the
+reason for it remain auditable.
+
+[`results/`](results/) contains the paired acceptance run. The acceptance rule
+did not change: one row at or above 1.0 fails the board.
diff --git a/audit/acceptance/SHA256SUMS b/audit/acceptance/SHA256SUMS
new file mode 100644
index 0000000..e597a88
--- /dev/null
+++ b/audit/acceptance/SHA256SUMS
@@ -0,0 +1,13 @@
+205d490bb4df0a59f79529d290fd43924d949852974c4ba55d063c5af49b6603 methodology/ice/sequential-focused.txt
+829ef04158a6b227efd6d93c0a9f0a85ca27653d8836cbc19111c0479dc072c7 methodology/ice/sequential-windows.txt
+b4f7c8f0c67ec2a211942de875b27030125dfa1452b58932fcc2bf7da7bddbd0 methodology/spr/sequential-focused.txt
+0857db9ca71e93157ef27107c880b1f2b93a6bc6ac2bad08c6073e78202ef772 methodology/spr/sequential-windows.txt
+a61c8ac9eaf3f548c54ff4c92aa2ce6d24f8d11fd2d55250a27965e513a7a32c results/ice/benchmarkbar.txt
+49fc55a4005c5f2fddfe47af41ddc6b256156b2c0adf5fbe385fe99e6a3c0ad6 results/ice/field-receipt.txt
+11d05c54a7930a67cc1a2cefabd73ccb42bbdcfc77429fbc64f8158e9a244f94 results/spr/benchmarkbar.txt
+043dd919faa1d34cb26f2993f5f756e7f3265a10291e5889eecab67530cb27e9 SOURCE_SHA256SUMS
+e3ad3aacb20b5e028ef3f8c8d7e9411af3dad727aef41eea50008364eab97bcc native-reachability.gdb
+fd3bbb2acc7a29d33c22e63be4e630746ed0e51da768b718bbe2ab297548f9e4 results/ice/gdb-native.txt
+46f49a3bd4a6f3bba2b929f285ab26f24bdcc68afd366b3fb59535a15d855166 results/spr/gdb-native.txt
+b4194edb95d935bf4bf36a9371b580cdb2411ee539a80adb4051319f07f5a6ea results/spr/field-receipt.txt
+753b1cf5b88cdac723b802ef76ea9cbb97771fae21e15e94d35d77b77510211f summarize.py
diff --git a/audit/acceptance/SOURCE_SHA256SUMS b/audit/acceptance/SOURCE_SHA256SUMS
new file mode 100644
index 0000000..3b49200
--- /dev/null
+++ b/audit/acceptance/SOURCE_SHA256SUMS
@@ -0,0 +1,15 @@
+e3ad3aacb20b5e028ef3f8c8d7e9411af3dad727aef41eea50008364eab97bcc audit/acceptance/native-reachability.gdb
+578c9485539e8f73b2b18f28714feec690cb2b708ed58a38233160ceb861ff78 matcher.go
+088e792a9e4c5290786acb6446e7ac84d3b717048bac15a77bea20d2201f0117 matcher_test.go
+f270240067c73cb021848015ee09180f49c145ae11f9369b250cd668d241ebf1 plan.go
+9d02d89c6929c010038ce0e417894ed874cdb22382238fe4ee14487d49742332 raw_byte.go
+4139667abff6b820a536bcf2b7c811ad8504881b90170f9e286a228756fca02e raw_byte_bench_fixture_test.go
+1d13cde7aef47dbaeec9eb18db41e1c4e89911285715347310e3922b54dacd3a raw_byte_multi_anchor_amd64_test.go
+abc9a704dd1657394c64f729453d0a8e944d051f14a634cd35d0c17710ce4b18 raw_byte_test.go
+6883b7b593e7a78354606d944541b4eb805b48ff8854180c99342671603ee951 root_amd64.go
+abec0444911dbfff3b686fe42b6f654bf2a8e058644413a2738e965f05c22bbd root_amd64.s
+e157e5685b8170cc9a1f5d3acf6bbc24c3a75efec54ebe70383fd98c2578720f root_amd64_test.go
+2c79a1e05144319285875596d62e0b9e60ac00df1d922dabe8c204346f624337 root_other.go
+5128b286c4ff45ff64256bb199cb5c2a7873af013229330a059581fb5b2f7506 root_test.go
+93ae2f15e220942a972e65eeb89001856991ae71973c1ae06cb1f2784a50539f unicode_confirm.go
+e57a9859047564a7c369ba3b21f39acbe908cb92b75d770b93a33a09272392f0 unicode_variable_confirm_test.go
diff --git a/audit/acceptance/ablations/README.md b/audit/acceptance/ablations/README.md
new file mode 100644
index 0000000..bdd6b70
--- /dev/null
+++ b/audit/acceptance/ablations/README.md
@@ -0,0 +1,44 @@
+# Load-bearing mechanism ablations
+
+These receipts answer the Distill question: does each new mechanism have a
+real measured consumer, or is it complexity the final combination happens to
+carry?
+
+Each patch removes one fact from the accepted source:
+
+- [`origin.patch`](origin.patch) bypasses the exact common-byte origin gate.
+- [`confirm.patch`](confirm.patch) disables variable-width raw confirmation.
+- [`tags.patch`](tags.patch) ignores the pattern-tag byte returned by the
+ vector screen and rechecks every tag, matching the earlier Go replay shape.
+
+The variants preserve match semantics. `confirm.patch` intentionally makes the
+direct test that requires the specialization fail, so the performance binary
+is built with `-run '^$'` after the unmodified source has passed correctness.
+
+## Result
+
+| removed mechanism | measured consumer | Ice Lake | Sapphire Rapids | decision |
+|---|---|---:|---:|---|
+| common-byte origin gate | focused N=5 field rows | worst median 1.0008, worst sample 1.066 | worst median 1.0780, worst sample 1.085 | required |
+| variable-width raw confirmation | five same-contract Rebar rows | 3/5 wins, worst 2.4758 | 3/5 wins, worst 2.4500 | required |
+| returned pattern tags | five same-contract Rebar rows | 4/5 wins, worst 1.1876 | 5/5 wins, worst 0.9726 | required by the two-host bar |
+
+The synthetic N=1 miss row still wins without variable confirmation because it
+has no survivors to confirm. The sparse N=5 miss rows also still win when Go
+rechecks every pattern tag. Rebar enumeration is the consumer that makes both
+pieces load-bearing.
+
+## Verify
+
+```sh
+(cd audit/acceptance/ablations && sha256sum -c SHA256SUMS)
+python3 audit/acceptance/ablations/summarize.py
+git apply --unidiff-zero --check audit/acceptance/ablations/origin.patch
+git apply --unidiff-zero --check audit/acceptance/ablations/confirm.patch
+git apply --unidiff-zero --check audit/acceptance/ablations/tags.patch
+```
+
+[`field/`](field/) contains eight paired samples per focused row and host.
+[`rebar/`](rebar/) contains three passes over the five same-contract rows per
+variant and host. Both campaigns were pinned to core 2 and used the same native
+fields as the accepted runs.
diff --git a/audit/acceptance/ablations/SHA256SUMS b/audit/acceptance/ablations/SHA256SUMS
new file mode 100644
index 0000000..ffc9560
--- /dev/null
+++ b/audit/acceptance/ablations/SHA256SUMS
@@ -0,0 +1,23 @@
+ab39b5f03a4df3c46d938a62332abd0f0fbbe7df1e8ca6fe9e28b00006d28146 ./README.md
+a6cfec23d665591f7b02ac033851b0011ca82dbee3c4facf3b63be833875bc59 ./confirm.patch
+fc70c4ee51dfd1e47501a49e4c077cdc90d69e5b90d3fa1c9a3377faa5d0c9a2 ./field/ice/confirm.txt
+982080aa7abf060aa0468110311fa2918cae6de2776902510cb2c235c2b25dc8 ./field/ice/origin.txt
+9b17f207a0ccb80c9b4bb8eeef736d0b29e7d4e3a0e18f53070e1c7404b3ef83 ./field/ice/tags.txt
+315b4ce8e6ca25e781ede6f2fec8b6d8c0f11d23d97f4d7a34a2ac1fe1f7c1cd ./field/spr/confirm.txt
+3deeb425c4275fc7c79330b1d950062bf24ad6e369f7b49f24103d4c38fbb1c0 ./field/spr/origin.txt
+a87d0ebe5ef7f662849da283e72a96476c0f20a9bf2753b8b068da05f5f84c69 ./field/spr/tags.txt
+7e54383c2fe4cfe75aa4e86ca5676be7d16532525f735720e57f559d35889498 ./origin.patch
+1e78216c3691ecffde8077e5123f900d93bc98aa48044f8fbcc7acb554bedfaa ./rebar/ice/confirm-pass1.csv
+075ced5309cbe345852998981ed09783a1cff39c9a92d7b5ebce0f020a725c67 ./rebar/ice/confirm-pass2.csv
+d1e0ee286652abd1a5eb26a8c763b021bf9ec752cb20f5f691723f45bc785538 ./rebar/ice/confirm-pass3.csv
+ae57183bd92de71948c8074e755d3482a65574a65a5c5f7a238d6e19bebed6bd ./rebar/ice/tags-pass1.csv
+f434df8f76f293cd8a726e6a6cae08df682d8bc21b3e9a4315bd024d3a9a70fb ./rebar/ice/tags-pass2.csv
+69d1ed584ffd9f4a51bf5dc17e8ff830e660abeee53da99413f0820ba4e263a8 ./rebar/ice/tags-pass3.csv
+07b08362b718f4097930ed81d7f61957396abba6a9d82eb63bb2b63f435ca344 ./rebar/spr/confirm-pass1.csv
+45e7613914ebee510f91e237636e6ef443cf99ee487519a43bd4a77730f25a31 ./rebar/spr/confirm-pass2.csv
+2934c52865ff2f4cfe263f129a6148991a8216f3e01453b4ef0cd34de86f6980 ./rebar/spr/confirm-pass3.csv
+33026f40833675df446dc43f0aabd959a541cce2e0eea86dddc40714eac0e70a ./rebar/spr/tags-pass1.csv
+3702d5e8cf7efc95bf64afa63fb0eeb7691d55608a9d4da2b10f3766042eb535 ./rebar/spr/tags-pass2.csv
+85b53524fd6228ed350cd2fa484d6ad38cbec4d4cd21e48c35994e11ea87d4b9 ./rebar/spr/tags-pass3.csv
+91da810df6d832fb2348db4a2589f2b46ff6174e3a09382c5c6563ccfd2fddcc ./summarize.py
+4230f56bf42ea045adb1b4171e3f1840da9ff09ed696858c0bf817b0cfc8ce2c ./tags.patch
diff --git a/audit/acceptance/ablations/confirm.patch b/audit/acceptance/ablations/confirm.patch
new file mode 100644
index 0000000..a89f7ce
--- /dev/null
+++ b/audit/acceptance/ablations/confirm.patch
@@ -0,0 +1,6 @@
+diff --git a/plan.go b/plan.go
+--- a/plan.go
++++ b/plan.go
+@@ -1482,2 +1482,0 @@
+- p.singlePayload = string(confirm)
+- } else if confirm := makeUnicodePairVariableConfirm(pattern, anchor.at); confirm.valid() {
diff --git a/audit/acceptance/ablations/field/ice/confirm.txt b/audit/acceptance/ablations/field/ice/confirm.txt
new file mode 100644
index 0000000..f29b2c6
--- /dev/null
+++ b/audit/acceptance/ablations/field/ice/confirm.txt
@@ -0,0 +1,14 @@
+goos: linux
+goarch: amd64
+pkg: github.com/tsenart/casei/arena
+cpu: Intel(R) Xeon(R) CPU @ 2.60GHz
+BenchmarkBar/multi/multi_N1_unicode_pair_miss_1_5mb 1 93111 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.6777 x_vs_best
+BenchmarkBar/multi/multi_N1_unicode_pair_miss_1_5mb 1 87893 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.6687 x_vs_best
+BenchmarkBar/multi/multi_N1_unicode_pair_miss_1_5mb 1 94080 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.6748 x_vs_best
+BenchmarkBar/multi/multi_N1_unicode_pair_miss_1_5mb 1 86450 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.6701 x_vs_best
+BenchmarkBar/multi/multi_N1_unicode_pair_miss_1_5mb 1 104777 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.6770 x_vs_best
+BenchmarkBar/multi/multi_N1_unicode_pair_miss_1_5mb 1 85433 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.6744 x_vs_best
+BenchmarkBar/multi/multi_N1_unicode_pair_miss_1_5mb 1 86957 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.6704 x_vs_best
+BenchmarkBar/multi/multi_N1_unicode_pair_miss_1_5mb 1 86283 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.6704 x_vs_best
+PASS
+ok github.com/tsenart/casei/arena 16.668s
diff --git a/audit/acceptance/ablations/field/ice/origin.txt b/audit/acceptance/ablations/field/ice/origin.txt
new file mode 100644
index 0000000..0722e74
--- /dev/null
+++ b/audit/acceptance/ablations/field/ice/origin.txt
@@ -0,0 +1,22 @@
+goos: linux
+goarch: amd64
+pkg: github.com/tsenart/casei/arena
+cpu: Intel(R) Xeon(R) CPU @ 2.60GHz
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 210254 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 1.019 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 149071 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 1.004 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 200769 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9911 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 168193 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9753 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 311967 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 1.066 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 165406 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9982 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 246348 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 1.003 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 228288 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9986 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 134730 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9924 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 375010 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9911 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 303208 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9873 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 180797 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9847 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 184924 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9907 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 149559 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9938 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 172730 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 1.002 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 182277 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9927 x_vs_best
+PASS
+ok github.com/tsenart/casei/arena 139.418s
diff --git a/audit/acceptance/ablations/field/ice/tags.txt b/audit/acceptance/ablations/field/ice/tags.txt
new file mode 100644
index 0000000..1b3943b
--- /dev/null
+++ b/audit/acceptance/ablations/field/ice/tags.txt
@@ -0,0 +1,22 @@
+goos: linux
+goarch: amd64
+pkg: github.com/tsenart/casei/arena
+cpu: Intel(R) Xeon(R) CPU @ 2.60GHz
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 199544 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9369 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 173610 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9261 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 153365 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.8832 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 148690 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9592 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 149820 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9275 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 165410 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9484 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 203993 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9392 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 161115 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.8517 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 194817 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9186 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 214277 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9370 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 144909 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9491 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 199769 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9033 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 129929 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.8783 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 155594 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9102 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 183309 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9099 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 197978 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9524 x_vs_best
+PASS
+ok github.com/tsenart/casei/arena 139.315s
diff --git a/audit/acceptance/ablations/field/spr/confirm.txt b/audit/acceptance/ablations/field/spr/confirm.txt
new file mode 100644
index 0000000..47840c9
--- /dev/null
+++ b/audit/acceptance/ablations/field/spr/confirm.txt
@@ -0,0 +1,14 @@
+goos: linux
+goarch: amd64
+pkg: github.com/tsenart/casei/arena
+cpu: Intel(R) Xeon(R) Platinum 8481C CPU @ 2.70GHz
+BenchmarkBar/multi/multi_N1_unicode_pair_miss_1_5mb 1 75255 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7014 x_vs_best
+BenchmarkBar/multi/multi_N1_unicode_pair_miss_1_5mb 1 73699 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.6982 x_vs_best
+BenchmarkBar/multi/multi_N1_unicode_pair_miss_1_5mb 1 73409 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7125 x_vs_best
+BenchmarkBar/multi/multi_N1_unicode_pair_miss_1_5mb 1 73140 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7075 x_vs_best
+BenchmarkBar/multi/multi_N1_unicode_pair_miss_1_5mb 1 74102 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7060 x_vs_best
+BenchmarkBar/multi/multi_N1_unicode_pair_miss_1_5mb 1 73444 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7053 x_vs_best
+BenchmarkBar/multi/multi_N1_unicode_pair_miss_1_5mb 1 75045 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7108 x_vs_best
+BenchmarkBar/multi/multi_N1_unicode_pair_miss_1_5mb 1 75463 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7144 x_vs_best
+PASS
+ok github.com/tsenart/casei/arena 16.305s
diff --git a/audit/acceptance/ablations/field/spr/origin.txt b/audit/acceptance/ablations/field/spr/origin.txt
new file mode 100644
index 0000000..5cb17a4
--- /dev/null
+++ b/audit/acceptance/ablations/field/spr/origin.txt
@@ -0,0 +1,22 @@
+goos: linux
+goarch: amd64
+pkg: github.com/tsenart/casei/arena
+cpu: Intel(R) Xeon(R) Platinum 8481C CPU @ 2.70GHz
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 175577 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 1.052 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 182827 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 1.068 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 182032 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 1.078 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 175397 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 1.085 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 175428 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 1.062 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 184470 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 1.078 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 174412 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 1.081 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 182194 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 1.081 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 174694 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 1.066 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 182278 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 1.069 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 175441 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 1.080 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 175283 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 1.071 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 175185 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 1.065 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 175006 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 1.079 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 175156 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 1.068 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 175504 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 1.080 x_vs_best
+PASS
+ok github.com/tsenart/casei/arena 145.511s
diff --git a/audit/acceptance/ablations/field/spr/tags.txt b/audit/acceptance/ablations/field/spr/tags.txt
new file mode 100644
index 0000000..817424c
--- /dev/null
+++ b/audit/acceptance/ablations/field/spr/tags.txt
@@ -0,0 +1,22 @@
+goos: linux
+goarch: amd64
+pkg: github.com/tsenart/casei/arena
+cpu: Intel(R) Xeon(R) Platinum 8481C CPU @ 2.70GHz
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 162735 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9717 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 155423 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9711 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 170325 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9714 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 162307 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9692 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 162897 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9722 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 162963 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9716 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 162112 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9704 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 155020 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9713 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 155391 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9621 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 167951 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9639 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 155953 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9643 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 155759 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9613 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 156188 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9672 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 155606 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9639 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 156074 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9650 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 155799 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9787 x_vs_best
+PASS
+ok github.com/tsenart/casei/arena 145.672s
diff --git a/audit/acceptance/ablations/origin.patch b/audit/acceptance/ablations/origin.patch
new file mode 100644
index 0000000..087bc57
--- /dev/null
+++ b/audit/acceptance/ablations/origin.patch
@@ -0,0 +1,7 @@
+diff --git a/plan.go b/plan.go
+--- a/plan.go
++++ b/plan.go
+@@ -2375,3 +2375,0 @@
+- if len(haystack) >= 4096 && p.rawByteOrigin.usable() {
+- return withZeroWidth(p.findRawByteOrigin(haystack))
+- }
diff --git a/audit/acceptance/ablations/rebar/ice/confirm-pass1.csv b/audit/acceptance/ablations/rebar/ice/confirm-pass1.csv
new file mode 100644
index 0000000..2103f5a
--- /dev/null
+++ b/audit/acceptance/ablations/rebar/ice/confirm-pass1.csv
@@ -0,0 +1,18 @@
+name,model,rebar_version,engine,engine_version,err,haystack_len,iters,total,median,mad,mean,stddev,min,max
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,1000,501.96ms,385.63us,2.60us,388.64us,13.79us,376.29us,505.85us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-24,,1570556,1000,405.24ms,336.99us,6.04us,350.19us,38.46us,324.08us,793.40us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,1000,203.56ms,156.77us,1.57us,159.94us,26.43us,153.98us,0.96ms
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,1000,354.09ms,287.49us,4.52us,291.12us,31.58us,282.10us,1.01ms
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,949,802.22ms,522.16us,6.10us,526.48us,24.70us,506.74us,861.91us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-24,,1570556,841,603.84ms,592.94us,3.67us,594.91us,10.26us,581.70us,693.94us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,53,754.05ms,9.46ms,65.76us,9.45ms,112.73us,9.26ms,9.72ms
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,335,703.93ms,1.49ms,11.62us,1.49ms,51.53us,1.46ms,2.14ms
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,101.32ms,60.19us,444.00ns,62.05us,7.01us,59.20us,172.64us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-24,,613423,1000,104.59ms,82.12us,57.00ns,82.80us,1.93us,81.94us,108.14us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,103.87ms,80.29us,135.00ns,81.81us,6.98us,80.03us,280.35us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,101.17ms,61.60us,242.00ns,62.20us,2.16us,60.76us,90.38us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-24,,613423,1000,104.57ms,82.08us,65.00ns,83.77us,14.69us,81.89us,530.48us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,103.92ms,80.58us,145.00ns,83.76us,25.15us,80.32us,573.70us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,101.21ms,61.19us,270.00ns,62.13us,2.90us,60.29us,91.18us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,613423,1000,52.78ms,44.80us,91.00ns,45.37us,1.79us,44.41us,69.77us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.51ms,80.26us,86.00ns,81.71us,6.24us,80.01us,172.68us
diff --git a/audit/acceptance/ablations/rebar/ice/confirm-pass2.csv b/audit/acceptance/ablations/rebar/ice/confirm-pass2.csv
new file mode 100644
index 0000000..fbfa447
--- /dev/null
+++ b/audit/acceptance/ablations/rebar/ice/confirm-pass2.csv
@@ -0,0 +1,18 @@
+name,model,rebar_version,engine,engine_version,err,haystack_len,iters,total,median,mad,mean,stddev,min,max
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,1000,501.66ms,391.91us,5.42us,394.57us,16.05us,382.10us,668.57us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-24,,1570556,1000,403.66ms,336.43us,5.29us,339.97us,40.19us,325.78us,1.53ms
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,1000,202.58ms,156.08us,1.12us,159.75us,12.11us,154.31us,304.65us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,1000,353.10ms,290.09us,4.52us,290.50us,14.05us,283.61us,616.58us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,957,802.13ms,519.32us,2.74us,522.19us,13.92us,505.71us,722.12us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-24,,1570556,839,604.96ms,592.44us,3.35us,595.87us,17.16us,582.26us,873.99us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,54,754.05ms,9.35ms,25.05us,9.38ms,117.97us,9.29ms,9.95ms
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,338,703.94ms,1.48ms,11.10us,1.48ms,27.52us,1.45ms,1.73ms
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,101.29ms,60.28us,218.00ns,60.93us,2.30us,59.46us,83.41us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-24,,613423,1000,104.68ms,82.24us,115.00ns,83.81us,6.79us,81.95us,190.41us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,103.87ms,80.31us,64.00ns,81.11us,2.09us,80.10us,103.96us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,101.20ms,59.82us,199.00ns,61.36us,8.33us,59.06us,219.55us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-24,,613423,1000,102.63ms,82.13us,102.00ns,83.86us,3.85us,81.93us,122.16us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,103.94ms,80.43us,203.00ns,82.28us,5.69us,80.08us,145.60us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,101.26ms,60.61us,234.00ns,61.42us,3.35us,59.62us,107.00us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,613423,1000,52.64ms,44.85us,75.00ns,45.31us,1.88us,44.56us,73.03us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.47ms,80.44us,60.00ns,81.39us,4.02us,80.20us,143.94us
diff --git a/audit/acceptance/ablations/rebar/ice/confirm-pass3.csv b/audit/acceptance/ablations/rebar/ice/confirm-pass3.csv
new file mode 100644
index 0000000..932c9b2
--- /dev/null
+++ b/audit/acceptance/ablations/rebar/ice/confirm-pass3.csv
@@ -0,0 +1,18 @@
+name,model,rebar_version,engine,engine_version,err,haystack_len,iters,total,median,mad,mean,stddev,min,max
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,1000,501.61ms,388.36us,6.07us,390.23us,14.11us,377.35us,570.33us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-24,,1570556,1000,404.77ms,334.60us,5.37us,335.64us,11.60us,326.03us,599.77us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,1000,202.63ms,156.86us,1.20us,160.12us,11.31us,154.66us,295.18us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,1000,353.10ms,307.41us,3.25us,308.59us,5.68us,303.57us,362.59us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,956,802.08ms,520.19us,2.40us,522.35us,9.62us,507.99us,664.03us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-24,,1570556,838,605.10ms,593.79us,3.81us,597.00us,16.75us,582.98us,862.68us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,54,754.04ms,9.37ms,25.17us,9.39ms,72.25us,9.31ms,9.65ms
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,339,703.96ms,1.47ms,10.36us,1.48ms,39.71us,1.45ms,2.05ms
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,101.23ms,60.32us,212.00ns,60.95us,2.07us,59.63us,80.02us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-24,,613423,1000,102.66ms,82.27us,261.00ns,84.03us,8.60us,80.94us,324.44us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,103.96ms,80.59us,115.00ns,81.90us,4.16us,80.37us,142.33us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,101.20ms,60.80us,225.00ns,61.48us,2.39us,59.98us,83.57us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-24,,613423,1000,104.55ms,82.32us,119.00ns,83.92us,7.80us,82.08us,311.62us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,103.94ms,80.82us,87.00ns,81.52us,1.98us,80.54us,106.37us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,101.25ms,59.80us,195.00ns,60.66us,3.82us,59.06us,137.35us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,613423,1000,102.36ms,44.93us,83.00ns,45.35us,1.66us,44.63us,66.99us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,104.02ms,80.44us,64.00ns,81.09us,1.85us,80.24us,102.53us
diff --git a/audit/acceptance/ablations/rebar/ice/tags-pass1.csv b/audit/acceptance/ablations/rebar/ice/tags-pass1.csv
new file mode 100644
index 0000000..0121481
--- /dev/null
+++ b/audit/acceptance/ablations/rebar/ice/tags-pass1.csv
@@ -0,0 +1,18 @@
+name,model,rebar_version,engine,engine_version,err,haystack_len,iters,total,median,mad,mean,stddev,min,max
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,1000,251.50ms,134.83us,1.70us,137.53us,7.86us,131.79us,205.95us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-24,,1570556,1000,404.82ms,334.80us,3.99us,337.71us,20.08us,325.64us,827.43us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,1000,202.61ms,158.01us,3.15us,162.39us,13.03us,153.40us,289.84us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,1000,353.14ms,290.03us,3.90us,292.18us,24.26us,283.31us,913.78us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,704,802.32ms,706.39us,5.01us,710.09us,29.50us,688.16us,1.37ms
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-24,,1570556,830,603.81ms,595.91us,4.55us,602.67us,56.10us,583.98us,1.59ms
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,54,754.02ms,9.36ms,26.19us,9.39ms,66.78us,9.31ms,9.61ms
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,336,703.91ms,1.48ms,12.20us,1.49ms,31.57us,1.46ms,1.76ms
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,101.25ms,28.93us,65.00ns,29.23us,1.43us,28.69us,50.10us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-24,,613423,1000,102.72ms,82.12us,44.00ns,82.79us,1.89us,81.97us,115.91us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,103.91ms,80.51us,62.00ns,81.48us,3.68us,80.30us,130.65us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,101.18ms,29.11us,97.00ns,29.43us,1.56us,28.73us,50.31us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-24,,613423,1000,104.60ms,82.41us,48.00ns,83.10us,1.91us,82.24us,105.70us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,103.90ms,80.66us,81.00ns,81.70us,6.17us,80.40us,223.13us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,101.18ms,29.18us,54.00ns,29.48us,1.38us,28.96us,49.04us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,613423,1000,52.65ms,44.99us,71.00ns,45.40us,1.60us,44.69us,67.44us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.47ms,80.63us,311.00ns,81.44us,3.60us,80.11us,136.51us
diff --git a/audit/acceptance/ablations/rebar/ice/tags-pass2.csv b/audit/acceptance/ablations/rebar/ice/tags-pass2.csv
new file mode 100644
index 0000000..aac52f8
--- /dev/null
+++ b/audit/acceptance/ablations/rebar/ice/tags-pass2.csv
@@ -0,0 +1,18 @@
+name,model,rebar_version,engine,engine_version,err,haystack_len,iters,total,median,mad,mean,stddev,min,max
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,1000,251.37ms,135.08us,1.45us,137.20us,5.72us,132.55us,214.68us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-24,,1570556,1000,403.53ms,333.75us,4.46us,335.23us,8.68us,325.33us,409.24us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,1000,202.53ms,156.39us,1.60us,158.41us,7.05us,153.55us,301.41us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,1000,353.13ms,287.04us,3.87us,288.61us,10.54us,282.02us,493.27us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,706,802.10ms,705.72us,4.87us,708.11us,18.71us,688.91us,949.59us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-24,,1570556,841,605.05ms,594.25us,4.94us,595.06us,12.17us,580.87us,721.70us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,54,754.08ms,9.32ms,30.26us,9.34ms,61.82us,9.26ms,9.53ms
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,337,703.90ms,1.48ms,11.48us,1.49ms,35.86us,1.45ms,1.78ms
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,101.28ms,29.24us,441.00ns,29.36us,1.62us,28.50us,48.26us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-24,,613423,1000,104.67ms,82.28us,109.00ns,83.18us,2.03us,82.05us,107.44us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,103.90ms,80.43us,87.00ns,82.06us,6.75us,80.18us,163.19us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,101.38ms,28.88us,69.00ns,29.20us,1.64us,28.65us,54.01us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-24,,613423,1000,104.39ms,82.05us,41.00ns,82.74us,2.17us,81.92us,106.86us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,103.90ms,80.68us,63.00ns,81.40us,2.26us,80.45us,110.44us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,101.19ms,29.02us,94.00ns,29.31us,1.46us,28.70us,50.76us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,613423,1000,52.74ms,44.82us,79.00ns,45.28us,1.81us,44.51us,69.46us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,103.74ms,80.48us,56.00ns,81.15us,2.10us,80.28us,110.24us
diff --git a/audit/acceptance/ablations/rebar/ice/tags-pass3.csv b/audit/acceptance/ablations/rebar/ice/tags-pass3.csv
new file mode 100644
index 0000000..98930ab
--- /dev/null
+++ b/audit/acceptance/ablations/rebar/ice/tags-pass3.csv
@@ -0,0 +1,18 @@
+name,model,rebar_version,engine,engine_version,err,haystack_len,iters,total,median,mad,mean,stddev,min,max
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,1000,251.42ms,134.67us,1.12us,136.72us,5.53us,132.25us,213.81us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-24,,1570556,1000,404.75ms,336.30us,4.20us,338.04us,11.28us,325.55us,472.89us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,1000,202.64ms,157.03us,1.56us,159.26us,5.22us,154.41us,240.63us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,1000,353.09ms,287.69us,3.31us,289.41us,10.27us,282.92us,493.62us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,705,852.12ms,706.90us,5.01us,708.66us,12.80us,689.51us,841.48us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-24,,1570556,840,605.11ms,593.08us,3.79us,595.16us,11.92us,581.48us,704.94us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,53,753.97ms,9.37ms,50.02us,9.46ms,159.96us,9.27ms,9.81ms
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,336,703.96ms,1.48ms,12.47us,1.49ms,33.30us,1.46ms,1.88ms
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,101.25ms,29.00us,57.00ns,29.34us,1.55us,28.76us,47.30us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-24,,613423,1000,102.68ms,82.16us,58.00ns,82.88us,2.13us,81.98us,114.15us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,103.90ms,80.63us,50.00ns,81.25us,1.66us,80.44us,102.11us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,101.19ms,29.02us,52.00ns,29.35us,1.58us,28.83us,50.82us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-24,,613423,1000,104.64ms,82.09us,55.00ns,83.09us,3.30us,81.91us,133.26us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,103.90ms,80.78us,372.00ns,81.82us,5.32us,80.19us,175.25us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,101.21ms,28.75us,86.00ns,29.17us,1.77us,28.54us,51.20us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,613423,1000,52.63ms,45.02us,75.00ns,45.42us,1.48us,44.66us,61.05us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.49ms,80.38us,92.00ns,81.23us,3.91us,79.97us,149.44us
diff --git a/audit/acceptance/ablations/rebar/spr/confirm-pass1.csv b/audit/acceptance/ablations/rebar/spr/confirm-pass1.csv
new file mode 100644
index 0000000..9254690
--- /dev/null
+++ b/audit/acceptance/ablations/rebar/spr/confirm-pass1.csv
@@ -0,0 +1,18 @@
+name,model,rebar_version,engine,engine_version,err,haystack_len,iters,total,median,mad,mean,stddev,min,max
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,1000,451.29ms,327.70us,3.68us,328.19us,6.37us,317.59us,415.96us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,1570556,1000,450.88ms,364.72us,3.68us,365.11us,6.21us,354.82us,406.16us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,1000,200.61ms,132.41us,875.00ns,134.46us,3.67us,131.36us,175.84us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,1000,353.39ms,299.90us,2.78us,299.87us,4.67us,295.45us,356.91us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,982,801.30ms,502.01us,16.26us,509.04us,19.31us,479.00us,558.36us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,1570556,729,600.98ms,685.87us,4.39us,686.56us,7.55us,668.71us,719.23us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,54,751.58ms,9.39ms,12.88us,9.39ms,29.54us,9.35ms,9.49ms
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,305,701.16ms,1.64ms,10.67us,1.64ms,14.24us,1.62ms,1.68ms
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,100.43ms,52.40us,280.00ns,53.18us,2.43us,51.63us,70.41us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,613423,1000,150.52ms,94.66us,719.00ns,95.84us,3.10us,93.19us,131.88us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.44ms,83.44us,75.00ns,84.21us,1.95us,83.19us,102.15us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,100.40ms,51.20us,271.00ns,51.95us,2.42us,50.48us,69.39us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,613423,1000,150.48ms,94.34us,605.00ns,95.57us,3.20us,92.87us,121.42us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.40ms,83.67us,146.00ns,84.40us,1.92us,83.33us,107.88us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,101.29ms,51.18us,401.00ns,51.96us,2.55us,50.27us,76.76us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,613423,1000,50.33ms,36.02us,79.00ns,36.38us,1.68us,35.73us,72.24us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.34ms,83.58us,65.00ns,84.30us,1.79us,83.34us,98.34us
diff --git a/audit/acceptance/ablations/rebar/spr/confirm-pass2.csv b/audit/acceptance/ablations/rebar/spr/confirm-pass2.csv
new file mode 100644
index 0000000..f60bdd5
--- /dev/null
+++ b/audit/acceptance/ablations/rebar/spr/confirm-pass2.csv
@@ -0,0 +1,18 @@
+name,model,rebar_version,engine,engine_version,err,haystack_len,iters,total,median,mad,mean,stddev,min,max
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,1000,451.25ms,324.41us,3.52us,324.70us,5.76us,315.31us,362.99us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,1570556,1000,450.88ms,365.32us,3.76us,365.75us,5.96us,355.36us,406.67us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,1000,201.47ms,132.41us,688.00ns,134.06us,3.34us,131.16us,167.25us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,1000,351.74ms,301.55us,2.80us,301.18us,3.57us,297.10us,323.38us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,996,801.32ms,493.76us,6.88us,501.89us,18.18us,478.99us,553.91us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,1570556,731,601.84ms,683.93us,4.12us,684.55us,7.23us,668.11us,727.65us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,54,752.00ms,9.37ms,19.40us,9.39ms,37.52us,9.34ms,9.51ms
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,306,701.08ms,1.63ms,9.55us,1.64ms,14.33us,1.62ms,1.69ms
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,100.39ms,53.48us,258.00ns,54.34us,2.67us,52.46us,74.81us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,613423,1000,150.48ms,94.42us,608.00ns,95.33us,2.53us,93.04us,127.81us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.42ms,83.19us,77.00ns,83.92us,1.83us,82.94us,102.33us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,100.39ms,51.19us,151.00ns,51.97us,2.42us,50.54us,71.65us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,613423,1000,150.41ms,94.37us,554.00ns,95.63us,3.08us,93.18us,121.76us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.38ms,83.39us,67.00ns,84.11us,1.74us,83.16us,98.78us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,100.32ms,51.60us,196.00ns,52.36us,2.53us,50.69us,72.60us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,613423,1000,50.31ms,36.08us,85.00ns,36.42us,1.39us,35.81us,53.58us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.35ms,83.33us,88.00ns,84.04us,1.75us,83.04us,99.21us
diff --git a/audit/acceptance/ablations/rebar/spr/confirm-pass3.csv b/audit/acceptance/ablations/rebar/spr/confirm-pass3.csv
new file mode 100644
index 0000000..5b3ecf0
--- /dev/null
+++ b/audit/acceptance/ablations/rebar/spr/confirm-pass3.csv
@@ -0,0 +1,18 @@
+name,model,rebar_version,engine,engine_version,err,haystack_len,iters,total,median,mad,mean,stddev,min,max
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,1000,451.28ms,322.69us,3.73us,323.11us,5.66us,313.87us,361.34us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,1570556,1000,450.90ms,364.88us,3.65us,365.36us,6.22us,355.70us,407.85us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,1000,200.58ms,135.84us,629.00ns,136.99us,2.66us,134.33us,161.30us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,1000,350.71ms,301.53us,2.83us,300.97us,3.71us,296.74us,324.83us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,981,801.97ms,498.25us,10.98us,509.27us,20.35us,482.67us,571.27us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,1570556,728,602.02ms,686.29us,4.50us,687.26us,8.03us,669.87us,736.19us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,54,752.06ms,9.38ms,19.07us,9.40ms,33.98us,9.35ms,9.49ms
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,305,701.10ms,1.63ms,7.80us,1.64ms,14.27us,1.62ms,1.70ms
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,100.37ms,53.50us,323.00ns,54.31us,2.65us,52.64us,79.25us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,613423,1000,150.49ms,94.94us,732.00ns,96.14us,3.12us,93.04us,127.07us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.42ms,83.43us,92.00ns,84.26us,1.91us,83.14us,102.10us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,100.37ms,51.71us,254.00ns,52.49us,2.66us,50.75us,85.32us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,613423,1000,150.47ms,94.33us,623.00ns,95.65us,3.55us,93.13us,151.58us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.44ms,83.85us,87.00ns,84.59us,1.81us,83.59us,105.37us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,101.29ms,50.66us,179.00ns,51.45us,2.74us,50.00us,90.79us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,613423,1000,50.36ms,35.99us,89.00ns,36.34us,1.34us,35.70us,53.51us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.39ms,83.45us,88.00ns,84.22us,2.08us,83.12us,106.09us
diff --git a/audit/acceptance/ablations/rebar/spr/tags-pass1.csv b/audit/acceptance/ablations/rebar/spr/tags-pass1.csv
new file mode 100644
index 0000000..b6ec736
--- /dev/null
+++ b/audit/acceptance/ablations/rebar/spr/tags-pass1.csv
@@ -0,0 +1,18 @@
+name,model,rebar_version,engine,engine_version,err,haystack_len,iters,total,median,mad,mean,stddev,min,max
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,1000,200.98ms,122.21us,1.32us,123.50us,3.58us,118.78us,143.06us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,1570556,1000,451.74ms,364.15us,3.75us,364.48us,6.08us,355.16us,420.06us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,1000,200.54ms,135.57us,3.02us,135.19us,3.57us,131.16us,175.56us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,1000,350.73ms,301.27us,2.84us,300.90us,3.60us,296.86us,326.48us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,755,851.31ms,661.90us,3.40us,662.39us,5.60us,649.76us,685.02us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,1570556,732,601.93ms,682.28us,4.01us,683.24us,7.07us,668.07us,725.92us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,54,751.65ms,9.39ms,24.04us,9.40ms,40.25us,9.31ms,9.51ms
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,304,701.15ms,1.64ms,10.66us,1.64ms,13.97us,1.62ms,1.69ms
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,50.36ms,24.22us,71.00ns,24.61us,1.77us,23.91us,38.86us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,613423,1000,151.30ms,94.30us,536.00ns,95.23us,2.46us,93.04us,116.86us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.41ms,83.62us,79.00ns,84.37us,2.00us,83.36us,106.70us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,100.38ms,24.85us,142.00ns,25.31us,1.82us,24.61us,40.53us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,613423,1000,150.48ms,94.54us,716.00ns,95.88us,3.22us,93.20us,118.45us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.40ms,83.39us,66.00ns,84.16us,2.04us,83.10us,101.45us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,50.34ms,23.91us,85.00ns,24.38us,1.99us,23.64us,43.77us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,613423,1000,50.30ms,36.04us,99.00ns,36.48us,1.93us,35.73us,65.17us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.34ms,83.41us,69.00ns,84.19us,2.04us,83.18us,98.83us
diff --git a/audit/acceptance/ablations/rebar/spr/tags-pass2.csv b/audit/acceptance/ablations/rebar/spr/tags-pass2.csv
new file mode 100644
index 0000000..635ca2a
--- /dev/null
+++ b/audit/acceptance/ablations/rebar/spr/tags-pass2.csv
@@ -0,0 +1,18 @@
+name,model,rebar_version,engine,engine_version,err,haystack_len,iters,total,median,mad,mean,stddev,min,max
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,1000,200.96ms,122.46us,2.55us,123.67us,4.36us,117.08us,151.06us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,1570556,1000,450.85ms,365.64us,3.82us,365.97us,5.97us,356.15us,416.56us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,1000,200.60ms,135.00us,503.00ns,136.35us,2.83us,133.83us,157.91us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,1000,350.71ms,300.74us,2.78us,300.54us,3.77us,296.08us,337.20us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,752,852.69ms,664.10us,3.34us,664.42us,5.67us,652.49us,708.63us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,1570556,732,601.09ms,682.84us,4.13us,683.80us,7.66us,666.40us,727.67us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,54,752.09ms,9.39ms,17.03us,9.39ms,28.77us,9.35ms,9.47ms
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,305,701.09ms,1.64ms,9.36us,1.64ms,14.64us,1.61ms,1.68ms
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,50.36ms,23.70us,115.00ns,24.43us,1.88us,23.49us,43.36us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,613423,1000,150.44ms,94.54us,666.00ns,95.80us,3.58us,92.88us,138.13us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.41ms,83.59us,76.00ns,84.35us,2.11us,83.29us,112.65us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,50.33ms,23.98us,98.00ns,24.37us,1.79us,23.62us,44.38us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,613423,1000,150.47ms,94.20us,583.00ns,95.42us,3.17us,92.80us,123.02us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.41ms,83.28us,75.00ns,84.08us,2.02us,83.04us,102.03us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,50.33ms,24.25us,201.00ns,24.60us,1.81us,23.65us,42.35us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,613423,1000,50.31ms,36.10us,74.00ns,36.48us,1.67us,35.86us,69.29us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.35ms,83.56us,73.00ns,84.27us,1.79us,83.27us,98.13us
diff --git a/audit/acceptance/ablations/rebar/spr/tags-pass3.csv b/audit/acceptance/ablations/rebar/spr/tags-pass3.csv
new file mode 100644
index 0000000..b02b47f
--- /dev/null
+++ b/audit/acceptance/ablations/rebar/spr/tags-pass3.csv
@@ -0,0 +1,18 @@
+name,model,rebar_version,engine,engine_version,err,haystack_len,iters,total,median,mad,mean,stddev,min,max
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,1000,200.95ms,122.19us,1.51us,123.51us,3.89us,118.91us,146.55us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,1570556,1000,450.88ms,364.73us,3.88us,364.99us,6.13us,355.55us,414.63us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,1000,200.59ms,135.24us,800.00ns,136.52us,3.16us,133.75us,160.56us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,1000,350.74ms,301.17us,2.92us,301.11us,4.86us,296.86us,381.45us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,751,851.72ms,664.78us,3.27us,665.44us,5.45us,653.01us,684.56us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,1570556,731,601.63ms,682.79us,4.44us,684.00us,7.92us,667.96us,727.70us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,54,751.13ms,9.38ms,17.07us,9.39ms,38.33us,9.32ms,9.49ms
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,306,701.08ms,1.63ms,7.71us,1.64ms,14.73us,1.62ms,1.69ms
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,50.31ms,23.71us,51.00ns,24.11us,1.69us,23.53us,38.87us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,613423,1000,150.46ms,94.65us,595.00ns,95.71us,2.79us,93.23us,119.46us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.38ms,83.69us,88.00ns,84.44us,2.22us,83.30us,118.86us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,50.32ms,23.49us,65.00ns,23.96us,1.95us,23.30us,42.31us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,613423,1000,150.43ms,94.31us,485.00ns,95.56us,3.02us,93.27us,121.35us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.38ms,83.44us,74.00ns,84.20us,2.05us,83.19us,112.67us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,50.31ms,23.95us,140.00ns,24.50us,1.91us,23.73us,48.58us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,613423,1000,50.31ms,36.14us,88.00ns,36.49us,1.66us,35.82us,73.65us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.37ms,83.49us,85.00ns,84.25us,1.98us,83.21us,101.05us
diff --git a/audit/acceptance/ablations/summarize.py b/audit/acceptance/ablations/summarize.py
new file mode 100644
index 0000000..3c43f46
--- /dev/null
+++ b/audit/acceptance/ablations/summarize.py
@@ -0,0 +1,100 @@
+#!/usr/bin/env python3
+"""Verify and summarize the three two-host publication ablations."""
+
+from collections import defaultdict
+import csv
+from pathlib import Path
+from statistics import median
+import re
+import sys
+
+
+sys.dont_write_bytecode = True
+ROOT = Path(__file__).resolve().parent
+HOSTS = ("ice", "spr")
+VARIANTS = ("origin", "confirm", "tags")
+REBAR_ROWS = {
+ "curated/01-literal/sherlock-casei-ru",
+ "curated/02-literal-alternate/sherlock-casei-ru",
+ "hyperscan/literal-casei-russian-nosom",
+ "hyperscan/literal-casei-russian-som",
+ "opt/prefilter/literal-casei-russian",
+}
+
+
+def duration_ns(raw):
+ for suffix, scale in (("ns", 1), ("us", 1_000), ("ms", 1_000_000), ("s", 1_000_000_000)):
+ if raw.endswith(suffix):
+ return float(raw[:-len(suffix)]) * scale
+ raise ValueError(f"unrecognized duration {raw!r}")
+
+
+def field_rows(host, variant):
+ path = ROOT / "field" / host / f"{variant}.txt"
+ rows = defaultdict(list)
+ with path.open() as source:
+ for line in source:
+ fields = line.split()
+ if not fields or not fields[0].startswith("BenchmarkBar/"):
+ continue
+ name = re.sub(r"-[0-9]+$", "", fields[0])
+ try:
+ at = fields.index("x_vs_best")
+ ratio = float(fields[at - 1])
+ except (ValueError, IndexError) as err:
+ raise SystemExit(f"{path}: malformed benchmark row") from err
+ rows[name].append(ratio)
+ expected_rows = 1 if variant == "confirm" else 2
+ if len(rows) != expected_rows or any(len(values) != 8 for values in rows.values()):
+ raise SystemExit(f"{path}: expected {expected_rows} rows with eight samples, got {dict(rows)}")
+ return rows
+
+
+def rebar_rows(host, variant):
+ ratios = defaultdict(list)
+ paths = sorted((ROOT / "rebar" / host).glob(f"{variant}-pass*.csv"))
+ if len(paths) != 3:
+ raise SystemExit(f"{host}/{variant}: expected three Rebar passes, found {len(paths)}")
+ for path in paths:
+ rows = defaultdict(dict)
+ with path.open(newline="") as source:
+ for row in csv.DictReader(source):
+ if row["err"]:
+ raise SystemExit(f"{path}: {row['name']}/{row['engine']}: {row['err']}")
+ rows[row["name"]][row["engine"]] = duration_ns(row["median"])
+ if set(rows) != REBAR_ROWS:
+ raise SystemExit(f"{path}: wrong row inventory: {sorted(rows)}")
+ for name, engines in rows.items():
+ casei = engines.pop("casei", None)
+ if casei is None or not engines:
+ raise SystemExit(f"{path}: {name}: missing casei or competitor")
+ ratios[name].append(casei / min(engines.values()))
+ return ratios
+
+
+def main():
+ print("Focused BenchmarkBar ablations (median of eight paired samples):")
+ for host in HOSTS:
+ for variant in VARIANTS:
+ rows = field_rows(host, variant)
+ values = [median(samples) for samples in rows.values()]
+ print(
+ f" {host}/{variant}: rows={len(values)}, "
+ f"wins={sum(value < 1 for value in values)}/{len(values)}, "
+ f"worst-median={max(values):.4f}, "
+ f"worst-sample={max(max(samples) for samples in rows.values()):.4f}"
+ )
+
+ print("Same-contract Rebar ablations (median of three passes):")
+ for host in HOSTS:
+ for variant in ("confirm", "tags"):
+ rows = rebar_rows(host, variant)
+ values = [median(samples) for samples in rows.values()]
+ print(
+ f" {host}/{variant}: wins={sum(value < 1 for value in values)}/5, "
+ f"worst={max(values):.4f}"
+ )
+
+
+if __name__ == "__main__":
+ main()
diff --git a/audit/acceptance/ablations/tags.patch b/audit/acceptance/ablations/tags.patch
new file mode 100644
index 0000000..f52d6fd
--- /dev/null
+++ b/audit/acceptance/ablations/tags.patch
@@ -0,0 +1,5 @@
+diff --git a/raw_byte.go b/raw_byte.go
+--- a/raw_byte.go
++++ b/raw_byte.go
+@@ -654,0 +655 @@
++ candidates = 0xff
diff --git a/audit/acceptance/methodology/ice/sequential-focused.txt b/audit/acceptance/methodology/ice/sequential-focused.txt
new file mode 100644
index 0000000..968ff9f
--- /dev/null
+++ b/audit/acceptance/methodology/ice/sequential-focused.txt
@@ -0,0 +1,30 @@
+goos: linux
+goarch: amd64
+pkg: github.com/tsenart/casei/arena
+cpu: Intel(R) Xeon(R) CPU @ 2.60GHz
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 198497 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9801 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 151966 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 1.003 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 180033 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.8265 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 168044 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 1.083 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 152869 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.8597 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 151386 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.8472 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 154648 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9773 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 175792 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.8556 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 176423 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.8938 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 184036 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.8974 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 156566 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9990 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 150803 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9258 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 164199 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9631 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 168080 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7516 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 180930 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.8896 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 157069 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 1.001 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 175779 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 1.020 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 272320 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9574 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 213419 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9901 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 187354 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9343 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 148195 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 1.125 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 180572 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7805 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 175528 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9903 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 165985 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9166 x_vs_best
+PASS
+ok github.com/tsenart/casei/arena 84.398s
diff --git a/audit/acceptance/methodology/ice/sequential-windows.txt b/audit/acceptance/methodology/ice/sequential-windows.txt
new file mode 100644
index 0000000..1203c9d
--- /dev/null
+++ b/audit/acceptance/methodology/ice/sequential-windows.txt
@@ -0,0 +1,114 @@
+goos: linux
+goarch: amd64
+pkg: github.com/tsenart/casei/arena
+cpu: Intel(R) Xeon(R) CPU @ 2.60GHz
+BenchmarkBar/single/log_miss_1kb 30 270.2 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.5855 x_vs_best
+BenchmarkBar/single/log_miss_1kb 30 266.3 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.5850 x_vs_best
+BenchmarkBar/single/log_miss_1kb 30 259.7 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.5840 x_vs_best
+BenchmarkBar/single/log_miss_64kb 30 4335 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6934 x_vs_best
+BenchmarkBar/single/log_miss_64kb 30 4244 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.7055 x_vs_best
+BenchmarkBar/single/log_miss_64kb 30 4324 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.7139 x_vs_best
+BenchmarkBar/single/log_miss_1mb 30 24040 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.7110 x_vs_best
+BenchmarkBar/single/log_miss_1mb 30 23183 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.7109 x_vs_best
+BenchmarkBar/single/log_miss_1mb 30 23732 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.7048 x_vs_best
+BenchmarkBar/single/prose_miss_1mb 30 27249 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.2913 x_vs_best
+BenchmarkBar/single/prose_miss_1mb 30 22564 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.3116 x_vs_best
+BenchmarkBar/single/prose_miss_1mb 30 25700 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.3110 x_vs_best
+BenchmarkBar/single/code_miss_256kb 30 11307 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.4192 x_vs_best
+BenchmarkBar/single/code_miss_256kb 30 7839 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.4126 x_vs_best
+BenchmarkBar/single/code_miss_256kb 30 9292 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.4132 x_vs_best
+BenchmarkBar/single/log_needle3_64kb 30 3274 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.7256 x_vs_best
+BenchmarkBar/single/log_needle3_64kb 30 3275 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.7414 x_vs_best
+BenchmarkBar/single/log_needle3_64kb 30 3240 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.7254 x_vs_best
+BenchmarkBar/single/log_needle8_64kb 30 3262 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.2951 x_vs_best
+BenchmarkBar/single/log_needle8_64kb 30 3261 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.2997 x_vs_best
+BenchmarkBar/single/log_needle8_64kb 30 3329 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.3016 x_vs_best
+BenchmarkBar/single/log_needle16_64kb 30 3257 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.5711 x_vs_best
+BenchmarkBar/single/log_needle16_64kb 30 3258 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.5667 x_vs_best
+BenchmarkBar/single/log_needle16_64kb 30 3272 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.5673 x_vs_best
+BenchmarkBar/single/log_needle32_64kb 30 3274 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.3011 x_vs_best
+BenchmarkBar/single/log_needle32_64kb 30 3259 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.3070 x_vs_best
+BenchmarkBar/single/log_needle32_64kb 30 3271 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.3036 x_vs_best
+BenchmarkBar/single/log_hit_sparse_1mb 30 47172 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.2489 x_vs_best
+BenchmarkBar/single/log_hit_sparse_1mb 30 43060 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.2469 x_vs_best
+BenchmarkBar/single/log_hit_sparse_1mb 30 41614 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.2518 x_vs_best
+BenchmarkBar/single/prose_hit_dense_1mb 30 96262 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.4872 x_vs_best
+BenchmarkBar/single/prose_hit_dense_1mb 30 96290 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.4829 x_vs_best
+BenchmarkBar/single/prose_hit_dense_1mb 30 95964 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.4846 x_vs_best
+BenchmarkBar/single/code_hit_brackets_256kb 30 33707 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 6.000 competitors 7.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 1.000 rure_active 256.0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.5565 x_vs_best
+BenchmarkBar/single/code_hit_brackets_256kb 30 34776 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 6.000 competitors 7.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 1.000 rure_active 256.0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.5809 x_vs_best
+BenchmarkBar/single/code_hit_brackets_256kb 30 36106 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 6.000 competitors 7.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 1.000 rure_active 256.0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.5586 x_vs_best
+BenchmarkBar/single/latency_match_start_1kb 30 59.17 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.8483 x_vs_best
+BenchmarkBar/single/latency_match_start_1kb 30 60.53 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.8683 x_vs_best
+BenchmarkBar/single/latency_match_start_1kb 30 70.73 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.8489 x_vs_best
+BenchmarkBar/single/latency_match_mid_1kb 30 208.9 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.7330 x_vs_best
+BenchmarkBar/single/latency_match_mid_1kb 30 212.8 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.7307 x_vs_best
+BenchmarkBar/single/latency_match_mid_1kb 30 233.8 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.7215 x_vs_best
+BenchmarkBar/single/latency_match_end_1kb 30 332.0 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6051 x_vs_best
+BenchmarkBar/single/latency_match_end_1kb 30 324.0 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6683 x_vs_best
+BenchmarkBar/single/latency_match_end_1kb 30 384.2 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6597 x_vs_best
+BenchmarkBar/single/latency_miss_1kb 30 346.1 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6293 x_vs_best
+BenchmarkBar/single/latency_miss_1kb 30 324.6 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6389 x_vs_best
+BenchmarkBar/single/latency_miss_1kb 30 344.7 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6491 x_vs_best
+BenchmarkBar/single/ru_miss_1mb 30 55978 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7402 x_vs_best
+BenchmarkBar/single/ru_miss_1mb 30 55132 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7472 x_vs_best
+BenchmarkBar/single/ru_miss_1mb 30 56174 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7689 x_vs_best
+BenchmarkBar/single/ru_hit_sparse_1mb 30 55874 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.8124 x_vs_best
+BenchmarkBar/single/ru_hit_sparse_1mb 30 62980 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.8066 x_vs_best
+BenchmarkBar/single/ru_hit_sparse_1mb 30 61519 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7776 x_vs_best
+BenchmarkBar/single/kelvin_hazard_1mb 30 52482 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.2938 x_vs_best
+BenchmarkBar/single/kelvin_hazard_1mb 30 51518 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.2987 x_vs_best
+BenchmarkBar/single/kelvin_hazard_1mb 30 51450 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.2946 x_vs_best
+BenchmarkBar/single/ru_latency_miss_1kb 30 583.5 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7896 x_vs_best
+BenchmarkBar/single/ru_latency_miss_1kb 30 288.2 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7663 x_vs_best
+BenchmarkBar/single/ru_latency_miss_1kb 30 291.3 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7886 x_vs_best
+BenchmarkBar/single/periodic_miss_64kb 30 4936 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.7636 x_vs_best
+BenchmarkBar/single/periodic_miss_64kb 30 6243 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.7570 x_vs_best
+BenchmarkBar/single/periodic_miss_64kb 30 5068 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.7523 x_vs_best
+BenchmarkBar/single/samechar_miss_64kb 30 2934 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.5526 x_vs_best
+BenchmarkBar/single/samechar_miss_64kb 30 3049 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.5362 x_vs_best
+BenchmarkBar/single/samechar_miss_64kb 30 2926 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.5352 x_vs_best
+BenchmarkBar/single/torture_miss_64kb 30 11661 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.04035 x_vs_best
+BenchmarkBar/single/torture_miss_64kb 30 9316 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.04035 x_vs_best
+BenchmarkBar/single/torture_miss_64kb 30 9385 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.04040 x_vs_best
+BenchmarkBar/multi/multi_N2_miss_log_1mb 30 80923 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7621 x_vs_best
+BenchmarkBar/multi/multi_N2_miss_log_1mb 30 77195 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7675 x_vs_best
+BenchmarkBar/multi/multi_N2_miss_log_1mb 30 77378 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7651 x_vs_best
+BenchmarkBar/multi/multi_N8_miss_log_1mb 30 40242 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.4307 x_vs_best
+BenchmarkBar/multi/multi_N8_miss_log_1mb 30 39566 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.4365 x_vs_best
+BenchmarkBar/multi/multi_N8_miss_log_1mb 30 40817 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.4319 x_vs_best
+BenchmarkBar/multi/multi_N64_miss_log_64kb 30 6514 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.6326 x_vs_best
+BenchmarkBar/multi/multi_N64_miss_log_64kb 30 7539 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.6475 x_vs_best
+BenchmarkBar/multi/multi_N64_miss_log_64kb 30 8508 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.6462 x_vs_best
+BenchmarkBar/multi/multi_N512_miss_log_64kb 30 11107 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.4897 x_vs_best
+BenchmarkBar/multi/multi_N512_miss_log_64kb 30 6323 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.5117 x_vs_best
+BenchmarkBar/multi/multi_N512_miss_log_64kb 30 8363 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.4853 x_vs_best
+BenchmarkBar/multi/multi_N512_miss_hazard_64kb 30 7497 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.3753 x_vs_best
+BenchmarkBar/multi/multi_N512_miss_hazard_64kb 30 6766 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.3574 x_vs_best
+BenchmarkBar/multi/multi_N512_miss_hazard_64kb 30 6853 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.3665 x_vs_best
+BenchmarkBar/multi/multi_N8_hit_log_1mb 30 112817 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 6.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.2734 x_vs_best
+BenchmarkBar/multi/multi_N8_hit_log_1mb 30 113996 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 6.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.2677 x_vs_best
+BenchmarkBar/multi/multi_N8_hit_log_1mb 30 116077 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 6.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.2691 x_vs_best
+BenchmarkBar/multi/multi_N1_unicode_pair_miss_1_5mb 30 92219 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.6629 x_vs_best
+BenchmarkBar/multi/multi_N1_unicode_pair_miss_1_5mb 30 88279 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.6684 x_vs_best
+BenchmarkBar/multi/multi_N1_unicode_pair_miss_1_5mb 30 101082 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.6696 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 30 173076 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.8419 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 30 165168 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.8765 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 30 160509 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 1.002 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 30 139080 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.8382 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 30 168577 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 1.072 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 30 177183 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9888 x_vs_best
+BenchmarkBar/multi/multi_N8_miss_ru_1mb 30 29513 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.4135 x_vs_best
+BenchmarkBar/multi/multi_N8_miss_ru_1mb 30 33380 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.4077 x_vs_best
+BenchmarkBar/multi/multi_N8_miss_ru_1mb 30 30639 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.4104 x_vs_best
+BenchmarkBar/multi/multi_N64_miss_ru_64kb 30 2080 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.4561 x_vs_best
+BenchmarkBar/multi/multi_N64_miss_ru_64kb 30 1945 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.4435 x_vs_best
+BenchmarkBar/multi/multi_N64_miss_ru_64kb 30 2009 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.4467 x_vs_best
+BenchmarkBar/multi/multi_N8_miss_hazard_1mb 30 75854 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.2138 x_vs_best
+BenchmarkBar/multi/multi_N8_miss_hazard_1mb 30 61128 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.2248 x_vs_best
+BenchmarkBar/multi/multi_N8_miss_hazard_1mb 30 65243 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.2132 x_vs_best
+BenchmarkBar/multi/multi_N8_hazard_hit_1mb 30 32183 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.08507 x_vs_best
+BenchmarkBar/multi/multi_N8_hazard_hit_1mb 30 33559 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.08729 x_vs_best
+BenchmarkBar/multi/multi_N8_hazard_hit_1mb 30 30281 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.08533 x_vs_best
+PASS
+ok github.com/tsenart/casei/arena 86.824s
diff --git a/audit/acceptance/methodology/spr/sequential-focused.txt b/audit/acceptance/methodology/spr/sequential-focused.txt
new file mode 100644
index 0000000..07bc110
--- /dev/null
+++ b/audit/acceptance/methodology/spr/sequential-focused.txt
@@ -0,0 +1,30 @@
+goos: linux
+goarch: amd64
+pkg: github.com/tsenart/casei/arena
+cpu: Intel(R) Xeon(R) Platinum 8481C CPU @ 2.70GHz
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 169298 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9644 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 160810 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9599 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 160567 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9610 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 160384 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9625 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 159970 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9609 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 160388 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9624 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 165969 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9601 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 161013 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9607 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 161382 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9611 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 160778 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9592 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 160615 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9588 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 1 160030 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9623 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 215414 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9514 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 215160 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9491 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 190621 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9510 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 163717 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9503 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 163632 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9535 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 161635 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9531 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 168368 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9520 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 162121 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9538 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 163241 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9498 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 162835 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9495 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 204266 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9524 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 1 191159 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9510 x_vs_best
+PASS
+ok github.com/tsenart/casei/arena 87.008s
diff --git a/audit/acceptance/methodology/spr/sequential-windows.txt b/audit/acceptance/methodology/spr/sequential-windows.txt
new file mode 100644
index 0000000..fadda6a
--- /dev/null
+++ b/audit/acceptance/methodology/spr/sequential-windows.txt
@@ -0,0 +1,114 @@
+goos: linux
+goarch: amd64
+pkg: github.com/tsenart/casei/arena
+cpu: Intel(R) Xeon(R) Platinum 8481C CPU @ 2.70GHz
+BenchmarkBar/single/log_miss_1kb 30 163.6 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6503 x_vs_best
+BenchmarkBar/single/log_miss_1kb 30 156.5 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6563 x_vs_best
+BenchmarkBar/single/log_miss_1kb 30 146.2 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6470 x_vs_best
+BenchmarkBar/single/log_miss_64kb 30 1313 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.8637 x_vs_best
+BenchmarkBar/single/log_miss_64kb 30 1300 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.8623 x_vs_best
+BenchmarkBar/single/log_miss_64kb 30 1306 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.8779 x_vs_best
+BenchmarkBar/single/log_miss_1mb 30 18855 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.8854 x_vs_best
+BenchmarkBar/single/log_miss_1mb 30 19307 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.8654 x_vs_best
+BenchmarkBar/single/log_miss_1mb 30 18665 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.8977 x_vs_best
+BenchmarkBar/single/prose_miss_1mb 30 18763 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.3857 x_vs_best
+BenchmarkBar/single/prose_miss_1mb 30 18964 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.3833 x_vs_best
+BenchmarkBar/single/prose_miss_1mb 30 18958 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.3928 x_vs_best
+BenchmarkBar/single/code_miss_256kb 30 4904 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.5072 x_vs_best
+BenchmarkBar/single/code_miss_256kb 30 4889 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.4983 x_vs_best
+BenchmarkBar/single/code_miss_256kb 30 4866 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.5067 x_vs_best
+BenchmarkBar/single/log_needle3_64kb 30 1462 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.8523 x_vs_best
+BenchmarkBar/single/log_needle3_64kb 30 1464 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.8505 x_vs_best
+BenchmarkBar/single/log_needle3_64kb 30 1476 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.8508 x_vs_best
+BenchmarkBar/single/log_needle8_64kb 30 1487 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.4305 x_vs_best
+BenchmarkBar/single/log_needle8_64kb 30 1473 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.4197 x_vs_best
+BenchmarkBar/single/log_needle8_64kb 30 1456 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.4238 x_vs_best
+BenchmarkBar/single/log_needle16_64kb 30 1478 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6850 x_vs_best
+BenchmarkBar/single/log_needle16_64kb 30 1460 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6802 x_vs_best
+BenchmarkBar/single/log_needle16_64kb 30 1476 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6863 x_vs_best
+BenchmarkBar/single/log_needle32_64kb 30 1465 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.4102 x_vs_best
+BenchmarkBar/single/log_needle32_64kb 30 1654 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.4115 x_vs_best
+BenchmarkBar/single/log_needle32_64kb 30 1452 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.4141 x_vs_best
+BenchmarkBar/single/log_hit_sparse_1mb 30 32854 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.2811 x_vs_best
+BenchmarkBar/single/log_hit_sparse_1mb 30 32718 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.2736 x_vs_best
+BenchmarkBar/single/log_hit_sparse_1mb 30 32875 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.2788 x_vs_best
+BenchmarkBar/single/prose_hit_dense_1mb 30 89336 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.5165 x_vs_best
+BenchmarkBar/single/prose_hit_dense_1mb 30 83426 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.5115 x_vs_best
+BenchmarkBar/single/prose_hit_dense_1mb 30 90107 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.5109 x_vs_best
+BenchmarkBar/single/code_hit_brackets_256kb 30 24673 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 6.000 competitors 7.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 1.000 rure_active 256.0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.5162 x_vs_best
+BenchmarkBar/single/code_hit_brackets_256kb 30 24934 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 6.000 competitors 7.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 1.000 rure_active 256.0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.5166 x_vs_best
+BenchmarkBar/single/code_hit_brackets_256kb 30 24108 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 6.000 competitors 7.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 1.000 rure_active 256.0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.5097 x_vs_best
+BenchmarkBar/single/latency_match_start_1kb 30 40.93 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.8246 x_vs_best
+BenchmarkBar/single/latency_match_start_1kb 30 39.73 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.8246 x_vs_best
+BenchmarkBar/single/latency_match_start_1kb 30 39.20 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.8245 x_vs_best
+BenchmarkBar/single/latency_match_mid_1kb 30 171.4 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.7113 x_vs_best
+BenchmarkBar/single/latency_match_mid_1kb 30 146.3 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.7127 x_vs_best
+BenchmarkBar/single/latency_match_mid_1kb 30 141.0 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.7133 x_vs_best
+BenchmarkBar/single/latency_match_end_1kb 30 187.2 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6355 x_vs_best
+BenchmarkBar/single/latency_match_end_1kb 30 182.7 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6344 x_vs_best
+BenchmarkBar/single/latency_match_end_1kb 30 167.7 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6320 x_vs_best
+BenchmarkBar/single/latency_miss_1kb 30 161.6 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6418 x_vs_best
+BenchmarkBar/single/latency_miss_1kb 30 160.0 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6401 x_vs_best
+BenchmarkBar/single/latency_miss_1kb 30 157.2 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6411 x_vs_best
+BenchmarkBar/single/ru_miss_1mb 30 41567 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.6461 x_vs_best
+BenchmarkBar/single/ru_miss_1mb 30 37934 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7981 x_vs_best
+BenchmarkBar/single/ru_miss_1mb 30 38066 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.6992 x_vs_best
+BenchmarkBar/single/ru_hit_sparse_1mb 30 41317 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.8533 x_vs_best
+BenchmarkBar/single/ru_hit_sparse_1mb 30 46936 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.8291 x_vs_best
+BenchmarkBar/single/ru_hit_sparse_1mb 30 41419 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.8202 x_vs_best
+BenchmarkBar/single/kelvin_hazard_1mb 30 65451 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.5755 x_vs_best
+BenchmarkBar/single/kelvin_hazard_1mb 30 58287 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.5082 x_vs_best
+BenchmarkBar/single/kelvin_hazard_1mb 30 58080 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.5036 x_vs_best
+BenchmarkBar/single/ru_latency_miss_1kb 30 260.2 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.8365 x_vs_best
+BenchmarkBar/single/ru_latency_miss_1kb 30 224.6 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.8365 x_vs_best
+BenchmarkBar/single/ru_latency_miss_1kb 30 267.6 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.8311 x_vs_best
+BenchmarkBar/single/periodic_miss_64kb 30 1935 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.8055 x_vs_best
+BenchmarkBar/single/periodic_miss_64kb 30 1933 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.7961 x_vs_best
+BenchmarkBar/single/periodic_miss_64kb 30 1912 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.8044 x_vs_best
+BenchmarkBar/single/samechar_miss_64kb 30 1145 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6654 x_vs_best
+BenchmarkBar/single/samechar_miss_64kb 30 1168 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6760 x_vs_best
+BenchmarkBar/single/samechar_miss_64kb 30 1148 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6702 x_vs_best
+BenchmarkBar/single/torture_miss_64kb 30 5084 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.03884 x_vs_best
+BenchmarkBar/single/torture_miss_64kb 30 5175 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.03849 x_vs_best
+BenchmarkBar/single/torture_miss_64kb 30 5242 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.03838 x_vs_best
+BenchmarkBar/multi/multi_N2_miss_log_1mb 30 67924 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7638 x_vs_best
+BenchmarkBar/multi/multi_N2_miss_log_1mb 30 68667 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7670 x_vs_best
+BenchmarkBar/multi/multi_N2_miss_log_1mb 30 68595 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7733 x_vs_best
+BenchmarkBar/multi/multi_N8_miss_log_1mb 30 36050 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.4837 x_vs_best
+BenchmarkBar/multi/multi_N8_miss_log_1mb 30 36389 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.4762 x_vs_best
+BenchmarkBar/multi/multi_N8_miss_log_1mb 30 51855 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.4767 x_vs_best
+BenchmarkBar/multi/multi_N64_miss_log_64kb 30 2498 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7961 x_vs_best
+BenchmarkBar/multi/multi_N64_miss_log_64kb 30 2460 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.8003 x_vs_best
+BenchmarkBar/multi/multi_N64_miss_log_64kb 30 2543 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.8000 x_vs_best
+BenchmarkBar/multi/multi_N512_miss_log_64kb 30 2795 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7234 x_vs_best
+BenchmarkBar/multi/multi_N512_miss_log_64kb 30 2482 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7036 x_vs_best
+BenchmarkBar/multi/multi_N512_miss_log_64kb 30 2571 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.6961 x_vs_best
+BenchmarkBar/multi/multi_N512_miss_hazard_64kb 30 7728 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.4669 x_vs_best
+BenchmarkBar/multi/multi_N512_miss_hazard_64kb 30 6887 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.4723 x_vs_best
+BenchmarkBar/multi/multi_N512_miss_hazard_64kb 30 6837 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.4671 x_vs_best
+BenchmarkBar/multi/multi_N8_hit_log_1mb 30 111099 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 6.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.3798 x_vs_best
+BenchmarkBar/multi/multi_N8_hit_log_1mb 30 108102 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 6.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.3699 x_vs_best
+BenchmarkBar/multi/multi_N8_hit_log_1mb 30 111190 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 6.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.3814 x_vs_best
+BenchmarkBar/multi/multi_N1_unicode_pair_miss_1_5mb 30 73758 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.6859 x_vs_best
+BenchmarkBar/multi/multi_N1_unicode_pair_miss_1_5mb 30 74080 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.6908 x_vs_best
+BenchmarkBar/multi/multi_N1_unicode_pair_miss_1_5mb 30 75332 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.6963 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 30 174248 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9565 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 30 166950 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9603 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 30 168990 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9601 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 30 167140 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9515 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 30 175118 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9512 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 30 170328 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9519 x_vs_best
+BenchmarkBar/multi/multi_N8_miss_ru_1mb 30 27547 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.5815 x_vs_best
+BenchmarkBar/multi/multi_N8_miss_ru_1mb 30 28042 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.5526 x_vs_best
+BenchmarkBar/multi/multi_N8_miss_ru_1mb 30 27941 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.5529 x_vs_best
+BenchmarkBar/multi/multi_N64_miss_ru_64kb 30 1815 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.5929 x_vs_best
+BenchmarkBar/multi/multi_N64_miss_ru_64kb 30 2038 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.5947 x_vs_best
+BenchmarkBar/multi/multi_N64_miss_ru_64kb 30 1984 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.5957 x_vs_best
+BenchmarkBar/multi/multi_N8_miss_hazard_1mb 30 63103 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.2537 x_vs_best
+BenchmarkBar/multi/multi_N8_miss_hazard_1mb 30 63509 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.2550 x_vs_best
+BenchmarkBar/multi/multi_N8_miss_hazard_1mb 30 70365 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.2540 x_vs_best
+BenchmarkBar/multi/multi_N8_hazard_hit_1mb 30 33991 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.1266 x_vs_best
+BenchmarkBar/multi/multi_N8_hazard_hit_1mb 30 34110 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.1269 x_vs_best
+BenchmarkBar/multi/multi_N8_hazard_hit_1mb 30 33210 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.1268 x_vs_best
+PASS
+ok github.com/tsenart/casei/arena 88.233s
diff --git a/audit/acceptance/native-reachability.gdb b/audit/acceptance/native-reachability.gdb
new file mode 100644
index 0000000..0023a22
--- /dev/null
+++ b/audit/acceptance/native-reachability.gdb
@@ -0,0 +1,29 @@
+# Run against a root test binary on an amd64 AVX-512F/BW/VBMI host. Each
+# breakpoint disables itself after the first hit, leaving one HIT line per
+# changed native entry.
+set pagination off
+set confirm off
+set print thread-events off
+set startup-with-shell off
+break github.com/tsenart/casei.literalSkipExact64.abi0
+break github.com/tsenart/casei.pairPairConfirmVBMI64.abi0
+break github.com/tsenart/casei.rawByteMultiAnchorSkip64.abi0
+commands 1
+silent
+printf "HIT 1\n"
+disable 1
+continue
+end
+commands 2
+silent
+printf "HIT 2\n"
+disable 2
+continue
+end
+commands 3
+silent
+printf "HIT 3\n"
+disable 3
+continue
+end
+run -test.run ^(TestLiteralSkipExact64MatchesModel|TestUnicodePairVariableConfirm|TestRawByteMultiAnchorVBMISkip64MatchesTableModel)$ -test.count=1
diff --git a/audit/acceptance/results/ice/benchmarkbar.txt b/audit/acceptance/results/ice/benchmarkbar.txt
new file mode 100644
index 0000000..76b0089
--- /dev/null
+++ b/audit/acceptance/results/ice/benchmarkbar.txt
@@ -0,0 +1,114 @@
+goos: linux
+goarch: amd64
+pkg: github.com/tsenart/casei/arena
+cpu: Intel(R) Xeon(R) CPU @ 2.60GHz
+BenchmarkBar/single/log_miss_1kb 30 94.10 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.5742 x_vs_best
+BenchmarkBar/single/log_miss_1kb 30 98.20 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.5736 x_vs_best
+BenchmarkBar/single/log_miss_1kb 30 98.57 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.5742 x_vs_best
+BenchmarkBar/single/log_miss_64kb 30 1207 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.7196 x_vs_best
+BenchmarkBar/single/log_miss_64kb 30 1201 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.7280 x_vs_best
+BenchmarkBar/single/log_miss_64kb 30 1204 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.7189 x_vs_best
+BenchmarkBar/single/log_miss_1mb 30 20421 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.7544 x_vs_best
+BenchmarkBar/single/log_miss_1mb 30 20489 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.7481 x_vs_best
+BenchmarkBar/single/log_miss_1mb 30 19438 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.7486 x_vs_best
+BenchmarkBar/single/prose_miss_1mb 30 20180 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.3175 x_vs_best
+BenchmarkBar/single/prose_miss_1mb 30 20117 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.3018 x_vs_best
+BenchmarkBar/single/prose_miss_1mb 30 19658 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.2827 x_vs_best
+BenchmarkBar/single/code_miss_256kb 30 4807 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.4132 x_vs_best
+BenchmarkBar/single/code_miss_256kb 30 4698 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.4122 x_vs_best
+BenchmarkBar/single/code_miss_256kb 30 4577 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.4112 x_vs_best
+BenchmarkBar/single/log_needle3_64kb 30 1246 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.7521 x_vs_best
+BenchmarkBar/single/log_needle3_64kb 30 1258 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.7458 x_vs_best
+BenchmarkBar/single/log_needle3_64kb 30 1238 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.7554 x_vs_best
+BenchmarkBar/single/log_needle8_64kb 30 1254 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.3036 x_vs_best
+BenchmarkBar/single/log_needle8_64kb 30 1256 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.3057 x_vs_best
+BenchmarkBar/single/log_needle8_64kb 30 1260 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.3028 x_vs_best
+BenchmarkBar/single/log_needle16_64kb 30 1253 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.5616 x_vs_best
+BenchmarkBar/single/log_needle16_64kb 30 1246 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.5608 x_vs_best
+BenchmarkBar/single/log_needle16_64kb 30 1246 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.5607 x_vs_best
+BenchmarkBar/single/log_needle32_64kb 30 1246 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.3059 x_vs_best
+BenchmarkBar/single/log_needle32_64kb 30 1262 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.3026 x_vs_best
+BenchmarkBar/single/log_needle32_64kb 30 1249 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.2988 x_vs_best
+BenchmarkBar/single/log_hit_sparse_1mb 30 39333 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.2759 x_vs_best
+BenchmarkBar/single/log_hit_sparse_1mb 30 39042 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.2775 x_vs_best
+BenchmarkBar/single/log_hit_sparse_1mb 30 39587 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.2775 x_vs_best
+BenchmarkBar/single/prose_hit_dense_1mb 30 88025 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.4898 x_vs_best
+BenchmarkBar/single/prose_hit_dense_1mb 30 87748 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.4957 x_vs_best
+BenchmarkBar/single/prose_hit_dense_1mb 30 87590 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.4934 x_vs_best
+BenchmarkBar/single/code_hit_brackets_256kb 30 28331 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 6.000 competitors 7.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 1.000 rure_active 256.0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.5527 x_vs_best
+BenchmarkBar/single/code_hit_brackets_256kb 30 28497 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 6.000 competitors 7.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 1.000 rure_active 256.0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.5501 x_vs_best
+BenchmarkBar/single/code_hit_brackets_256kb 30 28413 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 6.000 competitors 7.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 1.000 rure_active 256.0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.5523 x_vs_best
+BenchmarkBar/single/latency_match_start_1kb 30 20.37 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.8124 x_vs_best
+BenchmarkBar/single/latency_match_start_1kb 30 21.00 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.8109 x_vs_best
+BenchmarkBar/single/latency_match_start_1kb 30 20.90 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.8110 x_vs_best
+BenchmarkBar/single/latency_match_mid_1kb 30 67.20 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.7145 x_vs_best
+BenchmarkBar/single/latency_match_mid_1kb 30 72.37 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.7126 x_vs_best
+BenchmarkBar/single/latency_match_mid_1kb 30 66.20 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.7093 x_vs_best
+BenchmarkBar/single/latency_match_end_1kb 30 110.9 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6175 x_vs_best
+BenchmarkBar/single/latency_match_end_1kb 30 110.4 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6174 x_vs_best
+BenchmarkBar/single/latency_match_end_1kb 30 112.9 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6163 x_vs_best
+BenchmarkBar/single/latency_miss_1kb 30 107.7 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6000 x_vs_best
+BenchmarkBar/single/latency_miss_1kb 30 97.97 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6022 x_vs_best
+BenchmarkBar/single/latency_miss_1kb 30 98.40 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6031 x_vs_best
+BenchmarkBar/single/ru_miss_1mb 30 49357 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7860 x_vs_best
+BenchmarkBar/single/ru_miss_1mb 30 49531 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7749 x_vs_best
+BenchmarkBar/single/ru_miss_1mb 30 49624 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7761 x_vs_best
+BenchmarkBar/single/ru_hit_sparse_1mb 30 57057 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.8000 x_vs_best
+BenchmarkBar/single/ru_hit_sparse_1mb 30 57041 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.8130 x_vs_best
+BenchmarkBar/single/ru_hit_sparse_1mb 30 57532 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.8036 x_vs_best
+BenchmarkBar/single/kelvin_hazard_1mb 30 51438 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.4265 x_vs_best
+BenchmarkBar/single/kelvin_hazard_1mb 30 51713 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.4357 x_vs_best
+BenchmarkBar/single/kelvin_hazard_1mb 30 51750 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.4420 x_vs_best
+BenchmarkBar/single/ru_latency_miss_1kb 30 150.2 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.8306 x_vs_best
+BenchmarkBar/single/ru_latency_miss_1kb 30 151.0 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.8193 x_vs_best
+BenchmarkBar/single/ru_latency_miss_1kb 30 151.5 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7956 x_vs_best
+BenchmarkBar/single/periodic_miss_64kb 30 2118 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.7510 x_vs_best
+BenchmarkBar/single/periodic_miss_64kb 30 2113 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.7550 x_vs_best
+BenchmarkBar/single/periodic_miss_64kb 30 2119 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.7505 x_vs_best
+BenchmarkBar/single/samechar_miss_64kb 30 921.3 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.5091 x_vs_best
+BenchmarkBar/single/samechar_miss_64kb 30 935.7 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.5606 x_vs_best
+BenchmarkBar/single/samechar_miss_64kb 30 921.1 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.5529 x_vs_best
+BenchmarkBar/single/torture_miss_64kb 30 6096 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.03797 x_vs_best
+BenchmarkBar/single/torture_miss_64kb 30 5950 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.03797 x_vs_best
+BenchmarkBar/single/torture_miss_64kb 30 6334 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.03818 x_vs_best
+BenchmarkBar/multi/multi_N2_miss_log_1mb 30 70149 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7647 x_vs_best
+BenchmarkBar/multi/multi_N2_miss_log_1mb 30 71462 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7674 x_vs_best
+BenchmarkBar/multi/multi_N2_miss_log_1mb 30 72187 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7735 x_vs_best
+BenchmarkBar/multi/multi_N8_miss_log_1mb 30 34249 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.4378 x_vs_best
+BenchmarkBar/multi/multi_N8_miss_log_1mb 30 34261 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.4378 x_vs_best
+BenchmarkBar/multi/multi_N8_miss_log_1mb 30 34407 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.4481 x_vs_best
+BenchmarkBar/multi/multi_N64_miss_log_64kb 30 2218 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.6253 x_vs_best
+BenchmarkBar/multi/multi_N64_miss_log_64kb 30 2224 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.6284 x_vs_best
+BenchmarkBar/multi/multi_N64_miss_log_64kb 30 2216 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.6283 x_vs_best
+BenchmarkBar/multi/multi_N512_miss_log_64kb 30 2220 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.4690 x_vs_best
+BenchmarkBar/multi/multi_N512_miss_log_64kb 30 2226 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.4758 x_vs_best
+BenchmarkBar/multi/multi_N512_miss_log_64kb 30 2239 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.4742 x_vs_best
+BenchmarkBar/multi/multi_N512_miss_hazard_64kb 30 6816 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.3911 x_vs_best
+BenchmarkBar/multi/multi_N512_miss_hazard_64kb 30 6677 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.3854 x_vs_best
+BenchmarkBar/multi/multi_N512_miss_hazard_64kb 30 6612 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.3970 x_vs_best
+BenchmarkBar/multi/multi_N8_hit_log_1mb 30 110606 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 6.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.3414 x_vs_best
+BenchmarkBar/multi/multi_N8_hit_log_1mb 30 108605 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 6.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.3422 x_vs_best
+BenchmarkBar/multi/multi_N8_hit_log_1mb 30 107612 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 6.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.3382 x_vs_best
+BenchmarkBar/multi/multi_N1_unicode_pair_miss_1_5mb 30 85342 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.6822 x_vs_best
+BenchmarkBar/multi/multi_N1_unicode_pair_miss_1_5mb 30 86077 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.6792 x_vs_best
+BenchmarkBar/multi/multi_N1_unicode_pair_miss_1_5mb 30 85191 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.6764 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 30 141246 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9582 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 30 141905 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9622 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 30 125222 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9646 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 30 173818 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9736 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 30 148247 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9579 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 30 134790 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9624 x_vs_best
+BenchmarkBar/multi/multi_N8_miss_ru_1mb 30 28392 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.4202 x_vs_best
+BenchmarkBar/multi/multi_N8_miss_ru_1mb 30 29175 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.4151 x_vs_best
+BenchmarkBar/multi/multi_N8_miss_ru_1mb 30 28556 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.4197 x_vs_best
+BenchmarkBar/multi/multi_N64_miss_ru_64kb 30 1858 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.4448 x_vs_best
+BenchmarkBar/multi/multi_N64_miss_ru_64kb 30 1860 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.4406 x_vs_best
+BenchmarkBar/multi/multi_N64_miss_ru_64kb 30 1859 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.4405 x_vs_best
+BenchmarkBar/multi/multi_N8_miss_hazard_1mb 30 60133 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.2275 x_vs_best
+BenchmarkBar/multi/multi_N8_miss_hazard_1mb 30 61805 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.2294 x_vs_best
+BenchmarkBar/multi/multi_N8_miss_hazard_1mb 30 60512 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.2274 x_vs_best
+BenchmarkBar/multi/multi_N8_hazard_hit_1mb 30 30607 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.1158 x_vs_best
+BenchmarkBar/multi/multi_N8_hazard_hit_1mb 30 30529 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.1152 x_vs_best
+BenchmarkBar/multi/multi_N8_hazard_hit_1mb 30 30653 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.1148 x_vs_best
+PASS
+ok github.com/tsenart/casei/arena 265.158s
diff --git a/audit/acceptance/results/ice/field-receipt.txt b/audit/acceptance/results/ice/field-receipt.txt
new file mode 100644
index 0000000..63a6ba7
--- /dev/null
+++ b/audit/acceptance/results/ice/field-receipt.txt
@@ -0,0 +1,32 @@
+receipt_format: casei-native-field-v1
+source_commit: 5ab3cc8c1501a701bbd023a16d9d7da317a634ca
+source_manifest: audit/acceptance/SOURCE_SHA256SUMS
+source_manifest_sha256: 043dd919faa1d34cb26f2993f5f756e7f3265a10291e5889eecab67530cb27e9
+vendor_id: GenuineIntel
+cpu_family: 6
+cpu_model: 106
+cpu_model_name: Intel(R) Xeon(R) CPU @ 2.60GHz
+lscpu_model_name: Intel(R) Xeon(R) CPU @ 2.60GHz
+required_cpu_features: avx2 avx512f avx512bw avx512vbmi
+go: go1.26.6 linux/amd64
+cpu_affinity: 2
+
+benchmarkbar_field: Vectorscan 5.4.12, PCRE2 10.47, rure 0.2.5, rust/aho-corasick 1.1.5, StringZilla 5.1.2, veloz
+benchmarkbar_command: taskset -c 2 go test -run '^$' -bench '^BenchmarkBar$' -benchtime 30x -count 3
+benchmarkbar_rows: 36
+benchmarkbar_passes: 3
+benchmarkbar_entrants: 5-7
+benchmarkbar_dispatch: casei=512-bit, Vectorscan=512-bit VBMI
+benchmarkbar_output: audit/acceptance/results/ice/benchmarkbar.txt
+benchmarkbar_sha256: a61c8ac9eaf3f548c54ff4c92aa2ce6d24f8d11fd2d55250a27965e513a7a32c
+
+rebar_commit: 463d00f31887e84c38467805b9e3122c314b9521
+rebar_field: Hyperscan 5.4.2, PCRE2 JIT 10.47, rust/regex 1.12.4
+rebar_hyperscan_source_commit: bc3b191ab56055e8560c7cdc161c289c4d76e3d2
+rebar_command: taskset -c 2 rebar measure -e '^(casei|hyperscan|pcre2/jit|rust/regex)$' -f '<18-row audit filter>' --max-warmup-iters 100 --max-warmup-time 200ms --max-iters 1000 --max-time 500ms
+rebar_rows: 18
+rebar_passes: 3
+rebar_entrants: casei, hyperscan, pcre2/jit, rust/regex
+rebar_pass1: audit/rebar/results/ice/rebar-audit-pass1.csv sha256=a37d4082e114e1ac6624d19f6ac216109d1812411822dbf5904fd75f9d6a7a11
+rebar_pass2: audit/rebar/results/ice/rebar-audit-pass2.csv sha256=cf7580a159b36ee04957f5ab67749f5b0ca2a2a18672509ed5a958e9460a6116
+rebar_pass3: audit/rebar/results/ice/rebar-audit-pass3.csv sha256=863f3430c07039fb270fbc2f3ea3800cfd8d94358195a8ec8898fac222e9f813
diff --git a/audit/acceptance/results/ice/gdb-native.txt b/audit/acceptance/results/ice/gdb-native.txt
new file mode 100644
index 0000000..6b1e49e
--- /dev/null
+++ b/audit/acceptance/results/ice/gdb-native.txt
@@ -0,0 +1,24 @@
+host: Ice Lake, GenuineIntel family 6 model 106
+go: go1.26.6 linux/amd64
+gdb: GNU gdb 15.1
+source checksum stream: 043dd919faa1d34cb26f2993f5f756e7f3265a10291e5889eecab67530cb27e9
+command: gdb -q -batch -x audit/acceptance/native-reachability.gdb casei.test
+
+warning: File "/usr/local/go/src/runtime/runtime-gdb.py" auto-loading has been declined by your `auto-load safe-path' set to "$debugdir:$datadir/auto-load".
+To enable execution of this file add
+ add-auto-load-safe-path /usr/local/go/src/runtime/runtime-gdb.py
+line to your configuration file "/home/ts_perfloop_com/.config/gdb/gdbinit".
+To completely disable this security protection add
+ set auto-load safe-path /
+line to your configuration file "/home/ts_perfloop_com/.config/gdb/gdbinit".
+For more information about this security protection see the
+"Auto-loading safe path" section in the GDB manual. E.g., run from the shell:
+ info "(gdb)Auto-loading safe path"
+Breakpoint 1 at 0x5edb40: file /home/ts_perfloop_com/casei/root_amd64.s, line 196.
+Breakpoint 2 at 0x5f0b80: file /home/ts_perfloop_com/casei/root_amd64.s, line 2982.
+Breakpoint 3 at 0x5f0f00: file /home/ts_perfloop_com/casei/root_amd64.s, line 3211.
+HIT 3
+HIT 1
+HIT 2
+PASS
+[Inferior 1 (process 37824) exited normally]
diff --git a/audit/acceptance/results/spr/benchmarkbar.txt b/audit/acceptance/results/spr/benchmarkbar.txt
new file mode 100644
index 0000000..5a90da2
--- /dev/null
+++ b/audit/acceptance/results/spr/benchmarkbar.txt
@@ -0,0 +1,114 @@
+goos: linux
+goarch: amd64
+pkg: github.com/tsenart/casei/arena
+cpu: Intel(R) Xeon(R) Platinum 8481C CPU @ 2.70GHz
+BenchmarkBar/single/log_miss_1kb 30 118.4 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6484 x_vs_best
+BenchmarkBar/single/log_miss_1kb 30 111.5 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6495 x_vs_best
+BenchmarkBar/single/log_miss_1kb 30 116.8 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6490 x_vs_best
+BenchmarkBar/single/log_miss_64kb 30 1278 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.8507 x_vs_best
+BenchmarkBar/single/log_miss_64kb 30 1282 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.8321 x_vs_best
+BenchmarkBar/single/log_miss_64kb 30 1293 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.8362 x_vs_best
+BenchmarkBar/single/log_miss_1mb 30 18615 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.8668 x_vs_best
+BenchmarkBar/single/log_miss_1mb 30 18584 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.8626 x_vs_best
+BenchmarkBar/single/log_miss_1mb 30 18580 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.8790 x_vs_best
+BenchmarkBar/single/prose_miss_1mb 30 18594 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.3852 x_vs_best
+BenchmarkBar/single/prose_miss_1mb 30 18632 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.3826 x_vs_best
+BenchmarkBar/single/prose_miss_1mb 30 18360 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.3829 x_vs_best
+BenchmarkBar/single/code_miss_256kb 30 4939 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.4971 x_vs_best
+BenchmarkBar/single/code_miss_256kb 30 4687 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.4947 x_vs_best
+BenchmarkBar/single/code_miss_256kb 30 4699 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.4905 x_vs_best
+BenchmarkBar/single/log_needle3_64kb 30 1277 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.8466 x_vs_best
+BenchmarkBar/single/log_needle3_64kb 30 1278 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.8466 x_vs_best
+BenchmarkBar/single/log_needle3_64kb 30 1282 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.8543 x_vs_best
+BenchmarkBar/single/log_needle8_64kb 30 1326 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.4161 x_vs_best
+BenchmarkBar/single/log_needle8_64kb 30 1276 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.4196 x_vs_best
+BenchmarkBar/single/log_needle8_64kb 30 1315 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.4114 x_vs_best
+BenchmarkBar/single/log_needle16_64kb 30 1284 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6852 x_vs_best
+BenchmarkBar/single/log_needle16_64kb 30 1275 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6841 x_vs_best
+BenchmarkBar/single/log_needle16_64kb 30 1282 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6857 x_vs_best
+BenchmarkBar/single/log_needle32_64kb 30 1315 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.3946 x_vs_best
+BenchmarkBar/single/log_needle32_64kb 30 1324 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.3872 x_vs_best
+BenchmarkBar/single/log_needle32_64kb 30 1312 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.3889 x_vs_best
+BenchmarkBar/single/log_hit_sparse_1mb 30 32778 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.2724 x_vs_best
+BenchmarkBar/single/log_hit_sparse_1mb 30 33067 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.2748 x_vs_best
+BenchmarkBar/single/log_hit_sparse_1mb 30 32994 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.2741 x_vs_best
+BenchmarkBar/single/prose_hit_dense_1mb 30 80951 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.5319 x_vs_best
+BenchmarkBar/single/prose_hit_dense_1mb 30 80877 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.5295 x_vs_best
+BenchmarkBar/single/prose_hit_dense_1mb 30 81351 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.5339 x_vs_best
+BenchmarkBar/single/code_hit_brackets_256kb 30 23753 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 6.000 competitors 7.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 1.000 rure_active 256.0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.5491 x_vs_best
+BenchmarkBar/single/code_hit_brackets_256kb 30 24152 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 6.000 competitors 7.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 1.000 rure_active 256.0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.5472 x_vs_best
+BenchmarkBar/single/code_hit_brackets_256kb 30 24403 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 6.000 competitors 7.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 1.000 rure_active 256.0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.5664 x_vs_best
+BenchmarkBar/single/latency_match_start_1kb 30 22.53 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.8144 x_vs_best
+BenchmarkBar/single/latency_match_start_1kb 30 29.20 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.8139 x_vs_best
+BenchmarkBar/single/latency_match_start_1kb 30 15.53 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.8143 x_vs_best
+BenchmarkBar/single/latency_match_mid_1kb 30 91.77 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.7079 x_vs_best
+BenchmarkBar/single/latency_match_mid_1kb 30 87.97 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.7076 x_vs_best
+BenchmarkBar/single/latency_match_mid_1kb 30 92.47 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.7071 x_vs_best
+BenchmarkBar/single/latency_match_end_1kb 30 126.9 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6397 x_vs_best
+BenchmarkBar/single/latency_match_end_1kb 30 131.8 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6403 x_vs_best
+BenchmarkBar/single/latency_match_end_1kb 30 130.5 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6406 x_vs_best
+BenchmarkBar/single/latency_miss_1kb 30 256.4 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6413 x_vs_best
+BenchmarkBar/single/latency_miss_1kb 30 129.0 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6423 x_vs_best
+BenchmarkBar/single/latency_miss_1kb 30 126.6 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6427 x_vs_best
+BenchmarkBar/single/ru_miss_1mb 30 37942 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7068 x_vs_best
+BenchmarkBar/single/ru_miss_1mb 30 38120 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.6958 x_vs_best
+BenchmarkBar/single/ru_miss_1mb 30 38258 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.6902 x_vs_best
+BenchmarkBar/single/ru_hit_sparse_1mb 30 41223 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.8285 x_vs_best
+BenchmarkBar/single/ru_hit_sparse_1mb 30 40885 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.8135 x_vs_best
+BenchmarkBar/single/ru_hit_sparse_1mb 30 41082 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.8273 x_vs_best
+BenchmarkBar/single/kelvin_hazard_1mb 30 57483 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.5697 x_vs_best
+BenchmarkBar/single/kelvin_hazard_1mb 30 57745 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.5724 x_vs_best
+BenchmarkBar/single/kelvin_hazard_1mb 30 57484 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.5755 x_vs_best
+BenchmarkBar/single/ru_latency_miss_1kb 30 164.9 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.8320 x_vs_best
+BenchmarkBar/single/ru_latency_miss_1kb 30 163.3 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.8206 x_vs_best
+BenchmarkBar/single/ru_latency_miss_1kb 30 166.4 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.8221 x_vs_best
+BenchmarkBar/single/periodic_miss_64kb 30 1888 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.8043 x_vs_best
+BenchmarkBar/single/periodic_miss_64kb 30 1893 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.8134 x_vs_best
+BenchmarkBar/single/periodic_miss_64kb 30 1889 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.8098 x_vs_best
+BenchmarkBar/single/samechar_miss_64kb 30 1006 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6691 x_vs_best
+BenchmarkBar/single/samechar_miss_64kb 30 1009 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6767 x_vs_best
+BenchmarkBar/single/samechar_miss_64kb 30 1017 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.6736 x_vs_best
+BenchmarkBar/single/torture_miss_64kb 30 5256 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.04007 x_vs_best
+BenchmarkBar/single/torture_miss_64kb 30 5320 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.03938 x_vs_best
+BenchmarkBar/single/torture_miss_64kb 30 5166 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 6.000 entrants 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 1.000 veloz_active 256.0 veloz_vector_bits 0.04012 x_vs_best
+BenchmarkBar/multi/multi_N2_miss_log_1mb 30 69352 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7929 x_vs_best
+BenchmarkBar/multi/multi_N2_miss_log_1mb 30 69036 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7968 x_vs_best
+BenchmarkBar/multi/multi_N2_miss_log_1mb 30 67818 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7890 x_vs_best
+BenchmarkBar/multi/multi_N8_miss_log_1mb 30 36046 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.4873 x_vs_best
+BenchmarkBar/multi/multi_N8_miss_log_1mb 30 36088 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.4775 x_vs_best
+BenchmarkBar/multi/multi_N8_miss_log_1mb 30 35698 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.4863 x_vs_best
+BenchmarkBar/multi/multi_N64_miss_log_64kb 30 2414 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7805 x_vs_best
+BenchmarkBar/multi/multi_N64_miss_log_64kb 30 2343 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7879 x_vs_best
+BenchmarkBar/multi/multi_N64_miss_log_64kb 30 2415 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7862 x_vs_best
+BenchmarkBar/multi/multi_N512_miss_log_64kb 30 2368 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.6389 x_vs_best
+BenchmarkBar/multi/multi_N512_miss_log_64kb 30 2357 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.6412 x_vs_best
+BenchmarkBar/multi/multi_N512_miss_log_64kb 30 2412 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 5.000 competitors 7.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 1.000 rustac_active 256.0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.6550 x_vs_best
+BenchmarkBar/multi/multi_N512_miss_hazard_64kb 30 7022 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.4713 x_vs_best
+BenchmarkBar/multi/multi_N512_miss_hazard_64kb 30 6910 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.4718 x_vs_best
+BenchmarkBar/multi/multi_N512_miss_hazard_64kb 30 6981 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.4619 x_vs_best
+BenchmarkBar/multi/multi_N8_hit_log_1mb 30 107357 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 6.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.3698 x_vs_best
+BenchmarkBar/multi/multi_N8_hit_log_1mb 30 111273 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 6.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.3849 x_vs_best
+BenchmarkBar/multi/multi_N8_hit_log_1mb 30 110634 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 6.000 entrants 1.000 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.3838 x_vs_best
+BenchmarkBar/multi/multi_N1_unicode_pair_miss_1_5mb 30 73716 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7140 x_vs_best
+BenchmarkBar/multi/multi_N1_unicode_pair_miss_1_5mb 30 74056 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.7136 x_vs_best
+BenchmarkBar/multi/multi_N1_unicode_pair_miss_1_5mb 30 74202 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.6944 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 30 156984 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9716 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 30 157734 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9799 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_miss_5mb 30 157026 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9707 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 30 157168 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9623 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 30 157961 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9641 x_vs_best
+BenchmarkBar/multi/multi_N5_raw_transition_late_hit_5mb 30 157563 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.9639 x_vs_best
+BenchmarkBar/multi/multi_N8_miss_ru_1mb 30 27136 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.5635 x_vs_best
+BenchmarkBar/multi/multi_N8_miss_ru_1mb 30 26961 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.5619 x_vs_best
+BenchmarkBar/multi/multi_N8_miss_ru_1mb 30 27147 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.5543 x_vs_best
+BenchmarkBar/multi/multi_N64_miss_ru_64kb 30 1815 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.5948 x_vs_best
+BenchmarkBar/multi/multi_N64_miss_ru_64kb 30 1810 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.5989 x_vs_best
+BenchmarkBar/multi/multi_N64_miss_ru_64kb 30 1974 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.5910 x_vs_best
+BenchmarkBar/multi/multi_N8_miss_hazard_1mb 30 57269 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.2641 x_vs_best
+BenchmarkBar/multi/multi_N8_miss_hazard_1mb 30 58548 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.2797 x_vs_best
+BenchmarkBar/multi/multi_N8_miss_hazard_1mb 30 57009 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.2636 x_vs_best
+BenchmarkBar/multi/multi_N8_hazard_hit_1mb 30 28632 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.1300 x_vs_best
+BenchmarkBar/multi/multi_N8_hazard_hit_1mb 30 28987 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.1297 x_vs_best
+BenchmarkBar/multi/multi_N8_hazard_hit_1mb 30 28778 ns/op 1.000 candidate_active 512.0 candidate_vector_bits 4.000 competitors 5.000 entrants 0 go_ac_active 0 go_ac_vector_bits 1.000 pcre2_active 128.0 pcre2_vector_bits 1.000 regexp_active 0 regexp_vector_bits 0 rure_active 0 rure_vector_bits 0 rustac_active 0 rustac_vector_bits 1.000 stringzilla_active 512.0 stringzilla_vector_bits 1.000 vectorscan_active 1.000 vectorscan_vbmi 512.0 vectorscan_vector_bits 0 veloz_active 0 veloz_vector_bits 0.1297 x_vs_best
+PASS
+ok github.com/tsenart/casei/arena 274.078s
diff --git a/audit/acceptance/results/spr/field-receipt.txt b/audit/acceptance/results/spr/field-receipt.txt
new file mode 100644
index 0000000..95c0c9b
--- /dev/null
+++ b/audit/acceptance/results/spr/field-receipt.txt
@@ -0,0 +1,32 @@
+receipt_format: casei-native-field-v1
+source_commit: 5ab3cc8c1501a701bbd023a16d9d7da317a634ca
+source_manifest: audit/acceptance/SOURCE_SHA256SUMS
+source_manifest_sha256: 043dd919faa1d34cb26f2993f5f756e7f3265a10291e5889eecab67530cb27e9
+vendor_id: GenuineIntel
+cpu_family: 6
+cpu_model: 143
+cpu_model_name: Intel(R) Xeon(R) Platinum 8481C CPU @ 2.70GHz
+lscpu_model_name: unknown (KVM virtualized CPUID)
+required_cpu_features: avx2 avx512f avx512bw avx512vbmi
+go: go1.26.6 linux/amd64
+cpu_affinity: 2
+
+benchmarkbar_field: Vectorscan 5.4.12, PCRE2 10.47, rure 0.2.5, rust/aho-corasick 1.1.5, StringZilla 5.1.2, veloz
+benchmarkbar_command: taskset -c 2 go test -run '^$' -bench '^BenchmarkBar$' -benchtime 30x -count 3
+benchmarkbar_rows: 36
+benchmarkbar_passes: 3
+benchmarkbar_entrants: 5-7
+benchmarkbar_dispatch: casei=512-bit, Vectorscan=512-bit VBMI
+benchmarkbar_output: audit/acceptance/results/spr/benchmarkbar.txt
+benchmarkbar_sha256: 11d05c54a7930a67cc1a2cefabd73ccb42bbdcfc77429fbc64f8158e9a244f94
+
+rebar_commit: 463d00f31887e84c38467805b9e3122c314b9521
+rebar_field: Hyperscan 5.4.2, PCRE2 JIT 10.47, rust/regex 1.12.4
+rebar_hyperscan_source_commit: bc3b191ab56055e8560c7cdc161c289c4d76e3d2
+rebar_command: taskset -c 2 rebar measure -e '^(casei|hyperscan|pcre2/jit|rust/regex)$' -f '<18-row audit filter>' --max-warmup-iters 100 --max-warmup-time 200ms --max-iters 1000 --max-time 500ms
+rebar_rows: 18
+rebar_passes: 3
+rebar_entrants: casei, hyperscan, pcre2/jit, rust/regex
+rebar_pass1: audit/rebar/results/spr/rebar-audit-pass1.csv sha256=c9221b1d036dfbfb7ada9ce3d2ec2be1236fae0627a7508327dc461bb65a6909
+rebar_pass2: audit/rebar/results/spr/rebar-audit-pass2.csv sha256=f5abea61e8f766190b8f360f16f6f786e48cffcdd4aef9764ff8a0a119fa94df
+rebar_pass3: audit/rebar/results/spr/rebar-audit-pass3.csv sha256=bfea805d4c64fa9b6596e279ea35439ae1a18bf94be97cac884065b8537b86eb
diff --git a/audit/acceptance/results/spr/gdb-native.txt b/audit/acceptance/results/spr/gdb-native.txt
new file mode 100644
index 0000000..c56cc45
--- /dev/null
+++ b/audit/acceptance/results/spr/gdb-native.txt
@@ -0,0 +1,24 @@
+host: Sapphire Rapids, GenuineIntel family 6 model 143
+go: go1.26.6 linux/amd64
+gdb: GNU gdb 15.1
+source checksum stream: 043dd919faa1d34cb26f2993f5f756e7f3265a10291e5889eecab67530cb27e9
+command: gdb -q -batch -x audit/acceptance/native-reachability.gdb casei.test
+
+warning: File "/usr/local/go/src/runtime/runtime-gdb.py" auto-loading has been declined by your `auto-load safe-path' set to "$debugdir:$datadir/auto-load".
+To enable execution of this file add
+ add-auto-load-safe-path /usr/local/go/src/runtime/runtime-gdb.py
+line to your configuration file "/home/tomas/.config/gdb/gdbinit".
+To completely disable this security protection add
+ set auto-load safe-path /
+line to your configuration file "/home/tomas/.config/gdb/gdbinit".
+For more information about this security protection see the
+"Auto-loading safe path" section in the GDB manual. E.g., run from the shell:
+ info "(gdb)Auto-loading safe path"
+Breakpoint 1 at 0x5edb40: file /home/tomas/casei-pr10-spr-0823.9nVjRd/casei/root_amd64.s, line 196.
+Breakpoint 2 at 0x5f0b80: file /home/tomas/casei-pr10-spr-0823.9nVjRd/casei/root_amd64.s, line 2982.
+Breakpoint 3 at 0x5f0f00: file /home/tomas/casei-pr10-spr-0823.9nVjRd/casei/root_amd64.s, line 3211.
+HIT 3
+HIT 1
+HIT 2
+PASS
+[Inferior 1 (process 16326) exited normally]
diff --git a/audit/acceptance/summarize.py b/audit/acceptance/summarize.py
new file mode 100644
index 0000000..e8a6f29
--- /dev/null
+++ b/audit/acceptance/summarize.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python3
+"""Verify and summarize the current two-host acceptance receipts."""
+
+import hashlib
+from pathlib import Path
+from statistics import median
+import sys
+
+
+sys.dont_write_bytecode = True
+SCRIPTS = Path(__file__).resolve().parents[2] / "scripts"
+sys.path.insert(0, str(SCRIPTS))
+import verify_benchmarkbar # noqa: E402
+
+
+ROOT = Path(__file__).resolve().parent / "results"
+SOURCE_MANIFEST = Path(__file__).resolve().parent / "SOURCE_SHA256SUMS"
+HOSTS = {"ice": "Ice Lake", "spr": "Sapphire Rapids"}
+
+
+def main():
+ source_digest = hashlib.sha256(SOURCE_MANIFEST.read_bytes()).hexdigest()
+ for host, title in HOSTS.items():
+ path = ROOT / host / "benchmarkbar.txt"
+ try:
+ verify_benchmarkbar.verify(path, expected_samples=3)
+ except verify_benchmarkbar.VerificationError as err:
+ raise SystemExit(err) from err
+ rows = verify_benchmarkbar.parse(path)
+
+ ratios = {
+ name: [sample["x_vs_best"] for sample in samples]
+ for name, samples in rows.items()
+ }
+ middle = {name: median(values) for name, values in ratios.items()}
+ worst_row = max(middle, key=middle.get)
+ worst_sample = max(value for values in ratios.values() for value in values)
+ speedup = median(1 / value for value in middle.values())
+ entrants = [
+ sample["entrants"]
+ for samples in rows.values()
+ for sample in samples
+ ]
+ native = (ROOT / host / "gdb-native.txt").read_text().splitlines()
+ required = {
+ "HIT 1",
+ "HIT 2",
+ "HIT 3",
+ "PASS",
+ f"source checksum stream: {source_digest}",
+ }
+ missing = required.difference(native)
+ if missing:
+ raise SystemExit(
+ f"{title} native receipt missing: {', '.join(sorted(missing))}"
+ )
+ print(
+ f"{title}: rows={len(rows)}, samples={sum(map(len, rows.values()))}, "
+ f"worst-row={worst_row}, median-x={middle[worst_row]:.4f}, "
+ f"worst-sample={worst_sample:.4f}, median-speedup={speedup:.2f}x, "
+ f"entrants={int(min(entrants))}-{int(max(entrants))}, native=3/3"
+ )
+
+
+if __name__ == "__main__":
+ main()
diff --git a/audit/publication/README.md b/audit/publication/README.md
index 08ac74e..23ef07e 100644
--- a/audit/publication/README.md
+++ b/audit/publication/README.md
@@ -1,12 +1,14 @@
-# Publication verification
+# Historical 33-row publication verification
-This directory contains the fresh hardware checks used for the August 2026
-publication review. These three-pass runs on each pinned host are the source of
-the per-row tables in the top-level README. Perfloop's public Case is separate:
-it co-measured the pre-engine and final source in ten pairs with random
-source-arm order, using the worst `x_vs_best` across all 33 rows as its metric.
-The purpose of this run was to rebuild the field, check that every row still
-passes, and isolate the two main speed mechanisms with the same compiled plans.
+This directory preserves the first August 2026 publication review. It covers
+the 33-row board before the three Rebar-derived rows were added. The current
+36-row claim and its raw transcripts live in
+[`audit/acceptance/`](../acceptance/README.md).
+
+Perfloop's original public Case co-measured the pre-engine and final source in
+ten pairs with random source-arm order, using the worst `x_vs_best` across all
+33 rows as its metric. This historical run rebuilt that field, checked every
+row, and isolated the two main speed mechanisms with the same compiled plans.
The search source was commit
`781eb8c36413f9a23c2d1f279ad9ef6554cac8bf`. The publication review then
@@ -79,10 +81,11 @@ and AVX2 plus AVX-512 disabled. The complete pinned arena agreement and dispatch
suite passed on both hosts. `FuzzIndexFold` and `FuzzMatcher` each ran for 30
seconds on both hosts.
-The amd64 source now contains 36 linked assembly entry points. One-shot GDB
-breakpoints observed all 36 across the normal, AVX-512-disabled, and
-BMI2-disabled test runs. Before this check, `runSkip32` and `runSkip64` were
-unreferenced source and absent from the linked test binary; they were removed.
+The amd64 source at the audited commit contained 36 linked assembly entry
+points. One-shot GDB breakpoints observed all 36 across the normal,
+AVX-512-disabled, and BMI2-disabled test runs. Before this check, `runSkip32`
+and `runSkip64` were unreferenced source and absent from the linked test binary;
+they were removed.
[`asm-reachability.gdb`](asm-reachability.gdb) is the command file used for the
check. The three files named `gdb-*.txt` in `results/ice` retain the `HIT` and
final `PASS` lines captured from these runs:
@@ -96,9 +99,10 @@ gdb -q -batch -ex 'set environment GODEBUG cpu.bmi2=off' \
-x audit/publication/asm-reachability.gdb ./casei.test
```
-The union of the reported breakpoint numbers must be 1 through 36, and each
-test process must print `PASS`. `summarize.py` checks both conditions from the
-captured receipts.
+For that historical source, the union of the reported breakpoint numbers must
+be 1 through 36, and each test process must print `PASS`. `summarize.py` checks
+both conditions from the captured receipts. Current-kernel reachability lives
+with the current acceptance record in [`audit/acceptance/`](../acceptance/README.md).
## Recompute the summaries
diff --git a/audit/rebar/README.md b/audit/rebar/README.md
index 0991951..b56bba0 100644
--- a/audit/rebar/README.md
+++ b/audit/rebar/README.md
@@ -1,22 +1,30 @@
# Rebar audit artifacts
-This directory contains the adapter and aggregate measurements behind
-[`REBAR.md`](../../REBAR.md). It is evidence for the boundary of the public
-claim, not another favorable benchmark suite.
-
-- [`runner/main.go`](runner/main.go) is the exact adapter used on both hosts.
- It compiles a `Matcher` once, enumerates non-overlapping matches by calling
- `Find` on each remaining suffix, verifies each matched byte width, and emits
- rebar's timing/count samples after reading its KLV request.
-- [`prepare.py`](prepare.py) registers that runner on all 18 representable
- performance workloads and all three relevant semantic checks in the pinned
- rebar checkout. It refuses any other rebar commit.
-- [`results/`](results/) contains all six rebar CSVs: three passes on Ice Lake
- and three on Sapphire Rapids, plus their SHA-256 receipt file.
-- [`summarize.py`](summarize.py) validates the receipts and recomputes every
- ratio and summary in `REBAR.md`.
-
-Run the receipt calculation from any directory:
+This directory contains the adapter and measurements behind
+[`REBAR.md`](../../REBAR.md).
+
+The current receipts say:
+
+- `casei` wins all 5/5 rows with the same Unicode contract on both hosts;
+- the worst same-contract ratio is 0.8794 on Ice Lake and 0.8999 on Sapphire
+ Rapids;
+- across all 18 representable stress rows, including 13 ASCII-only contracts,
+ `casei` wins 9/18 on each host.
+
+## What is here
+
+- [`runner/main.go`](runner/main.go) compiles a `Matcher` once, validates full
+ non-overlapping enumeration against an independent simple-fold oracle, then
+ times `Matcher.Each` or a single `Matcher.Find` query with only a scalar sink.
+- [`prepare.py`](prepare.py) registers that runner on all 18 performance rows
+ and three behavior checks in the pinned Rebar checkout.
+- [`results/`](results/) contains three CSV passes from each host and their
+ SHA-256 receipt file.
+- [`summarize.py`](summarize.py) validates the inventory and error columns,
+ selects the fastest competitor on each pass, and recomputes every ratio in
+ `REBAR.md`.
+
+Verify the checked-in record from any directory:
```sh
(cd audit/rebar/results && sha256sum -c SHA256SUMS)
@@ -25,17 +33,11 @@ python3 audit/rebar/summarize.py
## Reproduce on a qualifying Linux host
-Check out casei and rebar as siblings. The audit files landed after the measured
-casei commit, but the search implementation must still be byte-identical to
-`3954dbe40e8e21c4c7b2e2716f22647dd7cd880c`; the first command below proves
-that before rebar is prepared:
+Check out `casei` and Rebar as siblings:
```sh
git clone https://github.com/tsenart/casei.git
git clone https://github.com/BurntSushi/rebar.git
-git -C casei diff --exit-code \
- 3954dbe40e8e21c4c7b2e2716f22647dd7cd880c -- \
- casei.go matcher.go plan.go root_amd64.go root_amd64.s root_other.go go.mod go.sum
git -C rebar checkout 463d00f31887e84c38467805b9e3122c314b9521
cd rebar
python3 ../casei/audit/rebar/prepare.py
@@ -43,32 +45,33 @@ cargo build --release --bin rebar
./target/release/rebar build -e '^(casei|hyperscan|pcre2/jit|rust/regex)$'
```
-`prepare.py` also omits the unused `pcre2posix.c` wrapper because this pinned
-rebar snapshot lacks its header. The native PCRE2 API and JIT sources used by
-the runner are unchanged.
+`prepare.py` refuses any other Rebar commit. It also omits the unused
+`pcre2posix.c` wrapper because this pinned snapshot lacks its header. Rebar's
+native PCRE2 API and JIT sources are unchanged.
-The exact performance selection is the 18 rows to which the script adds
-`casei`. Run three passes using rebar's protocol, with 100 warm-up iterations
-or 200 ms and 1,000 measured iterations or 500 ms. The checked-in CSVs were
-produced with:
+The exact 18-row selection is:
```sh
-./target/release/rebar measure \
+filter='^(curated/(01-literal|02-literal-alternate)/sherlock-casei-(en|ru)|hyperscan/literal-casei-(english|russian)-(no)?som|imported/leipzig/(twain-insensitive|tom-sawyer-huckle-fin-insensitive)|imported/sherlock/(name-(sherlock|holmes|sherlock-holmes|alt3|alt5)-casei|the-casei)|opt/prefilter/literal-casei-(english|russian))$'
+
+taskset -c 2 ./target/release/rebar measure \
-e '^(casei|hyperscan|pcre2/jit|rust/regex)$' \
- -f '^(curated/(01-literal|02-literal-alternate)/sherlock-casei-(en|ru)|hyperscan/literal-casei-(english|russian)-(no)?som|imported/leipzig/(twain-insensitive|tom-sawyer-huckle-fin-insensitive)|imported/sherlock/(name-(sherlock|holmes|sherlock-holmes|alt3|alt5)-casei|the-casei)|opt/prefilter/literal-casei-(english|russian))$' \
+ -f "$filter" \
--max-warmup-iters 100 --max-warmup-time 200ms \
--max-iters 1000 --max-time 500ms > rebar-audit-pass1.csv
```
-Repeat for passes two and three. Rebar's runner validates every answer while
-measuring; any mismatch appears in the CSV's `err` column. The two compatible
-Unicode behavior checks can also be isolated with:
+Repeat for passes two and three. The checked-in record used core 2 on both
+hosts. The runner validates every answer in an untimed preflight; a mismatch
+appears in the CSV `err` column and makes `summarize.py` fail.
+
+The compatible behavior checks can be run directly:
```sh
-./target/release/rebar measure --verify --verbose -e '^casei$' \
+taskset -c 2 ./target/release/rebar measure --verify --verbose \
+ -e '^casei$' \
-f '^test/unicode/case/(ascii-with-unicode|unicode)$'
```
-The excluded `test/unicode/case/ascii-only` check is intentionally not made to
-pass: it expects `s` not to match `ſ`, contrary to casei's fixed Unicode
-simple-fold contract.
+The excluded `test/unicode/case/ascii-only` row expects `s` to miss `ſ`.
+Unicode simple folding requires them to match.
diff --git a/audit/rebar/results/SHA256SUMS b/audit/rebar/results/SHA256SUMS
index d1b0d14..fb9bb8f 100644
--- a/audit/rebar/results/SHA256SUMS
+++ b/audit/rebar/results/SHA256SUMS
@@ -1,6 +1,6 @@
-d86cfe95cf7f21c3600a94d29e072f6d707ba56d649d72117bcbea16edc5a741 ice/rebar-audit-pass1.csv
-c4ce41b8c89f0d7f59e9f55a1c06a665e0a6298d278c2010e5ae3b7a6039c504 ice/rebar-audit-pass2.csv
-3c5f9a0ffe3f7da1292536c15f461f7746ca80f2f73b978a59f60d551c92669b ice/rebar-audit-pass3.csv
-3d347e6131ea04c8df26839fdae29f90a9a375b888925973fafec05ab4d7ba7e spr/rebar-audit-pass1.csv
-6a3cbf5bca2a347392c2c4541f480daf3686a7fe07b5cfa62372069914eef676 spr/rebar-audit-pass2.csv
-70ffcf7125c9c5b132002cecaa7166f88fcbfdd499c66a21a0d061bf6f25b137 spr/rebar-audit-pass3.csv
+a37d4082e114e1ac6624d19f6ac216109d1812411822dbf5904fd75f9d6a7a11 ice/rebar-audit-pass1.csv
+cf7580a159b36ee04957f5ab67749f5b0ca2a2a18672509ed5a958e9460a6116 ice/rebar-audit-pass2.csv
+863f3430c07039fb270fbc2f3ea3800cfd8d94358195a8ec8898fac222e9f813 ice/rebar-audit-pass3.csv
+c9221b1d036dfbfb7ada9ce3d2ec2be1236fae0627a7508327dc461bb65a6909 spr/rebar-audit-pass1.csv
+f5abea61e8f766190b8f360f16f6f786e48cffcdd4aef9764ff8a0a119fa94df spr/rebar-audit-pass2.csv
+bfea805d4c64fa9b6596e279ea35439ae1a18bf94be97cac884065b8537b86eb spr/rebar-audit-pass3.csv
diff --git a/audit/rebar/results/ice/rebar-audit-pass1.csv b/audit/rebar/results/ice/rebar-audit-pass1.csv
index 1d22b68..1f9339a 100644
--- a/audit/rebar/results/ice/rebar-audit-pass1.csv
+++ b/audit/rebar/results/ice/rebar-audit-pass1.csv
@@ -1,61 +1,61 @@
name,model,rebar_version,engine,engine_version,err,haystack_len,iters,total,median,mad,mean,stddev,min,max
-curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,899232,1000,351.38ms,272.24us,2.82us,273.22us,7.51us,259.58us,335.05us
-curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,899232,1000,50.64ms,36.57us,648.00ns,37.01us,1.95us,35.43us,58.55us
-curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,899232,1000,150.88ms,90.92us,1.44us,91.60us,3.11us,87.48us,111.94us
-curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,899232,1000,201.06ms,134.28us,212.00ns,135.68us,2.83us,133.73us,163.05us
-curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,1000,451.51ms,397.32us,3.11us,398.94us,11.72us,382.79us,572.41us
-curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,1570556,1000,451.45ms,381.27us,1.88us,381.10us,4.50us,372.96us,408.00us
-curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,1000,200.90ms,155.26us,1.51us,157.08us,4.11us,152.94us,208.29us
-curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,1000,351.28ms,292.22us,3.00us,291.30us,4.20us,285.89us,318.09us
-curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,899232,888,601.48ms,561.48us,3.39us,562.66us,6.62us,545.84us,602.12us
-curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,899232,1000,100.67ms,79.97us,110.00ns,81.03us,2.80us,79.68us,109.50us
-curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,899232,248,751.81ms,2.02ms,4.26us,2.02ms,8.05us,1.99ms,2.05ms
-curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,899232,1000,501.61ms,440.85us,1.46us,442.10us,6.44us,435.22us,550.59us
-curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,74,751.89ms,6.80ms,19.43us,6.80ms,36.49us,6.73ms,7.02ms
-curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,1570556,722,601.57ms,692.02us,3.81us,693.30us,11.39us,680.91us,891.00us
-curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,54,751.83ms,9.34ms,20.25us,9.34ms,32.23us,9.29ms,9.45ms
-curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,338,701.96ms,1.48ms,6.82us,1.48ms,11.73us,1.46ms,1.54ms
-hyperscan/literal-casei-english-nosom,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613357,1000,100.73ms,82.01us,456.00ns,82.86us,2.69us,79.01us,111.53us
-hyperscan/literal-casei-english-nosom,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,613357,1000,50.50ms,16.56us,25.00ns,16.80us,1.55us,16.46us,45.45us
-hyperscan/literal-casei-english-nosom,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613357,1000,100.70ms,63.71us,54.00ns,64.39us,1.94us,63.56us,80.52us
-hyperscan/literal-casei-english-som,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613357,1000,100.64ms,82.01us,453.00ns,82.82us,2.45us,79.30us,102.69us
-hyperscan/literal-casei-english-som,count-spans,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,613357,1000,50.58ms,16.56us,28.00ns,16.81us,1.56us,16.45us,42.48us
-hyperscan/literal-casei-english-som,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613357,1000,100.74ms,63.75us,54.00ns,64.38us,1.81us,63.58us,78.35us
-hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,100.71ms,57.30us,160.00ns,58.04us,2.15us,56.50us,79.33us
-hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,613423,1000,150.81ms,86.61us,231.00ns,87.59us,2.94us,86.22us,119.49us
-hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.71ms,80.69us,61.00ns,81.59us,2.54us,80.42us,107.68us
-hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,100.67ms,58.17us,148.00ns,59.05us,3.66us,57.49us,134.75us
-hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,613423,1000,150.80ms,86.59us,162.00ns,87.53us,2.29us,86.30us,117.81us
-hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.71ms,80.34us,77.00ns,81.22us,2.81us,79.99us,106.23us
-imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,16013977,550,651.67ms,901.13us,26.85us,908.42us,43.25us,822.89us,1.24ms
-imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,16013977,702,601.70ms,704.01us,16.85us,712.76us,35.62us,644.90us,1.00ms
-imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,16013977,359,701.81ms,1.38ms,39.02us,1.40ms,85.28us,1.22ms,1.92ms
-imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,16013977,231,751.91ms,2.17ms,49.53us,2.17ms,80.53us,1.98ms,2.46ms
-imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,16013977,110,801.98ms,4.56ms,33.88us,4.55ms,49.99us,4.44ms,4.70ms
-imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,16013977,318,701.80ms,1.56ms,49.25us,1.58ms,116.42us,1.44ms,3.19ms
-imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,16013977,10,752.01ms,51.21ms,72.26us,51.27ms,135.53us,51.13ms,51.53ms
-imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,16013977,24,802.21ms,21.42ms,40.17us,21.68ms,445.03us,21.34ms,22.89ms
-imported/sherlock/name-sherlock-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,150.80ms,106.65us,1.30us,108.11us,5.37us,103.56us,212.95us
-imported/sherlock/name-sherlock-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,100.71ms,67.80us,1.34us,68.26us,2.35us,65.26us,83.36us
-imported/sherlock/name-sherlock-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,100.70ms,73.12us,60.00ns,73.88us,2.36us,72.88us,96.83us
-imported/sherlock/name-holmes-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,100.69ms,66.39us,297.00ns,67.42us,3.44us,65.57us,108.82us
-imported/sherlock/name-holmes-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,100.72ms,83.77us,2.07us,84.37us,3.30us,80.11us,110.48us
-imported/sherlock/name-holmes-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,150.85ms,108.02us,153.00ns,109.28us,3.36us,107.47us,138.31us
-imported/sherlock/name-sherlock-holmes-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,150.81ms,117.69us,1.40us,120.42us,9.76us,114.43us,313.58us
-imported/sherlock/name-sherlock-holmes-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,100.77ms,44.35us,299.00ns,44.87us,1.76us,43.46us,61.38us
-imported/sherlock/name-sherlock-holmes-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,100.78ms,75.29us,53.00ns,76.03us,1.89us,75.10us,93.18us
-imported/sherlock/name-alt3-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,451.30ms,381.97us,4.70us,382.72us,6.46us,370.15us,421.75us
-imported/sherlock/name-alt3-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,199,751.88ms,2.52ms,10.66us,2.52ms,20.91us,2.49ms,2.68ms
-imported/sherlock/name-alt3-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,551.54ms,491.67us,1.07us,492.75us,3.78us,486.60us,544.08us
-imported/sherlock/name-alt5-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,300.99ms,251.56us,3.12us,252.50us,5.14us,243.82us,282.60us
-imported/sherlock/name-alt5-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,367,651.79ms,1.36ms,5.17us,1.36ms,12.36us,1.34ms,1.47ms
-imported/sherlock/name-alt5-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,301.06ms,241.70us,2.26us,242.32us,11.77us,238.28us,592.96us
-imported/sherlock/the-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,970,601.94ms,513.45us,3.66us,515.23us,8.52us,502.44us,656.45us
-imported/sherlock/the-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,551.84ms,480.20us,5.03us,481.06us,9.70us,466.10us,633.78us
-imported/sherlock/the-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,551.81ms,486.09us,1.97us,488.03us,13.90us,478.34us,782.52us
-opt/prefilter/literal-casei-english,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613357,1000,100.72ms,82.12us,535.00ns,83.08us,3.61us,79.10us,122.57us
-opt/prefilter/literal-casei-english,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,613357,1000,100.78ms,44.44us,214.00ns,44.86us,1.60us,43.69us,63.55us
-opt/prefilter/literal-casei-english,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613357,1000,100.76ms,63.65us,57.00ns,64.34us,1.93us,63.43us,79.38us
-opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,100.66ms,57.31us,171.00ns,58.30us,3.29us,56.65us,86.80us
-opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,613423,1000,100.69ms,44.82us,76.00ns,45.29us,1.69us,44.44us,64.49us
-opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.74ms,80.79us,138.00ns,81.50us,1.74us,80.47us,95.85us
+curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,899232,1000,300.96ms,230.60us,2.69us,231.19us,4.82us,225.15us,268.42us
+curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,899232,1000,100.50ms,50.70us,1.06us,51.47us,2.35us,49.16us,78.80us
+curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,899232,1000,150.57ms,90.17us,1.38us,90.62us,2.88us,87.19us,120.22us
+curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,899232,1000,201.56ms,134.23us,185.00ns,135.42us,2.66us,133.57us,165.43us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,1000,251.72ms,134.89us,1.48us,137.25us,5.53us,131.79us,184.33us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,1570556,1000,401.19ms,330.27us,3.04us,330.63us,5.75us,321.56us,368.88us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,1000,201.61ms,155.21us,1.49us,156.72us,3.81us,152.73us,188.27us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,1000,350.98ms,290.14us,3.64us,290.05us,5.85us,284.48us,365.90us
+curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,899232,1000,602.13ms,465.49us,4.68us,467.63us,9.64us,448.06us,590.25us
+curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,899232,1000,150.58ms,99.27us,1.68us,99.80us,2.87us,96.50us,125.29us
+curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,899232,253,751.62ms,1.98ms,7.25us,1.98ms,10.37us,1.96ms,2.02ms
+curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,899232,1000,501.54ms,442.06us,2.74us,442.80us,7.69us,433.58us,628.38us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,962,802.22ms,518.25us,4.42us,519.36us,7.45us,504.29us,564.11us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,1570556,847,601.85ms,589.33us,3.54us,590.32us,7.12us,577.16us,630.43us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,54,752.18ms,9.32ms,19.34us,9.33ms,46.88us,9.26ms,9.49ms
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,336,701.78ms,1.49ms,11.01us,1.49ms,16.37us,1.46ms,1.61ms
+hyperscan/literal-casei-english-nosom,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613357,1000,150.58ms,85.24us,1.10us,87.18us,19.81us,81.50us,683.10us
+hyperscan/literal-casei-english-nosom,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,613357,1000,50.44ms,24.01us,80.00ns,24.26us,1.32us,23.73us,40.11us
+hyperscan/literal-casei-english-nosom,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613357,1000,100.48ms,63.74us,45.00ns,64.28us,1.71us,63.46us,87.66us
+hyperscan/literal-casei-english-som,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613357,1000,150.55ms,84.26us,643.00ns,84.91us,2.47us,81.29us,115.88us
+hyperscan/literal-casei-english-som,count-spans,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,613357,1000,50.42ms,23.82us,62.00ns,24.13us,1.60us,23.56us,47.24us
+hyperscan/literal-casei-english-som,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613357,1000,101.32ms,63.78us,45.00ns,64.32us,1.69us,63.55us,88.77us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,100.49ms,29.07us,115.00ns,29.40us,1.60us,28.68us,50.09us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,613423,1000,100.49ms,81.52us,84.00ns,82.25us,2.04us,81.32us,106.65us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.46ms,80.48us,76.00ns,81.12us,1.66us,80.20us,99.44us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,100.46ms,29.17us,65.00ns,29.49us,1.36us,28.96us,46.45us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,613423,1000,100.49ms,83.02us,1.40us,82.95us,2.07us,81.33us,105.40us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.47ms,80.66us,57.00ns,81.37us,2.20us,80.46us,118.72us
+imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,16013977,598,852.38ms,828.23us,8.87us,835.84us,21.70us,803.86us,1.05ms
+imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,16013977,681,601.56ms,733.91us,4.17us,734.67us,7.34us,723.52us,778.78us
+imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,16013977,426,652.20ms,1.17ms,7.99us,1.18ms,16.15us,1.15ms,1.26ms
+imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,16013977,282,701.71ms,1.77ms,8.35us,1.78ms,15.38us,1.76ms,1.83ms
+imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,16013977,122,1.60s,4.12ms,48.38us,4.12ms,58.24us,4.01ms,4.25ms
+imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,16013977,292,701.87ms,1.70ms,6.64us,1.71ms,40.05us,1.69ms,2.15ms
+imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,16013977,10,751.87ms,51.94ms,291.55us,51.65ms,582.57us,50.94ms,52.29ms
+imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,16013977,24,752.20ms,21.21ms,37.85us,21.20ms,54.13us,21.12ms,21.32ms
+imported/sherlock/name-sherlock-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,150.54ms,104.70us,789.00ns,105.63us,2.67us,102.36us,127.45us
+imported/sherlock/name-sherlock-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,100.47ms,68.15us,1.44us,68.44us,2.27us,65.80us,92.10us
+imported/sherlock/name-sherlock-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,100.50ms,72.98us,65.00ns,73.64us,2.00us,72.76us,100.31us
+imported/sherlock/name-holmes-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,101.09ms,46.97us,162.00ns,47.55us,2.04us,46.53us,68.03us
+imported/sherlock/name-holmes-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,100.47ms,83.11us,1.72us,83.89us,2.91us,79.80us,101.35us
+imported/sherlock/name-holmes-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,150.54ms,107.04us,220.00ns,107.94us,2.23us,106.56us,134.47us
+imported/sherlock/name-sherlock-holmes-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,150.55ms,111.73us,820.00ns,112.65us,2.76us,108.78us,139.93us
+imported/sherlock/name-sherlock-holmes-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,100.50ms,44.44us,301.00ns,44.90us,1.84us,43.45us,69.55us
+imported/sherlock/name-sherlock-holmes-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,100.52ms,74.39us,61.00ns,75.04us,1.83us,74.16us,98.27us
+imported/sherlock/name-alt3-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,451.27ms,344.20us,3.94us,345.57us,6.12us,333.57us,369.69us
+imported/sherlock/name-alt3-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,198,752.17ms,2.53ms,13.32us,2.53ms,17.77us,2.49ms,2.58ms
+imported/sherlock/name-alt3-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,551.78ms,491.71us,2.91us,491.85us,5.01us,481.45us,546.14us
+imported/sherlock/name-alt5-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,301.71ms,215.50us,1.63us,216.40us,3.19us,210.79us,234.85us
+imported/sherlock/name-alt5-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,369,652.01ms,1.35ms,4.02us,1.36ms,8.10us,1.34ms,1.42ms
+imported/sherlock/name-alt5-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,300.82ms,241.30us,1.37us,242.60us,3.19us,239.68us,272.50us
+imported/sherlock/the-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,451.07ms,371.35us,3.18us,371.75us,5.59us,358.15us,405.75us
+imported/sherlock/the-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,551.87ms,475.80us,4.52us,476.20us,6.57us,460.57us,513.35us
+imported/sherlock/the-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,551.73ms,483.58us,2.25us,483.82us,3.97us,475.47us,523.60us
+opt/prefilter/literal-casei-english,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613357,1000,150.55ms,84.15us,587.00ns,84.83us,2.36us,81.06us,105.18us
+opt/prefilter/literal-casei-english,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,613357,1000,100.49ms,44.49us,217.00ns,44.87us,1.63us,43.59us,70.20us
+opt/prefilter/literal-casei-english,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613357,1000,100.50ms,63.80us,45.00ns,64.34us,1.76us,63.62us,92.47us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,100.52ms,28.88us,59.00ns,29.21us,1.54us,28.68us,48.39us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,613423,1000,101.35ms,44.84us,81.00ns,45.29us,1.67us,44.53us,69.98us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.50ms,80.40us,54.00ns,81.09us,1.92us,80.20us,107.16us
diff --git a/audit/rebar/results/ice/rebar-audit-pass2.csv b/audit/rebar/results/ice/rebar-audit-pass2.csv
index 92b99ee..864a359 100644
--- a/audit/rebar/results/ice/rebar-audit-pass2.csv
+++ b/audit/rebar/results/ice/rebar-audit-pass2.csv
@@ -1,61 +1,61 @@
name,model,rebar_version,engine,engine_version,err,haystack_len,iters,total,median,mad,mean,stddev,min,max
-curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,899232,1000,351.29ms,272.87us,2.88us,273.35us,5.52us,259.99us,312.32us
-curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,899232,1000,50.64ms,35.95us,393.00ns,36.69us,2.86us,35.33us,73.97us
-curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,899232,1000,150.86ms,92.69us,1.41us,93.50us,3.75us,89.41us,115.92us
-curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,899232,1000,201.02ms,135.46us,188.00ns,137.16us,3.75us,134.97us,164.52us
-curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,1000,501.58ms,412.56us,2.52us,413.00us,6.96us,400.97us,520.86us
-curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,1570556,1000,451.49ms,381.68us,2.48us,381.74us,6.91us,373.04us,482.55us
-curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,1000,200.87ms,154.72us,810.00ns,157.20us,4.49us,153.20us,179.73us
-curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,1000,351.28ms,290.77us,3.07us,289.75us,4.79us,283.81us,326.78us
-curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,899232,885,601.64ms,562.66us,3.75us,564.45us,10.68us,546.96us,709.44us
-curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,899232,1000,100.73ms,80.17us,123.00ns,80.98us,1.97us,79.85us,95.40us
-curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,899232,252,702.16ms,1.98ms,8.57us,1.99ms,21.71us,1.97ms,2.14ms
-curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,899232,1000,501.77ms,441.84us,3.43us,442.92us,5.31us,435.03us,472.11us
-curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,72,752.19ms,7.00ms,19.67us,7.01ms,53.62us,6.96ms,7.32ms
-curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,1570556,719,601.90ms,693.84us,3.86us,695.95us,14.24us,681.86us,933.31us
-curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,53,752.30ms,9.49ms,23.23us,9.48ms,26.10us,9.43ms,9.54ms
-curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,332,702.23ms,1.51ms,9.61us,1.51ms,12.62us,1.48ms,1.55ms
-hyperscan/literal-casei-english-nosom,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613357,1000,100.76ms,81.99us,531.00ns,83.05us,3.61us,78.90us,122.80us
-hyperscan/literal-casei-english-nosom,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,613357,1000,50.58ms,16.55us,28.00ns,16.92us,2.30us,16.44us,53.15us
-hyperscan/literal-casei-english-nosom,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613357,1000,100.73ms,63.68us,107.00ns,65.27us,6.05us,63.47us,201.62us
-hyperscan/literal-casei-english-som,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613357,1000,100.68ms,82.36us,547.00ns,83.30us,2.80us,79.32us,109.00us
-hyperscan/literal-casei-english-som,count-spans,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,613357,1000,50.67ms,16.52us,30.00ns,16.97us,2.46us,16.41us,52.77us
-hyperscan/literal-casei-english-som,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613357,1000,100.69ms,63.78us,54.00ns,64.50us,2.21us,63.57us,86.09us
-hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,100.67ms,57.84us,168.00ns,58.45us,2.01us,57.13us,82.25us
-hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,613423,1000,150.90ms,86.53us,193.00ns,87.48us,2.31us,86.20us,115.48us
-hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.73ms,80.55us,77.00ns,81.37us,2.23us,80.27us,103.80us
-hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,100.70ms,56.71us,136.00ns,57.39us,2.21us,56.07us,77.20us
-hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,613423,1000,150.86ms,86.52us,205.00ns,87.35us,2.14us,86.11us,111.31us
-hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.68ms,80.65us,62.00ns,81.33us,1.65us,80.45us,92.65us
-imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,16013977,504,701.66ms,0.98ms,37.32us,0.99ms,61.93us,894.56us,1.53ms
-imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,16013977,684,651.88ms,726.01us,19.74us,731.52us,31.10us,669.44us,921.34us
-imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,16013977,344,701.92ms,1.44ms,52.50us,1.46ms,86.04us,1.22ms,1.78ms
-imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,16013977,217,752.01ms,2.30ms,43.48us,2.31ms,84.56us,2.16ms,2.91ms
-imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,16013977,109,751.81ms,4.59ms,38.65us,4.59ms,58.34us,4.46ms,4.78ms
-imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,16013977,294,752.10ms,1.70ms,53.97us,1.71ms,89.28us,1.50ms,2.01ms
-imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,16013977,10,752.05ms,51.08ms,70.90us,51.09ms,92.65us,50.98ms,51.30ms
-imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,16013977,24,752.16ms,21.35ms,14.94us,21.36ms,22.86us,21.32ms,21.41ms
-imported/sherlock/name-sherlock-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,150.83ms,106.66us,1.34us,107.82us,3.37us,102.65us,125.48us
-imported/sherlock/name-sherlock-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,100.73ms,67.83us,1.27us,68.46us,2.84us,65.48us,96.46us
-imported/sherlock/name-sherlock-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,100.74ms,72.86us,64.00ns,73.74us,2.20us,72.58us,94.93us
-imported/sherlock/name-holmes-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,100.69ms,66.41us,323.00ns,67.39us,2.77us,65.55us,105.91us
-imported/sherlock/name-holmes-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,100.74ms,83.95us,2.17us,84.52us,3.32us,80.10us,105.25us
-imported/sherlock/name-holmes-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,150.82ms,107.32us,139.00ns,108.59us,2.98us,106.73us,140.50us
-imported/sherlock/name-sherlock-holmes-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,150.88ms,116.98us,830.00ns,118.14us,2.95us,114.27us,146.41us
-imported/sherlock/name-sherlock-holmes-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,100.73ms,44.39us,287.00ns,44.88us,1.89us,43.29us,65.18us
-imported/sherlock/name-sherlock-holmes-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,100.76ms,74.67us,56.00ns,75.42us,1.93us,74.49us,92.56us
-imported/sherlock/name-alt3-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,451.48ms,383.72us,3.08us,384.72us,9.35us,370.08us,545.42us
-imported/sherlock/name-alt3-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,198,752.24ms,2.52ms,11.01us,2.53ms,26.52us,2.50ms,2.69ms
-imported/sherlock/name-alt3-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,551.82ms,489.89us,1.36us,491.13us,3.94us,484.13us,515.56us
-imported/sherlock/name-alt5-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,301.11ms,252.09us,2.42us,253.59us,6.41us,243.94us,293.22us
-imported/sherlock/name-alt5-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,360,651.89ms,1.36ms,8.17us,1.39ms,54.95us,1.34ms,1.55ms
-imported/sherlock/name-alt5-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,301.21ms,239.76us,1.95us,239.94us,3.08us,236.58us,275.08us
-imported/sherlock/the-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,969,601.91ms,516.12us,5.86us,516.06us,7.87us,501.23us,568.91us
-imported/sherlock/the-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,551.89ms,476.68us,4.97us,477.51us,8.51us,460.74us,585.79us
-imported/sherlock/the-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,551.91ms,485.49us,2.76us,488.16us,10.93us,475.23us,639.56us
-opt/prefilter/literal-casei-english,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613357,1000,100.73ms,82.39us,718.00ns,84.96us,9.80us,79.56us,269.93us
-opt/prefilter/literal-casei-english,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,613357,1000,100.81ms,44.60us,378.00ns,45.36us,2.32us,43.82us,75.42us
-opt/prefilter/literal-casei-english,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613357,1000,100.86ms,63.73us,82.00ns,64.81us,2.33us,63.47us,85.59us
-opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,100.87ms,56.93us,234.00ns,57.88us,2.81us,56.14us,85.38us
-opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,613423,1000,100.87ms,44.95us,295.00ns,46.53us,4.13us,44.49us,110.20us
-opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.89ms,80.59us,68.00ns,81.46us,2.37us,80.35us,113.22us
+curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,899232,1000,301.31ms,230.38us,2.60us,230.58us,4.27us,225.31us,268.40us
+curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,899232,1000,100.53ms,51.44us,1.42us,52.12us,2.72us,49.37us,81.93us
+curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,899232,1000,150.59ms,91.22us,1.64us,91.78us,3.07us,87.38us,120.02us
+curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,899232,1000,201.48ms,135.38us,162.00ns,136.65us,2.70us,134.89us,164.15us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,1000,250.82ms,135.34us,835.00ns,137.61us,4.76us,133.26us,173.88us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,1570556,1000,401.09ms,332.82us,5.14us,333.41us,6.75us,323.22us,379.60us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,1000,200.71ms,154.20us,611.00ns,156.56us,4.42us,152.58us,200.90us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,1000,351.08ms,290.15us,4.50us,290.04us,5.76us,283.91us,333.00us
+curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,899232,1000,601.74ms,467.16us,6.62us,470.46us,18.54us,447.16us,663.95us
+curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,899232,1000,150.86ms,99.02us,1.74us,99.80us,3.57us,95.86us,132.04us
+curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,899232,254,751.52ms,1.97ms,4.26us,1.97ms,9.11us,1.96ms,2.02ms
+curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,899232,1000,501.77ms,447.73us,2.92us,448.10us,4.93us,439.19us,477.00us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,958,802.29ms,519.81us,5.62us,521.29us,9.88us,505.08us,601.01us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,1570556,846,601.58ms,591.11us,4.11us,591.50us,7.08us,578.00us,634.69us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,54,751.90ms,9.32ms,25.34us,9.34ms,68.74us,9.25ms,9.51ms
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,335,702.13ms,1.49ms,9.85us,1.49ms,13.72us,1.46ms,1.55ms
+hyperscan/literal-casei-english-nosom,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613357,1000,150.56ms,84.32us,586.00ns,85.18us,2.65us,81.36us,107.33us
+hyperscan/literal-casei-english-nosom,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,613357,1000,50.44ms,23.66us,62.00ns,23.93us,1.48us,23.39us,43.29us
+hyperscan/literal-casei-english-nosom,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613357,1000,100.50ms,63.74us,47.00ns,64.42us,3.00us,63.54us,137.20us
+hyperscan/literal-casei-english-som,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613357,1000,151.44ms,84.21us,682.00ns,85.08us,3.07us,82.03us,120.18us
+hyperscan/literal-casei-english-som,count-spans,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,613357,1000,50.43ms,23.89us,74.00ns,24.14us,1.24us,23.68us,45.37us
+hyperscan/literal-casei-english-som,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613357,1000,100.50ms,63.84us,43.00ns,64.40us,1.80us,63.63us,87.75us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,100.49ms,29.07us,40.00ns,29.37us,1.33us,28.91us,47.52us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,613423,1000,100.46ms,83.33us,88.00ns,84.06us,2.14us,83.02us,110.33us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.46ms,80.34us,63.00ns,81.04us,2.03us,80.13us,108.34us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,100.48ms,29.05us,46.00ns,29.35us,1.36us,28.88us,45.45us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,613423,1000,100.49ms,83.21us,118.00ns,84.00us,2.04us,82.89us,103.34us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.47ms,80.39us,80.00ns,81.07us,2.09us,80.15us,120.80us
+imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,16013977,585,852.13ms,851.66us,4.23us,854.75us,10.48us,837.60us,912.77us
+imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,16013977,651,601.58ms,768.19us,4.10us,768.36us,7.23us,756.78us,804.98us
+imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,16013977,427,652.11ms,1.17ms,5.46us,1.17ms,10.37us,1.16ms,1.23ms
+imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,16013977,283,701.78ms,1.77ms,5.79us,1.77ms,11.76us,1.75ms,1.81ms
+imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,16013977,124,1.55s,4.05ms,20.36us,4.05ms,38.95us,4.00ms,4.23ms
+imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,16013977,293,701.75ms,1.71ms,6.12us,1.71ms,14.58us,1.69ms,1.78ms
+imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,16013977,10,752.17ms,51.09ms,70.31us,51.08ms,77.73us,50.97ms,51.18ms
+imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,16013977,24,751.82ms,21.05ms,45.88us,21.16ms,274.37us,20.97ms,21.88ms
+imported/sherlock/name-sherlock-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,151.35ms,104.25us,898.00ns,105.06us,2.52us,102.76us,127.65us
+imported/sherlock/name-sherlock-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,100.47ms,66.60us,851.00ns,67.34us,2.00us,65.17us,86.61us
+imported/sherlock/name-sherlock-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,101.30ms,72.57us,66.00ns,73.12us,1.45us,72.37us,87.21us
+imported/sherlock/name-holmes-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,100.44ms,47.14us,205.00ns,47.62us,1.82us,46.59us,73.31us
+imported/sherlock/name-holmes-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,100.46ms,83.04us,1.84us,83.77us,3.08us,79.93us,114.71us
+imported/sherlock/name-holmes-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,150.55ms,107.14us,233.00ns,107.92us,1.89us,106.65us,133.55us
+imported/sherlock/name-sherlock-holmes-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,150.52ms,111.72us,780.00ns,112.67us,2.57us,109.41us,140.66us
+imported/sherlock/name-sherlock-holmes-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,100.51ms,44.63us,303.00ns,45.09us,2.01us,43.61us,71.22us
+imported/sherlock/name-sherlock-holmes-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,100.49ms,74.49us,63.00ns,75.13us,1.83us,74.23us,100.06us
+imported/sherlock/name-alt3-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,451.77ms,343.98us,3.92us,345.38us,6.42us,333.35us,394.44us
+imported/sherlock/name-alt3-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,199,751.96ms,2.51ms,7.55us,2.51ms,11.67us,2.49ms,2.56ms
+imported/sherlock/name-alt3-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,551.84ms,493.52us,2.94us,493.63us,5.04us,483.49us,542.53us
+imported/sherlock/name-alt5-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,301.65ms,215.56us,1.50us,216.65us,3.52us,210.75us,249.06us
+imported/sherlock/name-alt5-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,350,651.25ms,1.42ms,56.46us,1.43ms,50.69us,1.34ms,1.56ms
+imported/sherlock/name-alt5-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,301.57ms,240.69us,1.80us,240.82us,2.40us,238.03us,258.91us
+imported/sherlock/the-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,451.09ms,369.39us,2.63us,369.56us,4.51us,354.33us,395.85us
+imported/sherlock/the-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,551.91ms,474.44us,4.47us,474.86us,6.52us,458.52us,523.16us
+imported/sherlock/the-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,551.84ms,481.89us,2.23us,482.23us,3.87us,474.34us,520.73us
+opt/prefilter/literal-casei-english,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613357,1000,150.58ms,84.16us,536.00ns,84.93us,2.49us,81.00us,108.11us
+opt/prefilter/literal-casei-english,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,613357,1000,100.52ms,44.37us,211.00ns,44.80us,1.65us,43.44us,69.47us
+opt/prefilter/literal-casei-english,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613357,1000,101.34ms,63.80us,47.00ns,64.32us,1.59us,63.57us,85.90us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,100.47ms,28.90us,41.00ns,29.18us,1.28us,28.71us,47.34us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,613423,1000,100.51ms,44.88us,81.00ns,45.28us,1.52us,44.57us,66.73us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.48ms,80.17us,65.00ns,80.81us,1.73us,79.97us,108.45us
diff --git a/audit/rebar/results/ice/rebar-audit-pass3.csv b/audit/rebar/results/ice/rebar-audit-pass3.csv
index 2a059bf..f1818b3 100644
--- a/audit/rebar/results/ice/rebar-audit-pass3.csv
+++ b/audit/rebar/results/ice/rebar-audit-pass3.csv
@@ -1,61 +1,61 @@
name,model,rebar_version,engine,engine_version,err,haystack_len,iters,total,median,mad,mean,stddev,min,max
-curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,899232,1000,351.41ms,270.65us,3.54us,272.65us,8.22us,258.84us,383.79us
-curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,899232,1000,50.70ms,35.75us,211.00ns,36.32us,1.86us,35.27us,59.10us
-curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,899232,1000,150.97ms,90.70us,1.68us,91.24us,3.12us,87.01us,110.43us
-curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,899232,1000,201.03ms,134.08us,341.00ns,135.74us,3.24us,133.43us,156.69us
-curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,1000,451.47ms,401.75us,4.07us,402.95us,9.69us,387.88us,501.96us
-curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,1570556,1000,451.60ms,381.92us,2.39us,383.06us,10.59us,373.46us,624.03us
-curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,1000,200.97ms,156.19us,1.20us,158.42us,4.28us,153.86us,183.71us
-curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,1000,351.36ms,292.97us,3.90us,295.06us,34.53us,284.91us,928.44us
-curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,899232,879,601.82ms,568.92us,9.06us,568.25us,12.04us,538.96us,603.81us
-curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,899232,1000,100.77ms,79.80us,154.00ns,80.84us,2.93us,79.19us,115.25us
-curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,899232,252,702.28ms,1.98ms,7.53us,1.99ms,18.23us,1.97ms,2.13ms
-curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,899232,1000,501.88ms,442.99us,1.80us,444.85us,8.44us,436.51us,556.30us
-curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,73,752.15ms,6.86ms,30.24us,6.91ms,121.86us,6.79ms,7.43ms
-curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,1570556,716,602.18ms,695.60us,6.27us,698.92us,18.16us,681.01us,0.96ms
-curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,54,752.43ms,9.44ms,43.39us,9.43ms,85.57us,9.30ms,9.76ms
-curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,337,702.32ms,1.48ms,9.41us,1.48ms,16.50us,1.46ms,1.63ms
-hyperscan/literal-casei-english-nosom,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613357,1000,100.77ms,82.33us,0.99us,83.24us,3.41us,79.05us,111.95us
-hyperscan/literal-casei-english-nosom,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,613357,1000,50.66ms,16.53us,29.00ns,16.86us,2.25us,16.42us,59.39us
-hyperscan/literal-casei-english-nosom,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613357,1000,100.79ms,63.72us,58.00ns,64.53us,2.45us,63.50us,83.47us
-hyperscan/literal-casei-english-som,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613357,1000,100.71ms,82.08us,448.00ns,83.21us,3.75us,79.26us,117.84us
-hyperscan/literal-casei-english-som,count-spans,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,613357,1000,50.67ms,16.54us,57.00ns,16.96us,1.95us,16.42us,55.43us
-hyperscan/literal-casei-english-som,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613357,1000,100.77ms,63.77us,54.00ns,64.48us,2.28us,63.55us,93.39us
-hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,100.73ms,57.12us,174.00ns,57.89us,2.61us,56.37us,87.57us
-hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,613423,1000,150.97ms,88.06us,189.00ns,89.15us,2.81us,87.76us,118.53us
-hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.73ms,80.44us,71.00ns,81.26us,2.22us,80.20us,107.71us
-hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,100.74ms,57.57us,179.00ns,58.57us,3.14us,56.82us,92.71us
-hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,613423,1000,150.92ms,86.59us,267.00ns,87.68us,2.47us,86.22us,110.73us
-hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.80ms,80.36us,71.00ns,81.19us,1.99us,80.11us,94.72us
-imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,16013977,438,702.17ms,1.13ms,54.98us,1.14ms,84.76us,0.96ms,1.40ms
-imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,16013977,661,652.04ms,752.37us,29.14us,756.83us,42.40us,673.47us,0.98ms
-imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,16013977,306,702.22ms,1.61ms,89.45us,1.63ms,161.37us,1.36ms,2.65ms
-imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,16013977,207,752.33ms,2.39ms,68.96us,2.42ms,119.63us,2.17ms,2.83ms
-imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,16013977,108,752.07ms,4.64ms,47.92us,4.65ms,73.29us,4.49ms,4.91ms
-imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,16013977,259,752.37ms,1.93ms,85.39us,1.94ms,121.09us,1.69ms,2.32ms
-imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,16013977,10,752.30ms,51.27ms,140.15us,51.43ms,391.38us,51.07ms,52.39ms
-imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,16013977,24,802.47ms,21.38ms,29.12us,21.40ms,72.77us,21.32ms,21.63ms
-imported/sherlock/name-sherlock-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,150.98ms,106.86us,1.06us,108.04us,4.37us,103.33us,159.96us
-imported/sherlock/name-sherlock-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,100.78ms,68.29us,1.70us,68.97us,3.19us,65.47us,99.41us
-imported/sherlock/name-sherlock-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,100.80ms,72.78us,96.00ns,73.99us,2.68us,72.54us,97.70us
-imported/sherlock/name-holmes-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,100.72ms,66.44us,352.00ns,67.92us,5.43us,65.64us,181.28us
-imported/sherlock/name-holmes-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,100.88ms,83.95us,2.20us,84.57us,3.31us,80.18us,103.18us
-imported/sherlock/name-holmes-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,150.90ms,107.25us,139.00ns,108.30us,2.31us,106.68us,130.40us
-imported/sherlock/name-sherlock-holmes-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,150.81ms,116.84us,1.09us,118.64us,6.74us,113.88us,240.37us
-imported/sherlock/name-sherlock-holmes-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,100.82ms,44.60us,314.00ns,46.21us,5.96us,43.72us,159.05us
-imported/sherlock/name-sherlock-holmes-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,100.80ms,74.25us,62.00ns,75.12us,2.19us,74.06us,97.05us
-imported/sherlock/name-alt3-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,451.57ms,383.22us,3.41us,383.75us,6.35us,369.55us,471.80us
-imported/sherlock/name-alt3-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,199,752.42ms,2.52ms,9.27us,2.52ms,16.56us,2.50ms,2.62ms
-imported/sherlock/name-alt3-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,551.91ms,491.73us,4.53us,491.86us,6.34us,482.86us,548.69us
-imported/sherlock/name-alt5-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,301.29ms,248.15us,4.48us,248.27us,7.00us,232.61us,282.32us
-imported/sherlock/name-alt5-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,364,652.12ms,1.37ms,7.28us,1.37ms,19.95us,1.35ms,1.45ms
-imported/sherlock/name-alt5-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,301.30ms,240.14us,783.00ns,242.57us,7.45us,238.89us,401.56us
-imported/sherlock/the-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,965,602.04ms,516.38us,4.60us,518.18us,10.03us,502.00us,635.03us
-imported/sherlock/the-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,551.88ms,479.54us,5.72us,479.90us,8.53us,460.87us,583.36us
-imported/sherlock/the-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,551.86ms,485.11us,2.10us,485.63us,4.22us,474.59us,521.96us
-opt/prefilter/literal-casei-english,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613357,1000,100.78ms,82.43us,1.07us,83.35us,3.18us,79.08us,124.11us
-opt/prefilter/literal-casei-english,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,613357,1000,100.73ms,44.26us,199.00ns,44.85us,2.42us,43.35us,76.15us
-opt/prefilter/literal-casei-english,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613357,1000,100.78ms,63.72us,58.00ns,64.38us,1.89us,63.53us,83.48us
-opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,100.73ms,56.66us,308.00ns,58.25us,5.15us,55.85us,149.34us
-opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,613423,1000,100.82ms,44.83us,84.00ns,45.35us,1.89us,44.50us,68.20us
-opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.81ms,80.22us,58.00ns,80.98us,2.02us,80.01us,101.75us
+curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,899232,1000,301.29ms,229.75us,2.60us,230.02us,4.15us,224.93us,265.25us
+curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,899232,1000,100.50ms,51.31us,424.00ns,51.85us,1.98us,49.92us,80.06us
+curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,899232,1000,150.58ms,90.40us,1.41us,90.67us,2.70us,86.95us,121.63us
+curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,899232,1000,200.63ms,134.12us,240.00ns,135.30us,2.39us,133.44us,158.66us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,1000,251.26ms,136.18us,1.31us,138.08us,4.67us,133.38us,179.85us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,1570556,1000,401.09ms,332.04us,4.33us,332.14us,6.14us,323.30us,364.49us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,1000,201.62ms,156.35us,1.04us,158.38us,3.92us,154.52us,191.46us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,1000,351.11ms,289.54us,4.99us,289.47us,7.69us,282.82us,438.75us
+curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,899232,1000,602.11ms,465.86us,5.08us,467.63us,9.20us,447.13us,553.14us
+curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,899232,1000,150.60ms,99.52us,1.86us,99.96us,3.21us,95.95us,138.93us
+curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,899232,252,751.52ms,1.99ms,5.54us,1.99ms,11.70us,1.97ms,2.08ms
+curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,899232,1000,501.63ms,442.01us,1.96us,442.63us,6.05us,434.39us,542.58us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,962,802.14ms,518.09us,2.74us,519.23us,6.40us,505.24us,550.78us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,1570556,837,601.76ms,594.00us,3.92us,597.87us,15.46us,582.55us,846.85us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,54,752.27ms,9.31ms,26.35us,9.35ms,73.45us,9.27ms,9.56ms
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,335,701.65ms,1.49ms,10.35us,1.49ms,13.51us,1.47ms,1.55ms
+hyperscan/literal-casei-english-nosom,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613357,1000,151.43ms,84.20us,589.00ns,84.98us,2.60us,81.23us,107.54us
+hyperscan/literal-casei-english-nosom,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,613357,1000,50.42ms,23.91us,73.00ns,24.18us,1.43us,23.67us,45.51us
+hyperscan/literal-casei-english-nosom,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613357,1000,100.50ms,63.79us,49.00ns,64.38us,1.88us,63.54us,86.10us
+hyperscan/literal-casei-english-som,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613357,1000,150.62ms,84.72us,1.03us,86.39us,6.17us,81.45us,202.51us
+hyperscan/literal-casei-english-som,count-spans,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,613357,1000,50.45ms,24.02us,81.00ns,24.26us,1.28us,23.70us,42.72us
+hyperscan/literal-casei-english-som,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613357,1000,100.52ms,63.84us,46.00ns,64.44us,1.93us,63.62us,90.88us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,100.50ms,29.04us,94.00ns,29.42us,1.72us,28.81us,53.12us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,613423,1000,100.50ms,81.62us,90.00ns,82.34us,2.00us,81.40us,109.45us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.48ms,80.35us,82.00ns,81.01us,1.77us,80.11us,99.95us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,100.48ms,29.10us,118.00ns,29.43us,1.51us,28.83us,45.05us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,613423,1000,100.48ms,81.66us,96.00ns,82.42us,2.24us,81.40us,113.68us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.48ms,81.06us,856.00ns,85.41us,27.13us,79.93us,331.75us
+imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,16013977,596,854.32ms,832.64us,9.67us,839.13us,23.62us,809.49us,1.05ms
+imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,16013977,648,601.50ms,770.33us,4.78us,771.70us,9.68us,757.72us,833.53us
+imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,16013977,427,652.13ms,1.17ms,6.76us,1.17ms,14.89us,1.15ms,1.25ms
+imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,16013977,283,701.79ms,1.77ms,6.21us,1.77ms,13.91us,1.75ms,1.83ms
+imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,16013977,124,1.60s,4.04ms,24.21us,4.05ms,52.36us,3.98ms,4.42ms
+imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,16013977,290,702.25ms,1.72ms,9.30us,1.73ms,69.53us,1.69ms,2.75ms
+imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,16013977,10,751.84ms,51.07ms,25.82us,51.08ms,47.81us,51.03ms,51.20ms
+imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,16013977,24,752.19ms,21.32ms,193.11us,21.54ms,365.82us,21.09ms,21.98ms
+imported/sherlock/name-sherlock-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,150.49ms,105.42us,1.05us,106.61us,6.22us,103.02us,275.61us
+imported/sherlock/name-sherlock-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,100.48ms,68.87us,1.60us,69.33us,2.55us,66.10us,93.69us
+imported/sherlock/name-sherlock-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,100.48ms,73.03us,478.00ns,74.00us,3.53us,72.37us,157.11us
+imported/sherlock/name-holmes-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,100.50ms,46.88us,162.00ns,47.51us,2.43us,46.43us,74.19us
+imported/sherlock/name-holmes-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,100.47ms,83.28us,1.93us,83.92us,2.98us,79.85us,102.89us
+imported/sherlock/name-holmes-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,150.55ms,108.94us,171.00ns,109.78us,1.89us,108.51us,127.64us
+imported/sherlock/name-sherlock-holmes-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,150.54ms,111.48us,938.00ns,112.27us,2.60us,109.86us,142.49us
+imported/sherlock/name-sherlock-holmes-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,100.49ms,44.80us,220.00ns,45.21us,1.74us,43.90us,70.99us
+imported/sherlock/name-sherlock-holmes-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,100.51ms,74.56us,59.00ns,75.17us,1.72us,74.34us,100.03us
+imported/sherlock/name-alt3-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,451.11ms,341.56us,3.18us,343.23us,6.48us,331.67us,371.88us
+imported/sherlock/name-alt3-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,199,752.19ms,2.51ms,8.06us,2.52ms,17.27us,2.49ms,2.64ms
+imported/sherlock/name-alt3-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,551.90ms,495.39us,2.60us,497.21us,21.96us,486.15us,938.75us
+imported/sherlock/name-alt5-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,301.57ms,215.16us,1.60us,216.23us,3.08us,210.14us,238.04us
+imported/sherlock/name-alt5-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,351,652.05ms,1.47ms,31.88us,1.42ms,67.04us,1.34ms,1.52ms
+imported/sherlock/name-alt5-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,301.65ms,251.53us,1.71us,251.80us,2.61us,249.03us,278.50us
+imported/sherlock/the-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,451.03ms,369.85us,2.56us,370.24us,5.01us,355.53us,411.16us
+imported/sherlock/the-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,551.98ms,480.69us,4.76us,481.08us,7.00us,458.00us,524.55us
+imported/sherlock/the-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,551.12ms,485.11us,2.50us,485.14us,4.11us,473.96us,535.65us
+opt/prefilter/literal-casei-english,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613357,1000,150.57ms,84.17us,690.00ns,84.80us,2.29us,82.13us,113.31us
+opt/prefilter/literal-casei-english,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,613357,1000,100.52ms,44.40us,214.00ns,44.76us,1.47us,43.26us,65.48us
+opt/prefilter/literal-casei-english,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613357,1000,100.49ms,63.79us,47.00ns,64.35us,1.74us,63.53us,85.38us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,100.49ms,29.11us,61.00ns,29.39us,1.36us,28.83us,44.10us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,613423,1000,100.48ms,44.91us,98.00ns,45.28us,1.45us,44.51us,62.40us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,101.33ms,80.21us,52.00ns,80.83us,1.60us,80.00us,101.78us
diff --git a/audit/rebar/results/spr/rebar-audit-pass1.csv b/audit/rebar/results/spr/rebar-audit-pass1.csv
index 04553c9..145184a 100644
--- a/audit/rebar/results/spr/rebar-audit-pass1.csv
+++ b/audit/rebar/results/spr/rebar-audit-pass1.csv
@@ -1,61 +1,61 @@
name,model,rebar_version,engine,engine_version,err,haystack_len,iters,total,median,mad,mean,stddev,min,max
-curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,899232,1000,300.90ms,249.51us,3.18us,249.46us,4.89us,241.52us,308.46us
-curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,899232,1000,50.46ms,28.71us,79.00ns,29.39us,1.71us,28.48us,48.54us
-curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,899232,1000,100.51ms,81.80us,1.48us,82.14us,3.23us,77.84us,134.79us
-curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,899232,1000,200.77ms,137.64us,100.00ns,138.88us,2.29us,137.38us,150.56us
-curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,1000,401.01ms,325.61us,3.88us,326.49us,8.87us,315.99us,522.67us
-curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,1570556,1000,451.20ms,364.62us,2.65us,364.34us,5.21us,354.90us,426.04us
-curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,1000,200.77ms,132.21us,369.00ns,133.62us,2.80us,131.17us,159.42us
-curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,1000,351.02ms,300.33us,2.70us,300.09us,3.43us,296.15us,334.27us
-curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,899232,954,601.27ms,523.48us,2.86us,524.08us,4.58us,514.54us,548.46us
-curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,899232,1000,100.57ms,74.60us,174.00ns,75.44us,2.09us,73.97us,89.85us
-curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,899232,220,751.66ms,2.27ms,9.21us,2.28ms,16.67us,2.25ms,2.36ms
-curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,899232,1000,551.32ms,482.36us,1.89us,482.32us,3.36us,474.16us,511.16us
-curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,79,751.58ms,6.34ms,16.25us,6.34ms,20.70us,6.31ms,6.40ms
-curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,1570556,725,601.49ms,690.42us,4.44us,690.32us,8.15us,673.35us,730.61us
-curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,54,751.52ms,9.40ms,22.82us,9.40ms,38.19us,9.34ms,9.50ms
-curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,305,701.54ms,1.64ms,9.50us,1.64ms,13.66us,1.62ms,1.69ms
-hyperscan/literal-casei-english-nosom,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613357,1000,100.52ms,83.93us,1.14us,85.30us,3.68us,81.02us,107.85us
-hyperscan/literal-casei-english-nosom,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,613357,1000,50.42ms,13.13us,23.00ns,13.30us,1.14us,13.04us,26.39us
-hyperscan/literal-casei-english-nosom,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613357,1000,100.52ms,66.18us,46.00ns,66.85us,1.80us,66.03us,79.42us
-hyperscan/literal-casei-english-som,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613357,1000,100.50ms,84.30us,1.29us,85.91us,3.96us,81.20us,111.89us
-hyperscan/literal-casei-english-som,count-spans,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,613357,1000,50.48ms,13.18us,23.00ns,13.44us,1.64us,13.09us,38.28us
-hyperscan/literal-casei-english-som,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613357,1000,100.57ms,66.27us,43.00ns,66.90us,1.71us,66.07us,80.07us
-hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,100.54ms,48.57us,336.00ns,49.72us,4.26us,47.89us,150.32us
-hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,613423,1000,150.65ms,94.75us,777.00ns,95.62us,2.54us,93.05us,115.13us
-hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.55ms,83.34us,120.00ns,84.16us,2.05us,83.05us,104.38us
-hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,100.53ms,46.51us,317.00ns,47.32us,2.51us,45.75us,69.91us
-hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,613423,1000,150.70ms,93.33us,434.00ns,94.61us,3.07us,92.44us,121.72us
-hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.57ms,83.32us,81.00ns,84.12us,2.07us,83.03us,114.19us
-imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,16013977,651,651.35ms,768.72us,5.60us,767.89us,11.54us,747.52us,910.89us
-imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,16013977,766,601.35ms,651.26us,3.91us,653.17us,6.02us,645.06us,684.64us
-imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,16013977,525,651.33ms,0.95ms,1.42us,0.95ms,4.95us,939.80us,0.99ms
-imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,16013977,276,751.60ms,1.82ms,2.88us,1.82ms,6.61us,1.81ms,1.85ms
-imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,16013977,122,751.87ms,4.10ms,26.43us,4.10ms,39.67us,4.03ms,4.20ms
-imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,16013977,379,701.38ms,1.32ms,6.47us,1.32ms,9.05us,1.30ms,1.35ms
-imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,16013977,9,751.45ms,55.67ms,196.30us,55.79ms,251.50us,55.47ms,56.24ms
-imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,16013977,25,751.54ms,20.45ms,30.15us,20.46ms,45.03us,20.38ms,20.56ms
-imported/sherlock/name-sherlock-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,150.62ms,105.11us,1.21us,106.28us,2.84us,102.53us,128.34us
-imported/sherlock/name-sherlock-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,100.55ms,57.63us,799.00ns,58.51us,2.14us,56.31us,72.83us
-imported/sherlock/name-sherlock-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,100.57ms,75.43us,59.00ns,76.17us,1.88us,75.23us,91.51us
-imported/sherlock/name-holmes-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,100.57ms,57.06us,356.00ns,57.95us,2.54us,56.22us,76.29us
-imported/sherlock/name-holmes-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,100.54ms,75.27us,1.92us,76.18us,3.06us,72.17us,102.23us
-imported/sherlock/name-holmes-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,150.59ms,111.64us,144.00ns,112.67us,2.11us,111.23us,125.91us
-imported/sherlock/name-sherlock-holmes-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,150.57ms,114.06us,1.27us,115.48us,3.37us,111.68us,136.14us
-imported/sherlock/name-sherlock-holmes-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,50.43ms,37.57us,307.00ns,38.00us,1.43us,37.10us,51.77us
-imported/sherlock/name-sherlock-holmes-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,100.49ms,77.02us,58.00ns,77.85us,2.19us,76.80us,97.84us
-imported/sherlock/name-alt3-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,451.03ms,367.60us,4.53us,368.47us,5.65us,359.75us,402.29us
-imported/sherlock/name-alt3-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,182,751.60ms,2.76ms,6.96us,2.76ms,10.93us,2.73ms,2.80ms
-imported/sherlock/name-alt3-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,935,601.39ms,534.29us,2.53us,534.67us,4.55us,526.15us,597.05us
-imported/sherlock/name-alt5-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,300.88ms,229.16us,3.43us,230.23us,4.86us,223.26us,252.22us
-imported/sherlock/name-alt5-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,336,651.48ms,1.48ms,9.32us,1.49ms,20.23us,1.46ms,1.61ms
-imported/sherlock/name-alt5-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,300.91ms,242.14us,1.59us,243.46us,2.78us,239.96us,270.20us
-imported/sherlock/the-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,501.22ms,421.04us,4.19us,421.25us,5.32us,411.59us,451.45us
-imported/sherlock/the-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,551.31ms,466.87us,4.17us,466.77us,5.91us,452.16us,504.08us
-imported/sherlock/the-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,964,601.47ms,518.06us,2.04us,518.64us,3.83us,508.98us,539.31us
-opt/prefilter/literal-casei-english,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613357,1000,100.55ms,84.16us,1.19us,86.31us,4.77us,81.34us,106.92us
-opt/prefilter/literal-casei-english,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,613357,1000,50.43ms,37.68us,224.00ns,38.13us,1.79us,37.11us,57.85us
-opt/prefilter/literal-casei-english,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613357,1000,100.50ms,66.31us,41.00ns,66.99us,1.90us,66.09us,83.70us
-opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,100.52ms,45.98us,109.00ns,46.78us,2.79us,45.60us,78.98us
-opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,613423,1000,50.48ms,36.19us,124.00ns,36.57us,1.44us,35.80us,50.58us
-opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.52ms,83.52us,100.00ns,84.31us,1.89us,83.20us,94.50us
+curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,899232,1000,301.60ms,224.90us,3.10us,225.94us,4.78us,217.60us,262.00us
+curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,899232,1000,100.45ms,45.60us,125.00ns,46.06us,1.72us,45.26us,66.77us
+curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,899232,1000,100.43ms,82.11us,1.54us,82.51us,3.71us,78.32us,159.32us
+curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,899232,1000,200.54ms,137.48us,121.00ns,138.80us,2.63us,137.10us,166.15us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,1000,200.57ms,121.23us,1.15us,122.63us,3.51us,118.62us,157.68us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,1570556,1000,401.59ms,331.85us,4.30us,333.24us,7.73us,320.06us,394.87us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,1000,200.56ms,135.59us,2.48us,135.65us,3.83us,131.66us,175.40us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,1000,350.76ms,301.18us,2.08us,300.60us,3.96us,296.29us,340.88us
+curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,899232,1000,601.32ms,457.61us,3.66us,458.09us,6.22us,441.59us,488.69us
+curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,899232,1000,150.50ms,93.14us,1.94us,94.33us,3.70us,90.26us,129.78us
+curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,899232,220,751.44ms,2.27ms,5.85us,2.28ms,12.88us,2.26ms,2.35ms
+curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,899232,1000,550.92ms,484.11us,3.48us,484.67us,5.45us,473.76us,510.56us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,951,751.99ms,528.00us,4.65us,525.60us,14.00us,479.15us,600.82us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,1570556,802,601.03ms,622.70us,3.25us,623.94us,6.56us,610.40us,658.74us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,54,751.33ms,9.37ms,19.63us,9.38ms,27.89us,9.34ms,9.45ms
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,304,701.21ms,1.65ms,12.62us,1.65ms,17.84us,1.62ms,1.73ms
+hyperscan/literal-casei-english-nosom,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613357,1000,150.49ms,88.12us,2.17us,88.73us,4.33us,82.87us,116.78us
+hyperscan/literal-casei-english-nosom,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,613357,1000,50.38ms,20.39us,74.00ns,20.65us,1.48us,20.17us,47.62us
+hyperscan/literal-casei-english-nosom,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613357,1000,100.41ms,66.17us,44.00ns,66.80us,1.78us,65.99us,86.75us
+hyperscan/literal-casei-english-som,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613357,1000,150.51ms,88.08us,1.60us,88.72us,4.26us,82.99us,118.78us
+hyperscan/literal-casei-english-som,count-spans,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,613357,1000,50.35ms,19.84us,45.00ns,20.05us,1.12us,19.64us,33.82us
+hyperscan/literal-casei-english-som,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613357,1000,100.39ms,66.28us,54.00ns,66.90us,1.77us,66.12us,87.51us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,50.33ms,24.44us,180.00ns,24.79us,1.82us,24.01us,39.90us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,613423,1000,100.43ms,83.44us,130.00ns,84.44us,2.76us,83.14us,124.57us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.39ms,83.51us,267.00ns,84.22us,2.39us,82.98us,124.75us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,50.35ms,23.81us,89.00ns,24.25us,1.89us,23.53us,41.99us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,613423,1000,100.42ms,83.40us,63.00ns,84.39us,2.94us,83.21us,134.71us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.46ms,83.45us,124.00ns,84.30us,2.10us,83.15us,104.70us
+imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,16013977,698,802.39ms,714.22us,5.50us,716.04us,9.21us,704.79us,763.32us
+imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,16013977,757,601.18ms,657.95us,3.02us,660.49us,7.19us,647.10us,707.61us
+imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,16013977,546,652.23ms,914.25us,2.02us,915.71us,8.04us,900.92us,0.96ms
+imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,16013977,276,752.31ms,1.82ms,1.83us,1.82ms,7.26us,1.81ms,1.85ms
+imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,16013977,127,1.55s,3.97ms,35.36us,3.96ms,53.57us,3.82ms,4.07ms
+imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,16013977,267,752.33ms,1.87ms,2.77us,1.88ms,9.00us,1.86ms,1.92ms
+imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,16013977,9,751.28ms,55.80ms,173.53us,55.81ms,242.38us,55.42ms,56.17ms
+imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,16013977,25,752.04ms,20.50ms,43.24us,20.51ms,71.21us,20.41ms,20.65ms
+imported/sherlock/name-sherlock-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,150.45ms,106.44us,0.97us,107.19us,3.29us,101.73us,127.77us
+imported/sherlock/name-sherlock-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,100.43ms,58.00us,849.00ns,59.02us,2.79us,56.31us,104.48us
+imported/sherlock/name-sherlock-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,101.32ms,75.48us,51.00ns,76.21us,2.00us,75.30us,98.19us
+imported/sherlock/name-holmes-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,100.42ms,41.33us,241.00ns,42.05us,2.58us,40.69us,66.12us
+imported/sherlock/name-holmes-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,100.41ms,75.71us,2.16us,76.45us,3.32us,72.18us,95.33us
+imported/sherlock/name-holmes-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,150.50ms,111.70us,127.00ns,112.75us,2.35us,111.35us,134.26us
+imported/sherlock/name-sherlock-holmes-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,151.38ms,113.06us,2.14us,113.55us,3.38us,109.03us,139.35us
+imported/sherlock/name-sherlock-holmes-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,50.35ms,37.62us,147.00ns,38.11us,1.56us,37.16us,53.82us
+imported/sherlock/name-sherlock-holmes-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,100.41ms,77.11us,65.00ns,77.86us,2.05us,76.88us,100.93us
+imported/sherlock/name-alt3-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,450.97ms,341.03us,2.82us,341.35us,5.72us,331.75us,403.81us
+imported/sherlock/name-alt3-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,182,751.17ms,2.76ms,7.17us,2.76ms,11.91us,2.73ms,2.80ms
+imported/sherlock/name-alt3-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,929,601.00ms,537.85us,3.18us,538.20us,5.14us,527.14us,571.18us
+imported/sherlock/name-alt5-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,300.62ms,206.15us,2.44us,206.67us,4.01us,199.53us,235.83us
+imported/sherlock/name-alt5-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,338,651.96ms,1.48ms,8.46us,1.48ms,19.71us,1.46ms,1.57ms
+imported/sherlock/name-alt5-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,300.68ms,243.73us,2.17us,243.60us,2.84us,240.26us,260.00us
+imported/sherlock/the-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,350.71ms,308.06us,3.61us,308.06us,5.34us,293.22us,327.35us
+imported/sherlock/the-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,550.98ms,466.40us,4.59us,466.63us,6.87us,452.80us,539.57us
+imported/sherlock/the-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,967,601.06ms,516.38us,2.56us,517.18us,4.63us,505.86us,556.26us
+opt/prefilter/literal-casei-english,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613357,1000,150.49ms,88.22us,1.71us,89.38us,4.88us,82.64us,110.76us
+opt/prefilter/literal-casei-english,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,613357,1000,50.35ms,37.66us,171.00ns,38.05us,1.36us,37.26us,50.87us
+opt/prefilter/literal-casei-english,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613357,1000,100.40ms,66.30us,46.00ns,66.91us,1.81us,66.13us,84.88us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,50.34ms,24.82us,57.00ns,25.24us,1.75us,24.51us,42.98us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,613423,1000,50.32ms,36.07us,83.00ns,36.53us,1.94us,35.81us,61.68us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.37ms,83.42us,70.00ns,84.21us,2.08us,83.14us,102.96us
diff --git a/audit/rebar/results/spr/rebar-audit-pass2.csv b/audit/rebar/results/spr/rebar-audit-pass2.csv
index 3450e72..0b15051 100644
--- a/audit/rebar/results/spr/rebar-audit-pass2.csv
+++ b/audit/rebar/results/spr/rebar-audit-pass2.csv
@@ -1,61 +1,61 @@
name,model,rebar_version,engine,engine_version,err,haystack_len,iters,total,median,mad,mean,stddev,min,max
-curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,899232,1000,300.84ms,250.14us,3.08us,250.44us,4.58us,242.16us,275.78us
-curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,899232,1000,50.46ms,28.08us,96.00ns,28.56us,1.70us,27.88us,51.40us
-curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,899232,1000,100.51ms,82.08us,1.62us,82.39us,2.71us,78.19us,106.63us
-curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,899232,1000,200.70ms,137.64us,101.00ns,138.91us,2.44us,137.31us,158.59us
-curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,1000,401.06ms,328.53us,4.88us,329.91us,6.83us,317.93us,405.88us
-curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,1570556,1000,451.14ms,363.22us,2.42us,363.10us,4.54us,354.49us,391.87us
-curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,1000,200.78ms,132.84us,442.00ns,134.17us,2.70us,131.90us,150.09us
-curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,1000,350.94ms,297.83us,668.00ns,300.81us,4.53us,296.85us,347.91us
-curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,899232,958,601.32ms,522.83us,4.77us,521.83us,6.95us,508.50us,552.73us
-curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,899232,1000,100.55ms,74.91us,343.00ns,75.77us,3.18us,74.09us,149.47us
-curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,899232,220,751.50ms,2.27ms,7.73us,2.27ms,11.68us,2.25ms,2.33ms
-curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,899232,1000,551.28ms,483.40us,4.07us,483.44us,5.12us,474.99us,528.43us
-curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,87,751.41ms,5.79ms,14.19us,5.80ms,22.82us,5.74ms,5.88ms
-curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,1570556,727,601.32ms,687.63us,4.12us,688.48us,6.67us,675.10us,720.30us
-curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,53,751.52ms,9.43ms,25.25us,9.44ms,51.12us,9.36ms,9.67ms
-curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,304,701.41ms,1.64ms,9.32us,1.65ms,13.76us,1.62ms,1.69ms
-hyperscan/literal-casei-english-nosom,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613357,1000,150.63ms,86.44us,3.45us,89.54us,6.58us,81.64us,106.80us
-hyperscan/literal-casei-english-nosom,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,613357,1000,50.49ms,13.12us,26.00ns,13.29us,1.10us,13.01us,26.50us
-hyperscan/literal-casei-english-nosom,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613357,1000,100.50ms,66.30us,44.00ns,66.95us,1.78us,66.12us,78.80us
-hyperscan/literal-casei-english-som,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613357,1000,100.50ms,85.45us,1.91us,87.33us,5.22us,80.96us,107.56us
-hyperscan/literal-casei-english-som,count-spans,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,613357,1000,50.45ms,13.10us,27.00ns,13.39us,1.73us,12.99us,38.11us
-hyperscan/literal-casei-english-som,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613357,1000,100.55ms,66.37us,54.00ns,67.05us,1.87us,66.19us,84.70us
-hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,100.58ms,48.95us,360.00ns,49.82us,2.60us,48.19us,67.66us
-hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,613423,1000,150.62ms,95.18us,911.00ns,96.29us,3.14us,93.07us,120.43us
-hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.55ms,83.20us,140.00ns,84.10us,1.86us,82.94us,95.39us
-hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,100.58ms,46.75us,362.00ns,47.84us,3.09us,45.99us,70.37us
-hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,613423,1000,150.74ms,94.35us,650.00ns,95.33us,2.33us,92.92us,105.19us
-hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.53ms,83.93us,138.00ns,84.79us,1.97us,83.56us,99.94us
-imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,16013977,652,651.35ms,768.65us,2.13us,766.11us,10.99us,744.44us,924.57us
-imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,16013977,770,601.42ms,647.70us,3.49us,649.47us,5.95us,642.25us,711.45us
-imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,16013977,525,651.46ms,0.95ms,1.27us,0.95ms,6.06us,939.72us,1.04ms
-imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,16013977,275,751.62ms,1.82ms,2.54us,1.82ms,7.38us,1.81ms,1.87ms
-imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,16013977,123,751.50ms,4.08ms,25.10us,4.09ms,47.87us,4.00ms,4.36ms
-imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,16013977,378,701.50ms,1.32ms,7.67us,1.33ms,16.66us,1.30ms,1.48ms
-imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,16013977,9,751.52ms,55.67ms,122.07us,55.69ms,151.82us,55.49ms,55.96ms
-imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,16013977,25,751.49ms,20.48ms,36.88us,20.52ms,75.97us,20.43ms,20.74ms
-imported/sherlock/name-sherlock-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,150.63ms,105.21us,1.15us,106.92us,10.08us,102.73us,393.65us
-imported/sherlock/name-sherlock-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,100.53ms,57.91us,1.08us,58.88us,2.92us,56.26us,107.56us
-imported/sherlock/name-sherlock-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,100.54ms,75.42us,59.00ns,76.19us,1.93us,75.22us,90.10us
-imported/sherlock/name-holmes-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,100.52ms,56.98us,273.00ns,57.86us,2.50us,56.30us,75.27us
-imported/sherlock/name-holmes-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,100.54ms,75.71us,2.22us,76.38us,3.44us,72.22us,96.56us
-imported/sherlock/name-holmes-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,150.66ms,111.53us,145.00ns,112.58us,2.19us,111.04us,129.52us
-imported/sherlock/name-sherlock-holmes-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,150.58ms,113.90us,1.26us,115.53us,3.91us,111.80us,147.53us
-imported/sherlock/name-sherlock-holmes-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,50.44ms,37.83us,248.00ns,38.27us,1.61us,37.26us,51.23us
-imported/sherlock/name-sherlock-holmes-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,100.48ms,77.03us,60.00ns,77.85us,2.15us,76.82us,103.75us
-imported/sherlock/name-alt3-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,451.01ms,367.56us,4.64us,369.79us,33.26us,359.31us,1.40ms
-imported/sherlock/name-alt3-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,181,751.56ms,2.76ms,6.64us,2.77ms,132.69us,2.73ms,4.33ms
-imported/sherlock/name-alt3-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,935,601.30ms,534.63us,2.01us,534.99us,3.96us,526.39us,576.48us
-imported/sherlock/name-alt5-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,300.96ms,229.45us,3.20us,230.68us,20.97us,223.37us,869.86us
-imported/sherlock/name-alt5-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,337,701.46ms,1.48ms,8.87us,1.49ms,17.28us,1.46ms,1.57ms
-imported/sherlock/name-alt5-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,300.92ms,243.88us,2.47us,245.23us,30.03us,240.18us,1.04ms
-imported/sherlock/the-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,501.13ms,420.46us,2.28us,421.00us,4.52us,410.98us,446.35us
-imported/sherlock/the-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,551.33ms,470.53us,4.13us,470.79us,6.05us,454.88us,511.67us
-imported/sherlock/the-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,963,601.38ms,518.84us,3.38us,519.47us,5.31us,508.10us,543.09us
-opt/prefilter/literal-casei-english,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613357,1000,100.52ms,84.20us,1.74us,86.37us,4.58us,81.19us,103.02us
-opt/prefilter/literal-casei-english,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,613357,1000,50.43ms,37.89us,195.00ns,38.25us,1.42us,37.27us,50.60us
-opt/prefilter/literal-casei-english,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613357,1000,100.55ms,66.18us,45.00ns,66.84us,1.92us,66.01us,92.12us
-opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,100.58ms,48.02us,558.00ns,48.97us,2.92us,47.09us,68.14us
-opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,613423,1000,50.45ms,36.14us,71.00ns,36.58us,1.62us,35.86us,53.27us
-opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.63ms,83.25us,86.00ns,84.19us,3.13us,82.96us,145.94us
+curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,899232,1000,301.19ms,224.89us,2.98us,225.66us,4.46us,216.92us,243.16us
+curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,899232,1000,100.46ms,45.78us,119.00ns,46.25us,2.04us,45.26us,73.25us
+curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,899232,1000,100.46ms,81.97us,1.68us,82.21us,2.71us,78.23us,104.01us
+curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,899232,1000,200.54ms,137.77us,119.00ns,139.00us,2.31us,137.43us,153.29us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,1000,201.07ms,124.24us,4.10us,126.25us,10.05us,118.43us,225.21us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,1570556,1000,400.83ms,333.09us,4.48us,334.78us,8.51us,322.57us,445.24us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,1000,200.57ms,135.41us,744.00ns,136.59us,2.85us,134.00us,158.31us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,1000,351.65ms,300.81us,1.99us,300.19us,3.76us,295.93us,331.79us
+curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,899232,1000,601.89ms,456.95us,3.54us,457.68us,7.56us,444.02us,589.24us
+curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,899232,1000,150.54ms,95.25us,2.33us,96.08us,4.17us,90.81us,133.62us
+curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,899232,220,751.12ms,2.27ms,6.04us,2.28ms,10.20us,2.25ms,2.31ms
+curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,899232,1000,550.98ms,481.75us,2.79us,482.38us,4.72us,472.86us,513.94us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,979,752.15ms,518.35us,20.21us,510.31us,20.44us,480.74us,554.06us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,1570556,798,601.86ms,623.98us,3.96us,626.64us,20.50us,609.17us,0.97ms
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,54,752.05ms,9.40ms,16.17us,9.42ms,84.63us,9.36ms,9.97ms
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,304,701.11ms,1.64ms,8.88us,1.64ms,14.10us,1.62ms,1.71ms
+hyperscan/literal-casei-english-nosom,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613357,1000,151.30ms,88.09us,1.55us,88.70us,4.12us,82.82us,111.94us
+hyperscan/literal-casei-english-nosom,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,613357,1000,50.37ms,20.61us,74.00ns,20.87us,1.37us,20.39us,37.98us
+hyperscan/literal-casei-english-nosom,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613357,1000,100.40ms,66.17us,46.00ns,66.81us,1.91us,66.01us,87.37us
+hyperscan/literal-casei-english-som,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613357,1000,150.51ms,88.00us,2.68us,89.83us,5.89us,83.01us,114.44us
+hyperscan/literal-casei-english-som,count-spans,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,613357,1000,50.37ms,20.10us,60.00ns,20.37us,1.34us,19.93us,37.74us
+hyperscan/literal-casei-english-som,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613357,1000,100.42ms,66.23us,45.00ns,66.96us,2.13us,66.06us,92.49us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,50.33ms,24.27us,124.00ns,24.71us,1.79us,23.98us,38.90us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,613423,1000,100.40ms,83.34us,54.00ns,84.23us,2.24us,83.19us,103.35us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.40ms,83.04us,70.00ns,83.91us,2.46us,82.82us,123.50us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,50.36ms,24.36us,101.00ns,24.77us,1.76us,24.07us,39.11us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,613423,1000,100.44ms,83.39us,65.00ns,84.37us,2.75us,83.18us,114.56us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.43ms,83.50us,64.00ns,84.31us,2.10us,83.28us,103.93us
+imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,16013977,700,802.18ms,712.98us,6.43us,713.44us,8.94us,701.75us,753.99us
+imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,16013977,757,601.24ms,658.18us,3.34us,660.55us,7.12us,646.91us,707.40us
+imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,16013977,546,652.15ms,914.55us,1.38us,915.59us,8.06us,901.82us,0.97ms
+imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,16013977,275,752.33ms,1.82ms,4.50us,1.82ms,7.78us,1.81ms,1.85ms
+imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,16013977,127,1.55s,3.95ms,39.87us,3.95ms,48.74us,3.81ms,4.05ms
+imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,16013977,267,751.42ms,1.88ms,2.48us,1.88ms,8.81us,1.86ms,1.92ms
+imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,16013977,9,752.02ms,55.97ms,83.66us,55.93ms,182.66us,55.48ms,56.10ms
+imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,16013977,25,751.31ms,20.45ms,47.37us,20.49ms,160.46us,20.37ms,21.20ms
+imported/sherlock/name-sherlock-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,151.38ms,106.33us,2.28us,106.48us,3.36us,101.44us,124.57us
+imported/sherlock/name-sherlock-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,100.43ms,57.62us,845.00ns,58.67us,2.71us,56.15us,84.03us
+imported/sherlock/name-sherlock-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,101.32ms,75.49us,53.00ns,76.23us,2.04us,75.31us,97.64us
+imported/sherlock/name-holmes-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,100.45ms,41.48us,213.00ns,42.37us,3.04us,40.87us,70.49us
+imported/sherlock/name-holmes-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,100.44ms,75.96us,2.17us,76.58us,3.39us,71.91us,97.76us
+imported/sherlock/name-holmes-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,150.48ms,111.44us,156.00ns,112.42us,2.20us,110.91us,129.24us
+imported/sherlock/name-sherlock-holmes-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,150.50ms,113.00us,830.00ns,113.82us,3.35us,109.21us,137.18us
+imported/sherlock/name-sherlock-holmes-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,50.35ms,37.17us,88.00ns,37.73us,1.95us,36.91us,54.98us
+imported/sherlock/name-sherlock-holmes-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,101.25ms,77.05us,55.00ns,77.86us,2.27us,76.84us,96.73us
+imported/sherlock/name-alt3-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,450.84ms,339.94us,2.43us,339.99us,4.51us,331.93us,372.13us
+imported/sherlock/name-alt3-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,182,751.19ms,2.75ms,6.56us,2.75ms,10.49us,2.74ms,2.79ms
+imported/sherlock/name-alt3-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,941,601.93ms,531.22us,2.97us,531.37us,5.24us,518.75us,596.83us
+imported/sherlock/name-alt5-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,300.67ms,207.34us,3.03us,207.71us,4.38us,200.22us,231.67us
+imported/sherlock/name-alt5-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,337,652.04ms,1.48ms,10.09us,1.49ms,19.87us,1.46ms,1.63ms
+imported/sherlock/name-alt5-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,300.67ms,243.45us,2.35us,243.12us,2.94us,239.81us,260.11us
+imported/sherlock/the-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,350.71ms,308.28us,3.58us,308.24us,5.48us,293.65us,329.30us
+imported/sherlock/the-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,550.99ms,467.03us,4.19us,467.55us,6.91us,452.37us,541.04us
+imported/sherlock/the-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,967,601.04ms,516.94us,3.46us,517.44us,5.15us,505.99us,551.90us
+opt/prefilter/literal-casei-english,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613357,1000,150.49ms,88.16us,1.49us,88.45us,3.54us,82.80us,113.46us
+opt/prefilter/literal-casei-english,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,613357,1000,50.37ms,37.64us,188.00ns,38.09us,1.84us,37.16us,62.31us
+opt/prefilter/literal-casei-english,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613357,1000,100.40ms,66.22us,48.00ns,66.91us,2.07us,66.05us,85.93us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,50.37ms,24.30us,78.00ns,24.73us,1.93us,24.08us,47.26us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,613423,1000,50.34ms,36.18us,87.00ns,36.67us,2.12us,35.84us,59.67us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.41ms,83.65us,76.00ns,84.40us,2.00us,83.34us,105.73us
diff --git a/audit/rebar/results/spr/rebar-audit-pass3.csv b/audit/rebar/results/spr/rebar-audit-pass3.csv
index fd909b6..641ea14 100644
--- a/audit/rebar/results/spr/rebar-audit-pass3.csv
+++ b/audit/rebar/results/spr/rebar-audit-pass3.csv
@@ -1,61 +1,61 @@
name,model,rebar_version,engine,engine_version,err,haystack_len,iters,total,median,mad,mean,stddev,min,max
-curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,899232,1000,300.83ms,248.97us,3.06us,249.11us,4.65us,241.68us,276.62us
-curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,899232,1000,50.44ms,28.03us,218.00ns,28.57us,1.71us,27.52us,50.80us
-curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,899232,1000,100.47ms,81.85us,1.56us,82.07us,2.69us,77.92us,102.95us
-curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,899232,1000,200.70ms,138.10us,575.00ns,139.49us,3.24us,137.29us,203.59us
-curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,1000,401.00ms,333.76us,4.83us,335.23us,6.26us,323.00us,368.30us
-curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,1570556,1000,451.11ms,363.09us,2.31us,362.92us,4.24us,354.62us,389.73us
-curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,1000,200.69ms,132.24us,370.00ns,133.75us,2.74us,131.37us,158.14us
-curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,1000,350.91ms,300.54us,2.85us,300.44us,4.33us,296.43us,379.42us
-curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,899232,951,601.30ms,526.79us,4.82us,525.84us,6.75us,510.57us,549.44us
-curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,899232,1000,100.56ms,74.86us,231.00ns,75.73us,2.30us,74.17us,89.43us
-curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,899232,220,751.59ms,2.27ms,7.21us,2.27ms,11.66us,2.25ms,2.32ms
-curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,899232,1000,551.28ms,484.29us,1.65us,484.42us,4.84us,476.96us,599.34us
-curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,78,751.51ms,6.41ms,14.00us,6.41ms,21.79us,6.37ms,6.46ms
-curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,1570556,728,601.35ms,686.85us,4.14us,686.75us,7.33us,669.38us,711.92us
-curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,54,751.54ms,9.40ms,19.12us,9.42ms,80.50us,9.33ms,9.79ms
-curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,302,701.52ms,1.65ms,13.75us,1.66ms,52.10us,1.62ms,2.29ms
-hyperscan/literal-casei-english-nosom,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613357,1000,100.54ms,84.37us,1.06us,86.15us,4.36us,81.59us,112.62us
-hyperscan/literal-casei-english-nosom,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,613357,1000,50.44ms,13.18us,24.00ns,13.48us,1.70us,13.09us,38.00us
-hyperscan/literal-casei-english-nosom,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613357,1000,100.51ms,66.18us,46.00ns,66.83us,1.83us,66.00us,85.35us
-hyperscan/literal-casei-english-som,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613357,1000,150.64ms,86.68us,4.01us,89.03us,6.34us,80.90us,112.53us
-hyperscan/literal-casei-english-som,count-spans,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,613357,1000,50.53ms,13.14us,21.00ns,13.34us,1.30us,13.05us,32.45us
-hyperscan/literal-casei-english-som,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613357,1000,100.53ms,66.27us,50.00ns,66.97us,1.93us,66.10us,80.95us
-hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,100.57ms,47.35us,264.00ns,48.41us,3.09us,46.77us,73.82us
-hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,613423,1000,150.64ms,93.24us,564.00ns,94.54us,3.04us,91.99us,107.88us
-hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.55ms,83.06us,98.00ns,83.93us,2.19us,82.76us,110.94us
-hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,100.53ms,47.24us,406.00ns,48.19us,3.05us,46.42us,69.94us
-hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,613423,1000,150.68ms,93.72us,768.00ns,94.92us,3.11us,92.40us,124.10us
-hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.53ms,83.31us,88.00ns,84.14us,1.96us,83.03us,97.48us
-imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,16013977,653,651.36ms,765.66us,5.87us,765.55us,12.95us,735.27us,0.98ms
-imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,16013977,771,601.43ms,652.45us,4.56us,648.92us,7.72us,636.06us,679.16us
-imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,16013977,525,651.52ms,0.95ms,1.64us,0.95ms,5.59us,939.71us,0.98ms
-imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,16013977,275,751.64ms,1.82ms,2.08us,1.82ms,8.51us,1.80ms,1.86ms
-imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,16013977,123,752.08ms,4.09ms,20.88us,4.10ms,33.95us,4.02ms,4.21ms
-imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2024-04-19,,16013977,378,701.48ms,1.32ms,6.38us,1.32ms,16.04us,1.30ms,1.55ms
-imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,16013977,9,751.58ms,55.82ms,101.70us,55.78ms,149.09us,55.45ms,55.97ms
-imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,16013977,25,751.63ms,20.50ms,26.26us,20.51ms,48.66us,20.42ms,20.62ms
-imported/sherlock/name-sherlock-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,150.64ms,107.09us,1.77us,107.73us,3.66us,101.84us,160.75us
-imported/sherlock/name-sherlock-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,100.58ms,58.49us,1.11us,59.35us,2.47us,56.85us,84.29us
-imported/sherlock/name-sherlock-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,100.55ms,75.42us,54.00ns,76.13us,1.82us,75.18us,88.55us
-imported/sherlock/name-holmes-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,100.52ms,56.87us,288.00ns,57.76us,2.63us,56.15us,82.44us
-imported/sherlock/name-holmes-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,100.58ms,77.08us,2.35us,77.53us,3.51us,72.68us,100.72us
-imported/sherlock/name-holmes-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,150.64ms,111.46us,152.00ns,112.48us,2.13us,111.03us,126.95us
-imported/sherlock/name-sherlock-holmes-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,150.58ms,114.24us,1.30us,115.77us,3.90us,112.17us,144.10us
-imported/sherlock/name-sherlock-holmes-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,50.45ms,37.62us,286.00ns,38.09us,1.73us,37.04us,55.17us
-imported/sherlock/name-sherlock-holmes-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,100.50ms,77.03us,60.00ns,77.75us,1.83us,76.79us,90.94us
-imported/sherlock/name-alt3-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,451.16ms,368.74us,3.77us,369.06us,6.04us,360.07us,444.34us
-imported/sherlock/name-alt3-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,182,751.66ms,2.76ms,6.97us,2.76ms,11.34us,2.73ms,2.79ms
-imported/sherlock/name-alt3-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,937,601.49ms,533.76us,2.09us,534.04us,3.86us,524.38us,565.72us
-imported/sherlock/name-alt5-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,300.90ms,229.27us,3.15us,230.36us,15.27us,223.33us,676.69us
-imported/sherlock/name-alt5-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,335,701.54ms,1.49ms,13.00us,1.49ms,30.36us,1.46ms,1.85ms
-imported/sherlock/name-alt5-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,300.89ms,243.35us,2.19us,243.57us,2.98us,240.31us,274.45us
-imported/sherlock/the-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,501.22ms,420.40us,3.98us,420.94us,5.84us,411.18us,510.08us
-imported/sherlock/the-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,551.34ms,466.39us,4.83us,466.71us,6.90us,450.44us,499.53us
-imported/sherlock/the-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,964,601.47ms,519.60us,4.64us,518.83us,5.82us,507.67us,548.63us
-opt/prefilter/literal-casei-english,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613357,1000,100.57ms,84.05us,1.53us,85.71us,3.96us,80.79us,113.40us
-opt/prefilter/literal-casei-english,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,613357,1000,50.43ms,37.90us,218.00ns,38.44us,2.16us,37.34us,58.07us
-opt/prefilter/literal-casei-english,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613357,1000,100.51ms,66.18us,45.00ns,66.81us,1.80us,66.00us,87.48us
-opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,100.58ms,46.35us,116.00ns,47.18us,2.67us,45.95us,64.31us
-opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,613423,1000,50.50ms,36.15us,92.00ns,36.52us,1.53us,35.78us,57.23us
-opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.57ms,83.25us,92.00ns,84.18us,3.48us,82.84us,167.80us
+curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,899232,1000,301.30ms,224.72us,3.27us,225.45us,4.52us,217.26us,242.04us
+curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,899232,1000,100.46ms,45.19us,113.00ns,45.69us,2.01us,44.70us,65.27us
+curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,899232,1000,100.40ms,81.90us,1.48us,82.21us,2.92us,78.08us,106.81us
+curated/01-literal/sherlock-casei-en,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,899232,1000,200.58ms,137.62us,98.00ns,138.94us,2.51us,137.33us,157.03us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,1000,201.38ms,122.06us,1.77us,123.48us,4.51us,118.34us,163.14us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,1570556,1000,400.81ms,332.04us,4.61us,333.15us,7.07us,321.16us,367.55us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,1000,200.59ms,135.63us,2.14us,136.12us,3.62us,131.38us,159.48us
+curated/01-literal/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,1000,350.78ms,301.59us,2.41us,301.10us,3.84us,296.02us,332.44us
+curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,899232,1000,601.82ms,461.65us,3.63us,462.14us,5.99us,448.16us,488.08us
+curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,899232,1000,150.51ms,94.02us,2.12us,94.82us,3.91us,90.45us,135.77us
+curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,899232,220,751.19ms,2.28ms,7.15us,2.28ms,10.71us,2.26ms,2.33ms
+curated/02-literal-alternate/sherlock-casei-en,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,899232,1000,550.98ms,481.81us,2.24us,482.19us,4.46us,473.64us,512.79us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,1570556,968,751.92ms,525.00us,7.92us,516.32us,18.82us,480.55us,550.22us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,1570556,800,601.05ms,623.43us,3.67us,624.97us,7.53us,610.85us,661.34us
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,1570556,54,751.19ms,9.39ms,25.79us,9.40ms,46.41us,9.33ms,9.54ms
+curated/02-literal-alternate/sherlock-casei-ru,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,1570556,305,702.08ms,1.64ms,9.04us,1.64ms,14.02us,1.61ms,1.69ms
+hyperscan/literal-casei-english-nosom,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613357,1000,150.51ms,87.99us,2.13us,88.15us,3.72us,82.98us,111.64us
+hyperscan/literal-casei-english-nosom,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,613357,1000,50.36ms,19.80us,37.00ns,20.00us,1.16us,19.63us,38.50us
+hyperscan/literal-casei-english-nosom,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613357,1000,100.43ms,66.20us,42.00ns,66.80us,1.69us,66.03us,82.49us
+hyperscan/literal-casei-english-som,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613357,1000,150.47ms,87.96us,2.15us,88.50us,4.44us,82.70us,112.31us
+hyperscan/literal-casei-english-som,count-spans,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,613357,1000,50.36ms,20.04us,43.00ns,20.25us,1.14us,19.84us,36.99us
+hyperscan/literal-casei-english-som,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613357,1000,100.40ms,66.23us,43.00ns,66.89us,1.97us,66.08us,88.99us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,50.38ms,24.52us,105.00ns,24.91us,1.78us,24.24us,39.49us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,613423,1000,100.38ms,83.37us,83.00ns,84.26us,2.30us,83.21us,108.75us
+hyperscan/literal-casei-russian-nosom,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.41ms,83.64us,77.00ns,84.49us,2.63us,83.40us,135.56us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,50.34ms,24.50us,253.00ns,24.99us,1.72us,24.16us,38.06us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,613423,1000,100.38ms,83.32us,78.00ns,84.17us,2.18us,83.12us,110.87us
+hyperscan/literal-casei-russian-som,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.41ms,83.75us,88.00ns,84.61us,2.29us,83.45us,107.66us
+imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,16013977,698,801.74ms,712.43us,5.39us,715.61us,10.48us,703.83us,820.72us
+imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,16013977,757,601.18ms,657.98us,3.17us,660.38us,7.20us,646.95us,708.42us
+imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,16013977,546,652.23ms,914.88us,1.50us,916.01us,7.38us,904.09us,0.96ms
+imported/leipzig/twain-insensitive,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,16013977,275,752.33ms,1.82ms,3.27us,1.82ms,7.71us,1.81ms,1.86ms
+imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,16013977,127,1.55s,3.95ms,31.32us,3.95ms,47.51us,3.84ms,4.05ms
+imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),hyperscan,5.4.2 2026-08-25,,16013977,267,751.38ms,1.88ms,3.75us,1.88ms,10.11us,1.86ms,1.92ms
+imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,16013977,9,752.23ms,55.56ms,39.16us,55.57ms,97.10us,55.40ms,55.78ms
+imported/leipzig/tom-sawyer-huckle-fin-insensitive,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,16013977,25,752.26ms,20.47ms,30.72us,20.48ms,36.31us,20.41ms,20.57ms
+imported/sherlock/name-sherlock-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,151.37ms,106.39us,2.16us,106.69us,3.17us,101.79us,124.36us
+imported/sherlock/name-sherlock-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,100.42ms,57.87us,1.10us,58.73us,2.47us,56.13us,79.07us
+imported/sherlock/name-sherlock-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,100.44ms,75.44us,55.00ns,76.27us,2.20us,75.27us,93.94us
+imported/sherlock/name-holmes-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,100.42ms,41.69us,266.00ns,42.30us,2.14us,41.06us,74.89us
+imported/sherlock/name-holmes-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,101.31ms,76.18us,2.17us,76.70us,3.19us,72.23us,101.42us
+imported/sherlock/name-holmes-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,150.47ms,111.53us,120.00ns,112.51us,2.25us,111.14us,138.39us
+imported/sherlock/name-sherlock-holmes-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,150.49ms,113.17us,1.12us,113.87us,3.45us,109.09us,134.58us
+imported/sherlock/name-sherlock-holmes-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,50.34ms,37.92us,268.00ns,38.32us,1.93us,37.19us,66.71us
+imported/sherlock/name-sherlock-holmes-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,100.36ms,77.09us,57.00ns,77.82us,1.96us,76.88us,98.17us
+imported/sherlock/name-alt3-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,450.96ms,339.26us,2.11us,339.56us,3.99us,331.77us,364.75us
+imported/sherlock/name-alt3-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,182,751.16ms,2.75ms,7.41us,2.75ms,11.54us,2.73ms,2.81ms
+imported/sherlock/name-alt3-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,934,601.93ms,535.57us,2.76us,535.55us,5.14us,522.05us,571.82us
+imported/sherlock/name-alt5-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,300.63ms,206.74us,2.36us,207.39us,3.91us,200.08us,236.78us
+imported/sherlock/name-alt5-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,337,701.15ms,1.48ms,9.16us,1.49ms,21.12us,1.46ms,1.58ms
+imported/sherlock/name-alt5-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,1000,300.69ms,243.57us,2.25us,243.46us,3.00us,240.18us,272.24us
+imported/sherlock/the-casei,count-spans,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,594933,1000,350.73ms,307.59us,3.52us,308.08us,5.73us,294.06us,343.10us
+imported/sherlock/the-casei,count-spans,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,594933,1000,550.99ms,465.32us,4.18us,465.51us,6.83us,450.91us,524.66us
+imported/sherlock/the-casei,count-spans,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,594933,968,602.06ms,516.26us,2.35us,516.79us,4.54us,507.90us,573.94us
+opt/prefilter/literal-casei-english,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613357,1000,150.50ms,87.89us,2.15us,88.01us,3.35us,83.21us,116.72us
+opt/prefilter/literal-casei-english,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,613357,1000,50.40ms,37.82us,213.00ns,38.28us,1.88us,37.33us,54.65us
+opt/prefilter/literal-casei-english,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613357,1000,100.41ms,66.29us,46.00ns,66.97us,2.00us,66.14us,85.22us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),casei,casei-rebar 1,,613423,1000,50.37ms,23.88us,56.00ns,24.32us,1.83us,23.67us,38.35us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),pcre2/jit,10.47 2025-10-21,,613423,1000,50.34ms,36.09us,82.00ns,36.56us,2.12us,35.81us,61.78us
+opt/prefilter/literal-casei-russian,count,0.1.0 (rev 463d00f318),rust/regex,1.12.4,,613423,1000,100.41ms,83.74us,84.00ns,84.57us,2.40us,83.47us,128.02us
diff --git a/audit/rebar/runner/main.go b/audit/rebar/runner/main.go
index 9c6af78..6dad97f 100644
--- a/audit/rebar/runner/main.go
+++ b/audit/rebar/runner/main.go
@@ -51,7 +51,7 @@ func run() error {
if !c.caseInsensitive {
return errors.New("casei runner only accepts case-insensitive benchmarks")
}
- if c.model != "count" && c.model != "count-spans" {
+ if c.model != "count" && c.model != "count-spans" && c.model != "find" {
return fmt.Errorf("unsupported model %q", c.model)
}
patterns, err := literalAlternation(c.patterns)
@@ -59,15 +59,20 @@ func run() error {
return err
}
matcher := casei.NewMatcher(patterns)
- bench := func() (int, error) {
- return countMatches(c.haystack, patterns, matcher, c.model == "count-spans")
+ spans := c.model == "count-spans"
+ if _, err := verifyEnumeration(c.haystack, patterns, matcher, spans); err != nil {
+ return err
+ }
+ bench := func() int {
+ if c.model == "find" {
+ return findMatch(c.haystack, matcher)
+ }
+ return countMatches(c.haystack, matcher, spans)
}
warmupStart := time.Now()
for i := uint64(0); i < c.maxWarmupIters; i++ {
- if _, err := bench(); err != nil {
- return err
- }
+ _ = bench()
if time.Since(warmupStart) >= c.maxWarmupTime {
break
}
@@ -77,11 +82,8 @@ func run() error {
runStart := time.Now()
for i := uint64(0); i < c.maxIters; i++ {
start := time.Now()
- count, err := bench()
+ count := bench()
duration := time.Since(start)
- if err != nil {
- return err
- }
fmt.Fprintf(out, "%d,%d\n", duration.Nanoseconds(), count)
if time.Since(runStart) >= c.maxTime {
break
@@ -90,28 +92,34 @@ func run() error {
return out.Flush()
}
-func countMatches(haystack string, patterns []string, matcher *casei.Matcher, spans bool) (int, error) {
+// findMatch is the timed single-query operation for Find-shaped workloads.
+// verifyEnumeration still validates the complete non-overlapping contract once
+// before the runner starts its warm-up and measurement loops.
+func findMatch(haystack string, matcher *casei.Matcher) int {
+ if _, ok := matcher.Find(haystack); ok {
+ return 1
+ }
+ return 0
+}
+
+// countMatches is the timed Rebar operation: one compiled Matcher enumeration
+// and only the count/span sink. verifyEnumeration runs the independent oracle
+// once before the runner starts its warm-up and measurement loops.
+func countMatches(haystack string, matcher *casei.Matcher, spans bool) int {
total := 0
- for at := 0; at <= len(haystack); {
- match, ok := matcher.Find(haystack[at:])
- if !ok {
- break
- }
- start := at + match.Start
- width, ok := foldPrefixWidth(haystack[start:], patterns[match.Pattern])
- if !ok || width == 0 {
- return 0, fmt.Errorf("casei returned an unverifiable match at %d for pattern %q", start, patterns[match.Pattern])
- }
+ matcher.Each(haystack, func(_ casei.Match, width int) bool {
if spans {
total += width
} else {
total++
}
- at = start + width
- }
- return total, nil
+ return true
+ })
+ return total
}
+// foldPrefixWidth is deliberately independent from Matcher.Each. The Rebar
+// preflight uses it to verify every source width and match boundary.
func foldPrefixWidth(haystack, pattern string) (int, bool) {
consumed := 0
for len(pattern) > 0 {
@@ -149,6 +157,61 @@ func foldEqual(a, b rune) bool {
return false
}
+// nextMatch is a package-independent, source-boundary oracle for the next
+// non-empty literal occurrence. At one source offset, the first matching
+// pattern index wins; offsets are then considered left to right.
+func nextMatch(haystack string, patterns []string, from int) (casei.Match, int, bool) {
+ for at := from; at <= len(haystack); {
+ for pattern, literal := range patterns {
+ if width, ok := foldPrefixWidth(haystack[at:], literal); ok && width != 0 {
+ return casei.Match{Pattern: pattern, Start: at}, width, true
+ }
+ }
+ if at == len(haystack) {
+ break
+ }
+ _, size := utf8.DecodeRuneInString(haystack[at:])
+ at += size
+ }
+ return casei.Match{}, 0, false
+}
+
+// verifyEnumeration compares every result to the canonical source scan before
+// timing begins. It validates Pattern bounds, leftmost/lowest-ID order, exact
+// source width, and the non-overlapping resume point without using Matcher.Find.
+func verifyEnumeration(haystack string, patterns []string, matcher *casei.Matcher, spans bool) (int, error) {
+ at, total := 0, 0
+ var verifyErr error
+ complete := matcher.Each(haystack, func(match casei.Match, width int) bool {
+ if match.Pattern < 0 || match.Pattern >= len(patterns) || match.Start < at || match.Start > len(haystack) || width <= 0 {
+ verifyErr = fmt.Errorf("casei returned invalid match %+v with width %d", match, width)
+ return false
+ }
+ want, expectedWidth, ok := nextMatch(haystack, patterns, at)
+ if !ok || match != want || width != expectedWidth {
+ verifyErr = fmt.Errorf("casei returned match %+v with width %d; canonical next match is %+v with width %d, ok=%t", match, width, want, expectedWidth, ok)
+ return false
+ }
+ if spans {
+ total += width
+ } else {
+ total++
+ }
+ at = match.Start + width
+ return true
+ })
+ if verifyErr != nil {
+ return 0, verifyErr
+ }
+ if !complete {
+ return 0, errors.New("casei enumeration stopped during preflight")
+ }
+ if want, width, ok := nextMatch(haystack, patterns, at); ok {
+ return 0, fmt.Errorf("casei omitted canonical match %+v with width %d", want, width)
+ }
+ return total, nil
+}
+
func literalAlternation(raw []string) ([]string, error) {
if len(raw) != 1 {
return nil, fmt.Errorf("expected one rebar regex, got %d", len(raw))
diff --git a/audit/rebar/runner/main_test.go b/audit/rebar/runner/main_test.go
index 9b3a284..d71b93f 100644
--- a/audit/rebar/runner/main_test.go
+++ b/audit/rebar/runner/main_test.go
@@ -28,7 +28,7 @@ func TestLiteralAlternation(t *testing.T) {
}
}
-func TestFoldPrefixWidth(t *testing.T) {
+func TestEachWidth(t *testing.T) {
for _, tt := range []struct {
haystack string
pattern string
@@ -38,15 +38,36 @@ func TestFoldPrefixWidth(t *testing.T) {
{"Kelvin", "k", 3, true},
{"ſuffix", "S", 2, true},
{"ς", "Σ", 2, true},
+ {"шЕРЛОК хОЛМС", "Шерлок Холмс", len("шЕРЛОК хОЛМС"), true},
+ {"Ёлка", "ёлка", len("Ёлка"), true},
{"x", "s", 0, false},
{string([]byte{0xff}), string([]byte{0xff}), 1, true},
{string([]byte{0xfe}), string([]byte{0xff}), 0, false},
} {
- width, ok := foldPrefixWidth(tt.haystack, tt.pattern)
- if width != tt.width || ok != tt.ok {
- t.Errorf("foldPrefixWidth(%q, %q) = (%d, %v), want (%d, %v)",
- tt.haystack, tt.pattern, width, ok, tt.width, tt.ok)
+ expected, expectedOK := foldPrefixWidth(tt.haystack, tt.pattern)
+ if expected != tt.width || expectedOK != tt.ok {
+ t.Fatalf("foldPrefixWidth(%q, %q) = (%d, %v), want (%d, %v)",
+ tt.haystack, tt.pattern, expected, expectedOK, tt.width, tt.ok)
}
+ got, ok := 0, false
+ casei.NewMatcher([]string{tt.pattern}).Each(tt.haystack, func(_ casei.Match, width int) bool {
+ got, ok = width, true
+ return false
+ })
+ if got != expected || ok != expectedOK {
+ t.Errorf("Each(%q, %q) = (%d, %v), want independently verified (%d, %v)",
+ tt.haystack, tt.pattern, got, ok, expected, expectedOK)
+ }
+ }
+}
+
+func TestFindMatch(t *testing.T) {
+ matcher := casei.NewMatcher([]string{"needle"})
+ if got := findMatch("a needle", matcher); got != 1 {
+ t.Fatalf("findMatch(hit) = %d, want 1", got)
+ }
+ if got := findMatch("a haystack", matcher); got != 0 {
+ t.Fatalf("findMatch(miss) = %d, want 0", got)
}
}
@@ -60,10 +81,10 @@ func TestCountMatches(t *testing.T) {
{false, 2},
{true, 5},
} {
- got, err := countMatches("SSſs", patterns, matcher, tt.spans)
- if err != nil {
- t.Fatal(err)
+ if got, err := verifyEnumeration("SSſs", patterns, matcher, tt.spans); err != nil || got != tt.want {
+ t.Fatalf("verifyEnumeration(spans=%v) = %d, %v; want %d, nil", tt.spans, got, err, tt.want)
}
+ got := countMatches("SSſs", matcher, tt.spans)
if got != tt.want {
t.Fatalf("countMatches(spans=%v) = %d, want %d", tt.spans, got, tt.want)
}
diff --git a/casei_test.go b/casei_test.go
index 5e2f4e7..9c74796 100644
--- a/casei_test.go
+++ b/casei_test.go
@@ -305,6 +305,9 @@ func FuzzIndexFold(f *testing.F) {
f.Add("große", "GROSSE")
f.Add(strings.Repeat("ab", 64), "abc")
f.Add("na\xc3\xafve", "\xc3\x8f")
+ f.Add(strings.Repeat("x", 64)+"ПРИКЛЮЧЕНИЯ ЛИЛИЙ"+strings.Repeat("x", 4096), "приключения лилий")
+ f.Add(strings.Repeat("x", 64)+"приключения лилия"+"x"+"ПРИКЛЮЧЕНИЯ ЛИЛИЙ"+strings.Repeat("x", 4096), "приключения лилий")
+ f.Add(strings.Repeat("x", 64)+"\x80ПРИКЛЮЧЕНИЯ ЛИЛИЙ"+strings.Repeat("x", 4096), "\x80приключения лилий")
f.Fuzz(func(t *testing.T, haystack, needle string) {
got, want := IndexFold(haystack, needle), reference(haystack, needle)
if got != want {
diff --git a/each_harness_test.go b/each_harness_test.go
new file mode 100644
index 0000000..fa7a1fc
--- /dev/null
+++ b/each_harness_test.go
@@ -0,0 +1,38 @@
+package casei
+
+import "testing"
+
+// TestEachHarnessContract fixes the public enumeration reduction that the
+// Rebar adapter times. The optimized implementation must preserve this
+// repeated-Find baseline's non-overlap order and source widths.
+func TestEachHarnessContract(t *testing.T) {
+ matcher := NewMatcher([]string{"ss", "s"})
+ want := []struct {
+ match Match
+ width int
+ }{
+ {Match{Pattern: 0, Start: 0}, len("SS")},
+ {Match{Pattern: 0, Start: len("SS")}, len("ſs")},
+ }
+ var got []struct {
+ match Match
+ width int
+ }
+ if complete := matcher.Each("SSſs", func(match Match, width int) bool {
+ got = append(got, struct {
+ match Match
+ width int
+ }{match, width})
+ return true
+ }); !complete {
+ t.Fatal("Each stopped before completing enumeration")
+ }
+ if len(got) != len(want) {
+ t.Fatalf("Each returned %d matches, want %d", len(got), len(want))
+ }
+ for i := range want {
+ if got[i] != want[i] {
+ t.Fatalf("match %d = %+v, want %+v", i, got[i], want[i])
+ }
+ }
+}
diff --git a/matcher.go b/matcher.go
index 7cfbb10..50dd1aa 100644
--- a/matcher.go
+++ b/matcher.go
@@ -1,5 +1,7 @@
package casei
+import "unicode/utf8"
+
// Matcher searches for any of a set of patterns under the same Unicode
// simple-fold semantics as IndexFold. IndexFold is the one-pattern form of the
// same compiled search plan. The implementation scans the haystack once rather
@@ -16,8 +18,8 @@ type Match struct {
}
// Matcher searches for any of a fixed set of patterns. Construction compiles
-// their shared fold-orbit transition plan; Find only advances that plan over
-// the haystack.
+// their shared fold-orbit transition plan; Find returns one answer and Each
+// enumerates non-overlapping answers over the haystack.
type Matcher struct {
patterns []string
plan *searchPlan
@@ -48,6 +50,57 @@ func (m *Matcher) Find(haystack string) (Match, bool) {
return m.plan.find(haystack)
}
+// Each calls yield for each non-overlapping match in haystack, in the same
+// leftmost and lowest-pattern-ID order as repeated calls to Find. width is the
+// exact byte width consumed by this occurrence, which can differ from the
+// matched pattern's byte length under Unicode simple folding. Returning false
+// from yield stops enumeration and makes Each return false.
+//
+// A nil Matcher or nil yield has no matches and returns true. Each is safe for
+// concurrent use when yield itself is safe.
+func (m *Matcher) Each(haystack string, yield func(match Match, width int) bool) bool {
+ if m == nil || m.plan == nil || yield == nil {
+ return true
+ }
+ if m.plan.empty < 0 && m.plan.rawByteMulti.usable() {
+ return m.plan.eachRawByteFixedAnchored(haystack, yield)
+ }
+ for at := 0; at <= len(haystack); {
+ match, width, ok := m.plan.findWithWidth(haystack[at:])
+ if !ok {
+ return true
+ }
+ match.Start += at
+ if width == 0 {
+ units := utf8.RuneCountInString(m.patterns[match.Pattern])
+ width = matcherMatchEnd(haystack, match.Start, units) - match.Start
+ }
+ end := match.Start + width
+ if !yield(match, width) {
+ return false
+ }
+ if width != 0 {
+ at = end
+ continue
+ }
+ if match.Start == len(haystack) {
+ return true
+ }
+ _, size := utf8.DecodeRuneInString(haystack[match.Start:])
+ at = match.Start + size
+ }
+ return true
+}
+
+func matcherMatchEnd(haystack string, start, units int) int {
+ at := start
+ for range units {
+ _, size := utf8.DecodeRuneInString(haystack[at:])
+ at += size
+ }
+ return at
+}
+
// VectorBits reports the widest runtime-gated block transition available to
// this package, with the same contract as RuntimeVectorBits.
func (m *Matcher) VectorBits() int { return RuntimeVectorBits() }
diff --git a/matcher_test.go b/matcher_test.go
index 73924f5..493d134 100644
--- a/matcher_test.go
+++ b/matcher_test.go
@@ -6,6 +6,7 @@ import (
"strings"
"sync"
"testing"
+ "unicode/utf8"
)
// refFind is the independent multi-needle reference: per-pattern canonical
@@ -27,6 +28,42 @@ func refFind(haystack string, patterns []string) (Match, bool) {
return best, true
}
+type refEachResult struct {
+ match Match
+ width int
+}
+
+// refEach repeats the independent refFind oracle and advances by decoded
+// source units. It is deliberately separate from Matcher.findWithWidth, whose
+// optimized raw confirmation can return the width without decoding again.
+func refEach(haystack string, patterns []string) []refEachResult {
+ var results []refEachResult
+ for from := 0; from <= len(haystack); {
+ match, ok := refFind(haystack[from:], patterns)
+ if !ok {
+ return results
+ }
+ match.Start += from
+ end := match.Start
+ for range utf8.RuneCountInString(patterns[match.Pattern]) {
+ _, size := utf8.DecodeRuneInString(haystack[end:])
+ end += size
+ }
+ width := end - match.Start
+ results = append(results, refEachResult{match: match, width: width})
+ if width != 0 {
+ from = end
+ continue
+ }
+ if match.Start == len(haystack) {
+ return results
+ }
+ _, size := utf8.DecodeRuneInString(haystack[match.Start:])
+ from = match.Start + size
+ }
+ return results
+}
+
var matcherTraps = []struct {
name string
haystack string
@@ -552,10 +589,28 @@ func FuzzMatcher(f *testing.F) {
f.Add("доктор Ватсон", "ватсон", "ШЕРЛОК", "z")
f.Fuzz(func(t *testing.T, haystack, p0, p1, p2 string) {
pats := []string{p0, p1, p2}
- got, gotOK := NewMatcher(pats).Find(haystack)
+ matcher := NewMatcher(pats)
+ got, gotOK := matcher.Find(haystack)
want, wantOK := refFind(haystack, pats)
if gotOK != wantOK || (gotOK && got != want) {
t.Fatalf("Find(%q, %q) = %+v,%v want %+v,%v", haystack, pats, got, gotOK, want, wantOK)
}
+
+ var gotEach []refEachResult
+ if complete := matcher.Each(haystack, func(match Match, width int) bool {
+ gotEach = append(gotEach, refEachResult{match: match, width: width})
+ return true
+ }); !complete {
+ t.Fatal("Each stopped with a callback that always returns true")
+ }
+ wantEach := refEach(haystack, pats)
+ if len(gotEach) != len(wantEach) {
+ t.Fatalf("Each(%q, %q) returned %d matches, want %d: got=%+v want=%+v", haystack, pats, len(gotEach), len(wantEach), gotEach, wantEach)
+ }
+ for i := range wantEach {
+ if gotEach[i] != wantEach[i] {
+ t.Fatalf("Each(%q, %q) match %d = %+v, want %+v", haystack, pats, i, gotEach[i], wantEach[i])
+ }
+ }
})
}
diff --git a/plan.go b/plan.go
index 3422f2d..15df4d0 100644
--- a/plan.go
+++ b/plan.go
@@ -40,35 +40,43 @@ type searchPlan struct {
// asciiProbe is a single-pattern, byte-aligned block transition. It
// intersects three dispersed literal positions, then confirms the same
// compiled pattern at the surviving start.
- asciiProbe asciiProbe
- asciiOnlyProbe asciiProbe
- asciiOnlyNeedle string
- asciiOnlyWord uint64
- asciiOnlyFold uint64
- asciiOnly bool
- asciiOnlyLong bool
- asciiPair asciiPairProbe
- asciiNeedle string
- asciiFirstWord uint64
- asciiFirstFold uint64
- asciiTailWord uint64
- asciiTailFold uint64
- asciiTailMask uint64
- asciiVerifyTokens bool
- asciiFixedPrefix int
- asciiByteAnchor bool
- asciiStaticAnchor bool
- asciiStaticAt int
- asciiStaticKind uint8
- asciiStaticByte byte
- asciiRun bool
- asciiRunKind uint8
- asciiRunByte byte
- unicodeAnchor tripleFilter
- unicodeAt int
- unicodePairs [8]unicodePairAnchor
- unicodePairN uint8
- singleTokens []uint32
+ asciiProbe asciiProbe
+ asciiOnlyProbe asciiProbe
+ // singlePayload is the literal for the all-ASCII route. That route is
+ // disabled for Unicode patterns, where this otherwise unused string instead
+ // holds the packed raw terminal confirmation for the N=1 VBMI transition.
+ singlePayload string
+ asciiOnlyWord uint64
+ asciiOnlyFold uint64
+ asciiOnly bool
+ asciiOnlyLong bool
+ asciiPair asciiPairProbe
+ asciiNeedle string
+ asciiFirstWord uint64
+ asciiFirstFold uint64
+ asciiTailWord uint64
+ asciiTailFold uint64
+ asciiTailMask uint64
+ asciiVerifyTokens bool
+ asciiFixedPrefix int
+ asciiByteAnchor bool
+ asciiStaticAnchor bool
+ asciiStaticAt int
+ asciiStaticKind uint8
+ asciiStaticByte byte
+ asciiRun bool
+ asciiRunKind uint8
+ asciiRunByte byte
+ unicodeAnchor tripleFilter
+ unicodeAt int
+ unicodePairs [8]unicodePairAnchor
+ unicodePairN uint8
+ singleTokens []uint32
+ // rawByteMulti is the fixed, tagged interior-pair screen used only by
+ // eligible multi-pattern enumeration. It is compiled into the plan with the
+ // raw transition map; it never depends on a caller's haystack or history.
+ rawByteMulti rawByteMultiAnchorFilter
+ rawByteOrigin rawByteOriginGate
runes map[rune]uint32
opaqueContinuation bool
@@ -81,8 +89,11 @@ type searchPlan struct {
dense []uint32
stride int
- empty int
- maxUnits int
+ empty int
+ maxUnits int
+ // maxBytes bounds the source bytes consumed by any simple-fold spelling
+ // of a compiled pattern. Boundary windows use it to preserve cross-run matches.
+ maxBytes int
patternCount int
}
@@ -105,8 +116,16 @@ type rootFilter struct {
}
const (
- pairShuftiGroups = 2
- pairShuftiSlots = 8
+ pairShuftiGroups = 2
+ pairShuftiSlots = 8
+ asciiTripleMinBytes = 64
+
+ // A rolling exceptional-byte budget keeps a sparse prefix from admitting a
+ // later dense region. The partitioner checks the same bounded neighborhood
+ // as its entry guard while it discovers subsequent spans.
+ asciiPartitionSampleBytes = 1024
+ asciiPartitionMaxHigh = 16
+ asciiPartitionLookaheadBytes = asciiPartitionSampleBytes * asciiPartitionMaxHigh
)
// pairShuftiGroup stores four nibble-to-slot tables. The slot bits identify
@@ -885,9 +904,15 @@ func newSearchPlan(patterns []string) *searchPlan {
continue
}
- state, units := 0, 0
+ state, units, maxBytes := 0, 0, 0
for at := 0; at < len(pattern); {
token, size := p.patternToken(pattern, at, &nextToken)
+ r, _ := utf8.DecodeRuneInString(pattern[at:])
+ unitBytes := size
+ if r != utf8.RuneError || size != 1 {
+ unitBytes = maxFoldRuneWidth(r)
+ }
+ maxBytes += unitBytes
next, ok := p.nodes[state].edges[token]
if !ok {
next = len(p.nodes)
@@ -912,6 +937,9 @@ func newSearchPlan(patterns []string) *searchPlan {
if units > p.maxUnits {
p.maxUnits = units
}
+ if maxBytes > p.maxBytes {
+ p.maxBytes = maxBytes
+ }
}
p.finish(nextToken)
@@ -922,9 +950,24 @@ func newSearchPlan(patterns []string) *searchPlan {
p.makeASCIIAnchor(patterns[0])
p.makeUnicodeAnchor(patterns[0])
}
+ p.makeRawByteTokenPlan(patterns)
return p
}
+// maxFoldRuneWidth returns the widest UTF-8 encoding in r's simple-fold orbit.
+func maxFoldRuneWidth(r rune) int {
+ maxWidth := 1
+ for member := r; ; member = unicode.SimpleFold(member) {
+ if width := utf8.RuneLen(member); width > maxWidth {
+ maxWidth = width
+ }
+ if unicode.SimpleFold(member) == r {
+ break
+ }
+ }
+ return maxWidth
+}
+
// patternToken emits one token for a pattern unit and advances at by its
// source width. A malformed pattern byte is an opaque unit, just as it is in a
// haystack scan.
@@ -1206,7 +1249,7 @@ func (p *searchPlan) makeASCIIOnlyProbe(pattern string) {
p.asciiOnlyWord |= uint64(value) << (8 * at)
}
}
- p.asciiOnlyNeedle = pattern
+ p.singlePayload = pattern
p.asciiOnly = true
}
@@ -1467,6 +1510,14 @@ func (p *searchPlan) makeUnicodePairAnchor(pattern string) {
func (p *searchPlan) makeUnicodeAnchor(pattern string) {
p.makeUnicodePairAnchor(pattern)
+ if !p.asciiOnly && p.unicodePairN != 0 && p.unicodePairs[0].pairPair.valid != 0 {
+ anchor := p.unicodePairs[0]
+ if confirm := makeUnicodePairConfirm(pattern, anchor.at, anchor.confirmAt); confirm.valid() {
+ p.singlePayload = string(confirm)
+ } else if confirm := makeUnicodePairVariableConfirm(pattern, anchor.at); confirm.valid() {
+ p.singlePayload = string(confirm)
+ }
+ }
forms, widths := patternRawForms(pattern)
offset, fixedPrefix := 0, true
var best tripleFilter
@@ -1901,6 +1952,12 @@ func (p *searchPlan) haystackToken(s string, at int) (uint32, int) {
}
r, size := utf8.DecodeRuneInString(s[at:])
if r == utf8.RuneError && size == 1 {
+ // An eligible raw-byte plan has no opaque pattern bytes. Its opaque
+ // storage holds direct two-byte tokens instead, so malformed input must
+ // remain the ordinary zero-token reset.
+ if p.hasRawByteTokenPlan() {
+ return 0, 1
+ }
return p.opaque[byteValue], 1
}
return p.runes[r], size
@@ -2197,8 +2254,55 @@ func pairFilterAt(haystack string, at int, filter *rootFilter) bool {
return false
}
-func (p *searchPlan) findUnicodePairAnchor(haystack string, anchor *unicodePairAnchor) (Match, bool) {
+// findUnicodePairConfirm keeps the exact N=1 raw confirmation in the VBMI
+// transition for full vector blocks. The scalar tail remains bounded and uses
+// the same compiled raw forms, while unavailable vector hosts retain the
+// decoded executor below.
+func (p *searchPlan) unicodePairConfirm() unicodePairConfirm {
+ if p.asciiOnly {
+ return ""
+ }
+ return unicodePairConfirm(p.singlePayload)
+}
+
+func (p *searchPlan) findUnicodePairConfirm(haystack string, anchor *unicodePairAnchor) (Match, int, bool) {
+ confirm := p.unicodePairConfirm()
+ if len(haystack) < confirm.minLength() {
+ return Match{}, 0, false
+ }
+
+ at := anchor.at
+ if lastStart := len(haystack) - confirm.maxLength(); lastStart >= 0 {
+ full := (lastStart + 1) &^ 63
+ if full != 0 {
+ skipped, width := pairPairConfirmBytes(haystack, at, full, &anchor.pairPair, confirm)
+ if skipped < full {
+ return Match{Pattern: 0, Start: at + skipped - anchor.at}, width, true
+ }
+ at += full
+ }
+ }
+
+ lastAnchor := len(haystack) - confirm.minLength() + anchor.at
+ for at <= lastAnchor {
+ at += pairPairSkipBytes(haystack, at, &anchor.pairPair)
+ if at > lastAnchor {
+ break
+ }
+ start := at - anchor.at
+ if width, ok := confirm.matchWidthAt(haystack, start); ok {
+ return Match{Pattern: 0, Start: start}, width, true
+ }
+ at++
+ }
+ return Match{}, 0, false
+}
+
+func (p *searchPlan) findUnicodePairAnchor(haystack string, anchor *unicodePairAnchor) (Match, int, bool) {
if anchor.pairPair.valid != 0 {
+ if p.unicodePairConfirm().valid() && unicodePairConfirmVectorEnabled() {
+ return p.findUnicodePairConfirm(haystack, anchor)
+ }
for at := 0; at+int(anchor.pairPair.offset)+1 < len(haystack); {
at += pairPairSkipBytes(haystack, at, &anchor.pairPair)
if at+int(anchor.pairPair.offset)+1 >= len(haystack) {
@@ -2206,11 +2310,11 @@ func (p *searchPlan) findUnicodePairAnchor(haystack string, anchor *unicodePairA
}
start := at - anchor.at
if start >= 0 && p.matchesSingleAt(haystack, start) {
- return Match{Pattern: 0, Start: start}, true
+ return Match{Pattern: 0, Start: start}, 0, true
}
at++
}
- return Match{}, false
+ return Match{}, 0, false
}
for at := 0; at+1 < len(haystack); {
at += filterSkipBytes(haystack, at, &anchor.filter)
@@ -2219,11 +2323,11 @@ func (p *searchPlan) findUnicodePairAnchor(haystack string, anchor *unicodePairA
}
start := at - anchor.at
if start >= 0 && pairFilterAt(haystack, start+anchor.confirmAt, &anchor.confirm) && p.matchesSingleAt(haystack, start) {
- return Match{Pattern: 0, Start: start}, true
+ return Match{Pattern: 0, Start: start}, 0, true
}
at++
}
- return Match{}, false
+ return Match{}, 0, false
}
func (p *searchPlan) findUnicodeAnchor(haystack string) (Match, bool) {
@@ -2241,56 +2345,77 @@ func (p *searchPlan) findUnicodeAnchor(haystack string) (Match, bool) {
return Match{}, false
}
+func withZeroWidth(match Match, ok bool) (Match, int, bool) {
+ return match, 0, ok
+}
+
func (p *searchPlan) find(haystack string) (Match, bool) {
+ match, _, ok := p.findWithWidth(haystack)
+ return match, ok
+}
+
+// findWithWidth is the package's one search decision tree. Most routes return
+// zero width; an exact raw confirmation returns the source width it has already
+// proved so Matcher.Each does not decode the same match again.
+func (p *searchPlan) findWithWidth(haystack string) (Match, int, bool) {
if p.maxUnits == 0 {
if p.empty >= 0 {
- return Match{Pattern: p.empty}, true
+ return Match{Pattern: p.empty}, 0, true
}
- return Match{}, false
+ return Match{}, 0, false
}
if p.opaqueContinuation {
- return p.findUnfiltered(haystack)
+ return withZeroWidth(p.findUnfiltered(haystack))
}
if p.asciiRun {
- return p.findASCIIRun(haystack)
+ return withZeroWidth(p.findASCIIRun(haystack))
}
if p.asciiPair.usable() && len(haystack) >= len(p.asciiNeedle) && p.asciiFixedAt(haystack, 0) {
- return Match{Pattern: 0}, true
+ return Match{Pattern: 0}, 0, true
}
if p.asciiPairVBMIDisplaced() && len(haystack) >= 4096 && asciiPairVBMIEnabled() {
- return p.findASCIIPairAnchor(haystack)
+ return withZeroWidth(p.findASCIIPairAnchor(haystack))
}
if !p.asciiPairVBMIDisplaced() && p.asciiPair.usable() && p.asciiPairPromising() {
- return p.findASCIIPairAnchor(haystack)
+ return withZeroWidth(p.findASCIIPairAnchor(haystack))
}
if p.asciiStaticAnchor && len(haystack) >= 4096 {
- return p.findASCIIByteAnchor(haystack, p.asciiStaticAt, p.asciiStaticKind, p.asciiStaticByte)
+ return withZeroWidth(p.findASCIIByteAnchor(haystack, p.asciiStaticAt, p.asciiStaticKind, p.asciiStaticByte))
}
if p.asciiByteAnchor {
if anchorAt, kind, needle, ok := p.chooseASCIIByteAnchor(haystack); ok {
- return p.findASCIIByteAnchor(haystack, anchorAt, kind, needle)
+ return withZeroWidth(p.findASCIIByteAnchor(haystack, anchorAt, kind, needle))
}
}
if !p.asciiPairVBMIDisplaced() && p.asciiPair.usable() && len(haystack) >= 4096 && p.asciiPairSparse(haystack) {
- return p.findASCIIPairAnchor(haystack)
+ return withZeroWidth(p.findASCIIPairAnchor(haystack))
}
if p.asciiProbe.usable() {
- return p.findASCIIAnchor(haystack)
+ return withZeroWidth(p.findASCIIAnchor(haystack))
}
// k and s have width-changing Unicode orbit members, so their patterns do
// not enter the fixed ASCII probe above. The integrated high-byte check lets
// a short or structured ASCII haystack use the same vector transition in one
// pass; any high byte falls through to the full Unicode plan unchanged.
if p.asciiOnly && (len(haystack) <= 4096 || p.asciiOnlyLong) {
- if match, ok, handled := p.findASCIIOnlyAnchor(haystack, p.asciiOnlyNeedle); handled {
- return match, ok
+ if match, ok, handled := p.findASCIIOnlyAnchor(haystack, p.singlePayload); handled {
+ return match, 0, ok
}
}
+ // The tagged multi-anchor filter is one plan-owned raw transition scan for
+ // both Find and Each. Its exact replay decides the result, so Find can stop
+ // at the first completed leftmost candidate without selecting a second engine.
+ if p.rawByteMulti.usable() {
+ if len(haystack) >= 4096 && p.rawByteOrigin.usable() {
+ return withZeroWidth(p.findRawByteOrigin(haystack))
+ }
+ return withZeroWidth(p.findRawByteFixedAnchored(haystack))
+ }
if anchor := p.chooseUnicodePairAnchor(haystack); anchor != nil {
return p.findUnicodePairAnchor(haystack, anchor)
}
if p.unicodeAnchor.n == 1 {
- return p.findUnicodeAnchor(haystack)
+ return withZeroWidth(p.findUnicodeAnchor(haystack))
}
// A partial root triple set cannot skip a general UTF-8 stream because a
// non-ASCII-only root may occur later. On AVX-512 BW, the high-byte scan
@@ -2298,29 +2423,248 @@ func (p *searchPlan) find(haystack string) (Match, bool) {
// the separately covered ASCII roots for the bounded Shufti transition. It
// still advances this one compiled plan at every survivor; it is a block
// transition, not a second matcher.
- const asciiTripleMinBytes = 64
if p.asciiPairAnchors.usable() && runtimeVectorBits() == 512 && len(haystack) >= asciiTripleMinBytes &&
rootSkipASCII(haystack, 0, rootExact, 0) == len(haystack) {
- return p.findASCIIPairAnchored(haystack)
+ return withZeroWidth(p.findASCIIPairAnchored(haystack))
}
if p.patternCount > 1 && p.rootKind == rootGeneric && p.asciiTriplesComplete && p.asciiTriples.shufti.usable() &&
runtimeVectorBits() == 512 && len(haystack) >= asciiTripleMinBytes &&
rootSkipASCII(haystack, 0, rootExact, 0) == len(haystack) {
- return p.findASCIITripleFiltered(haystack)
+ return withZeroWidth(p.findASCIITripleFiltered(haystack))
}
if p.triplesComplete && p.triples.usable() && (p.patternCount == 1 || p.rootKind == rootGeneric) {
- return p.findFiltered(haystack)
+ return withZeroWidth(p.findFiltered(haystack))
}
if p.rootKind == rootGeneric && p.filter.usable() {
- return p.findFiltered(haystack)
+ return withZeroWidth(p.findFiltered(haystack))
}
- return p.findUnfiltered(haystack)
+ return withZeroWidth(p.findUnfiltered(haystack))
+}
+
+// asciiPartitionWindowWorthwhile keeps a widened window from dominating short
+// haystacks with unusually wide compiled patterns.
+func asciiPartitionWindowWorthwhile(length, maxBytes int) bool {
+ // A widened window spends up to maxBytes on either side of an exceptional
+ // span. Keep at least three quarters of a sufficiently long haystack
+ // available for ASCII-run work; short inputs and very wide compiled patterns
+ // otherwise turn the partition into extra work.
+ return length >= 2*asciiPartitionSampleBytes && maxBytes > 0 && maxBytes <= length/8
}
// findUnfiltered advances the decoded plan without raw byte filters. It is the
// boundary-safe path for plans containing opaque UTF-8 continuation bytes.
func (p *searchPlan) findUnfiltered(haystack string) (Match, bool) {
+ if p.asciiPartitionUsable() && asciiPartitionWindowWorthwhile(len(haystack), p.maxBytes) && !asciiPartitionTailBoundary(haystack) {
+ firstHigh := rootSkipASCII(haystack, 0, rootExact, 0)
+ if firstHigh < len(haystack) && asciiPartitionSparseEnough(haystack, firstHigh) {
+ return p.findPartitionedASCII(haystack, firstHigh)
+ }
+ }
+ return p.findUnfilteredDecodedLegacy(haystack)
+}
+
+// asciiPartitionStats is an optional diagnostic view of the partition route.
+// It is populated only by focused package benchmarks; normal searches pass nil
+// and retain the same hot path without counters.
+type asciiPartitionStats struct {
+ highBytes int
+ firstExceptional int
+ asciiCandidateBytes int
+ decodedWindowBytes int
+ decodedWindows int
+ fallbackEntries int
+}
+
+func (p *searchPlan) findUnfilteredWithStats(haystack string, stats *asciiPartitionStats) (Match, bool) {
+ if stats != nil {
+ stats.firstExceptional = -1
+ }
+ if p.asciiPartitionUsable() && asciiPartitionWindowWorthwhile(len(haystack), p.maxBytes) && !asciiPartitionTailBoundary(haystack) {
+ firstHigh := rootSkipASCII(haystack, 0, rootExact, 0)
+ if firstHigh < len(haystack) && asciiPartitionSparseEnough(haystack, firstHigh) {
+ if stats != nil {
+ stats.firstExceptional = firstHigh
+ return p.findPartitionedASCIIWithStats(haystack, firstHigh, stats)
+ }
+ return p.findPartitionedASCII(haystack, firstHigh)
+ }
+ }
+ if stats != nil {
+ stats.fallbackEntries++
+ for at := 0; at < len(haystack); at++ {
+ if haystack[at] >= utf8.RuneSelf {
+ stats.highBytes++
+ if stats.firstExceptional < 0 {
+ stats.firstExceptional = at
+ }
+ }
+ }
+ }
+ return p.findUnfilteredDecodedLegacy(haystack)
+}
+
+// asciiPartitionTailBoundary keeps the old decoded executor for dense input
+// and for a final boundary with no block-sized run after it. Earlier runs are
+// still eligible when the tail is clean, while the check itself is bounded by
+// the existing ASCII block threshold.
+func asciiPartitionTailBoundary(haystack string) bool {
+ if len(haystack) < asciiTripleMinBytes {
+ return true
+ }
+ start := len(haystack) - asciiTripleMinBytes
+ for at := start; at < len(haystack); at++ {
+ if haystack[at] >= utf8.RuneSelf || haystack[at] == 0 {
+ return true
+ }
+ }
+ return false
+}
+
+// asciiPartitionSparseEnough rejects exceptional-byte densities for which the
+// boundary work and repeated decoded transitions cost more than the legacy
+// executor. The samples are deliberately capped and spread through the input:
+// sparse inputs retain the vector ASCII gaps, while a later dense region is
+// rejected before the first partitioned window is spent on it.
+func asciiPartitionSparseEnough(haystack string, firstExceptional int) bool {
+ sparseSample := func(start, end int) bool {
+ high := 0
+ for at := start; at < end; {
+ at += rootSkipASCII(haystack[:end], at, rootExact, 0)
+ if at >= end {
+ break
+ }
+ if haystack[at] == 0 {
+ return false
+ }
+ high++
+ if high >= asciiPartitionMaxHigh {
+ return false
+ }
+ at++
+ }
+ return true
+ }
+
+ end := firstExceptional + asciiPartitionSampleBytes
+ if end > len(haystack) {
+ end = len(haystack)
+ }
+ if !sparseSample(firstExceptional, end) {
+ return false
+ }
+ if len(haystack)-firstExceptional <= asciiPartitionSampleBytes {
+ return true
+ }
+
+ // A bounded set of fixed-fraction samples catches a dense suffix or middle
+ // without turning admission into a second full haystack scan. Keep samples
+ // disjoint from the first window and clamp them at the input boundary.
+ for _, start := range [...]int{len(haystack) / 4, len(haystack) / 2, len(haystack) - asciiPartitionSampleBytes} {
+ if start < end {
+ continue
+ }
+ if !sparseSample(start, start+asciiPartitionSampleBytes) {
+ return false
+ }
+ }
+ return true
+}
+
+// findUnfilteredDecodedLegacy retains the original decoded prefix and
+// fallback shape for plans that cannot profit from ASCII-run re-entry. Keeping
+// this owner separate prevents the partition state and its boundary checks
+// from changing ordinary fallback work.
+func (p *searchPlan) findUnfilteredDecodedLegacy(haystack string) (Match, bool) {
+ state, unit := 0, 0
+ bestUnitStart := -1
+ best := Match{Pattern: -1, Start: -1}
+ if p.empty >= 0 {
+ // An empty pattern is a candidate at start zero, not an immediate
+ // answer: a lower-index non-empty pattern may also start at zero.
+ bestUnitStart = 0
+ best = Match{Pattern: p.empty, Start: 0}
+ }
+
+ // Common text stays in this no-allocation path. Because each preceding
+ // unit is one byte, a terminal's source start is direct arithmetic rather
+ // than a lookup in the variable-width offset ring used after the first
+ // non-ASCII byte.
+ at := 0
+ for at < len(haystack) && haystack[at] < utf8.RuneSelf {
+ // A root-to-root block cannot emit a non-empty terminal. Advance over
+ // sixteen such bytes at once; every other block remains on the exact
+ // same transition path below.
+ if state == 0 {
+ if p.rootKind != rootGeneric {
+ for at < len(haystack) {
+ skipped := rootSkipASCII(haystack, at, p.rootKind, p.rootNeedle)
+ if p.pairKind != rootGeneric {
+ skipped = pairSkipASCII(haystack, at, p.rootKind, p.rootNeedle, p.pairKind, p.pairNeedle)
+ }
+ if skipped == 0 {
+ break
+ }
+ if bestUnitStart >= 0 && unit+skipped-1 >= bestUnitStart+p.maxUnits-1 {
+ return best, true
+ }
+ at += skipped
+ unit += skipped
+ }
+ } else {
+ for at+16 <= len(haystack) {
+ block := haystack[at : at+16]
+ if p.rootByte[block[0]]|p.rootByte[block[1]]|p.rootByte[block[2]]|p.rootByte[block[3]]|
+ p.rootByte[block[4]]|p.rootByte[block[5]]|p.rootByte[block[6]]|p.rootByte[block[7]]|
+ p.rootByte[block[8]]|p.rootByte[block[9]]|p.rootByte[block[10]]|p.rootByte[block[11]]|
+ p.rootByte[block[12]]|p.rootByte[block[13]]|p.rootByte[block[14]]|p.rootByte[block[15]] != 0 {
+ break
+ }
+ if bestUnitStart >= 0 && unit+15 >= bestUnitStart+p.maxUnits-1 {
+ return best, true
+ }
+ at += len(block)
+ unit += len(block)
+ }
+ }
+ if at == len(haystack) || haystack[at] >= utf8.RuneSelf {
+ break
+ }
+ }
+ state = p.advance(state, p.ascii[haystack[at]])
+ if output := p.nodes[state].output; output.pattern >= 0 {
+ startUnit := unit - output.units + 1
+ if bestUnitStart < 0 || startUnit < bestUnitStart ||
+ startUnit == bestUnitStart && output.pattern < best.Pattern {
+ bestUnitStart = startUnit
+ best = Match{Pattern: output.pattern, Start: startUnit}
+ }
+ }
+ if bestUnitStart >= 0 && unit >= bestUnitStart+p.maxUnits-1 {
+ return best, true
+ }
+ at++
+ unit++
+ }
+ if at == len(haystack) {
+ if bestUnitStart < 0 {
+ return Match{}, false
+ }
+ return best, true
+ }
+
+ var inlineStarts [256]int
+ starts := inlineStarts[:]
+ if p.maxUnits > len(starts) {
+ starts = make([]int, p.maxUnits)
+ }
+ return p.findNonASCIIDecoded(haystack, at, unit, state, bestUnitStart, best, starts)
+}
+
+// findUnfilteredWithStarts is the decoded executor used for a bounded window.
+// A caller can provide its offset ring so each window reuses the same stack
+// storage rather than allocating a new ring.
+func (p *searchPlan) findUnfilteredWithStarts(haystack string, starts []int) (Match, bool) {
state, unit := 0, 0
bestUnitStart := -1
best := Match{Pattern: -1, Start: -1}
@@ -2397,7 +2741,14 @@ func (p *searchPlan) findUnfiltered(haystack string) (Match, bool) {
}
return best, true
}
- return p.findNonASCII(haystack, at, unit, state, bestUnitStart, best)
+ if starts == nil {
+ var inlineStarts [256]int
+ starts = inlineStarts[:]
+ if p.maxUnits > len(starts) {
+ starts = make([]int, p.maxUnits)
+ }
+ }
+ return p.findNonASCII(haystack, at, unit, state, bestUnitStart, best, starts)
}
// findASCIITripleFiltered is the all-ASCII specialization of the shared plan.
@@ -2449,6 +2800,70 @@ func (p *searchPlan) findASCIITripleFiltered(haystack string) (Match, bool) {
return best, true
}
+// tripleSkipASCIIRegion scans candidate starts in [start,end). The byte scout
+// may inspect the two following bytes needed by overlapping triple loads, but
+// the returned skip is clamped to the known-ASCII region. The decoded boundary
+// window owns starts whose source crosses the following exceptional span.
+func tripleSkipASCIIRegion(s string, start, end int, filter *tripleFilter) int {
+ if start >= end {
+ return 0
+ }
+ scanEnd := end + 2
+ if scanEnd > len(s) {
+ scanEnd = len(s)
+ }
+ skipped := tripleSkipBytes(s[start:scanEnd], 0, filter)
+ if limit := end - start; skipped > limit {
+ return limit
+ }
+ return skipped
+}
+
+// findASCIITripleFilteredRange is the bounded form used for a maximal ASCII
+// run. Its vector scout may read two bytes beyond end from the original
+// haystack, but it never advances a candidate beyond end; a decoded window
+// handles matches whose source crosses the following exceptional span.
+func (p *searchPlan) findASCIITripleFilteredRange(haystack string, start, end int) (Match, bool) {
+ var inlineStarts [256]int
+ starts := inlineStarts[:]
+ if p.maxUnits > len(starts) {
+ starts = make([]int, p.maxUnits)
+ }
+
+ state, history := 0, 0
+ best := Match{Pattern: -1, Start: -1}
+ for at := start; at < end; {
+ if state == 0 {
+ if best.Pattern >= 0 && at > best.Start {
+ return best, true
+ }
+ history = 0
+ skipped := tripleSkipASCIIRegion(haystack, at, end, &p.asciiTriples)
+ at += skipped
+ if at == end {
+ break
+ }
+ }
+
+ starts[history%len(starts)] = at
+ token, size := p.haystackToken(haystack, at)
+ state = p.advance(state, token)
+ history++
+ if output := p.nodes[state].output; output.pattern >= 0 {
+ startAt := starts[(history-output.units)%len(starts)]
+ if best.Pattern < 0 || startAt < best.Start ||
+ startAt == best.Start && output.pattern < best.Pattern {
+ best = Match{Pattern: output.pattern, Start: startAt}
+ }
+ }
+ at += size
+ }
+ if best.Pattern < 0 {
+ return Match{}, false
+ }
+ return best, true
+}
+
// replayASCIIAnchorStart feeds a bounded candidate window back through the
// shared compiled plan. Filtering never confirms a pattern directly: the
// plan's propagated outputs select both the terminal and its lowest-ID tie.
@@ -2537,6 +2952,7 @@ func (p *searchPlan) findFiltered(haystack string) (Match, bool) {
if p.empty >= 0 {
best = Match{Pattern: p.empty, Start: 0}
}
+ raw := p.hasRawByteTokenPlan()
for at := 0; at < len(haystack); {
if state == 0 {
// Once no prefix is live, a previously found start cannot be
@@ -2566,8 +2982,39 @@ func (p *searchPlan) findFiltered(haystack string) (Match, bool) {
}
starts[history%len(starts)] = at
- token, size := p.haystackToken(haystack, at)
- state = p.advance(state, token)
+ size := 0
+ if raw {
+ // Keep the raw classification in this loop so the source-byte loads
+ // and dense transition stay in one generated body. Wider and malformed
+ // input falls through to the complete decoded transition below.
+ value := haystack[at]
+ if value < utf8.RuneSelf {
+ token := p.ascii[value]
+ if token == 0 {
+ state = 0
+ } else {
+ state = int(p.dense[state*p.stride+int(token)])
+ }
+ size = 1
+ } else if value >= 0xc2 && value <= 0xdf && at+1 < len(haystack) {
+ trail := haystack[at+1]
+ if trail >= 0x80 && trail < 0xc0 {
+ row := p.opaque[value]
+ if row == 0 {
+ state = 0
+ } else if token := p.opaque[(int(row)-1)*rawByteTrailClasses+int(trail-0x80)]; token == 0 {
+ state = 0
+ } else {
+ state = int(p.dense[state*p.stride+int(token)])
+ }
+ size = 2
+ }
+ }
+ }
+ if size == 0 {
+ token, decodedSize := p.haystackToken(haystack, at)
+ state, size = p.advance(state, token), decodedSize
+ }
history++
if output := p.nodes[state].output; output.pattern >= 0 {
start := starts[(history-output.units)%len(starts)]
@@ -2584,15 +3031,243 @@ func (p *searchPlan) findFiltered(haystack string) (Match, bool) {
return best, true
}
-// findNonASCII resumes the plan at its first non-ASCII byte. The ASCII prefix
-// has one source byte per unit, so its still-live starts can be reconstructed
-// into the offset ring without a second traversal of the haystack.
-func (p *searchPlan) findNonASCII(haystack string, at, unit, state, bestUnitStart int, best Match) (Match, bool) {
+func (p *searchPlan) asciiPartitionUsable() bool {
+ if p.empty >= 0 || p.opaqueContinuation || p.rootKind != rootGeneric || p.patternCount <= 1 ||
+ !p.asciiTriplesComplete || !p.asciiTriples.shufti.usable() {
+ return false
+ }
+ return runtimeVectorBits() == 512
+}
+
+// findASCIIRegion runs the strongest existing byte candidate transition that
+// is sound on a known-ASCII region. The caller has already separated all bytes
+// at or above UTF-8's RuneSelf, so the all-ASCII routes do not need to prove
+// that property again.
+func (p *searchPlan) findASCIIRegion(haystack string, start, end int, starts []int) (Match, bool) {
+ if end-start >= asciiTripleMinBytes {
+ return p.findASCIITripleFilteredRange(haystack, start, end)
+ }
+ match, ok := p.findUnfilteredWithStarts(haystack[start:end], starts)
+ if ok {
+ match.Start += start
+ }
+ return match, ok
+}
+
+// findPartitionedASCII resumes after the first high byte. It visits each
+// maximal ASCII run with the existing candidate transition and decodes only a
+// coalesced window around exceptional spans. The ASCII transition stops at the
+// exceptional byte, so it can confirm matches that finish before that byte;
+// the decoded window owns starts whose source crosses the span.
+func (p *searchPlan) findPartitionedASCII(haystack string, firstHigh int) (Match, bool) {
+ return p.findPartitionedASCIIWithStats(haystack, firstHigh, nil)
+}
+
+func (p *searchPlan) findPartitionedASCIIWithStats(haystack string, firstHigh int, stats *asciiPartitionStats) (Match, bool) {
+ maxBytes := p.maxBytes
+ if maxBytes < 1 || firstHigh >= len(haystack) {
+ return Match{}, false
+ }
+
var inlineStarts [256]int
starts := inlineStarts[:]
if p.maxUnits > len(starts) {
starts = make([]int, p.maxUnits)
}
+
+ var recentHigh [asciiPartitionMaxHigh]int
+ recentHighCount := 0
+ checkExceptional := func(start, end int, recent [asciiPartitionMaxHigh]int, count int) ([asciiPartitionMaxHigh]int, int, bool) {
+ for at := start; at < end; at++ {
+ if haystack[at] == 0 {
+ return recent, count, false
+ }
+ if haystack[at] < utf8.RuneSelf {
+ continue
+ }
+ if count >= asciiPartitionMaxHigh-1 &&
+ at-recent[(count-(asciiPartitionMaxHigh-1))%asciiPartitionMaxHigh] < asciiPartitionSampleBytes {
+ return recent, count, false
+ }
+ recent[count%asciiPartitionMaxHigh] = at
+ count++
+ }
+ // Malformed bytes have no stable decoded window to amortize. Keep their
+ // established opaque-byte executor rather than paying one transition per
+ // isolated high byte. The density checks above run first so a contiguous
+ // exceptional suffix is rejected without validating it in full.
+ return recent, count, utf8.ValidString(haystack[start:end])
+ }
+ recordExceptional := func(start, end int) bool {
+ var ok bool
+ recentHigh, recentHighCount, ok = checkExceptional(start, end, recentHigh, recentHighCount)
+ return ok
+ }
+
+ cursor := 0
+ fallback := func() (Match, bool) {
+ if stats != nil {
+ stats.fallbackEntries++
+ }
+ match, ok := p.findUnfilteredDecodedLegacy(haystack[cursor:])
+ if ok {
+ match.Start += cursor
+ }
+ return match, ok
+ }
+ spanStart := firstHigh
+ pendingMatch := Match{}
+ pendingOK := false
+ for {
+ spanEnd := spanStart
+ for spanEnd < len(haystack) && (haystack[spanEnd] >= utf8.RuneSelf || haystack[spanEnd] == 0) {
+ spanEnd++
+ }
+ if !recordExceptional(spanStart, spanEnd) {
+ return fallback()
+ }
+ if stats != nil {
+ for at := spanStart; at < spanEnd; at++ {
+ if haystack[at] >= utf8.RuneSelf {
+ stats.highBytes++
+ }
+ }
+ }
+ windowStart := spanStart - maxBytes
+ if windowStart < 0 {
+ windowStart = 0
+ }
+ windowEnd := spanEnd + maxBytes
+ if windowEnd > len(haystack) {
+ windowEnd = len(haystack)
+ }
+ lastHighEnd := spanEnd
+
+ // Coalesce exceptional spans whose widened windows touch. This leaves
+ // one decoded transition for a cluster rather than restarting it at
+ // every high byte.
+ nextStart := -1
+ for at := spanEnd; at < len(haystack); {
+ at += rootSkipASCII(haystack, at, rootExact, 0)
+ if at == len(haystack) {
+ break
+ }
+ nextSpanEnd := at
+ for nextSpanEnd < len(haystack) && (haystack[nextSpanEnd] >= utf8.RuneSelf || haystack[nextSpanEnd] == 0) {
+ nextSpanEnd++
+ }
+ nextWindowStart := at - maxBytes
+ if nextWindowStart < 0 {
+ nextWindowStart = 0
+ }
+ nextWindowEnd := nextSpanEnd + maxBytes
+ if nextWindowEnd > len(haystack) {
+ nextWindowEnd = len(haystack)
+ }
+ // Probe nearby next spans before spending the current group's window. A
+ // dense or malformed span must not make us partition work and then
+ // restart the legacy decoder from the same safe cursor. For a distant
+ // span, process this group first so a later fallback can resume after
+ // its widened window instead of rescanning this ASCII gap.
+ if nextWindowStart > windowEnd && at-spanStart > asciiPartitionLookaheadBytes {
+ nextStart = at
+ break
+ }
+ probeHigh, probeCount, ok := checkExceptional(at, nextSpanEnd, recentHigh, recentHighCount)
+ if !ok {
+ return fallback()
+ }
+ if nextWindowStart > windowEnd {
+ nextStart = at
+ break
+ }
+ recentHigh, recentHighCount = probeHigh, probeCount
+ if stats != nil {
+ for high := at; high < nextSpanEnd; high++ {
+ if haystack[high] >= utf8.RuneSelf {
+ stats.highBytes++
+ }
+ }
+ }
+ if nextWindowEnd > windowEnd {
+ windowEnd = nextWindowEnd
+ }
+ lastHighEnd = nextSpanEnd
+ at = nextSpanEnd
+ }
+
+ asciiMatch := pendingMatch
+ asciiOK := pendingOK
+ pendingOK = false
+ if cursor < spanStart {
+ if stats != nil {
+ stats.asciiCandidateBytes += spanStart - cursor
+ }
+ regionMatch, regionOK := p.findASCIIRegion(haystack, cursor, spanStart, starts)
+ if regionOK && (!asciiOK || regionMatch.Start < asciiMatch.Start ||
+ regionMatch.Start == asciiMatch.Start && regionMatch.Pattern < asciiMatch.Pattern) {
+ asciiMatch, asciiOK = regionMatch, true
+ }
+ }
+ if asciiOK && asciiMatch.Start < windowStart {
+ return asciiMatch, true
+ }
+ if stats != nil {
+ stats.decodedWindowBytes += windowEnd - windowStart
+ stats.decodedWindows++
+ }
+ windowMatch, windowOK := p.findUnfilteredWithStarts(haystack[windowStart:windowEnd], starts)
+ if windowOK {
+ windowMatch.Start += windowStart
+ }
+ if asciiOK && (!windowOK || asciiMatch.Start < windowMatch.Start ||
+ asciiMatch.Start == windowMatch.Start && asciiMatch.Pattern < windowMatch.Pattern) {
+ return asciiMatch, true
+ }
+ if windowOK {
+ return windowMatch, true
+ }
+
+ if nextStart < 0 {
+ if lastHighEnd < len(haystack) {
+ if stats != nil {
+ stats.asciiCandidateBytes += len(haystack) - lastHighEnd
+ }
+ return p.findASCIIRegion(haystack, lastHighEnd, len(haystack), starts)
+ }
+ return Match{}, false
+ }
+
+ // The next group starts after this group's widened window. Search the
+ // ASCII gap before its decoded window; starts in that window's overlap
+ // are checked by the next iteration against cross-span matches.
+ nextWindowStart := nextStart - maxBytes
+ if nextWindowStart < 0 {
+ nextWindowStart = 0
+ }
+ if lastHighEnd < nextStart {
+ if stats != nil {
+ stats.asciiCandidateBytes += nextStart - lastHighEnd
+ }
+ if match, ok := p.findASCIIRegion(haystack, lastHighEnd, nextStart, starts); ok {
+ if match.Start < nextWindowStart {
+ return match, true
+ }
+ pendingMatch, pendingOK = match, true
+ }
+ }
+ cursor = nextWindowStart
+ spanStart = nextStart
+ }
+}
+
+// findNonASCII resumes the plan at its first non-ASCII byte. It retains the
+// original decoded executor for bounded windows and short known-ASCII regions.
+func (p *searchPlan) findNonASCII(haystack string, at, unit, state, bestUnitStart int, best Match, starts []int) (Match, bool) {
+ return p.findNonASCIIDecoded(haystack, at, unit, state, bestUnitStart, best, starts)
+}
+
+func (p *searchPlan) findNonASCIIDecoded(haystack string, at, unit, state, bestUnitStart int, best Match, starts []int) (Match, bool) {
first := unit - p.maxUnits + 1
if first < 0 {
first = 0
diff --git a/raw_byte.go b/raw_byte.go
new file mode 100644
index 0000000..63045f3
--- /dev/null
+++ b/raw_byte.go
@@ -0,0 +1,824 @@
+package casei
+
+import (
+ "math/bits"
+ "unicode"
+ "unicode/utf8"
+)
+
+// A raw-byte token plan keeps the two-byte UTF-8 spellings needed by a small
+// dense multi-pattern plan in the plan's already-reserved opaque-token array.
+// The normal opaque table is unused for an eligible plan: it has no malformed
+// pattern bytes. Reusing that storage makes the replacement transition
+// allocation-free for a fresh Matcher as well as for a reused one.
+const (
+ rawByteMaxLeadRows = 2
+ rawByteTrailClasses = 0x40
+ rawByteTokenClasses = rawByteMaxLeadRows * rawByteTrailClasses
+ rawByteTransitionStride = utf8.RuneSelf + rawByteTokenClasses
+ rawByteMaxTransitionSize = 48 << 10
+)
+
+// rawByteTokenConfig maps the two-byte UTF-8 spellings in the existing fold
+// token map to their dense token. ASCII already has p.ascii, so it needs no
+// duplicate entries. A lead row is deliberately correlated with its following
+// continuation byte; no independent byte masks can make a two-byte rune.
+type rawByteTokenConfig struct {
+ leads [rawByteMaxLeadRows]byte
+ leadN uint8
+ tokens [rawByteTokenClasses]uint32
+}
+
+// rawByteTokenConfigFor proves that every direct class is the same token the
+// decoded path would select. The compact path is intentionally limited to the
+// small plans for which its dense transition table remains cache-resident.
+func (p *searchPlan) rawByteTokenConfigFor() (rawByteTokenConfig, bool) {
+ var config rawByteTokenConfig
+ if p.patternCount < 2 || p.dense == nil ||
+ len(p.nodes) > rawByteMaxTransitionSize/(2*rawByteTransitionStride) ||
+ len(p.dense) > rawByteMaxTransitionSize/4 {
+ return config, false
+ }
+ for _, token := range p.opaque {
+ if token != 0 {
+ return config, false
+ }
+ }
+
+ var haveLead [256]bool
+ for r := range p.runes {
+ if r < utf8.RuneSelf {
+ continue
+ }
+ var encoded [utf8.UTFMax]byte
+ if utf8.EncodeRune(encoded[:], r) == 2 {
+ haveLead[encoded[0]] = true
+ }
+ }
+ for lead := range haveLead {
+ if !haveLead[lead] {
+ continue
+ }
+ if int(config.leadN) == len(config.leads) {
+ return rawByteTokenConfig{}, false
+ }
+ config.leads[config.leadN] = byte(lead)
+ config.leadN++
+ }
+ if config.leadN == 0 {
+ return rawByteTokenConfig{}, false
+ }
+
+ for r, token := range p.runes {
+ if r < utf8.RuneSelf {
+ continue
+ }
+ var encoded [utf8.UTFMax]byte
+ if utf8.EncodeRune(encoded[:], r) != 2 || encoded[1] < 0x80 || encoded[1] >= 0xc0 {
+ continue
+ }
+ row := -1
+ for i := 0; i < int(config.leadN); i++ {
+ if config.leads[i] == encoded[0] {
+ row = i
+ break
+ }
+ }
+ if row < 0 {
+ return rawByteTokenConfig{}, false
+ }
+ class := row*rawByteTrailClasses + int(encoded[1]-0x80)
+ if prior := config.tokens[class]; prior != 0 && prior != token {
+ return rawByteTokenConfig{}, false
+ }
+ config.tokens[class] = token
+ }
+ return config, true
+}
+
+// makeRawByteTokenPlan records a proven compact raw map during normal plan
+// construction. singleTokens is otherwise used only by one-pattern plans; an
+// empty, non-nil slice marks this multi-pattern plan while pointing at storage
+// it already owns. No lazy publication, allocation, or caller history is
+// involved.
+func (p *searchPlan) makeRawByteTokenPlan(patterns []string) {
+ config, ok := p.rawByteTokenConfigFor()
+ if !ok {
+ return
+ }
+ copy(p.opaque[:rawByteTokenClasses], config.tokens[:])
+ for row := 0; row < int(config.leadN); row++ {
+ p.opaque[config.leads[row]] = uint32(row + 1)
+ }
+ p.singleTokens = p.opaque[:0]
+ p.makeRawByteMultiAnchorFilter(patterns)
+ if p.rawByteMulti.usable() {
+ p.rawByteOrigin = rawByteOriginGateFor(patterns)
+ }
+}
+
+func (p *searchPlan) hasRawByteTokenPlan() bool {
+ return p.patternCount > 1 && p.dense != nil && p.singleTokens != nil && len(p.singleTokens) == 0
+}
+
+func (p *searchPlan) rawByteAdvanceToken(state int, token uint32) int {
+ if token == 0 {
+ return 0
+ }
+ return int(p.dense[state*p.stride+int(token)])
+}
+
+// rawByteAdvance consumes exactly one ASCII byte or one complete two-byte UTF-8
+// sequence. Unsupported widths and malformed encodings return ok=false so the
+// decoded path retains its full Unicode and opaque-byte authority.
+func (p *searchPlan) rawByteAdvance(haystack string, at, state int) (next, size int, ok bool) {
+ value := haystack[at]
+ if value < utf8.RuneSelf {
+ return p.rawByteAdvanceToken(state, p.ascii[value]), 1, true
+ }
+ if value < 0xc2 || value > 0xdf || at+1 == len(haystack) {
+ return 0, 0, false
+ }
+ trail := haystack[at+1]
+ if trail < 0x80 || trail >= 0xc0 {
+ return 0, 0, false
+ }
+ row := p.opaque[value]
+ if row == 0 {
+ // This valid two-byte rune is absent from every pattern orbit, so its
+ // decoded token is zero and the shared state machine resets.
+ return 0, 2, true
+ }
+ token := p.opaque[(int(row)-1)*rawByteTrailClasses+int(trail-0x80)]
+ return p.rawByteAdvanceToken(state, token), 2, true
+}
+
+// rawByteMultiAnchorGroups is bounded by the eight tag bits carried through
+// the VBMI tables. Limiting the projection is a plan property, not a workload
+// admission rule: plans outside it keep the ordinary decoded enumeration.
+const (
+ rawByteMultiAnchorGroups = 8
+ rawByteMultiAnchorForms = 4
+ rawByteMultiAnchorStartOffsets = 4
+ rawByteMultiAnchorConfirmGroups = 3
+)
+
+// rawByteMultiAnchorPairSet lists the exact two-byte spellings of one selected
+// fold orbit. The vector tables retain only low-six-bit membership, so this
+// exact representation removes aliases before the shared plan is replayed.
+type rawByteMultiAnchorPairSet struct {
+ pairs [rawByteMultiAnchorForms]uint16
+ n uint8
+}
+
+func (pairs rawByteMultiAnchorPairSet) matches(s string, at int) bool {
+ if at < 0 || at+1 >= len(s) {
+ return false
+ }
+ value := uint16(s[at]) | uint16(s[at+1])<<8
+ for i := 0; i < int(pairs.n); i++ {
+ if pairs.pairs[i] == value {
+ return true
+ }
+ }
+ return false
+}
+
+// rawByteMultiAnchor records one literal's three correlated interior UTF-8
+// pairs. starts includes every possible source-byte prefix width before the
+// primary pair, so a width-changing fold before an anchor can only create a
+// replayed extra candidate, never hide the true match.
+type rawByteMultiAnchor struct {
+ primary, confirm, guard rawByteMultiAnchorPairSet
+ starts [rawByteMultiAnchorStartOffsets]uint8
+ confirmOffset [rawByteMultiAnchorConfirmGroups]uint8
+ guardOffset [rawByteMultiAnchorStartOffsets]uint8
+ startN, confirmN, guardN uint8
+}
+
+// rawByteMultiAnchorFilter is the compact vector-facing representation of one
+// tagged pair for every literal. A bit identifies the literal group. The first
+// confirmation table for an offset retains that bit only when the same literal
+// owns both pairs; the exact third pair is checked in Go before the shared raw
+// transition replay. The first 512 bytes have a fixed table layout consumed by
+// rawByteMultiAnchorSkip64.
+type rawByteMultiAnchorFilter struct {
+ first, second [64]byte
+ confirmFirst [rawByteMultiAnchorConfirmGroups][64]byte
+ confirmSecond [rawByteMultiAnchorConfirmGroups][64]byte
+ confirmOffset [rawByteMultiAnchorConfirmGroups]uint8
+ confirmN, maxConfirmOffset uint8
+ maxOffset, valid uint8
+ anchors [rawByteMultiAnchorGroups]rawByteMultiAnchor
+}
+
+// rawByteOriginGate records an exact ASCII byte found in every rendering of
+// every raw-filter literal. maxPrefix bounds its latest folded source offset;
+// a Find start before the first byte minus that bound is impossible.
+type rawByteOriginGate struct {
+ byte byte
+ maxPrefix uint16
+ valid uint8
+}
+
+func (gate rawByteOriginGate) usable() bool { return gate.valid != 0 }
+
+func (filter *rawByteMultiAnchorFilter) usable() bool {
+ return filter != nil && filter.valid != 0
+}
+
+// tagDiverse reports whether the compiled tagged anchors can distinguish at
+// least two literals before replay. A shared prefix whose primary,
+// confirmation, guard, and source-width sets are identical for every tag pays
+// the vector filter and then replays every pattern at the same survivor; it has
+// no multi-pattern selectivity and must retain the ordinary Unicode route.
+func (filter *rawByteMultiAnchorFilter) tagDiverse() bool {
+ var first rawByteMultiAnchor
+ haveFirst := false
+ for _, anchor := range filter.anchors {
+ if anchor.startN == 0 {
+ continue
+ }
+ if !haveFirst {
+ first, haveFirst = anchor, true
+ continue
+ }
+ if anchor != first {
+ return true
+ }
+ }
+ return false
+}
+
+// rawByteOriginGateFor intersects fixed ASCII runes across the literals. A
+// chosen rune encodes as the same byte in every simple-fold spelling. For each
+// literal the earliest such occurrence minimizes its worst-case prefix; the
+// global maximum is the safe Find lookback bound.
+func rawByteOriginGateFor(patterns []string) rawByteOriginGate {
+ var common [utf8.RuneSelf]bool
+ for i := range common {
+ common[i] = true
+ }
+ var maxPrefix [utf8.RuneSelf]uint16
+ for _, pattern := range patterns {
+ var seen [utf8.RuneSelf]bool
+ var localPrefix [utf8.RuneSelf]uint16
+ prefix := 0
+ for at := 0; at < len(pattern); {
+ r, size := utf8.DecodeRuneInString(pattern[at:])
+ if r == utf8.RuneError && size == 1 {
+ return rawByteOriginGate{}
+ }
+ if r < utf8.RuneSelf && unicode.SimpleFold(r) == r {
+ value := byte(r)
+ if !seen[value] || prefix < int(localPrefix[value]) {
+ seen[value] = true
+ localPrefix[value] = uint16(prefix)
+ }
+ }
+ maxWidth := 0
+ for member := r; ; member = unicode.SimpleFold(member) {
+ var encoded [utf8.UTFMax]byte
+ if width := utf8.EncodeRune(encoded[:], member); width > maxWidth {
+ maxWidth = width
+ }
+ if unicode.SimpleFold(member) == r {
+ break
+ }
+ }
+ prefix += maxWidth
+ if prefix > int(^uint16(0)) {
+ return rawByteOriginGate{}
+ }
+ at += size
+ }
+ for value := range common {
+ if !seen[value] {
+ common[value] = false
+ continue
+ }
+ if localPrefix[value] > maxPrefix[value] {
+ maxPrefix[value] = localPrefix[value]
+ }
+ }
+ }
+
+ frequency := rawByteMultiAnchorFrequency(patterns)
+ best, bestScore := -1, uint16(^uint16(0))
+ for value, present := range common {
+ if !present {
+ continue
+ }
+ score := frequency[value]
+ if best < 0 || score < bestScore || score == bestScore &&
+ (maxPrefix[value] < maxPrefix[best] || maxPrefix[value] == maxPrefix[best] && value < best) {
+ best, bestScore = value, score
+ }
+ }
+ if best < 0 {
+ return rawByteOriginGate{}
+ }
+ return rawByteOriginGate{byte: byte(best), maxPrefix: maxPrefix[best], valid: 1}
+}
+
+func rawByteMultiAnchorPairSetFor(r rune) (rawByteMultiAnchorPairSet, bool) {
+ var out rawByteMultiAnchorPairSet
+ for member := r; ; member = unicode.SimpleFold(member) {
+ var encoded [utf8.UTFMax]byte
+ if utf8.EncodeRune(encoded[:], member) != 2 {
+ return rawByteMultiAnchorPairSet{}, false
+ }
+ pair := uint16(encoded[0]) | uint16(encoded[1])<<8
+ duplicate := false
+ for i := 0; i < int(out.n); i++ {
+ duplicate = duplicate || out.pairs[i] == pair
+ }
+ if !duplicate {
+ if int(out.n) == len(out.pairs) {
+ return rawByteMultiAnchorPairSet{}, false
+ }
+ out.pairs[out.n] = pair
+ out.n++
+ }
+ if unicode.SimpleFold(member) == r {
+ break
+ }
+ }
+ return out, out.n != 0
+}
+
+// rawByteMultiAnchorPrefixWidths records the bounded set of possible byte
+// widths before an anchor. The cross product is deliberately collapsed by
+// width: it is only used to nominate replay starts, whose shared plan decides
+// whether that spelling actually matches.
+func rawByteMultiAnchorPrefixWidths(pattern string, end int, out *[rawByteMultiAnchorStartOffsets]uint8) (int, bool) {
+ out[0] = 0
+ n := 1
+ for at := 0; at < end; {
+ r, size := utf8.DecodeRuneInString(pattern[at:])
+ if r == utf8.RuneError && size == 1 {
+ return 0, false
+ }
+ var next [rawByteMultiAnchorStartOffsets]uint8
+ nextN := 0
+ for member := r; ; member = unicode.SimpleFold(member) {
+ var encoded [utf8.UTFMax]byte
+ width := utf8.EncodeRune(encoded[:], member)
+ for i := 0; i < n; i++ {
+ value := int(out[i]) + width
+ if value > int(^uint8(0)) {
+ return 0, false
+ }
+ duplicate := false
+ for j := 0; j < nextN; j++ {
+ duplicate = duplicate || next[j] == uint8(value)
+ }
+ if !duplicate {
+ if nextN == len(next) {
+ return 0, false
+ }
+ next[nextN] = uint8(value)
+ nextN++
+ }
+ }
+ if unicode.SimpleFold(member) == r {
+ break
+ }
+ }
+ *out = next
+ n = nextN
+ at += size
+ }
+ return n, true
+}
+
+// rawByteMultiAnchorFrequency estimates selectivity only from the immutable
+// literal set. It intentionally never samples a haystack, so plan selection
+// cannot become benchmark- or caller-dependent.
+func rawByteMultiAnchorFrequency(patterns []string) (out [256]uint16) {
+ for _, pattern := range patterns {
+ for at := 0; at < len(pattern); {
+ r, size := utf8.DecodeRuneInString(pattern[at:])
+ if r == utf8.RuneError && size == 1 {
+ break
+ }
+ for member := r; ; member = unicode.SimpleFold(member) {
+ var encoded [utf8.UTFMax]byte
+ for _, value := range encoded[:utf8.EncodeRune(encoded[:], member)] {
+ if out[value] != ^uint16(0) {
+ out[value]++
+ }
+ }
+ if unicode.SimpleFold(member) == r {
+ break
+ }
+ }
+ at += size
+ }
+ }
+ return out
+}
+
+func rawByteMultiAnchorPairCost(pairs rawByteMultiAnchorPairSet, frequency *[256]uint16) uint64 {
+ var cost uint64
+ for i := 0; i < int(pairs.n); i++ {
+ pair := pairs.pairs[i]
+ first, second := byte(pair), byte(pair>>8)
+ firstCost, secondCost := uint64(frequency[first]), uint64(frequency[second])
+ if firstCost == 0 {
+ firstCost = 1
+ }
+ if secondCost == 0 {
+ secondCost = 1
+ }
+ cost += firstCost * secondCost
+ }
+ return cost
+}
+
+type rawByteMultiAnchorCandidate struct {
+ anchor rawByteMultiAnchor
+ score uint64
+ at int
+}
+
+// rawByteMultiAnchorRelativeWidths records all possible byte displacements
+// from start to end. Independent width choices may yield a crossed offset, but
+// that only creates a scalar-guarded replay; it cannot remove the true one.
+func rawByteMultiAnchorRelativeWidths(pattern string, start, end int, out *[rawByteMultiAnchorStartOffsets]uint8) (int, bool) {
+ return rawByteMultiAnchorPrefixWidths(pattern[start:end], end-start, out)
+}
+
+type rawByteMultiAnchorUnit struct {
+ at int
+ pairs rawByteMultiAnchorPairSet
+}
+
+// rawByteMultiAnchorCandidateFor chooses three width-stable two-byte interior
+// pairs. Variable-width forms between pairs are represented by every bounded
+// displacement; this is why the vector filter has three confirmation groups.
+func rawByteMultiAnchorCandidateFor(pattern string, primary, confirm, guard rawByteMultiAnchorUnit, frequency *[256]uint16) (rawByteMultiAnchorCandidate, bool) {
+ if primary.at == 0 {
+ return rawByteMultiAnchorCandidate{}, false
+ }
+ var starts, guardOffsets [rawByteMultiAnchorStartOffsets]uint8
+ var possibleConfirmOffsets [rawByteMultiAnchorStartOffsets]uint8
+ startN, ok := rawByteMultiAnchorPrefixWidths(pattern, primary.at, &starts)
+ if !ok || startN == 0 {
+ return rawByteMultiAnchorCandidate{}, false
+ }
+ confirmN, ok := rawByteMultiAnchorRelativeWidths(pattern, primary.at, confirm.at, &possibleConfirmOffsets)
+ if !ok || confirmN == 0 || confirmN > rawByteMultiAnchorConfirmGroups {
+ return rawByteMultiAnchorCandidate{}, false
+ }
+ var confirmOffsets [rawByteMultiAnchorConfirmGroups]uint8
+ copy(confirmOffsets[:], possibleConfirmOffsets[:confirmN])
+ guardN, ok := rawByteMultiAnchorRelativeWidths(pattern, primary.at, guard.at, &guardOffsets)
+ if !ok || guardN == 0 {
+ return rawByteMultiAnchorCandidate{}, false
+ }
+ return rawByteMultiAnchorCandidate{
+ anchor: rawByteMultiAnchor{
+ primary: primary.pairs,
+ confirm: confirm.pairs,
+ guard: guard.pairs,
+ starts: starts,
+ confirmOffset: confirmOffsets,
+ guardOffset: guardOffsets,
+ startN: uint8(startN),
+ confirmN: uint8(confirmN),
+ guardN: uint8(guardN),
+ },
+ score: rawByteMultiAnchorPairCost(primary.pairs, frequency) +
+ rawByteMultiAnchorPairCost(confirm.pairs, frequency) +
+ rawByteMultiAnchorPairCost(guard.pairs, frequency),
+ at: primary.at,
+ }, true
+}
+
+func rawByteMultiAnchorAddTable(table *[64]byte, pairs rawByteMultiAnchorPairSet, second bool, bit byte) {
+ for i := 0; i < int(pairs.n); i++ {
+ value := byte(pairs.pairs[i])
+ if second {
+ value = byte(pairs.pairs[i] >> 8)
+ }
+ table[value&0x3f] |= bit
+ }
+}
+
+func (filter *rawByteMultiAnchorFilter) confirmationGroup(offset uint8) (int, bool) {
+ for i := 0; i < int(filter.confirmN); i++ {
+ if filter.confirmOffset[i] == offset {
+ return i, true
+ }
+ }
+ if int(filter.confirmN) == len(filter.confirmOffset) {
+ return 0, false
+ }
+ group := int(filter.confirmN)
+ filter.confirmOffset[group] = offset
+ filter.confirmN++
+ if offset > filter.maxConfirmOffset {
+ filter.maxConfirmOffset = offset
+ }
+ return group, true
+}
+
+// makeRawByteMultiAnchorFilter compiles the shared tagged interior-pair screen
+// at plan construction. It is intentionally available only after the compact
+// direct raw map is proven. Rejected shapes leave Matcher.Each on the existing
+// decoded enumerator without lazy state, allocations, or history thresholds.
+func (p *searchPlan) makeRawByteMultiAnchorFilter(patterns []string) {
+ p.rawByteMulti = rawByteMultiAnchorFilter{}
+ if len(patterns) < 2 || len(patterns) > rawByteMultiAnchorGroups {
+ return
+ }
+ frequency := rawByteMultiAnchorFrequency(patterns)
+ for patternID, pattern := range patterns {
+ var units [rawByteMultiAnchorStartOffsets * rawByteMultiAnchorStartOffsets]rawByteMultiAnchorUnit
+ unitN := 0
+ for at := 0; at < len(pattern); {
+ r, size := utf8.DecodeRuneInString(pattern[at:])
+ if r == utf8.RuneError && size == 1 {
+ return
+ }
+ if pairs, ok := rawByteMultiAnchorPairSetFor(r); ok {
+ if unitN == len(units) {
+ return
+ }
+ units[unitN] = rawByteMultiAnchorUnit{at: at, pairs: pairs}
+ unitN++
+ }
+ at += size
+ }
+ var best rawByteMultiAnchorCandidate
+ found := false
+ for primary := 0; primary < unitN; primary++ {
+ for confirm := primary + 1; confirm < unitN; confirm++ {
+ for guard := confirm + 1; guard < unitN; guard++ {
+ candidate, ok := rawByteMultiAnchorCandidateFor(pattern, units[primary], units[confirm], units[guard], &frequency)
+ if !ok || !p.rawByteMulti.canAddConfirmationOffsets(candidate.anchor.confirmOffset[:candidate.anchor.confirmN]) {
+ continue
+ }
+ if !found || candidate.anchor.confirmN < best.anchor.confirmN ||
+ candidate.anchor.confirmN == best.anchor.confirmN && (candidate.score < best.score || candidate.score == best.score && candidate.at > best.at) {
+ best, found = candidate, true
+ }
+ }
+ }
+ }
+ if !found {
+ p.rawByteMulti = rawByteMultiAnchorFilter{}
+ return
+ }
+ bit := byte(1 << uint(patternID))
+ rawByteMultiAnchorAddTable(&p.rawByteMulti.first, best.anchor.primary, false, bit)
+ rawByteMultiAnchorAddTable(&p.rawByteMulti.second, best.anchor.primary, true, bit)
+ for i := 0; i < int(best.anchor.confirmN); i++ {
+ group, ok := p.rawByteMulti.confirmationGroup(best.anchor.confirmOffset[i])
+ if !ok {
+ p.rawByteMulti = rawByteMultiAnchorFilter{}
+ return
+ }
+ rawByteMultiAnchorAddTable(&p.rawByteMulti.confirmFirst[group], best.anchor.confirm, false, bit)
+ rawByteMultiAnchorAddTable(&p.rawByteMulti.confirmSecond[group], best.anchor.confirm, true, bit)
+ if best.anchor.confirmOffset[i] > p.rawByteMulti.maxOffset {
+ p.rawByteMulti.maxOffset = best.anchor.confirmOffset[i]
+ }
+ }
+ p.rawByteMulti.anchors[patternID] = best.anchor
+ for i := 0; i < int(best.anchor.startN); i++ {
+ if best.anchor.starts[i] > p.rawByteMulti.maxOffset {
+ p.rawByteMulti.maxOffset = best.anchor.starts[i]
+ }
+ }
+ for i := 0; i < int(best.anchor.guardN); i++ {
+ if best.anchor.guardOffset[i] > p.rawByteMulti.maxOffset {
+ p.rawByteMulti.maxOffset = best.anchor.guardOffset[i]
+ }
+ }
+ }
+ // A table with one effective tag signature is not a selective multi-anchor
+ // filter. It cannot eliminate any shared-prefix alternative before the
+ // expensive raw-plan replays, so leave this plan on its existing fallback.
+ if !p.rawByteMulti.tagDiverse() {
+ p.rawByteMulti = rawByteMultiAnchorFilter{}
+ return
+ }
+ p.rawByteMulti.valid = 1
+}
+
+func (filter *rawByteMultiAnchorFilter) canAddConfirmationOffsets(offsets []uint8) bool {
+ n := int(filter.confirmN)
+ var pending [rawByteMultiAnchorConfirmGroups]uint8
+ for _, offset := range offsets {
+ found := false
+ for i := 0; i < int(filter.confirmN); i++ {
+ found = found || filter.confirmOffset[i] == offset
+ }
+ for i := 0; i < n; i++ {
+ found = found || pending[i] == offset
+ }
+ if !found {
+ if n == len(pending) {
+ return false
+ }
+ pending[n] = offset
+ n++
+ }
+ }
+ return true
+}
+
+// tagsAt applies exact pair checks only for tags retained by the vector or
+// scalar table screen. It retains literal tags that own the primary,
+// fixed-displacement confirmation, and scalar guard simultaneously.
+func (anchor rawByteMultiAnchor) maxOffset() int {
+ max := 0
+ for i := 0; i < int(anchor.confirmN); i++ {
+ if offset := int(anchor.confirmOffset[i]); offset > max {
+ max = offset
+ }
+ }
+ for i := 0; i < int(anchor.guardN); i++ {
+ if offset := int(anchor.guardOffset[i]); offset > max {
+ max = offset
+ }
+ }
+ return max
+}
+
+func (filter *rawByteMultiAnchorFilter) tagsAt(s string, at int, candidates byte) byte {
+ if !filter.usable() {
+ return 0
+ }
+ var tags byte
+ for candidates != 0 {
+ i := bits.TrailingZeros8(candidates)
+ candidates &= candidates - 1
+ anchor := filter.anchors[i]
+ if anchor.startN == 0 || at+anchor.maxOffset()+1 >= len(s) || !anchor.primary.matches(s, at) {
+ continue
+ }
+ confirmed := false
+ for j := 0; j < int(anchor.confirmN); j++ {
+ confirmed = confirmed || anchor.confirm.matches(s, at+int(anchor.confirmOffset[j]))
+ }
+ if !confirmed {
+ continue
+ }
+ guarded := false
+ for j := 0; j < int(anchor.guardN); j++ {
+ guarded = guarded || anchor.guard.matches(s, at+int(anchor.guardOffset[j]))
+ }
+ if guarded {
+ tags |= 1 << uint(i)
+ }
+ }
+ return tags
+}
+
+func rawByteMultiAnchorSkipScalar(s string, at int, filter *rawByteMultiAnchorFilter) (int, byte) {
+ start := at
+ for at+1 < len(s) {
+ // The vector loop has already reduced these same low-six-bit tables.
+ // Keep its conservative predicate in the scalar tail, then let the
+ // caller run tagsAt's exact pair and guard checks only at a survivor.
+ tags := filter.first[s[at]&0x3f]
+ if tags != 0 {
+ tags &= filter.second[s[at+1]&0x3f]
+ var candidates byte
+ for group := 0; tags != 0 && group < int(filter.confirmN); group++ {
+ offset := int(filter.confirmOffset[group])
+ if at+offset+1 >= len(s) {
+ continue
+ }
+ confirmed := filter.confirmFirst[group][s[at+offset]&0x3f] &
+ filter.confirmSecond[group][s[at+offset+1]&0x3f]
+ candidates |= tags & confirmed
+ }
+ if candidates != 0 {
+ return at - start, candidates
+ }
+ }
+ at++
+ }
+ return at - start, 0
+}
+
+// rawByteMatchAt confirms only an anchored start. It retains the same raw
+// two-byte map and decoded fallback as findFiltered, but does not let a later
+// root found through a failure link impersonate a match at start.
+func (p *searchPlan) rawByteMatchAt(haystack string, start int) (Match, int, bool) {
+ state, at := 0, start
+ for units := 1; units <= p.maxUnits && at < len(haystack); units++ {
+ next, size, raw := p.rawByteAdvance(haystack, at, state)
+ if !raw {
+ token, decodedSize := p.haystackToken(haystack, at)
+ next, size = p.advance(state, token), decodedSize
+ }
+ state = next
+ at += size
+ if output := p.nodes[state].output; output.pattern >= 0 && output.units == units {
+ return Match{Pattern: output.pattern, Start: start}, at - start, true
+ }
+ if state == 0 {
+ return Match{}, 0, false
+ }
+ }
+ return Match{}, 0, false
+}
+
+// findRawByteFixedAnchored is the first-result specialization of the same
+// tagged scan used by Matcher.Each. Stopping its callback leaves the shared
+// scan only after it has waited through every compiled primary-start offset, so
+// it preserves Find's leftmost and lowest-ID contract without a second engine.
+func (p *searchPlan) findRawByteFixedAnchored(haystack string) (Match, bool) {
+ var result Match
+ found := false
+ p.eachRawByteFixedAnchored(haystack, func(match Match, _ int) bool {
+ result, found = match, true
+ return false
+ })
+ return result, found
+}
+
+// findRawByteOrigin begins the tagged scan at the only suffix that can contain
+// a match. The exact-byte scan and the tagged/raw-plan replay retain authority
+// over malformed input, fold spelling, leftmost order, and pattern-ID ties.
+func (p *searchPlan) findRawByteOrigin(haystack string) (Match, bool) {
+ gate := p.rawByteOrigin
+ at := literalSkipExactASCII(haystack, 0, gate.byte)
+ if at == len(haystack) {
+ return Match{}, false
+ }
+ from := at - int(gate.maxPrefix)
+ if from < 0 {
+ from = 0
+ }
+ match, ok := p.findRawByteFixedAnchored(haystack[from:])
+ if ok {
+ match.Start += from
+ }
+ return match, ok
+}
+
+// eachRawByteFixedAnchored enumerates with one shared tagged interior-pair
+// scan. The table only nominates starts; exact pair checks and raw-plan replay
+// keep Unicode folding, malformed bytes, leftmost order, and lowest-ID ties in
+// the existing state machine.
+func (p *searchPlan) eachRawByteFixedAnchored(haystack string, yield func(Match, int) bool) bool {
+ filter := &p.rawByteMulti
+ if !filter.usable() {
+ return true
+ }
+
+ at, from := 0, 0
+ best := Match{Pattern: -1, Start: -1}
+ bestEnd := 0
+ emit := func() bool {
+ if !yield(best, bestEnd-best.Start) {
+ return false
+ }
+ at, from = bestEnd, bestEnd
+ best, bestEnd = Match{Pattern: -1, Start: -1}, 0
+ return true
+ }
+
+ for at+1 < len(haystack) {
+ if best.Pattern >= 0 && at > best.Start+int(filter.maxOffset) {
+ if !emit() {
+ return false
+ }
+ continue
+ }
+ skipped, candidates := rawByteMultiAnchorSkipBytes(haystack, at, filter)
+ at += skipped
+ if at+1 >= len(haystack) {
+ break
+ }
+ for tags := filter.tagsAt(haystack, at, candidates); tags != 0; tags &= tags - 1 {
+ patternID := bits.TrailingZeros8(tags)
+ anchor := filter.anchors[patternID]
+ for i := 0; i < int(anchor.startN); i++ {
+ start := at - int(anchor.starts[i])
+ if start < from || best.Pattern >= 0 && start > best.Start {
+ continue
+ }
+ match, width, ok := p.rawByteMatchAt(haystack, start)
+ if !ok || best.Pattern >= 0 && match.Start > best.Start {
+ continue
+ }
+ if best.Pattern < 0 || match.Start < best.Start ||
+ match.Start == best.Start && match.Pattern < best.Pattern {
+ best, bestEnd = match, match.Start+width
+ }
+ }
+ }
+ at++
+ }
+ if best.Pattern >= 0 {
+ return yield(best, bestEnd-best.Start)
+ }
+ return true
+}
diff --git a/raw_byte_bench_fixture_test.go b/raw_byte_bench_fixture_test.go
new file mode 100644
index 0000000..68b6b3d
--- /dev/null
+++ b/raw_byte_bench_fixture_test.go
@@ -0,0 +1,93 @@
+package casei
+
+import "strings"
+
+// These benchmark fixture sizes are independent of raw-plan admission. They
+// make the construction and candidate-density shapes available before the
+// transition optimization so the same public benchmark can measure both arms.
+const (
+ rawByteBenchmarkCorpusBytes = 2 << 20
+ rawBytePublicationCorpusBytes = 5 << 20
+ rawByteFreshSampleBytes = 4 << 10
+)
+
+var rawByteCyrillicPatterns = []string{
+ "Шерлок Холмс",
+ "Джон Уотсон",
+ "Ирен Адлер",
+ "инспектор Лестрейд",
+ "профессор Мориарти",
+}
+
+// rawByteFalseCandidates emits ordinary Cyrillic root units followed by an
+// ASCII mismatch. Each planted start reaches exactly two shared-plan unit
+// transitions: the root and the reset byte.
+func rawByteFalseCandidates(period, groups int) string {
+ if period < len("Дx") {
+ panic("raw byte candidate period is too short")
+ }
+ bytes := []byte(strings.Repeat("x", period*groups))
+ for at := 0; at+len("Дx") <= len(bytes); at += period {
+ copy(bytes[at:], "Дx")
+ }
+ return string(bytes)
+}
+
+// rawByteFalseCandidatesAtLeast rounds groups up so fixtures cross a requested
+// byte boundary even when the candidate period does not divide it.
+func rawByteFalseCandidatesAtLeast(period, bytes int) string {
+ return rawByteFalseCandidates(period, (bytes+period-1)/period)
+}
+
+// rawByteLateMatchCandidatesAtLeast keeps the same repeated false-root shape
+// through the final sample window, then replaces one late slot with the first
+// literal. The returned offset is the ordinary byte offset of that occurrence.
+func rawByteLateMatchCandidatesAtLeast(period, bytes int) (string, int) {
+ pattern := rawByteCyrillicPatterns[0]
+ if period < len(pattern) {
+ panic("raw byte late-match period is too short")
+ }
+ out := []byte(rawByteFalseCandidatesAtLeast(period, bytes))
+ start := len(out) - period
+ copy(out[start:], pattern)
+ return string(out), start
+}
+
+// rawByteEarlyMatchCandidatesAtLeast plants one complete literal at a fixed
+// early offset while retaining a long dense suffix. It guards the public Find
+// call that can stop before later admission sample windows.
+func rawByteEarlyMatchCandidatesAtLeast(period, bytes, start int) (string, int) {
+ pattern := rawByteCyrillicPatterns[0]
+ if period < len(pattern) || start < 0 || start%period != 0 {
+ panic("invalid raw byte early-match placement")
+ }
+ out := []byte(rawByteFalseCandidatesAtLeast(period, bytes))
+ if start+len(pattern) > len(out) {
+ panic("raw byte early match is outside its corpus")
+ }
+ copy(out[start:], pattern)
+ return string(out), start
+}
+
+// rawByteNearMissCandidates plants a whole literal prefix with a final Cyrillic
+// mismatch. It exercises candidate confirmation without returning a match.
+func rawByteNearMissCandidates(period, groups int) string {
+ const nearMiss = "Шерлок Холми"
+ if period < len(nearMiss) {
+ panic("raw byte near-miss period is too short")
+ }
+ bytes := []byte(strings.Repeat("x", period*groups))
+ for at := 0; at+len(nearMiss) <= len(bytes); at += period {
+ copy(bytes[at:], nearMiss)
+ }
+ return string(bytes)
+}
+
+func rawByteNearMissCandidatesAtLeast(period, bytes int) string {
+ return rawByteNearMissCandidates(period, (bytes+period-1)/period)
+}
+
+func rawByteLongPrefixPatterns() []string {
+ prefix := strings.Repeat("Д", 100)
+ return []string{prefix + "a", prefix + "b"}
+}
diff --git a/raw_byte_multi_anchor_amd64_test.go b/raw_byte_multi_anchor_amd64_test.go
new file mode 100644
index 0000000..713aea0
--- /dev/null
+++ b/raw_byte_multi_anchor_amd64_test.go
@@ -0,0 +1,169 @@
+//go:build amd64
+
+package casei
+
+import (
+ "math/rand/v2"
+ "strings"
+ "testing"
+ "unsafe"
+
+ "golang.org/x/sys/cpu"
+)
+
+// rawByteMultiAnchorVectorResult models only the conservative table predicate
+// implemented by rawByteMultiAnchorSkip64. Exact primary/guard checks belong
+// to tagsAt and intentionally occur after this candidate screen.
+func rawByteMultiAnchorVectorResult(s string, at int, filter *rawByteMultiAnchorFilter) (int, byte) {
+ start := at
+ for len(s)-at >= 65+int(filter.maxConfirmOffset) {
+ for lane := 0; lane < 64; lane++ {
+ primary := filter.first[s[at+lane]&0x3f] & filter.second[s[at+lane+1]&0x3f]
+ var tags byte
+ for group := 0; group < rawByteMultiAnchorConfirmGroups; group++ {
+ offset := int(filter.confirmOffset[group])
+ confirm := filter.confirmFirst[group][s[at+lane+offset]&0x3f] &
+ filter.confirmSecond[group][s[at+lane+offset+1]&0x3f]
+ tags |= primary & confirm
+ }
+ if tags != 0 {
+ return at - start + lane, tags
+ }
+ }
+ at += 64
+ }
+ return at - start, 0
+}
+
+// rawByteMultiAnchorDenseNoConfirmPrefix constructs a fixed counterexample to
+// an unbounded dense schedule: two of the first four primary masks are set,
+// but no vector-confirmed lane occurs before the sparse suffix.
+func rawByteMultiAnchorDenseNoConfirmPrefix(t *testing.T, filter *rawByteMultiAnchorFilter) string {
+ t.Helper()
+ buf := make([]byte, 512)
+ seed := uint32(1)
+ for attempt := 0; attempt < 10000; attempt++ {
+ for i := range buf {
+ seed = seed*1664525 + 1013904223
+ buf[i] = byte(seed >> 24)
+ }
+ occupied := 0
+ for block := 0; block < 4; block++ {
+ for lane := 0; lane < 64; lane++ {
+ at := block*64 + lane
+ if filter.first[buf[at]&0x3f]&filter.second[buf[at+1]&0x3f] != 0 {
+ occupied++
+ break
+ }
+ }
+ }
+ candidate := string(buf[:256])
+ skipped, _ := rawByteMultiAnchorVectorResult(candidate, 0, filter)
+ if occupied >= 2 && skipped >= 192 {
+ return candidate
+ }
+ }
+ t.Fatal("could not construct a dense/no-confirm prefix")
+ return ""
+}
+
+func TestRawByteMultiAnchorDenseEpochMatchesTableModel(t *testing.T) {
+ if !cpu.X86.HasAVX512F || !cpu.X86.HasAVX512BW || !cpu.X86.HasAVX512VBMI {
+ t.Skip("AVX-512 VBMI multi-anchor path is disabled")
+ }
+ plan := newSearchPlan(rawByteCyrillicPatterns)
+ filter := &plan.rawByteMulti
+ if !filter.usable() {
+ t.Fatal("eligible plan did not compile a raw multi-anchor filter")
+ }
+ prefix := rawByteMultiAnchorDenseNoConfirmPrefix(t, filter)
+ for _, tc := range []struct {
+ name string
+ haystack string
+ }{
+ {"dense_prefix_sparse_suffix", prefix + strings.Repeat("x", 5<<20)},
+ {"uniform_dense_no_confirm", strings.Repeat(prefix, 1<<14)},
+ } {
+ t.Run(tc.name, func(t *testing.T) {
+ want, wantTags := rawByteMultiAnchorVectorResult(tc.haystack, 0, filter)
+ if want < len(tc.haystack)-128 {
+ t.Fatalf("fixture unexpectedly has a vector tag at %d", want)
+ }
+ got, gotTags := rawByteMultiAnchorSkip64(unsafe.StringData(tc.haystack), len(tc.haystack), filter)
+ if got != want || gotTags != wantTags {
+ t.Fatalf("rawByteMultiAnchorSkip64 = %d,%02x, want %d,%02x", got, gotTags, want, wantTags)
+ }
+ })
+ }
+}
+
+func TestRawByteMultiAnchorScalarFallbackPreservesMatches(t *testing.T) {
+ if !cpu.X86.HasAVX512F || !cpu.X86.HasAVX512BW || !cpu.X86.HasAVX512VBMI {
+ t.Skip("AVX-512 VBMI multi-anchor path is disabled")
+ }
+ prior := cpu.X86.HasAVX512VBMI
+ cpu.X86.HasAVX512VBMI = false
+ defer func() { cpu.X86.HasAVX512VBMI = prior }()
+
+ patterns := append(append([]string(nil), rawByteCyrillicPatterns...), strings.ToLower(rawByteCyrillicPatterns[0]))
+ matcher := NewMatcher(patterns)
+ if !matcher.plan.rawByteMulti.usable() {
+ t.Fatal("eligible scalar-fallback plan did not compile a multi-anchor filter")
+ }
+ inputs := []string{
+ strings.Repeat("x", 129) + rawByteCyrillicPatterns[0],
+ strings.Repeat("x", 71) + "\xff" + rawByteCyrillicPatterns[3],
+ "ᲁжон уотсон " + rawByteCyrillicPatterns[2],
+ strings.Repeat("x", 63) + rawByteCyrillicPatterns[1] + strings.Repeat("x", 17) + rawByteCyrillicPatterns[0],
+ }
+ rng := rand.New(rand.NewPCG(20260908, 10))
+ units := []string{"x", "Д", "д", "ᲁ", "Ж", "ж", "Ш", "ш", "K", "ſ", "\xff", "\x80", "€"}
+ for iteration := 0; iteration < 128; iteration++ {
+ var haystack strings.Builder
+ for range 64 {
+ haystack.WriteString(units[rng.IntN(len(units))])
+ }
+ if iteration%3 == 0 {
+ haystack.WriteString(patterns[rng.IntN(len(patterns))])
+ }
+ inputs = append(inputs, haystack.String())
+ }
+ for inputIndex, haystack := range inputs {
+ got, gotOK := matcher.Find(haystack)
+ want, wantOK := refFind(haystack, patterns)
+ if gotOK != wantOK || gotOK && got != want {
+ t.Fatalf("input %d Find(%x) = %+v,%t; want %+v,%t", inputIndex, haystack, got, gotOK, want, wantOK)
+ }
+ rawByteCheckEach(t, matcher, haystack)
+ }
+}
+
+func TestRawByteMultiAnchorVBMISkip64MatchesTableModel(t *testing.T) {
+ if !cpu.X86.HasAVX512F || !cpu.X86.HasAVX512BW || !cpu.X86.HasAVX512VBMI {
+ t.Skip("AVX-512 VBMI multi-anchor path is disabled")
+ }
+ plan := newSearchPlan(rawByteCyrillicPatterns)
+ filter := &plan.rawByteMulti
+ if !filter.usable() {
+ t.Fatal("eligible plan did not compile a raw multi-anchor filter")
+ }
+
+ rng := rand.New(rand.NewPCG(20260827, 4))
+ // Cross the 512-byte sparse aggregate boundary as well as every tail
+ // alignment below it. The assembly must replay its four-block dispatcher
+ // without changing the earliest conservative table survivor.
+ for length := 0; length < 768; length++ {
+ input := make([]byte, length)
+ for i := range input {
+ input[i] = byte(rng.Uint32())
+ }
+ haystack := string(input)
+ for at := range haystack {
+ want, wantTags := rawByteMultiAnchorVectorResult(haystack, at, filter)
+ got, gotTags := rawByteMultiAnchorSkip64((*byte)(unsafe.Add(unsafe.Pointer(unsafe.StringData(haystack)), at)), len(haystack)-at, filter)
+ if got != want || gotTags != wantTags {
+ t.Fatalf("length %d at %d: rawByteMultiAnchorSkip64 = %d,%02x, want %d,%02x", length, at, got, gotTags, want, wantTags)
+ }
+ }
+ }
+}
diff --git a/raw_byte_test.go b/raw_byte_test.go
new file mode 100644
index 0000000..19025fa
--- /dev/null
+++ b/raw_byte_test.go
@@ -0,0 +1,513 @@
+package casei
+
+import (
+ "math/rand/v2"
+ "strings"
+ "testing"
+ "unicode/utf8"
+)
+
+func TestRawByteTokenPlanDirectTransitions(t *testing.T) {
+ plan := newSearchPlan(rawByteCyrillicPatterns)
+ if !plan.hasRawByteTokenPlan() {
+ t.Fatal("eligible Cyrillic plan did not retain a compact raw-byte map")
+ }
+ for state := range plan.nodes {
+ for value := range utf8.RuneSelf {
+ haystack := string([]byte{byte(value)})
+ got, size, ok := plan.rawByteAdvance(haystack, 0, state)
+ wantToken, wantSize := plan.haystackToken(haystack, 0)
+ want := plan.advance(state, wantToken)
+ if !ok || size != wantSize || got != want {
+ t.Fatalf("state %d ASCII %02x: raw=(%d,%d,%t), decoded=(%d,%d)",
+ state, value, got, size, ok, want, wantSize)
+ }
+ }
+ for lead := 0xc2; lead <= 0xdf; lead++ {
+ for trail := 0x80; trail <= 0xbf; trail++ {
+ haystack := string([]byte{byte(lead), byte(trail)})
+ got, size, ok := plan.rawByteAdvance(haystack, 0, state)
+ wantToken, wantSize := plan.haystackToken(haystack, 0)
+ want := plan.advance(state, wantToken)
+ if !ok || size != wantSize || got != want {
+ t.Fatalf("state %d UTF-8 %02x%02x: raw=(%d,%d,%t), decoded=(%d,%d)",
+ state, lead, trail, got, size, ok, want, wantSize)
+ }
+ }
+ }
+ }
+
+ for _, haystack := range []string{"\x80", "\xc2x", "€", "K"} {
+ if _, _, ok := plan.rawByteAdvance(haystack, 0, 0); ok {
+ t.Fatalf("unsupported input %x entered the raw-byte map", haystack)
+ }
+ }
+}
+
+// rawByteFilteredDirectTransitions mirrors the non-skipped portion of
+// findFiltered for a no-match stream. It verifies that the public filtered
+// route reaches the compact raw map rather than merely compiling it.
+func rawByteFilteredDirectTransitions(t *testing.T, plan *searchPlan, haystack string) int {
+ t.Helper()
+ if !plan.hasRawByteTokenPlan() {
+ t.Fatal("plan has no compact raw-byte map")
+ }
+
+ state, transitions := 0, 0
+ for at := 0; at < len(haystack); {
+ if state == 0 {
+ skipped := len(haystack) - at
+ if plan.pairSecond {
+ skipped = pairSecondSkipBytes(haystack, at, &plan.filter)
+ } else if plan.filter.usable() {
+ skipped = filterSkipBytes(haystack, at, &plan.filter)
+ }
+ if plan.triples.usable() {
+ if tripleSkipped := tripleSkipBytes(haystack, at, &plan.triples); tripleSkipped < skipped {
+ skipped = tripleSkipped
+ }
+ }
+ at += skipped
+ if at == len(haystack) {
+ break
+ }
+ }
+
+ rawState, rawSize, rawOK := plan.rawByteAdvance(haystack, at, state)
+ token, decodedSize := plan.haystackToken(haystack, at)
+ decodedState := plan.advance(state, token)
+ if !rawOK || rawSize != decodedSize || rawState != decodedState {
+ t.Fatalf("transition at byte %d: raw=(state=%d,size=%d,ok=%t), decoded=(state=%d,size=%d)",
+ at, rawState, rawSize, rawOK, decodedState, decodedSize)
+ }
+ state = rawState
+ transitions++
+ at += rawSize
+ }
+ return transitions
+}
+
+func rawByteSharedPrefixPatterns(n int) []string {
+ patterns := make([]string, n)
+ for i := range patterns {
+ patterns[i] = "щупальце" + string(rune('0'+i))
+ }
+ return patterns
+}
+
+func TestRawByteMultiAnchorRequiresTagDiversity(t *testing.T) {
+ for _, n := range []int{2, 4, 8} {
+ patterns := rawByteSharedPrefixPatterns(n)
+ plan := newSearchPlan(patterns)
+ if !plan.hasRawByteTokenPlan() {
+ t.Fatalf("N=%d shared-prefix plan lost the compact raw-byte map", n)
+ }
+ if plan.rawByteMulti.usable() {
+ t.Fatalf("N=%d indistinguishable shared-prefix anchors entered rawByteMulti", n)
+ }
+ matcher := NewMatcher(patterns)
+ for _, haystack := range []string{
+ strings.Repeat("щ", 63) + "упальцеx",
+ "ᲇупальце7", // width-changing fold spelling before the shared suffix.
+ strings.Repeat("x", 71) + "\xff" + patterns[n-1],
+ } {
+ got, gotOK := matcher.Find(haystack)
+ want, wantOK := refFind(haystack, patterns)
+ if gotOK != wantOK || gotOK && got != want {
+ t.Fatalf("N=%d Find(%x) = %+v,%t; want %+v,%t", n, haystack, got, gotOK, want, wantOK)
+ }
+ }
+ }
+
+ diverse := []string{
+ "абвгде0", "ёжзийк1", "лмнопр2", "стуфхц3",
+ "чшщьыъ4", "ыьэюяа5", "бвгдеж6", "зийклм7",
+ }
+ for _, patterns := range [][]string{rawByteCyrillicPatterns, diverse} {
+ plan := newSearchPlan(patterns)
+ if !plan.rawByteMulti.usable() || !plan.rawByteMulti.tagDiverse() {
+ t.Fatalf("diverse anchors were not admitted: patterns=%q usable=%t diverse=%t", patterns, plan.rawByteMulti.usable(), plan.rawByteMulti.tagDiverse())
+ }
+ }
+}
+
+func TestRawByteFilteredDensityUsesDirectTransitions(t *testing.T) {
+ for _, tc := range []struct {
+ name string
+ patterns []string
+ period int
+ groups int
+ }{
+ {"two_one_in_32", rawByteCyrillicPatterns[:2], 32, 4096 / 32},
+ {"five_one_in_256", rawByteCyrillicPatterns, 256, rawBytePublicationCorpusBytes / 256},
+ {"five_one_in_4", rawByteCyrillicPatterns, 4, rawByteBenchmarkCorpusBytes / 16 / 4},
+ } {
+ t.Run(tc.name, func(t *testing.T) {
+ haystack := rawByteFalseCandidates(tc.period, tc.groups)
+ plan := newSearchPlan(tc.patterns)
+ if plan.unicodePairN != 0 || plan.unicodeAnchor.n != 0 || !plan.filter.usable() {
+ t.Fatalf("fixture does not select generic filtered search: pairs=%d anchor=%d filter=%t",
+ plan.unicodePairN, plan.unicodeAnchor.n, plan.filter.usable())
+ }
+ if got := rawByteFilteredDirectTransitions(t, plan, haystack); got != 2*tc.groups {
+ t.Fatalf("direct non-skipped transitions = %d, want %d", got, 2*tc.groups)
+ }
+
+ matcher := NewMatcher(tc.patterns)
+ if got, ok := matcher.Find(haystack); ok || got != (Match{}) {
+ t.Fatalf("Find = %+v,%t, want no match", got, ok)
+ }
+ if !matcher.plan.hasRawByteTokenPlan() {
+ t.Fatal("public Matcher.Find did not retain the compact raw-byte map")
+ }
+ })
+ }
+}
+
+func TestRawByteTokenPlanPreservesFallbackOffsetsAndTies(t *testing.T) {
+ patterns := append(append([]string(nil), rawByteCyrillicPatterns...), "ДЖОН УОТСОН")
+ matcher := NewMatcher(patterns)
+ if !matcher.plan.hasRawByteTokenPlan() {
+ t.Fatal("eligible multi-pattern plan did not retain a raw-byte map")
+ }
+
+ for _, haystack := range []string{
+ "ᲁЖОН УОТСОН", // U+1C81 is a three-byte simple-fold spelling of Д.
+ strings.Repeat("x", 31) + "\xff" + strings.Repeat("x", 17) + "ДЖОН УОТСОН",
+ strings.Repeat("x", 29) + "K" + strings.Repeat("x", 11) + "ШЕРЛОК ХОЛМС",
+ strings.Repeat("x", 23) + "ДЖОН УОТСОН",
+ } {
+ got, gotOK := matcher.Find(haystack)
+ want, wantOK := refFind(haystack, patterns)
+ if gotOK != wantOK || gotOK && got != want {
+ t.Fatalf("Find(%x) = %+v,%t; want %+v,%t", haystack, got, gotOK, want, wantOK)
+ }
+ }
+
+ rng := rand.New(rand.NewPCG(20260824, 1))
+ units := []string{"x", " ", "Д", "д", "ᲁ", "Ж", "ж", "Ш", "ш", "K", "ſ", "\xff", "\x80", "€"}
+ for iteration := 0; iteration < 1000; iteration++ {
+ var haystack strings.Builder
+ for range 96 {
+ haystack.WriteString(units[rng.IntN(len(units))])
+ }
+ if iteration%3 == 0 {
+ haystack.WriteString(patterns[rng.IntN(len(rawByteCyrillicPatterns))])
+ }
+ input := haystack.String()
+ got, gotOK := matcher.Find(input)
+ want, wantOK := refFind(input, patterns)
+ if gotOK != wantOK || gotOK && got != want {
+ t.Fatalf("iteration %d Find(%x) = %+v,%t; want %+v,%t", iteration, input, got, gotOK, want, wantOK)
+ }
+ }
+}
+
+func TestRawByteOriginGatePreservesFind(t *testing.T) {
+ plan := newSearchPlan(rawByteCyrillicPatterns)
+ if !plan.rawByteMulti.usable() || !plan.rawByteOrigin.usable() {
+ t.Fatal("eligible plan did not compile the tagged filter and origin gate")
+ }
+ matcher := NewMatcher(rawByteCyrillicPatterns)
+ check := func(name, haystack string) {
+ t.Helper()
+ want, wantOK := refFind(haystack, rawByteCyrillicPatterns)
+ for _, route := range []struct {
+ name string
+ find func(string) (Match, bool)
+ }{
+ {"direct", plan.findRawByteOrigin},
+ {"public", matcher.Find},
+ } {
+ got, gotOK := route.find(haystack)
+ if gotOK != wantOK || gotOK && got != want {
+ t.Fatalf("%s/%s: Find(%x) = %+v,%t; want %+v,%t", name, route.name, haystack, got, gotOK, want, wantOK)
+ }
+ }
+ }
+
+ check("absent", strings.Repeat("x", 5<<10))
+ check("unrelated-earlier-gate", strings.Repeat("x", 97)+" "+strings.Repeat("x", 5<<10)+rawByteCyrillicPatterns[3])
+ check("opaque-before-match", strings.Repeat("x", 5<<10)+"\xff"+rawByteCyrillicPatterns[2])
+ for alignment := 0; alignment < 64; alignment++ {
+ // U+1C81 is a three-byte rendering of the pattern's initial Д. Its
+ // varying source width exercises the gate's maximum-prefix lookback.
+ check("width-changing-prefix", strings.Repeat("x", 4096+alignment)+"ᲁЖОН УОТСОН")
+ }
+
+ if gate := rawByteOriginGateFor([]string{"абв", "где"}); gate.usable() {
+ t.Fatalf("patterns with no common fold-invariant ASCII byte compiled gate %+v", gate)
+ }
+ if gate := rawByteOriginGateFor([]string{"абв ", "где \xff"}); gate.usable() {
+ t.Fatalf("malformed pattern compiled gate %+v", gate)
+ }
+}
+
+type rawByteEachResult struct {
+ match Match
+ width int
+}
+
+// rawByteReferenceEach is deliberately independent from Matcher.Find and the
+// compiled transition plan. It reduces the canonical fold reference used by
+// the package tests to the non-overlapping enumeration contract.
+func rawByteReferenceEach(haystack string, patterns []string) []rawByteEachResult {
+ var out []rawByteEachResult
+ for at := 0; at <= len(haystack); {
+ match, ok := refFind(haystack[at:], patterns)
+ if !ok {
+ return out
+ }
+ match.Start += at
+ canon, _ := canonFold(patterns[match.Pattern])
+ _, offsets := canonFold(haystack[match.Start:])
+ width := offsets[len(canon)]
+ out = append(out, rawByteEachResult{match, width})
+ at = match.Start + width
+ }
+ return out
+}
+
+func rawByteCheckEach(t *testing.T, matcher *Matcher, haystack string) {
+ t.Helper()
+ want := rawByteReferenceEach(haystack, matcher.patterns)
+ var got []rawByteEachResult
+ if complete := matcher.Each(haystack, func(match Match, width int) bool {
+ got = append(got, rawByteEachResult{match, width})
+ return true
+ }); !complete {
+ t.Fatal("Each stopped before completing enumeration")
+ }
+ if len(got) != len(want) {
+ t.Fatalf("Each(%x) returned %d matches, want %d: got=%+v want=%+v", haystack, len(got), len(want), got, want)
+ }
+ for i := range want {
+ if got[i] != want[i] {
+ t.Fatalf("Each(%x) match %d = %+v, want %+v", haystack, i, got[i], want[i])
+ }
+ }
+}
+
+// TestRawByteMultiAnchorFindOrderingAndTail exercises the first-result view of
+// the shared tagged scan. It keeps a duplicate fold-equivalent literal so the
+// exact replay, rather than tag iteration order, must select the lowest ID.
+func TestRawByteMultiAnchorFindOrderingAndTail(t *testing.T) {
+ patterns := append(append([]string(nil), rawByteCyrillicPatterns...), strings.ToLower(rawByteCyrillicPatterns[0]))
+ matcher := NewMatcher(patterns)
+ if !matcher.plan.rawByteMulti.usable() {
+ t.Fatal("eligible Find plan did not compile a raw multi-anchor filter")
+ }
+
+ check := func(name, haystack string) {
+ t.Helper()
+ got, gotOK := matcher.Find(haystack)
+ want, wantOK := refFind(haystack, patterns)
+ if gotOK != wantOK || gotOK && got != want {
+ t.Fatalf("%s: Find(%x) = %+v,%t; want %+v,%t", name, haystack, got, gotOK, want, wantOK)
+ }
+ }
+
+ // Shift the final literal through every vector-tail alignment. The prefix
+ // is long enough to take the VBMI scan before its scalar tail reaches the
+ // candidate; the duplicate pattern checks the lowest-ID tie at that tail.
+ for alignment := 0; alignment < 64; alignment++ {
+ check("tail", strings.Repeat("x", 128+alignment)+rawByteCyrillicPatterns[0])
+ }
+
+ check("earlier-start-wins", rawByteCyrillicPatterns[3]+" x "+rawByteCyrillicPatterns[0])
+ check("opaque-before-match", strings.Repeat("x", 91)+"\xff"+rawByteCyrillicPatterns[2])
+ // U+1C81 is the three-byte simple-fold spelling of the initial Д. The
+ // compiled start offsets may nominate extras, but exact raw-plan replay must
+ // still return the same ordinary byte offset as the reference.
+ check("width-changing-before-anchor", strings.Repeat("x", 77)+"ᲁжон уотсон")
+}
+
+func TestRawByteMultiAnchorEnumeration(t *testing.T) {
+ matcher := NewMatcher(rawByteCyrillicPatterns)
+ if !matcher.plan.hasRawByteTokenPlan() || !matcher.plan.rawByteMulti.usable() {
+ t.Fatalf("eligible plan did not compile raw multi-anchor state: raw=%t multi=%t", matcher.plan.hasRawByteTokenPlan(), matcher.plan.rawByteMulti.usable())
+ }
+
+ // Sweep every vector tail alignment. The second occurrence forces a resume
+ // after a nonzero width, while the last one makes the selected anchor cross
+ // from a vector block into the scalar tail.
+ for alignment := 0; alignment < 64; alignment++ {
+ haystack := rawByteCyrillicPatterns[0] + strings.Repeat("x", alignment) + rawByteCyrillicPatterns[2]
+ rawByteCheckEach(t, matcher, haystack)
+ }
+
+ for _, haystack := range []string{
+ "ᲁжон уотсон " + rawByteCyrillicPatterns[2], // width-changing Д form before an anchor.
+ strings.Repeat("x", 71) + "\xff" + rawByteCyrillicPatterns[3],
+ strings.Repeat("ж", 83) + rawByteCyrillicPatterns[4],
+ strings.Repeat("x", 19) + rawByteCyrillicPatterns[1] + rawByteCyrillicPatterns[0],
+ } {
+ rawByteCheckEach(t, matcher, haystack)
+ }
+
+ rng := rand.New(rand.NewPCG(20260826, 3))
+ units := []string{"x", " ", "Д", "д", "ᲁ", "Ж", "ж", "Ш", "ш", "О", "ᲂ", "о", "Н", "н", "И", "и", "€", "\xff", "\x80"}
+ for iteration := 0; iteration < 512; iteration++ {
+ var haystack strings.Builder
+ for range 96 {
+ haystack.WriteString(units[rng.IntN(len(units))])
+ }
+ for i := 0; i < iteration%5; i++ {
+ haystack.WriteString(rawByteCyrillicPatterns[rng.IntN(len(rawByteCyrillicPatterns))])
+ haystack.WriteString(" xx ")
+ }
+ rawByteCheckEach(t, matcher, haystack.String())
+ }
+}
+
+func TestRawByteMultiAnchorSkipNeverPassesAConfirmedTag(t *testing.T) {
+ plan := newSearchPlan(rawByteCyrillicPatterns)
+ filter := &plan.rawByteMulti
+ if !filter.usable() {
+ t.Fatal("eligible plan did not compile a raw multi-anchor filter")
+ }
+ for alignment := 0; alignment < 64; alignment++ {
+ haystack := strings.Repeat("x", alignment+128) + rawByteCyrillicPatterns[alignment%len(rawByteCyrillicPatterns)] + strings.Repeat("x", 96)
+ for at := range haystack {
+ skipped, _ := rawByteMultiAnchorSkipBytes(haystack, at, filter)
+ if skipped < 0 || at+skipped > len(haystack) {
+ t.Fatalf("alignment %d at %d: invalid skip %d", alignment, at, skipped)
+ }
+ for candidate := at; candidate < at+skipped; candidate++ {
+ if tags := filter.tagsAt(haystack, candidate, 0xff); tags != 0 {
+ t.Fatalf("alignment %d at %d: skip %d passed confirmed tag %08b at %d", alignment, at, skipped, tags, candidate)
+ }
+ }
+ }
+ }
+}
+
+func TestRawByteMultiAnchorScalarScreenUnionsConfirmationGroups(t *testing.T) {
+ pair := func(first, second byte) rawByteMultiAnchorPairSet {
+ return rawByteMultiAnchorPairSet{
+ pairs: [rawByteMultiAnchorForms]uint16{uint16(first) | uint16(second)<<8},
+ n: 1,
+ }
+ }
+ const (
+ aliasTag = byte(1 << iota)
+ matchTag
+ )
+ aliasPrimary := pair(0x01, 0x02) // Low six bits alias "AB".
+ matchPrimary := pair('A', 'B')
+ aliasConfirm := pair('C', 'D')
+ matchConfirm := pair('E', 'F')
+ guard := pair('G', 'H')
+ filter := rawByteMultiAnchorFilter{
+ confirmOffset: [rawByteMultiAnchorConfirmGroups]uint8{2, 4},
+ confirmN: 2,
+ valid: 1,
+ }
+ rawByteMultiAnchorAddTable(&filter.first, aliasPrimary, false, aliasTag)
+ rawByteMultiAnchorAddTable(&filter.second, aliasPrimary, true, aliasTag)
+ rawByteMultiAnchorAddTable(&filter.first, matchPrimary, false, matchTag)
+ rawByteMultiAnchorAddTable(&filter.second, matchPrimary, true, matchTag)
+ rawByteMultiAnchorAddTable(&filter.confirmFirst[0], aliasConfirm, false, aliasTag)
+ rawByteMultiAnchorAddTable(&filter.confirmSecond[0], aliasConfirm, true, aliasTag)
+ rawByteMultiAnchorAddTable(&filter.confirmFirst[1], matchConfirm, false, matchTag)
+ rawByteMultiAnchorAddTable(&filter.confirmSecond[1], matchConfirm, true, matchTag)
+ filter.anchors[0] = rawByteMultiAnchor{
+ primary: aliasPrimary,
+ confirm: aliasConfirm,
+ guard: guard,
+ starts: [rawByteMultiAnchorStartOffsets]uint8{0},
+ confirmOffset: [rawByteMultiAnchorConfirmGroups]uint8{2},
+ guardOffset: [rawByteMultiAnchorStartOffsets]uint8{6},
+ startN: 1,
+ confirmN: 1,
+ guardN: 1,
+ }
+ filter.anchors[1] = rawByteMultiAnchor{
+ primary: matchPrimary,
+ confirm: matchConfirm,
+ guard: guard,
+ starts: [rawByteMultiAnchorStartOffsets]uint8{0},
+ confirmOffset: [rawByteMultiAnchorConfirmGroups]uint8{4},
+ guardOffset: [rawByteMultiAnchorStartOffsets]uint8{6},
+ startN: 1,
+ confirmN: 1,
+ guardN: 1,
+ }
+
+ haystack := "ABCDEFGH"
+ skipped, candidates := rawByteMultiAnchorSkipScalar(haystack, 0, &filter)
+ if skipped != 0 || candidates != aliasTag|matchTag {
+ t.Fatalf("scalar screen = (%d, %08b), want (0, %08b)", skipped, candidates, aliasTag|matchTag)
+ }
+ if tags := filter.tagsAt(haystack, 0, candidates); tags != matchTag {
+ t.Fatalf("exact tags = %08b, want %08b", tags, matchTag)
+ }
+}
+
+// TestRawByteMultiAnchorScalarScreenNeverPassesConfirmedTag proves the table
+// screen used after a vector tail (and on portable hosts) can stop early on an
+// alias but never skips an exact tagged anchor. The later tagsAt replay remains
+// the match authority.
+func TestRawByteMultiAnchorScalarScreenNeverPassesConfirmedTag(t *testing.T) {
+ plan := newSearchPlan(rawByteCyrillicPatterns)
+ filter := &plan.rawByteMulti
+ if !filter.usable() {
+ t.Fatal("eligible plan did not compile a raw multi-anchor filter")
+ }
+ inputs := []string{
+ strings.Repeat("x", 257) + rawByteCyrillicPatterns[0],
+ strings.Repeat("x", 63) + "\xff\x80" + rawByteCyrillicPatterns[1],
+ strings.Repeat("x", 17) + "ᲁжон уотсон",
+ }
+ rng := rand.New(rand.NewPCG(20260907, 8))
+ for i := 0; i < 256; i++ {
+ buf := make([]byte, i)
+ for j := range buf {
+ buf[j] = byte(rng.Uint32())
+ }
+ inputs = append(inputs, string(buf))
+ }
+ for inputIndex, haystack := range inputs {
+ for at := range haystack {
+ skipped, _ := rawByteMultiAnchorSkipScalar(haystack, at, filter)
+ if skipped < 0 || at+skipped > len(haystack) {
+ t.Fatalf("input %d at %d: invalid scalar skip %d", inputIndex, at, skipped)
+ }
+ for candidate := at; candidate < at+skipped; candidate++ {
+ if tags := filter.tagsAt(haystack, candidate, 0xff); tags != 0 {
+ t.Fatalf("input %d at %d: scalar skip %d passed confirmed tag %08b at %d", inputIndex, at, skipped, tags, candidate)
+ }
+ }
+ }
+ }
+}
+
+func TestRawByteTokenPlanFallsBackForUnsupportedPlans(t *testing.T) {
+ for _, patterns := range [][]string{
+ {"Шерлок"},
+ {"Шерлок", "Δelta", "éclair"},
+ {"\xffШерлок", "Джон"},
+ } {
+ matcher := NewMatcher(patterns)
+ if matcher.plan.hasRawByteTokenPlan() {
+ t.Fatalf("unsupported plan %q retained raw-byte tokens", patterns)
+ }
+ input := "… δELTA … ÉCLAIR … ШЕРЛОК \xffДЖОН"
+ got, gotOK := matcher.Find(input)
+ want, wantOK := refFind(input, patterns)
+ if gotOK != wantOK || gotOK && got != want {
+ t.Fatalf("Find(%q, %x) = %+v,%t; want %+v,%t", patterns, input, got, gotOK, want, wantOK)
+ }
+ }
+}
+
+func TestRawByteTokenPlanFindAllocatesNothingAfterConstruction(t *testing.T) {
+ matcher := NewMatcher(rawByteCyrillicPatterns)
+ haystack := rawByteFalseCandidatesAtLeast(64, 64<<10)
+ if got, ok := matcher.Find(haystack); ok || got != (Match{}) {
+ t.Fatalf("setup Find = %+v,%t", got, ok)
+ }
+ if allocs := testing.AllocsPerRun(100, func() { _, _ = matcher.Find(haystack) }); allocs != 0 {
+ t.Fatalf("reused raw-byte Find allocations = %g, want 0", allocs)
+ }
+}
diff --git a/root_amd64.go b/root_amd64.go
index 54ca8aa..4986394 100644
--- a/root_amd64.go
+++ b/root_amd64.go
@@ -24,6 +24,10 @@ func asciiPairVBMIEnabled() bool {
return cpu.X86.HasAVX512F && cpu.X86.HasAVX512BW && cpu.X86.HasAVX512VBMI
}
+func unicodePairConfirmVectorEnabled() bool {
+ return asciiPairVBMIEnabled()
+}
+
// asciiFixedPrefix8 compares the compiled low eight pattern bytes after
// applying case bits only at ASCII-letter positions. Its callers establish an
// in-bounds eight-byte window before this unaligned amd64 load.
@@ -50,6 +54,7 @@ func rootSkip32(ptr *byte, n int, target, fold uint64) int
func rootSkip64(ptr *byte, n int, target, fold uint64) int
func literalSkip32(ptr *byte, n int, target, fold uint64) int
func literalSkip64(ptr *byte, n int, target, fold uint64) int
+func literalSkipExact64(ptr *byte, n int, target uint64) int
func runMask32(ptr *byte, target, fold uint64) uint32
func runMask64(ptr *byte, target, fold uint64) uint64
func probeSkip32(ptr *byte, n int, probe *asciiProbe) int
@@ -65,6 +70,7 @@ func pairShuftiSkip64(ptr *byte, n int, filter *pairShuftiFilter) int
func pairShuftiWithOnesSkip64(ptr *byte, n int, filter *pairShuftiFilter) int
func pairPairSkip64(ptr *byte, n int, filter *pairPairFilter) int
func pairPairVBMISkip64(ptr *byte, n int, filter *pairPairVBMIFilter) int
+func pairPairConfirmVBMI64(ptr *byte, n int, filter *pairPairVBMIFilter, confirm *byte) (ret, width int)
func pairPairWordSkip64(ptr *byte, n int, filter *pairPairFilter) int
func pairSecondSkip32(ptr *byte, n int, filter *rootFilter) int
func pairSecondSkip64(ptr *byte, n int, filter *rootFilter) int
@@ -75,6 +81,7 @@ func filterSkip64(ptr *byte, n int, filter *rootFilter) int
func tripleSkip32(ptr *byte, n int, filter *tripleFilter) int
func tripleSkip64(ptr *byte, n int, filter *tripleFilter) int
func tripleShuftiSkip64(ptr *byte, n int, filter *tripleShuftiFilter) int
+func rawByteMultiAnchorSkip64(ptr *byte, n int, filter *rawByteMultiAnchorFilter) (ret int, tags byte)
func asciiPairAnchorSkip64(ptr *byte, n int, filter *asciiPairAnchorFilter) int
func asciiPairAnchorVBMISkip64(ptr *byte, n int, filter *asciiPairVBMIAnchorFilter) int
func tripleSharedPrefixSkip64(ptr *byte, n int, filter *tripleFilter) int
@@ -156,6 +163,28 @@ func literalSkipASCII(s string, at int, kind uint8, needle byte) int {
return at - start
}
+// literalSkipExactASCII is the fixed-byte specialization used by a compiled
+// universal literal. It scans through high and malformed bytes just like
+// literalSkipASCII, but avoids the generic fold-zero vector operation.
+func literalSkipExactASCII(s string, at int, needle byte) int {
+ start := at
+ remaining := len(s) - at
+ target := uint64(needle) * byteOnes
+ if cpu.X86.HasAVX512F && cpu.X86.HasAVX512BW && remaining >= 64 {
+ full := remaining &^ 63
+ ptr := (*byte)(unsafe.Add(unsafe.Pointer(unsafe.StringData(s)), at))
+ skipped := literalSkipExact64(ptr, remaining, target)
+ at += skipped
+ if skipped < full {
+ return at - start
+ }
+ }
+ for at < len(s) && s[at] != needle {
+ at++
+ }
+ return at - start
+}
+
func probeSkipBytes(s string, at, candidates int, probe *asciiProbe) int {
start := at
if cpu.X86.HasAVX512F && cpu.X86.HasAVX512BW && candidates >= 64 {
@@ -433,6 +462,14 @@ func pairShuftiSkipBytes(s string, at int, filter *rootFilter) int {
return at - start + pairShuftiSkipScalar(s, at, &filter.shufti)
}
+// pairPairConfirmBytes scans full 64-start blocks and returns the first
+// fully confirmed anchor, or candidates when no full-block candidate matches.
+// findUnicodePairConfirm establishes the feature and bound guards.
+func pairPairConfirmBytes(s string, at, candidates int, filter *pairPairFilter, confirm unicodePairConfirm) (int, int) {
+ ptr := (*byte)(unsafe.Add(unsafe.Pointer(unsafe.StringData(s)), at))
+ return pairPairConfirmVBMI64(ptr, candidates, &filter.vbmi, unsafe.StringData(string(confirm)))
+}
+
func pairPairSkipBytes(s string, at int, filter *pairPairFilter) int {
start := at
offset := int(filter.offset)
@@ -586,6 +623,26 @@ func tripleShuftiSkipBytes(s string, at int, filter *tripleShuftiFilter) int {
// asciiPairAnchorSkipBytes scans a single bounded pair table. The route that
// calls it has already proved an all-ASCII, non-NUL haystack; this function
// remains a conservative filter and its caller replays plan transitions.
+// rawByteMultiAnchorSkipBytes scans primary and tagged confirmation pairs in
+// 64-byte VBMI blocks. Both paths return the conservative tag bits at the first
+// surviving lane; tagsAt removes low-six-bit aliases before plan replay.
+func rawByteMultiAnchorSkipBytes(s string, at int, filter *rawByteMultiAnchorFilter) (int, byte) {
+ start := at
+ remaining := len(s) - at
+ if filter.usable() && cpu.X86.HasAVX512F && cpu.X86.HasAVX512BW && cpu.X86.HasAVX512VBMI &&
+ remaining >= 65+int(filter.maxConfirmOffset) {
+ full := ((remaining - 1 - int(filter.maxConfirmOffset)) / 64) * 64
+ ptr := (*byte)(unsafe.Add(unsafe.Pointer(unsafe.StringData(s)), at))
+ skipped, tags := rawByteMultiAnchorSkip64(ptr, remaining, filter)
+ at += skipped
+ if skipped < full {
+ return at - start, tags
+ }
+ }
+ skipped, tags := rawByteMultiAnchorSkipScalar(s, at, filter)
+ return at - start + skipped, tags
+}
+
func asciiPairAnchorSkipBytes(s string, at int, filter *asciiPairAnchorFilter) int {
start := at
remaining := len(s) - at
diff --git a/root_amd64.s b/root_amd64.s
index 4a2649b..7d24520 100644
--- a/root_amd64.s
+++ b/root_amd64.s
@@ -189,6 +189,134 @@ literaldone64:
VZEROUPPER
RET
+// literalSkipExact64 is the exact-byte specialization of literalSkip64. Fixed
+// literal anchors need no fold vector. Eight unmasked memory-source compares
+// keep independent cache lines in flight before the first ordered mask test.
+TEXT ·literalSkipExact64(SB), NOSPLIT, $0-32
+ MOVQ ptr+0(FP), AX
+ MOVQ n+8(FP), DX
+ MOVQ target+16(FP), CX
+ VPBROADCASTB CX, Z1
+ MOVQ DX, R8
+ SHRQ $9, R8
+ JZ literalexactquad64
+
+literalexactloop64:
+ VPCMPEQB (AX), Z1, K0
+ VPCMPEQB 64(AX), Z1, K1
+ VPCMPEQB 128(AX), Z1, K2
+ VPCMPEQB 192(AX), Z1, K3
+ VPCMPEQB 256(AX), Z1, K4
+ VPCMPEQB 320(AX), Z1, K5
+ VPCMPEQB 384(AX), Z1, K6
+ VPCMPEQB 448(AX), Z1, K7
+ KORTESTQ K0, K1
+ JNE literalexactfirstpair64
+ KORTESTQ K2, K3
+ JNE literalexactsecondpair64
+ KORTESTQ K4, K5
+ JNE literalexactthirdpair64
+ KORTESTQ K6, K7
+ JNE literalexactfourthpair64
+ ADDQ $512, AX
+ DECQ R8
+ JNZ literalexactloop64
+ ANDQ $511, DX
+ JMP literalexactquad64
+
+literalexactfirstpair64:
+ KTESTQ K0, K0
+ JNE literalexactblock064
+ KMOVQ K1, CX
+ BSFQ CX, CX
+ ADDQ $64, AX
+ ADDQ CX, AX
+ JMP literalexactdone64
+literalexactsecondpair64:
+ KTESTQ K2, K2
+ JNE literalexactblock264
+ KMOVQ K3, CX
+ BSFQ CX, CX
+ ADDQ $192, AX
+ ADDQ CX, AX
+ JMP literalexactdone64
+literalexactthirdpair64:
+ KTESTQ K4, K4
+ JNE literalexactblock464
+ KMOVQ K5, CX
+ BSFQ CX, CX
+ ADDQ $320, AX
+ ADDQ CX, AX
+ JMP literalexactdone64
+literalexactfourthpair64:
+ KTESTQ K6, K6
+ JNE literalexactblock664
+ KMOVQ K7, CX
+ BSFQ CX, CX
+ ADDQ $448, AX
+ ADDQ CX, AX
+ JMP literalexactdone64
+literalexactblock064:
+ KMOVQ K0, CX
+ BSFQ CX, CX
+ ADDQ CX, AX
+ JMP literalexactdone64
+literalexactblock264:
+ KMOVQ K2, CX
+ BSFQ CX, CX
+ ADDQ $128, AX
+ ADDQ CX, AX
+ JMP literalexactdone64
+literalexactblock464:
+ KMOVQ K4, CX
+ BSFQ CX, CX
+ ADDQ $256, AX
+ ADDQ CX, AX
+ JMP literalexactdone64
+literalexactblock664:
+ KMOVQ K6, CX
+ BSFQ CX, CX
+ ADDQ $384, AX
+ ADDQ CX, AX
+ JMP literalexactdone64
+
+literalexactquad64:
+ CMPQ DX, $256
+ JL literalexactdouble64
+ VPCMPEQB (AX), Z1, K0
+ VPCMPEQB 64(AX), Z1, K1
+ VPCMPEQB 128(AX), Z1, K2
+ VPCMPEQB 192(AX), Z1, K3
+ KORTESTQ K0, K1
+ JNE literalexactfirstpair64
+ KORTESTQ K2, K3
+ JNE literalexactsecondpair64
+ ADDQ $256, AX
+ SUBQ $256, DX
+
+literalexactdouble64:
+ CMPQ DX, $128
+ JL literalexactsingle64
+ VPCMPEQB (AX), Z1, K0
+ VPCMPEQB 64(AX), Z1, K1
+ KORTESTQ K0, K1
+ JNE literalexactfirstpair64
+ ADDQ $128, AX
+ SUBQ $128, DX
+
+literalexactsingle64:
+ CMPQ DX, $64
+ JL literalexactdone64
+ VPCMPEQB (AX), Z1, K0
+ KTESTQ K0, K0
+ JNE literalexactblock064
+ ADDQ $64, AX
+literalexactdone64:
+ SUBQ ptr+0(FP), AX
+ MOVQ AX, ret+24(FP)
+ VZEROUPPER
+ RET
+
// runMask32 returns one equality bit per byte for a repeated-token block.
TEXT ·runMask32(SB), NOSPLIT, $0-28
MOVQ ptr+0(FP), AX
@@ -2842,3 +2970,518 @@ pairpairvbmidone64:
MOVQ BX, ret+24(FP)
VZEROUPPER
RET
+
+// pairPairConfirmVBMI64 keeps the pair-pair candidate mask in the AVX-512
+// loop and checks each set bit against the bounded raw-token representation.
+// The packed confirmation has ten-byte parts: values at 0, 2, and 4, source
+// offset at 6, width at 7, and value count at 8. Its anchor offset and vector
+// part count are at 201 and 202 after its twenty slots. The pair-pair slots
+// are excluded from that count after their UTF-8 byte classes make the VBMI
+// low-six-bit table hits exact.
+TEXT ·pairPairConfirmVBMI64(SB), NOSPLIT, $0-48
+ MOVQ ptr+0(FP), AX
+ MOVQ n+8(FP), DX
+ MOVQ filter+16(FP), SI
+ MOVQ confirm+24(FP), DI
+ XORQ BX, BX
+ MOVQ $-1, CX
+ KMOVQ CX, K1
+ VMOVDQU8 0(SI), K1, Z1
+ VMOVDQU8 64(SI), K1, Z2
+ VMOVDQU8 128(SI), K1, Z3
+ VMOVDQU8 192(SI), K1, Z4
+ MOVBLZX 256(SI), R8
+
+pairpairconfirmdouble64:
+ CMPQ DX, $128
+ JL pairpairconfirmsingle64
+ VMOVDQU8 (AX), K1, Z0
+ VMOVDQU8 1(AX), K1, Z9
+ VMOVDQU8 (AX)(R8*1), K1, Z10
+ VMOVDQU8 1(AX)(R8*1), K1, Z11
+ VMOVDQU8 64(AX), K1, Z12
+ VMOVDQU8 65(AX), K1, Z13
+ VMOVDQU8 64(AX)(R8*1), K1, Z14
+ VMOVDQU8 65(AX)(R8*1), K1, Z15
+
+ VPERMB Z1, Z0, Z0
+ VPERMB Z2, Z9, Z9
+ VPERMB Z3, Z10, Z10
+ VPERMB Z4, Z11, Z11
+ VPTESTMB Z9, Z0, K1, K2
+ VPTESTMB Z11, Z10, K1, K3
+ KANDQ K2, K3, K2
+
+ VPERMB Z1, Z12, Z12
+ VPERMB Z2, Z13, Z13
+ VPERMB Z3, Z14, Z14
+ VPERMB Z4, Z15, Z15
+ VPTESTMB Z13, Z12, K1, K3
+ VPTESTMB Z15, Z14, K1, K4
+ KANDQ K3, K4, K3
+ KORTESTQ K2, K3
+ JEQ pairpairconfirmadvance128
+
+ KMOVQ K2, CX
+ XORQ SI, SI
+ TESTQ CX, CX
+ JNZ pairpairconfirmcandidate
+ JMP pairpairconfirmsecond
+
+pairpairconfirmsecond:
+ MOVQ $1, SI
+ KMOVQ K3, CX
+ TESTQ CX, CX
+ JNZ pairpairconfirmcandidate
+ JMP pairpairconfirmadvance128
+
+pairpairconfirmcandidate:
+ BSFQ CX, R9
+ LEAQ (AX)(R9*1), R10
+ CMPQ SI, $1
+ JNE pairpairconfirmbase
+ ADDQ $64, R10
+pairpairconfirmbase:
+ MOVBLZX 201(DI), R13
+ SUBQ R13, R10
+ TESTB $2, 203(DI)
+ JNE pairpairconfirmvariable
+ LEAQ (R10)(R13*1), R11
+ MOVQ $0x80C0, R14
+ MOVWQZX (R11), R12
+ ANDQ $0xC0C0, R12
+ CMPQ R14, R12
+ JNE pairpairconfirmreject
+ MOVWQZX (R11)(R8*1), R12
+ ANDQ $0xC0C0, R12
+ CMPQ R14, R12
+ JNE pairpairconfirmreject
+ MOVQ DI, R11
+ MOVBLZX 202(DI), R12
+ TESTQ R12, R12
+ JZ pairpairconfirmaccepted
+pairpairconfirmpart:
+ MOVBLZX 6(R11), R13
+ MOVBLZX 7(R11), R14
+ CMPQ R14, $2
+ JEQ pairpairconfirmword
+ MOVBLZX (R10)(R13*1), R14
+ JMP pairpairconfirmvalue
+pairpairconfirmword:
+ MOVWQZX (R10)(R13*1), R14
+pairpairconfirmvalue:
+ MOVWQZX 0(R11), R15
+ CMPQ R14, R15
+ JEQ pairpairconfirmnext
+ CMPB 8(R11), $2
+ JL pairpairconfirmreject
+ MOVWQZX 2(R11), R15
+ CMPQ R14, R15
+ JEQ pairpairconfirmnext
+ CMPB 8(R11), $3
+ JNE pairpairconfirmreject
+ MOVWQZX 4(R11), R15
+ CMPQ R14, R15
+ JNE pairpairconfirmreject
+pairpairconfirmnext:
+ ADDQ $10, R11
+ DECQ R12
+ JNZ pairpairconfirmpart
+ JMP pairpairconfirmaccepted
+
+// Variable-width confirmation walks the raw forms in source order. Each
+// ten-byte part contains up to three zero-padded three-byte forms and a count
+// at byte nine. A form's UTF-8 lead byte determines whether the cursor advances
+// by one, two, or three bytes.
+pairpairconfirmvariable:
+ MOVQ R10, R13
+ MOVQ DI, R11
+ MOVBLZX 202(DI), R12
+pairpairconfirmvariablepart:
+ MOVBLZX 9(R11), R14
+ MOVQ R11, R15
+pairpairconfirmvariableform:
+ MOVBLZX 0(R15), R10
+ CMPB 0(R13), R10
+ JNE pairpairconfirmvariablenextform
+ CMPQ R10, $0x80
+ JL pairpairconfirmvariableone
+ MOVBLZX 1(R15), R10
+ CMPB 1(R13), R10
+ JNE pairpairconfirmvariablenextform
+ MOVBLZX 0(R15), R10
+ CMPQ R10, $0xe0
+ JL pairpairconfirmvariabletwo
+ MOVBLZX 2(R15), R10
+ CMPB 2(R13), R10
+ JNE pairpairconfirmvariablenextform
+ ADDQ $3, R13
+ JMP pairpairconfirmvariablenextpart
+pairpairconfirmvariabletwo:
+ ADDQ $2, R13
+ JMP pairpairconfirmvariablenextpart
+pairpairconfirmvariableone:
+ INCQ R13
+pairpairconfirmvariablenextpart:
+ ADDQ $10, R11
+ DECQ R12
+ JNZ pairpairconfirmvariablepart
+ LEAQ (AX)(R9*1), R10
+ CMPQ SI, $1
+ JNE pairpairconfirmvariablewidth
+ ADDQ $64, R10
+pairpairconfirmvariablewidth:
+ MOVBLZX 201(DI), R12
+ SUBQ R12, R10
+ SUBQ R10, R13
+ JMP pairpairconfirmreturnaccepted
+pairpairconfirmvariablenextform:
+ ADDQ $3, R15
+ DECQ R14
+ JNZ pairpairconfirmvariableform
+ JMP pairpairconfirmreject
+pairpairconfirmaccepted:
+ MOVBLZX 200(DI), R13
+pairpairconfirmreturnaccepted:
+ ADDQ R9, BX
+ CMPQ SI, $1
+ JNE pairpairconfirmdone
+ ADDQ $64, BX
+ JMP pairpairconfirmdone
+
+pairpairconfirmreject:
+ BTRQ R9, CX
+ TESTQ CX, CX
+ JNZ pairpairconfirmcandidate
+ CMPQ SI, $0
+ JEQ pairpairconfirmsecond
+ CMPQ SI, $1
+ JEQ pairpairconfirmadvance128
+ JMP pairpairconfirmadvance64
+
+pairpairconfirmadvance128:
+ ADDQ $128, AX
+ ADDQ $128, BX
+ SUBQ $128, DX
+ JMP pairpairconfirmdouble64
+
+pairpairconfirmsingle64:
+ CMPQ DX, $64
+ JL pairpairconfirmdone
+ VMOVDQU8 (AX), K1, Z0
+ VMOVDQU8 1(AX), K1, Z9
+ VMOVDQU8 (AX)(R8*1), K1, Z10
+ VMOVDQU8 1(AX)(R8*1), K1, Z11
+ VPERMB Z1, Z0, Z0
+ VPERMB Z2, Z9, Z9
+ VPERMB Z3, Z10, Z10
+ VPERMB Z4, Z11, Z11
+ VPTESTMB Z9, Z0, K1, K2
+ VPTESTMB Z11, Z10, K1, K3
+ KANDQ K2, K3, K2
+ KTESTQ K2, K2
+ JEQ pairpairconfirmadvance64
+ KMOVQ K2, CX
+ MOVQ $2, SI
+ JMP pairpairconfirmcandidate
+
+pairpairconfirmadvance64:
+ ADDQ $64, AX
+ ADDQ $64, BX
+ SUBQ $64, DX
+ JMP pairpairconfirmsingle64
+
+pairpairconfirmdone:
+ MOVQ n+8(FP), R12
+ CMPQ BX, R12
+ JNE pairpairconfirmwidthdone
+ XORQ R13, R13
+pairpairconfirmwidthdone:
+ MOVQ BX, ret+32(FP)
+ MOVQ R13, width+40(FP)
+ VZEROUPPER
+ RET
+
+// rawByteMultiAnchorSkip64 intersects a primary pattern-tag pair with up to
+// three fixed-displacement confirmation pairs. Tables use VPERMB's low six
+// source bits. It returns the surviving tag byte with the first lane; Go then
+// checks exact primary, confirmation, and guard pairs only for those tags.
+// rawByteMultiAnchorFilter lays out primary tables at 0/64, confirmation-first
+// tables at 128/192/256, and confirmation-second tables at 320/384/448.
+TEXT ·rawByteMultiAnchorSkip64(SB), NOSPLIT, $64-33
+ MOVQ ptr+0(FP), AX
+ MOVQ n+8(FP), DX
+ MOVQ filter+16(FP), SI
+ MOVQ $-1, CX
+ XORL R15, R15
+ KMOVQ CX, K1
+ VMOVDQU8 0(SI), K1, Z1
+ VMOVDQU8 64(SI), K1, Z2
+ VMOVDQU8 128(SI), K1, Z3
+ VMOVDQU8 192(SI), K1, Z4
+ VMOVDQU8 256(SI), K1, Z5
+ VMOVDQU8 320(SI), K1, Z6
+ VMOVDQU8 384(SI), K1, Z7
+ VMOVDQU8 448(SI), K1, Z8
+ MOVBLZX 512(SI), R8
+ MOVBLZX 513(SI), R9
+ MOVBLZX 514(SI), R10
+ MOVBLZX 516(SI), R11
+ ADDQ $65, R11
+ LEAQ 192(R11), R12
+ // The eight-block zero scan reads the adjacent primary byte through offset
+ // 512. Keep a complete confirmation horizon for every scanned block too:
+ // an aggregate hit must safely replay the four-/one-block dispatcher.
+ LEAQ 448(R11), BX
+rawbytemultianchorloop64:
+ CMPQ DX, R11
+ JL rawbytemultianchordone64
+ CMPQ DX, BX
+ JGE rawbytemultianchorzero512
+rawbytemultianchorfour64:
+ CMPQ DX, R12
+ JL rawbytemultianchorsingle64
+
+ // Four independent primary blocks hide VPERMB latency on no-candidate text.
+ // Materialize their tag products, then reduce them once. The common sparse
+ // no-hit path crosses only one byte mask; the per-block masks are derived
+ // only when the aggregate is nonzero and are then reused for density choice.
+ VMOVDQU8 (AX), K1, Z0
+ VMOVDQU8 1(AX), K1, Z9
+ VMOVDQU8 64(AX), K1, Z10
+ VMOVDQU8 65(AX), K1, Z11
+ VMOVDQU8 128(AX), K1, Z12
+ VMOVDQU8 129(AX), K1, Z13
+ VMOVDQU8 192(AX), K1, Z14
+ VMOVDQU8 193(AX), K1, Z15
+ VPERMB Z1, Z0, Z0
+ VPERMB Z2, Z9, Z9
+ VPERMB Z1, Z10, Z10
+ VPERMB Z2, Z11, Z11
+ VPERMB Z1, Z12, Z12
+ VPERMB Z2, Z13, Z13
+ VPERMB Z1, Z14, Z14
+ VPERMB Z2, Z15, Z15
+ VPANDQ Z9, Z0, K1, Z0
+ VPANDQ Z11, Z10, K1, Z10
+ VPANDQ Z13, Z12, K1, Z12
+ VPANDQ Z15, Z14, K1, Z14
+ VPORQ Z10, Z0, K1, Z9
+ // 0xfe is the three-input OR truth table. Z9 already carries blocks 0|1.
+ VPTERNLOGD $0xfe, Z14, Z12, K1, Z9
+ VPTESTMB Z9, Z9, K1, K2
+ KTESTQ K2, K2
+ JEQ rawbytemultianchoradvance256
+
+ // An aggregate hit is rare on sparse data. Only then recover the individual
+ // masks needed to select a sole block or enter the dense schedule.
+ VPTESTMB Z0, Z0, K1, K2
+ VPTESTMB Z10, Z10, K1, K3
+ VPTESTMB Z12, Z12, K1, K4
+ VPTESTMB Z14, Z14, K1, K5
+ // Any two occupied blocks choose the bounded dense one-block schedule; a
+ // sole occupied block keeps its already materialized primary product.
+ KORTESTQ K2, K3
+ JNE rawbytemultianchorfirstnonzero64
+ KORTESTQ K4, K5
+ JNE rawbytemultianchorlastnonzero64
+rawbytemultianchoradvance256:
+ ADDQ $256, AX
+ SUBQ $256, DX
+ JMP rawbytemultianchorloop64
+rawbytemultianchorfirstnonzero64:
+ KORTESTQ K4, K5
+ JNE rawbytemultianchordense64
+ KTESTQ K2, K2
+ JEQ rawbytemultianchorblock164
+ KTESTQ K3, K3
+ JNE rawbytemultianchordense64
+ JMP rawbytemultianchorconfirm64
+rawbytemultianchorlastnonzero64:
+ KTESTQ K4, K4
+ JEQ rawbytemultianchorblock364
+ KTESTQ K5, K5
+ JNE rawbytemultianchordense64
+rawbytemultianchorblock264:
+ VMOVDQA64 Z12, K1, Z0
+ ADDQ $128, AX
+ SUBQ $128, DX
+ JMP rawbytemultianchorconfirm64
+rawbytemultianchorblock164:
+ VMOVDQA64 Z10, K1, Z0
+ ADDQ $64, AX
+ SUBQ $64, DX
+ JMP rawbytemultianchorconfirm64
+rawbytemultianchorblock364:
+ VMOVDQA64 Z14, K1, Z0
+ ADDQ $192, AX
+ SUBQ $192, DX
+ JMP rawbytemultianchorconfirm64
+
+// The sparse fast path has no candidate ordering work: it only proves that
+// eight independent 64-byte primary blocks are all zero. Any aggregate hit
+// replays the existing four-block dispatcher at the unchanged AX/DX, where it
+// recovers masks, preserves leftmost order, and applies the density switch.
+rawbytemultianchorzero512:
+ VMOVDQU8 (AX), K1, Z0
+ VMOVDQU8 1(AX), K1, Z9
+ VMOVDQU8 64(AX), K1, Z10
+ VMOVDQU8 65(AX), K1, Z11
+ VMOVDQU8 128(AX), K1, Z12
+ VMOVDQU8 129(AX), K1, Z13
+ VMOVDQU8 192(AX), K1, Z14
+ VMOVDQU8 193(AX), K1, Z15
+ VMOVDQU8 256(AX), K1, Z16
+ VMOVDQU8 257(AX), K1, Z17
+ VMOVDQU8 320(AX), K1, Z18
+ VMOVDQU8 321(AX), K1, Z19
+ VMOVDQU8 384(AX), K1, Z20
+ VMOVDQU8 385(AX), K1, Z21
+ VMOVDQU8 448(AX), K1, Z22
+ VMOVDQU8 449(AX), K1, Z23
+ VPERMB Z1, Z0, Z0
+ VPERMB Z2, Z9, Z9
+ VPERMB Z1, Z10, Z10
+ VPERMB Z2, Z11, Z11
+ VPERMB Z1, Z12, Z12
+ VPERMB Z2, Z13, Z13
+ VPERMB Z1, Z14, Z14
+ VPERMB Z2, Z15, Z15
+ VPERMB Z1, Z16, Z16
+ VPERMB Z2, Z17, Z17
+ VPERMB Z1, Z18, Z18
+ VPERMB Z2, Z19, Z19
+ VPERMB Z1, Z20, Z20
+ VPERMB Z2, Z21, Z21
+ VPERMB Z1, Z22, Z22
+ VPERMB Z2, Z23, Z23
+ VPANDQ Z9, Z0, K1, Z0
+ VPANDQ Z11, Z10, K1, Z10
+ VPANDQ Z13, Z12, K1, Z12
+ VPANDQ Z15, Z14, K1, Z14
+ VPANDQ Z17, Z16, K1, Z16
+ VPANDQ Z19, Z18, K1, Z18
+ VPANDQ Z21, Z20, K1, Z20
+ VPANDQ Z23, Z22, K1, Z22
+ // Reduce eight products in four Boolean operations. Each 0xfe ternary
+ // instruction ORs its two sources with its old destination.
+ VPTERNLOGD $0xfe, Z12, Z10, K1, Z0
+ VPTERNLOGD $0xfe, Z18, Z16, K1, Z14
+ VPTERNLOGD $0xfe, Z20, Z14, K1, Z0
+ VPORQ Z22, Z0, K1, Z0
+ VPTESTMB Z0, Z0, K1, K2
+ KTESTQ K2, K2
+ JNE rawbytemultianchorfour64
+ ADDQ $512, AX
+ SUBQ $512, DX
+ JMP rawbytemultianchorloop64
+
+rawbytemultianchorsingle64:
+ VMOVDQU8 (AX), K1, Z0
+ VMOVDQU8 1(AX), K1, Z9
+ VPERMB Z1, Z0, Z0
+ VPERMB Z2, Z9, Z9
+ VPTESTMB Z9, Z0, K1, K2
+ KTESTQ K2, K2
+ JEQ rawbytemultianchoradvance64
+ VPANDQ Z9, Z0, K1, Z0
+
+rawbytemultianchorconfirm64:
+ // Do not pay the three confirmation lookups for a block with no primary
+ // tag. A primary hit remains conservative; the exact Go checks still decide
+ // the candidate before the common raw transition replay.
+ VMOVDQU8 0(AX)(R8*1), K1, Z9
+ VMOVDQU8 1(AX)(R8*1), K1, Z10
+ VMOVDQU8 0(AX)(R9*1), K1, Z11
+ VMOVDQU8 1(AX)(R9*1), K1, Z12
+ VMOVDQU8 0(AX)(R10*1), K1, Z13
+ VMOVDQU8 1(AX)(R10*1), K1, Z14
+ VPERMB Z3, Z9, Z9
+ VPERMB Z6, Z10, Z10
+ VPERMB Z4, Z11, Z11
+ VPERMB Z7, Z12, Z12
+ VPERMB Z5, Z13, Z13
+ VPERMB Z8, Z14, Z14
+ VPANDQ Z10, Z9, K1, Z9
+ VPANDQ Z12, Z11, K1, Z11
+ VPANDQ Z14, Z13, K1, Z13
+ VPANDQ Z9, Z0, K1, Z9
+ VPANDQ Z11, Z0, K1, Z11
+ VPANDQ Z13, Z0, K1, Z13
+ VPORQ Z11, Z9, K1, Z9
+ VPORQ Z13, Z9, K1, Z9
+ VPTESTMB Z9, Z9, K1, K2
+ KTESTQ K2, K2
+ JNE rawbytemultianchorstop64
+rawbytemultianchoradvance64:
+ ADDQ $64, AX
+ SUBQ $64, DX
+ JMP rawbytemultianchorloop64
+rawbytemultianchorstop64:
+ KMOVQ K2, CX
+ BSFQ CX, CX
+ VMOVDQU8 Z9, (SP)
+ MOVBLZX (SP)(CX*1), R15
+ ADDQ CX, AX
+ JMP rawbytemultianchordone64
+// The dense body is the proven one-block primary-plus-confirmation schedule.
+// It changes only batching; the same conservative tables and Go replay remain
+// the match authority.
+rawbytemultianchordense64:
+ // A dense-prefix/sparse-suffix diagnostic is three times slower when dense
+ // mode owns the whole input. Bound a dense epoch to 4 KiB, or to the
+ // remaining safe block count. The decrement below replaces the old per-block
+ // tail comparison; expiration re-enters the shared four-block dispatcher.
+ CMPQ DX, R11
+ JL rawbytemultianchordone64
+ MOVQ DX, R13
+ SUBQ R11, R13
+ SHRQ $6, R13
+ INCQ R13
+ CMPQ R13, $64
+ JLE rawbytemultianchordenseloop64
+ MOVQ $64, R13
+rawbytemultianchordenseloop64:
+ VMOVDQU8 (AX), K1, Z0
+ VMOVDQU8 1(AX), K1, Z9
+ VMOVDQU8 0(AX)(R8*1), K1, Z10
+ VMOVDQU8 1(AX)(R8*1), K1, Z11
+ VMOVDQU8 0(AX)(R9*1), K1, Z12
+ VMOVDQU8 1(AX)(R9*1), K1, Z13
+ VMOVDQU8 0(AX)(R10*1), K1, Z14
+ VMOVDQU8 1(AX)(R10*1), K1, Z15
+ VPERMB Z1, Z0, Z0
+ VPERMB Z2, Z9, Z9
+ VPERMB Z3, Z10, Z10
+ VPERMB Z6, Z11, Z11
+ VPERMB Z4, Z12, Z12
+ VPERMB Z7, Z13, Z13
+ VPERMB Z5, Z14, Z14
+ VPERMB Z8, Z15, Z15
+ VPANDQ Z9, Z0, K1, Z0
+ VPANDQ Z11, Z10, K1, Z10
+ VPANDQ Z13, Z12, K1, Z12
+ VPANDQ Z15, Z14, K1, Z14
+ VPANDQ Z10, Z0, K1, Z10
+ VPANDQ Z12, Z0, K1, Z12
+ VPANDQ Z14, Z0, K1, Z14
+ VPORQ Z12, Z10, K1, Z10
+ VPORQ Z14, Z10, K1, Z10
+ VPTESTMB Z10, Z10, K1, K2
+ KTESTQ K2, K2
+ JNE rawbytemultianchordensestop64
+ ADDQ $64, AX
+ SUBQ $64, DX
+ DECQ R13
+ JNE rawbytemultianchordenseloop64
+ JMP rawbytemultianchorloop64
+rawbytemultianchordensestop64:
+ VMOVDQA64 Z10, K1, Z9
+ JMP rawbytemultianchorstop64
+
+rawbytemultianchordone64:
+ SUBQ ptr+0(FP), AX
+ MOVQ AX, ret+24(FP)
+ MOVB R15, tags+32(FP)
+ VZEROUPPER
+ RET
diff --git a/root_amd64_bench_test.go b/root_amd64_bench_test.go
new file mode 100644
index 0000000..0b9bffe
--- /dev/null
+++ b/root_amd64_bench_test.go
@@ -0,0 +1,32 @@
+//go:build amd64 && go1.24
+
+package casei
+
+import (
+ "bytes"
+ "strings"
+ "testing"
+ "unsafe"
+
+ "golang.org/x/sys/cpu"
+)
+
+func BenchmarkLiteralSkipExactCeiling(b *testing.B) {
+ if !cpu.X86.HasAVX512F || !cpu.X86.HasAVX512BW {
+ b.Skip("AVX-512 BW exact-byte path is disabled")
+ }
+ input := []byte(strings.Repeat("x", 5<<20))
+ target := uint64(' ') * byteOnes
+ b.Run("candidate", func(b *testing.B) {
+ b.SetBytes(int64(len(input)))
+ for b.Loop() {
+ _ = literalSkipExact64(unsafe.SliceData(input), len(input), target)
+ }
+ })
+ b.Run("index_byte", func(b *testing.B) {
+ b.SetBytes(int64(len(input)))
+ for b.Loop() {
+ _ = bytes.IndexByte(input, ' ')
+ }
+ })
+}
diff --git a/root_amd64_test.go b/root_amd64_test.go
index 40d90a8..5b9a669 100644
--- a/root_amd64_test.go
+++ b/root_amd64_test.go
@@ -3,6 +3,7 @@
package casei
import (
+ "bytes"
"strings"
"testing"
"unsafe"
@@ -10,6 +11,41 @@ import (
"golang.org/x/sys/cpu"
)
+func TestLiteralSkipExact64MatchesModel(t *testing.T) {
+ if !cpu.X86.HasAVX512F || !cpu.X86.HasAVX512BW {
+ t.Skip("AVX-512 BW exact-byte path is disabled")
+ }
+ target := uint64(' ') * byteOnes
+ check := func(input []byte, n int) {
+ t.Helper()
+ full := n &^ 63
+ want := bytes.IndexByte(input[:full], ' ')
+ if want < 0 {
+ want = full
+ }
+ if got := literalSkipExact64(unsafe.SliceData(input), n, target); got != want {
+ t.Fatalf("n=%d: skip=%d want=%d", n, got, want)
+ }
+ }
+ lengths := []int{0, 1, 63, 64, 65, 127, 128, 129, 255, 256, 257, 511, 512, 513, 1023, 1024, 1025, 4095}
+ positions := []int{0, 1, 63, 64, 127, 128, 191, 192, 255, 256, 319, 320, 383, 384, 447, 448, 511, 512, 1023, 4094}
+ for _, n := range lengths {
+ for _, alignment := range []int{0, 1, 31, 63} {
+ backing := []byte(strings.Repeat("x", alignment+n+64))
+ input := backing[alignment : alignment+n]
+ check(input, n)
+ for _, at := range positions {
+ if at >= n {
+ continue
+ }
+ input[at] = ' '
+ check(input, n)
+ input[at] = 'x'
+ }
+ }
+ }
+}
+
func TestPairSetSkip(t *testing.T) {
filter := rootFilter{
pairs: [16]rootPair{
diff --git a/root_bench_test.go b/root_bench_test.go
index 588e893..23cc46b 100644
--- a/root_bench_test.go
+++ b/root_bench_test.go
@@ -35,6 +35,166 @@ func BenchmarkPairShuftiMatcher(b *testing.B) {
}
}
+var (
+ rawByteBenchmarkMatch Match
+ rawByteBenchmarkOK bool
+)
+
+// BenchmarkRawByteConstruction keeps plan construction and its first ordinary
+// Find in one operation. It catches a cache-sized raw transition allocation on
+// one-shot Matchers and separately measures raw publication plus its first use,
+// all through the same public route as the density sweep.
+func BenchmarkRawByteConstruction(b *testing.B) {
+ for _, tc := range []struct {
+ name string
+ patterns []string
+ haystack string
+ rawInstallAndFirstUse bool
+ }{
+ {"two_patterns_512B", rawByteCyrillicPatterns[:2], rawByteFalseCandidates(4, 128), false},
+ {"two_patterns_4KiB", rawByteCyrillicPatterns[:2], rawByteFalseCandidates(4, 1024), false},
+ {"five_patterns_512B", rawByteCyrillicPatterns, rawByteFalseCandidates(4, 128), false},
+ {"five_patterns_513B_zero_admission", rawByteCyrillicPatterns, strings.Repeat("x", 513), false},
+ {"five_patterns_5MiB_raw_install_and_first_use", rawByteCyrillicPatterns, rawByteFalseCandidatesAtLeast(256, rawBytePublicationCorpusBytes), true},
+ {"two_shared_100_units_1KiB_zero_admission", rawByteLongPrefixPatterns(), strings.Repeat("x", 1<<10), false},
+ {"two_mixed_folded_ascii_root_1KiB", []string{"Д", "xД"}, strings.Repeat("x", 1<<10), false},
+ } {
+ b.Run(tc.name, func(b *testing.B) {
+ bytes := len(tc.haystack)
+ if tc.rawInstallAndFirstUse {
+ bytes *= 3
+ }
+ b.SetBytes(int64(bytes))
+ b.ReportAllocs()
+ for b.Loop() {
+ matcher := NewMatcher(tc.patterns)
+ if got, ok := matcher.Find(tc.haystack); ok || got != (Match{}) {
+ b.Fatalf("false-candidate stream matched: %+v,%t", got, ok)
+ }
+ if tc.rawInstallAndFirstUse {
+ // Keep decoded admission, the next reuse, and one following reuse in
+ // one operation. An implementation may publish an eligible acceleration
+ // between those calls, but both source arms execute the same public work.
+ for range 2 {
+ if got, ok := matcher.Find(tc.haystack); ok || got != (Match{}) {
+ b.Fatalf("raw install/first use stream matched: %+v,%t", got, ok)
+ }
+ }
+ }
+ }
+ })
+ }
+}
+
+// BenchmarkRawByteDensity measures false-root streams through the public
+// Matcher.Find entry point. The baseline advances decoded units; an eligible
+// compiled plan may replace that transition with its raw row without changing
+// this workload.
+func BenchmarkRawByteDensity(b *testing.B) {
+ for _, tc := range []struct {
+ name string
+ patterns []string
+ haystack string
+ }{
+ {"two_4KiB_one_in_32", rawByteCyrillicPatterns[:2], rawByteFalseCandidates(32, 4096/32)},
+ {"two_4KiB_zero_admission", rawByteCyrillicPatterns[:2], strings.Repeat("x", 4<<10)},
+ {"five_one_in_64", rawByteCyrillicPatterns, rawByteFalseCandidatesAtLeast(64, rawByteBenchmarkCorpusBytes)},
+ {"five_one_in_4", rawByteCyrillicPatterns, rawByteFalseCandidatesAtLeast(4, rawByteBenchmarkCorpusBytes/16)},
+ } {
+ matcher := NewMatcher(tc.patterns)
+ b.Run(tc.name, func(b *testing.B) {
+ b.SetBytes(int64(len(tc.haystack)))
+ b.ReportAllocs()
+ if got, ok := matcher.Find(tc.haystack); ok || got != (Match{}) {
+ b.Fatalf("false-candidate stream matched: %+v,%t", got, ok)
+ }
+ // The construction benchmark owns the first-call crossover. This
+ // benchmark measures a reused public Matcher, so complete its second
+ // ordinary Find before the timed steady-state loop.
+ if got, ok := matcher.Find(tc.haystack); ok || got != (Match{}) {
+ b.Fatalf("false-candidate stream matched: %+v,%t", got, ok)
+ }
+ b.ResetTimer()
+ for b.Loop() {
+ rawByteBenchmarkMatch, rawByteBenchmarkOK = matcher.Find(tc.haystack)
+ }
+ })
+ }
+}
+
+// BenchmarkRawByteFreshFind measures one ordinary Find on a new Matcher. It
+// includes every setup cost that this call pays. The long miss, near miss,
+// sparse miss, clustered miss, and early answer keep the decision boundary
+// visible to both sides of a comparison.
+func BenchmarkRawByteFreshFind(b *testing.B) {
+ longMiss := rawByteFalseCandidatesAtLeast(256, 10<<20)
+ longNearMiss := rawByteNearMissCandidatesAtLeast(256, rawBytePublicationCorpusBytes)
+ longSparse := rawByteFalseCandidatesAtLeast(8192, rawBytePublicationCorpusBytes)
+ longClustered := rawByteFalseCandidates(64, rawByteFreshSampleBytes/64) +
+ strings.Repeat("x", rawBytePublicationCorpusBytes-rawByteFreshSampleBytes)
+ longLateMatch, longLateMatchStart := rawByteLateMatchCandidatesAtLeast(256, 10<<20)
+ longEarlyMatch, longEarlyMatchStart := rawByteEarlyMatchCandidatesAtLeast(256, rawBytePublicationCorpusBytes, 8<<10)
+ for _, tc := range []struct {
+ name string
+ haystack string
+ want Match
+ wantOK bool
+ }{
+ {"five_long_false_one_in_256", longMiss, Match{}, false},
+ {"five_long_near_miss_one_in_256", longNearMiss, Match{}, false},
+ {"five_long_sparse_one_in_8192", longSparse, Match{}, false},
+ {"five_long_clustered", longClustered, Match{}, false},
+ {"five_long_early_match_one_in_256", longEarlyMatch, Match{Pattern: 0, Start: longEarlyMatchStart}, true},
+ {"five_long_late_match_one_in_256", longLateMatch, Match{Pattern: 0, Start: longLateMatchStart}, true},
+ } {
+ b.Run(tc.name, func(b *testing.B) {
+ b.SetBytes(int64(len(tc.haystack)))
+ b.ReportAllocs()
+ for b.Loop() {
+ matcher := NewMatcher(rawByteCyrillicPatterns)
+ got, gotOK := matcher.Find(tc.haystack)
+ if gotOK != tc.wantOK || got != tc.want {
+ b.Fatalf("fresh Find = %+v,%t; want %+v,%t", got, gotOK, tc.want, tc.wantOK)
+ }
+ }
+ })
+ }
+}
+
+// BenchmarkRawByteFindAfterFreshFind measures a second Find after the same
+// Matcher has completed a long fresh no-match search. The setup is deliberately
+// outside the timer: the fresh benchmark owns its cost, while this benchmark
+// keeps the published-view reuse state comparable across both source arms.
+func BenchmarkRawByteFindAfterFreshFind(b *testing.B) {
+ publication := rawByteFalseCandidatesAtLeast(256, 10<<20)
+ lateMatch, lateMatchStart := rawByteLateMatchCandidatesAtLeast(256, rawBytePublicationCorpusBytes)
+ for _, tc := range []struct {
+ name string
+ haystack string
+ want Match
+ wantOK bool
+ }{
+ {"five_long_reused_near_miss_one_in_256", rawByteNearMissCandidatesAtLeast(256, rawBytePublicationCorpusBytes), Match{}, false},
+ {"five_long_reused_late_match_one_in_256", lateMatch, Match{Pattern: 0, Start: lateMatchStart}, true},
+ } {
+ b.Run(tc.name, func(b *testing.B) {
+ matcher := NewMatcher(rawByteCyrillicPatterns)
+ if got, ok := matcher.Find(publication); ok || got != (Match{}) {
+ b.Fatalf("publication Find = %+v,%t", got, ok)
+ }
+ b.SetBytes(int64(len(tc.haystack)))
+ b.ReportAllocs()
+ b.ResetTimer()
+ for b.Loop() {
+ got, gotOK := matcher.Find(tc.haystack)
+ if gotOK != tc.wantOK || got != tc.want {
+ b.Fatalf("reused Find = %+v,%t; want %+v,%t", got, gotOK, tc.want, tc.wantOK)
+ }
+ }
+ })
+ }
+}
+
func BenchmarkTripleSkipBytes(b *testing.B) {
plan := newSearchPlan([]string{"fatal panic", "segfault detected"})
haystack := strings.Repeat("x", 1<<20)
diff --git a/root_other.go b/root_other.go
index 99eef15..95259c9 100644
--- a/root_other.go
+++ b/root_other.go
@@ -8,6 +8,8 @@ func runtimeVectorBits() int { return 0 }
func asciiPairVBMIEnabled() bool { return false }
+func unicodePairConfirmVectorEnabled() bool { return false }
+
func asciiFixedPrefix8(s string, at int, word, fold uint64) bool {
for i := 0; i < 8; i++ {
if s[at+i]|byte(fold>>(8*i)) != byte(word>>(8*i)) {
@@ -62,6 +64,10 @@ func literalSkipASCII(s string, at int, kind uint8, needle byte) int {
return at - start
}
+func literalSkipExactASCII(s string, at int, needle byte) int {
+ return literalSkipASCII(s, at, rootExact, needle)
+}
+
func probeSkipBytes(s string, at, candidates int, probe *asciiProbe) int {
start := at
for at-start < candidates {
@@ -139,6 +145,19 @@ func pairShuftiSkipBytes(s string, at int, filter *rootFilter) int {
return pairShuftiSkipScalar(s, at, &filter.shufti)
}
+func pairPairConfirmBytes(s string, at, candidates int, filter *pairPairFilter, confirm unicodePairConfirm) (int, int) {
+ start := at
+ for at-start < candidates {
+ if pairPairAt(s, at, filter) {
+ if width, ok := confirm.matchWidthAt(s, at-confirm.anchorAt()); ok {
+ return at - start, width
+ }
+ }
+ at++
+ }
+ return candidates, 0
+}
+
func pairPairSkipBytes(s string, at int, filter *pairPairFilter) int {
start := at
for at+int(filter.offset)+1 < len(s) {
@@ -222,6 +241,10 @@ func tripleShuftiSkipBytes(s string, at int, filter *tripleShuftiFilter) int {
return tripleShuftiSkipScalar(s, at, filter)
}
+func rawByteMultiAnchorSkipBytes(s string, at int, filter *rawByteMultiAnchorFilter) (int, byte) {
+ return rawByteMultiAnchorSkipScalar(s, at, filter)
+}
+
func asciiPairAnchorSkipBytes(s string, at int, filter *asciiPairAnchorFilter) int {
return asciiPairAnchorSkipScalar(s, at, filter)
}
diff --git a/root_test.go b/root_test.go
index c7c5963..62d7b39 100644
--- a/root_test.go
+++ b/root_test.go
@@ -755,22 +755,173 @@ func TestPairPairVBMIProjection(t *testing.T) {
}
}
- // The byte projection may reach an alias, but the ordinary Unicode matcher
- // must reject it and continue through the later exact rendering.
- alias := []byte(strings.Repeat("x", int(filter.offset)+2))
- alias[0], alias[1] = filter.first0^0x40, filter.second0^0x40
- alias[filter.offset], alias[filter.offset+1] = filter.confirmFirst0^0x40, filter.confirmSecond0^0x40
- const gap = 64
- haystack := string(alias) + strings.Repeat("x", gap) + "ЯР"
- want := len(alias) + gap
- if cpu.X86.HasAVX512F && cpu.X86.HasAVX512BW && cpu.X86.HasAVX512VBMI {
- if got := pairPairSkipBytes(haystack, 0, filter); got != 0 {
- t.Fatalf("VBMI pair alias did not reach replay: skip=%d", got)
+ // The byte projection may reach either high-bit alias, but the fused and
+ // decoded Unicode matchers must reject it and continue to the exact form.
+ for _, aliasBit := range []byte{0x40, 0x80} {
+ alias := []byte(strings.Repeat("x", int(filter.offset)+2))
+ alias[0], alias[1] = filter.first0^aliasBit, filter.second0^aliasBit
+ alias[filter.offset], alias[filter.offset+1] = filter.confirmFirst0^aliasBit, filter.confirmSecond0^aliasBit
+ const gap = 4096
+ haystack := string(alias) + strings.Repeat("x", gap) + "ЯР"
+ want := len(alias) + gap
+ if cpu.X86.HasAVX512F && cpu.X86.HasAVX512BW && cpu.X86.HasAVX512VBMI {
+ if got := pairPairSkipBytes(haystack, 0, filter); got != 0 {
+ t.Fatalf("VBMI pair alias %#x did not reach replay: skip=%d", aliasBit, got)
+ }
+ }
+ match, ok := plan.find(haystack)
+ if !ok || match != (Match{Pattern: 0, Start: want}) {
+ t.Fatalf("VBMI pair alias %#x hid exact match: Find=%+v,%t want start %d", aliasBit, match, ok, want)
}
}
- match, ok := plan.find(haystack)
- if !ok || match != (Match{Pattern: 0, Start: want}) {
- t.Fatalf("VBMI pair alias hid exact match: Find=%+v,%t want start %d", match, ok, want)
+}
+
+func TestUnicodePairConfirm(t *testing.T) {
+ if unicodePairConfirmPartSize != 10 || unicodePairConfirmMaxLengthAt != 200 ||
+ unicodePairConfirmAnchorAt != 201 || unicodePairConfirmNAt != 202 ||
+ unicodePairConfirmPackedSize != 204 {
+ t.Fatalf("unexpected packed confirmation layout: part=%d length=%d anchor=%d n=%d size=%d",
+ unicodePairConfirmPartSize, unicodePairConfirmMaxLengthAt, unicodePairConfirmAnchorAt,
+ unicodePairConfirmNAt, unicodePairConfirmPackedSize)
+ }
+
+ const needle = "приключения лилий"
+ plan := newSearchPlan([]string{needle})
+ confirm := plan.unicodePairConfirm()
+ if plan.unicodePairN == 0 || plan.unicodePairs[0].pairPair.valid == 0 || !confirm.valid() {
+ t.Fatalf("no bounded Unicode confirmation: anchors=%+v confirm=%+v", plan.unicodePairs, confirm)
+ }
+ if got, want := confirm.maxLength(), len(needle); got != want {
+ t.Fatalf("confirmation length = %d, want %d", got, want)
+ }
+ if got, want := confirm.anchorAt(), plan.unicodePairs[0].at; got != want {
+ t.Fatalf("confirmation anchor = %d, want %d", got, want)
+ }
+ if got := confirm.skippedN(); got != unicodePairConfirmSkippedParts {
+ t.Fatalf("pair-pair-confirmed parts = %d, want %d", got, unicodePairConfirmSkippedParts)
+ }
+ asciiPlan := newSearchPlan([]string{"ascii literal"})
+ if !asciiPlan.asciiOnly || asciiPlan.singlePayload != "ascii literal" || asciiPlan.unicodePairConfirm().valid() {
+ t.Fatalf("all-ASCII payload was not kept separate: asciiOnly=%t payload=%q confirm=%+v",
+ asciiPlan.asciiOnly, asciiPlan.singlePayload, asciiPlan.unicodePairConfirm())
+ }
+
+ for _, rendering := range []string{needle, strings.ToUpper(needle)} {
+ if _, ok := confirm.matchWidthAt(rendering, 0); !ok || !plan.matchesSingleAt(rendering, 0) {
+ t.Fatalf("confirmation rejected simple-fold rendering %q", rendering)
+ }
+ }
+ nearMiss := needle[:len(needle)-len("й")] + "я"
+ if _, ok := confirm.matchWidthAt(nearMiss, 0); ok || plan.matchesSingleAt(nearMiss, 0) {
+ t.Fatalf("confirmation accepted near miss %q", nearMiss)
+ }
+ for _, nearMiss := range []string{
+ "я" + needle[len("п"):],
+ needle[:len("п")] + "я" + needle[len("п")+len("р"):],
+ } {
+ if _, ok := confirm.matchWidthAt(nearMiss, 0); ok {
+ t.Fatalf("confirmation accepted pair-pair near miss %q", nearMiss)
+ }
+ }
+
+ check := func(t *testing.T, haystack string) {
+ t.Helper()
+ got, gotOK := plan.find(haystack)
+ want := reference(haystack, needle)
+ if want < 0 {
+ if gotOK || got != (Match{}) {
+ t.Fatalf("Find = %+v,%t want no match", got, gotOK)
+ }
+ return
+ }
+ if !gotOK || got != (Match{Pattern: 0, Start: want}) {
+ t.Fatalf("Find = %+v,%t want start %d", got, gotOK, want)
+ }
+ }
+ for _, offset := range []int{0, 1, 63, 64, 127, 128, 4095} {
+ for _, rendering := range []string{needle, strings.ToUpper(needle)} {
+ check(t, strings.Repeat("x", offset)+rendering+strings.Repeat("x", 4096))
+ }
+ check(t, strings.Repeat("x", offset)+nearMiss+strings.Repeat("x", 4096))
+ }
+ // Both candidates occupy one 64-start vector block. The first is rejected
+ // by the final token, so the kernel must continue to the later exact one.
+ check(t, strings.Repeat("x", 64)+nearMiss+"x"+strings.ToUpper(needle)+strings.Repeat("x", 4096))
+ // The final valid start is outside a complete vector block and stays on the
+ // bounded scalar tail.
+ check(t, strings.Repeat("x", 4096)+strings.ToUpper(needle))
+
+ // The byte-pair anchor need not be the first token. Its coordinate is
+ // translated back to the literal start inside the vector kernel.
+ prefixedNeedle := "x" + needle
+ prefixedPlan := newSearchPlan([]string{prefixedNeedle})
+ prefixedConfirm := prefixedPlan.unicodePairConfirm()
+ if !prefixedConfirm.valid() || prefixedConfirm.anchorAt() != prefixedPlan.unicodePairs[0].at ||
+ prefixedPlan.unicodePairs[0].pairPair.valid == 0 || prefixedPlan.unicodePairs[0].at == 0 {
+ t.Fatalf("no displaced confirmation anchor: anchors=%+v confirm=%+v", prefixedPlan.unicodePairs, prefixedConfirm)
+ }
+ prefixedHaystack := strings.Repeat("z", 64) + strings.ToUpper(prefixedNeedle) + strings.Repeat("z", 4096)
+ if got, ok := prefixedPlan.find(prefixedHaystack); !ok || got != (Match{Pattern: 0, Start: 64}) {
+ t.Fatalf("displaced-anchor Find = %+v,%t want start 64", got, ok)
+ }
+
+ if got := makeUnicodePairConfirm("Σя", 0); !got.valid() || got[8] != 3 {
+ t.Fatalf("three-way width-stable confirmation = %+v, want three forms", got)
+ }
+ threePlan := newSearchPlan([]string{"яраΣ"})
+ threeConfirm := threePlan.unicodePairConfirm()
+ hasThreeWay := func(confirm unicodePairConfirm) bool {
+ for part := range int(confirm[unicodePairConfirmNAt]) {
+ if confirm[part*unicodePairConfirmPartSize+8] == 3 {
+ return true
+ }
+ }
+ for skipped := range confirm.skippedN() {
+ at := unicodePairConfirmSkippedAt + skipped*unicodePairConfirmPartSize
+ if confirm[at+8] == 3 {
+ return true
+ }
+ }
+ return false
+ }
+ if !threeConfirm.valid() || threeConfirm.skippedN() != unicodePairConfirmSkippedParts || !hasThreeWay(threeConfirm) {
+ t.Fatalf("three-way plan confirmation = %+v", threeConfirm)
+ }
+ threeHaystack := strings.Repeat("x", 64) + "ЯРАς" + strings.Repeat("x", 4096)
+ if got, ok := threePlan.find(threeHaystack); !ok || got != (Match{Pattern: 0, Start: 64}) {
+ t.Fatalf("three-way Find = %+v,%t want start 64", got, ok)
+ }
+ unsupported := []struct {
+ pattern, rendering string
+ }{
+ {"kя", "KЯ"}, // Kelvin sign changes the first token width.
+ {"ϴя", "θЯ"}, // Greek theta has four width-stable simple-fold spellings.
+ {"\x80я", "\x80Я"}, // Malformed bytes retain opaque matching.
+ {strings.Repeat("я", unicodePairConfirmMaxParts+1), strings.Repeat("Я", unicodePairConfirmMaxParts+1)},
+ }
+ for _, tc := range unsupported {
+ if got := makeUnicodePairConfirm(tc.pattern, 0); got.valid() {
+ t.Fatalf("unsupported confirmation shape %q compiled as %+v", tc.pattern, got)
+ }
+ fallback := newSearchPlan([]string{tc.pattern})
+ if confirm := fallback.unicodePairConfirm(); confirm.valid() {
+ t.Fatalf("unsupported plan %q retained raw confirmation %+v", tc.pattern, confirm)
+ }
+ haystack := strings.Repeat("x", 64) + tc.rendering + strings.Repeat("x", 4096)
+ want := reference(haystack, tc.pattern)
+ if got, ok := fallback.find(haystack); !ok || got != (Match{Pattern: 0, Start: want}) {
+ t.Fatalf("fallback Find(%q) = %+v,%t want start %d", tc.pattern, got, ok, want)
+ }
+ }
+
+ // The vector transition is an optimization only. Disabling its final ISA
+ // feature must keep the decoded pair-pair executor's answer unchanged.
+ if cpu.X86.HasAVX512F && cpu.X86.HasAVX512BW && cpu.X86.HasAVX512VBMI {
+ hasVBMI := cpu.X86.HasAVX512VBMI
+ cpu.X86.HasAVX512VBMI = false
+ defer func() { cpu.X86.HasAVX512VBMI = hasVBMI }()
+ check(t, strings.Repeat("x", 64)+strings.ToUpper(needle)+strings.Repeat("x", 4096))
+ check(t, strings.Repeat("x", 64)+nearMiss+strings.Repeat("x", 4096))
}
}
diff --git a/scripts/measure_benchmarkbar.py b/scripts/measure_benchmarkbar.py
new file mode 100644
index 0000000..e16e7fc
--- /dev/null
+++ b/scripts/measure_benchmarkbar.py
@@ -0,0 +1,63 @@
+#!/usr/bin/env python3
+"""Adapt a complete one-sample BenchmarkBar run to claim metrics.
+
+The acceptance marker is deliberately derived by the repository verifier rather
+than by an aggregate ratio calculation. A baseline may legitimately lose rows,
+so the adapter first validates the complete field and dispatch contract without
+the win predicate, then emits zero when the strict verifier finds any losing
+row. A claim requiring this marker to rise from zero to one therefore requires
+every row to satisfy x_vs_best < 1.
+"""
+
+import argparse
+import json
+from pathlib import Path
+
+import verify_benchmarkbar as verify
+
+
+def summarize(path: Path) -> dict[str, float]:
+ # This pass still requires the exact row inventory, one sample per row,
+ # entrant counts, and every dispatch invariant on both comparison arms.
+ verify.verify(path, expected_samples=1, require_wins=False)
+ try:
+ # Keep the strict repository verifier as the authority for the binding
+ # every-row win rule. The expected baseline is allowed to produce zero;
+ # the declared binary marker makes a losing candidate fail the claim.
+ verify.verify(path, expected_samples=1)
+ except verify.VerificationError:
+ all_rows_winning = 0.0
+ else:
+ all_rows_winning = 1.0
+
+ rows = verify.parse(path)
+ samples = [sample for row in rows.values() for sample in row]
+ ratios = [sample["x_vs_best"] for sample in samples]
+ return {
+ "benchmarkbar_all_rows_winning": all_rows_winning,
+ "benchmarkbar_worst_x_vs_best": max(ratios),
+ "benchmarkbar_rows_below_one": float(sum(ratio < 1 for ratio in ratios)),
+ "benchmarkbar_min_entrants": min(sample["entrants"] for sample in samples),
+ "benchmarkbar_candidate_vector_bits": min(
+ sample["candidate_vector_bits"] for sample in samples
+ ),
+ "benchmarkbar_vectorscan_vector_bits": min(
+ sample["vectorscan_vector_bits"] for sample in samples
+ ),
+ "benchmarkbar_vectorscan_vbmi": min(
+ sample["vectorscan_vbmi"] for sample in samples
+ ),
+ }
+
+
+def main() -> None:
+ parser = argparse.ArgumentParser(description=__doc__)
+ parser.add_argument("report", type=Path)
+ args = parser.parse_args()
+
+ for metric, value in summarize(args.report).items():
+ print(json.dumps({"metric": metric, "value": value}))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/scripts/measure_benchmarkbar_test.py b/scripts/measure_benchmarkbar_test.py
new file mode 100644
index 0000000..c3b4f10
--- /dev/null
+++ b/scripts/measure_benchmarkbar_test.py
@@ -0,0 +1,42 @@
+#!/usr/bin/env python3
+
+from pathlib import Path
+import tempfile
+import unittest
+
+from measure_benchmarkbar import summarize
+from verify_benchmarkbar_test import row
+import verify_benchmarkbar as verify
+
+
+def transcript(ratio=0.5, **override):
+ return "".join(
+ row(name, ratio=ratio, **override)
+ for name in sorted(verify.REQUIRED_ROWS)
+ )
+
+
+class MeasureBenchmarkBarTest(unittest.TestCase):
+ def summarize_text(self, text):
+ with tempfile.TemporaryDirectory() as directory:
+ path = Path(directory) / "bar.txt"
+ path.write_text(text)
+ return summarize(path)
+
+ def test_strict_verifier_marks_a_complete_winning_board(self):
+ metrics = self.summarize_text(transcript())
+ self.assertEqual(metrics["benchmarkbar_all_rows_winning"], 1)
+ self.assertEqual(metrics["benchmarkbar_rows_below_one"], len(verify.REQUIRED_ROWS))
+
+ def test_strict_verifier_marks_any_losing_row(self):
+ metrics = self.summarize_text(transcript(ratio=2.0))
+ self.assertEqual(metrics["benchmarkbar_all_rows_winning"], 0)
+ self.assertEqual(metrics["benchmarkbar_rows_below_one"], 0)
+
+ def test_non_win_contract_failure_remains_an_error(self):
+ with self.assertRaisesRegex(verify.VerificationError, "vectorscan_vector_bits"):
+ self.summarize_text(transcript(vectorscan_bits=256))
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/scripts/measure_raw_byte.py b/scripts/measure_raw_byte.py
new file mode 100644
index 0000000..041f1ac
--- /dev/null
+++ b/scripts/measure_raw_byte.py
@@ -0,0 +1,94 @@
+#!/usr/bin/env python3
+"""Adapt the shipped raw-byte Go benchmarks to the measurement contract."""
+
+import argparse
+import json
+import re
+from pathlib import Path
+
+
+SPECS = {
+ "steady_state": (
+ "BenchmarkRawByteDensity",
+ (
+ "two_4KiB_one_in_32",
+ "two_4KiB_zero_admission",
+ "five_one_in_64",
+ "five_one_in_4",
+ ),
+ ),
+ "construction": (
+ "BenchmarkRawByteConstruction",
+ (
+ "two_patterns_512B",
+ "two_patterns_4KiB",
+ "five_patterns_512B",
+ "five_patterns_513B_zero_admission",
+ "five_patterns_5MiB_raw_install_and_first_use",
+ "two_shared_100_units_1KiB_zero_admission",
+ "two_mixed_folded_ascii_root_1KiB",
+ ),
+ ),
+ "fresh_find": (
+ "BenchmarkRawByteFreshFind",
+ (
+ "five_long_false_one_in_256",
+ "five_long_near_miss_one_in_256",
+ "five_long_sparse_one_in_8192",
+ "five_long_clustered",
+ "five_long_early_match_one_in_256",
+ "five_long_late_match_one_in_256",
+ ),
+ ),
+ "reused_find": (
+ "BenchmarkRawByteFindAfterFreshFind",
+ (
+ "five_long_reused_near_miss_one_in_256",
+ "five_long_reused_late_match_one_in_256",
+ ),
+ ),
+}
+
+BENCHMARK = re.compile(
+ r"^(?PBenchmark\S+?)(?:-\d+)?\s+\d+\s+"
+ r"(?P[0-9.]+)\s+ns/op(?:\s+[0-9.]+\s+MB/s)?\s+"
+ r"(?P[0-9.]+)\s+B/op\s+(?P[0-9.]+)\s+allocs/op\s*$"
+)
+
+
+def parse(path: Path, mode: str) -> dict[str, dict[str, float]]:
+ prefix, labels = SPECS[mode]
+ expected = {f"{prefix}/{label}" for label in labels}
+ result: dict[str, dict[str, float]] = {}
+ for line in path.read_text().splitlines():
+ match = BENCHMARK.match(line)
+ if match is None or match["name"] not in expected:
+ continue
+ name = match["name"]
+ if name in result:
+ raise ValueError(f"duplicate benchmark result for {name}")
+ result[name] = {
+ "ns_per_op": float(match["ns"]),
+ "B_per_op": float(match["bytes"]),
+ "allocs_per_op": float(match["allocs"]),
+ }
+ missing = expected.difference(result)
+ if missing:
+ raise ValueError(f"missing benchmark results: {', '.join(sorted(missing))}")
+ return result
+
+
+def main() -> None:
+ parser = argparse.ArgumentParser()
+ parser.add_argument("mode", choices=sorted(SPECS))
+ parser.add_argument("report", type=Path)
+ args = parser.parse_args()
+
+ for name, values in parse(args.report, args.mode).items():
+ label = name.removeprefix(SPECS[args.mode][0] + "/").replace("/", "_")
+ for suffix, value in values.items():
+ print(json.dumps({"metric": f"{label}_{suffix}", "value": value}))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/scripts/measure_raw_byte_test.py b/scripts/measure_raw_byte_test.py
new file mode 100644
index 0000000..00edc70
--- /dev/null
+++ b/scripts/measure_raw_byte_test.py
@@ -0,0 +1,102 @@
+#!/usr/bin/env python3
+
+import tempfile
+import unittest
+from pathlib import Path
+
+from measure_raw_byte import parse
+
+
+class MeasureRawByteTest(unittest.TestCase):
+ def report(self, lines: list[str]) -> Path:
+ directory = tempfile.TemporaryDirectory()
+ self.addCleanup(directory.cleanup)
+ path = Path(directory.name) / "bench.txt"
+ path.write_text("\n".join(lines) + "\n")
+ return path
+
+ def test_steady_state(self) -> None:
+ labels = (
+ "two_4KiB_one_in_32",
+ "two_4KiB_zero_admission",
+ "five_one_in_64",
+ "five_one_in_4",
+ )
+ report = self.report(
+ [
+ f"BenchmarkRawByteDensity/{label}-1 100 12.5 ns/op 327.68 MB/s 0 B/op 0 allocs/op"
+ for label in labels
+ ]
+ )
+ got = parse(report, "steady_state")
+ self.assertEqual(len(got), len(labels))
+ self.assertEqual(got["BenchmarkRawByteDensity/five_one_in_4"]["ns_per_op"], 12.5)
+
+ def test_construction(self) -> None:
+ labels = (
+ "two_patterns_512B",
+ "two_patterns_4KiB",
+ "five_patterns_512B",
+ "five_patterns_513B_zero_admission",
+ "five_patterns_5MiB_raw_install_and_first_use",
+ "two_shared_100_units_1KiB_zero_admission",
+ "two_mixed_folded_ascii_root_1KiB",
+ )
+ report = self.report(
+ [
+ f"BenchmarkRawByteConstruction/{label}-1 100 12.5 ns/op 0 B/op 0 allocs/op"
+ for label in labels
+ ]
+ )
+ got = parse(report, "construction")
+ self.assertEqual(len(got), len(labels))
+ self.assertEqual(got["BenchmarkRawByteConstruction/two_mixed_folded_ascii_root_1KiB"]["ns_per_op"], 12.5)
+
+ def test_fresh_find(self) -> None:
+ labels = (
+ "five_long_false_one_in_256",
+ "five_long_near_miss_one_in_256",
+ "five_long_sparse_one_in_8192",
+ "five_long_clustered",
+ "five_long_early_match_one_in_256",
+ "five_long_late_match_one_in_256",
+ )
+ report = self.report(
+ [
+ f"BenchmarkRawByteFreshFind/{label}-1 100 12.5 ns/op 0 B/op 0 allocs/op"
+ for label in labels
+ ]
+ )
+ got = parse(report, "fresh_find")
+ self.assertEqual(len(got), len(labels))
+ self.assertEqual(
+ got["BenchmarkRawByteFreshFind/five_long_near_miss_one_in_256"]["ns_per_op"],
+ 12.5,
+ )
+
+ def test_reused_find(self) -> None:
+ labels = (
+ "five_long_reused_near_miss_one_in_256",
+ "five_long_reused_late_match_one_in_256",
+ )
+ report = self.report(
+ [
+ f"BenchmarkRawByteFindAfterFreshFind/{label}-1 100 12.5 ns/op 0 B/op 0 allocs/op"
+ for label in labels
+ ]
+ )
+ got = parse(report, "reused_find")
+ self.assertEqual(len(got), len(labels))
+ self.assertEqual(
+ got["BenchmarkRawByteFindAfterFreshFind/five_long_reused_late_match_one_in_256"]["ns_per_op"],
+ 12.5,
+ )
+
+ def test_rejects_missing_result(self) -> None:
+ report = self.report(["BenchmarkRawByteConstruction/two_patterns_512B-1 100 12 ns/op 0 B/op 0 allocs/op"])
+ with self.assertRaisesRegex(ValueError, "missing benchmark results"):
+ parse(report, "construction")
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/scripts/reproduce.sh b/scripts/reproduce.sh
index a06b315..4d61967 100755
--- a/scripts/reproduce.sh
+++ b/scripts/reproduce.sh
@@ -2,6 +2,8 @@
# Reproduce casei's benchmark: build the entire competitor field from source,
# then run the scoreboard. CI builds and correctness-checks the same pinned
# field on every push; the performance board requires the host contract below.
+# Set CASEI_NATIVE_DIR to retain the native field outside the default temporary
+# directory. CASEI_PREPARE_ONLY=1 stops after that unprivileged field build.
#
# Requirements: Go 1.24+ on x86-64 Linux with AVX2 and AVX-512F/BW/VBMI
# (Intel Ice Lake or newer).
@@ -42,24 +44,59 @@ if [ "${#missing[@]}" -ne 0 ]; then
exit 1
fi
-echo "==> Installing build dependencies (cargo, cmake, boost, pkg-config)"
-sudo apt-get update -qq
-sudo apt-get install -y -qq cargo cmake curl libboost-dev pkg-config python3-pip build-essential
+if command -v sudo >/dev/null 2>&1 && sudo -n true >/dev/null 2>&1; then
+ echo "==> Installing build dependencies (cargo, cmake, boost, pkg-config)"
+ sudo apt-get update -qq
+ sudo apt-get install -y -qq cargo cmake curl libboost-dev pkg-config python3-pip build-essential
+else
+ # The arena builders are unprivileged. Permit a prepared container to use its
+ # existing toolchain when sudo is unavailable or cannot run noninteractively.
+ missing_tools=()
+ for tool in cargo cc c++ cmake curl dpkg-deb make pkg-config python3 sha256sum tar; do
+ if ! command -v "$tool" >/dev/null 2>&1; then
+ missing_tools+=("$tool")
+ fi
+ done
+ if [ "${#missing_tools[@]}" -ne 0 ] || [ ! -r /usr/include/boost/version.hpp ]; then
+ echo "Build dependencies are missing and sudo is unavailable." >&2
+ if [ "${#missing_tools[@]}" -ne 0 ]; then
+ echo "Missing tools: ${missing_tools[*]}." >&2
+ fi
+ if [ ! -r /usr/include/boost/version.hpp ]; then
+ echo "Missing Boost headers (install libboost-dev)." >&2
+ fi
+ exit 1
+ fi
+ echo "==> Using preinstalled build dependencies (sudo is unavailable)"
+fi
root="$(cd "$(dirname "$0")/.." && pwd)"
-native="$(mktemp -d)"
+native="${CASEI_NATIVE_DIR:-$(mktemp -d)}"
+mkdir -p "$native"
export GOPATH="${GOPATH:-$native/go}"
export GOCACHE="${GOCACHE:-$native/go-build}"
+# rure's Cargo registry and target tree are part of this field build. Keep them
+# in the caller-owned native directory instead of a shared host CARGO_HOME.
+export CARGO_HOME="$native/cargo-home"
cd "$root/arena"
for dep in pcre2 vectorscan rure rustac stringzilla; do
echo "==> Building competitor from source: $dep"
"./$dep/prepare.sh" "$native"
done
+if [ "${CASEI_PREPARE_ONLY:-}" = 1 ]; then
+ echo "==> Native field prepared in $native"
+ exit 0
+fi
+
export PKG_CONFIG_PATH="$native/root/usr/lib/x86_64-linux-gnu/pkgconfig"
export PKG_CONFIG_SYSROOT_DIR="$native/root"
export LD_LIBRARY_PATH="$native/root/usr/lib/x86_64-linux-gnu"
+echo "==> Checking arena adapters"
+go vet ./...
+go test ./...
+
echo "==> Running the scoreboard (BenchmarkBar: x_vs_best per row, with per-entrant dispatched width)"
bar_output="$native/benchmarkbar.txt"
go test -run '^$' -bench '^BenchmarkBar$' -benchtime 30x -count 3 | tee "$bar_output"
diff --git a/scripts/verify_benchmarkbar.py b/scripts/verify_benchmarkbar.py
index 9968cde..5986f71 100755
--- a/scripts/verify_benchmarkbar.py
+++ b/scripts/verify_benchmarkbar.py
@@ -11,6 +11,9 @@
PREFIX = "BenchmarkBar/"
+# EXPECTED_ROWS is the board published before focused Unicode rows were added.
+# REQUIRED_ROWS is the current acceptance board: every member is subject to the
+# same win, entrant-count, and dispatch requirements.
EXPECTED_ROWS = frozenset(
{
"multi/multi_N2_miss_log_1mb",
@@ -48,8 +51,23 @@
"single/torture_miss_64kb",
}
)
+TARGETED_ROWS = frozenset(
+ {
+ "multi/multi_N1_unicode_pair_miss_1_5mb",
+ }
+)
+RAW_TRANSITION_ROWS = frozenset(
+ {
+ "multi/multi_N5_raw_transition_miss_5mb",
+ "multi/multi_N5_raw_transition_late_hit_5mb",
+ }
+)
+REQUIRED_ROWS = EXPECTED_ROWS | TARGETED_ROWS | RAW_TRANSITION_ROWS
UTF8_ROWS = frozenset(
{
+ "multi/multi_N1_unicode_pair_miss_1_5mb",
+ "multi/multi_N5_raw_transition_miss_5mb",
+ "multi/multi_N5_raw_transition_late_hit_5mb",
"multi/multi_N512_miss_hazard_64kb",
"multi/multi_N64_miss_ru_64kb",
"multi/multi_N8_hazard_hit_1mb",
@@ -137,14 +155,14 @@ def parse(path):
return rows
-def verify(path, expected_samples=3):
+def verify(path, expected_samples=3, require_wins=True):
rows = parse(path)
found = set(rows)
- if found != EXPECTED_ROWS:
+ if found != REQUIRED_ROWS:
raise VerificationError(
f"{path}: row inventory differs; "
- f"missing={sorted(EXPECTED_ROWS - found)}, "
- f"unexpected={sorted(found - EXPECTED_ROWS)}"
+ f"missing={sorted(REQUIRED_ROWS - found)}, "
+ f"unexpected={sorted(found - REQUIRED_ROWS)}"
)
wrong_counts = {
@@ -159,7 +177,11 @@ def verify(path, expected_samples=3):
for sample_number, sample in enumerate(samples, 1):
label = f"{name} sample {sample_number}"
ratio = sample["x_vs_best"]
- if not 0 < ratio < 1:
+ if ratio <= 0:
+ raise VerificationError(
+ f"{path}: {label} has non-positive x_vs_best={ratio:g}"
+ )
+ if require_wins and ratio >= 1:
raise VerificationError(
f"{path}: {label} loses with x_vs_best={ratio:g}"
)
@@ -249,8 +271,8 @@ def verify(path, expected_samples=3):
worst_row = max(medians, key=medians.get)
worst_sample = max(
sample["x_vs_best"]
- for samples in rows.values()
- for sample in samples
+ for name in REQUIRED_ROWS
+ for sample in rows[name]
)
median_speedup = median(1 / ratio for ratio in medians.values())
entrant_counts = [
@@ -259,7 +281,8 @@ def verify(path, expected_samples=3):
for sample in samples
]
return (
- f"PASS: 33/33 rows; worst median {worst_row}={medians[worst_row]:.4f}; "
+ f"PASS: {len(REQUIRED_ROWS)}/{len(REQUIRED_ROWS)} rows; "
+ f"worst median {worst_row}={medians[worst_row]:.4f}; "
f"worst sample={worst_sample:.4f}; median speedup={median_speedup:.2f}x; "
f"entrants={min(entrant_counts)}-{max(entrant_counts)}; "
"casei=512-bit; Vectorscan=512-bit VBMI; field dispatch verified"
diff --git a/scripts/verify_benchmarkbar_test.py b/scripts/verify_benchmarkbar_test.py
index b5262cc..606ef1b 100755
--- a/scripts/verify_benchmarkbar_test.py
+++ b/scripts/verify_benchmarkbar_test.py
@@ -56,7 +56,7 @@ def row(
def transcript(**override):
lines = []
- for index, name in enumerate(sorted(verify.EXPECTED_ROWS)):
+ for index, name in enumerate(sorted(verify.REQUIRED_ROWS)):
values = override if index == 0 else {}
for _ in range(3):
lines.append(row(name, **values))
@@ -72,7 +72,7 @@ def verify_text(self, text):
def test_accepts_complete_winning_full_width_board(self):
summary = self.verify_text(transcript())
- self.assertIn("PASS: 33/33 rows", summary)
+ self.assertIn("PASS: 36/36 rows", summary)
self.assertIn("casei=512-bit", summary)
def test_rejects_losing_sample(self):
@@ -98,16 +98,36 @@ def test_rejects_unmeasured_row(self):
def test_rejects_missing_row(self):
text = "".join(
row(name)
- for name in sorted(verify.EXPECTED_ROWS)[:-1]
+ for name in sorted(verify.REQUIRED_ROWS)[:-1]
for _ in range(3)
)
with self.assertRaisesRegex(verify.VerificationError, "row inventory differs"):
self.verify_text(text)
+ def test_rejects_missing_unicode_confirmation_row(self):
+ row_name = "multi/multi_N1_unicode_pair_miss_1_5mb"
+ text = "".join(
+ line
+ for line in transcript().splitlines(keepends=True)
+ if f"/{row_name.split('/', 1)[1]}-" not in line
+ )
+ with self.assertRaisesRegex(verify.VerificationError, "row inventory differs"):
+ self.verify_text(text)
+
+ def test_rejects_missing_raw_transition_row(self):
+ row_name = "multi/multi_N5_raw_transition_late_hit_5mb"
+ text = "".join(
+ line
+ for line in transcript().splitlines(keepends=True)
+ if f"/{row_name.split('/', 1)[1]}-" not in line
+ )
+ with self.assertRaisesRegex(verify.VerificationError, "row inventory differs"):
+ self.verify_text(text)
+
def test_rejects_wrong_sample_count(self):
with self.assertRaisesRegex(verify.VerificationError, "wrong sample counts"):
self.verify_text(
- transcript() + row(sorted(verify.EXPECTED_ROWS)[0])
+ transcript() + row(sorted(verify.REQUIRED_ROWS)[0])
)
diff --git a/scripts/verify_throughput.py b/scripts/verify_throughput.py
index af4ccb5..aed0835 100755
--- a/scripts/verify_throughput.py
+++ b/scripts/verify_throughput.py
@@ -13,7 +13,7 @@
sys.dont_write_bytecode = True
SCRIPT_DIR = Path(__file__).resolve().parent
sys.path.insert(0, str(SCRIPT_DIR))
-from verify_benchmarkbar import EXPECTED_ROWS, is_utf8_row # noqa: E402
+from verify_benchmarkbar import REQUIRED_ROWS, is_utf8_row # noqa: E402
VISIBLE = (
@@ -99,11 +99,11 @@ def parse(path):
def verify(path, expected_samples=3, require_wins=True):
rows = parse(path)
found = set(rows)
- if found != EXPECTED_ROWS:
+ if found != REQUIRED_ROWS:
raise VerificationError(
f"{path}: row inventory differs; "
- f"missing={sorted(EXPECTED_ROWS - found)}, "
- f"unexpected={sorted(found - EXPECTED_ROWS)}"
+ f"missing={sorted(REQUIRED_ROWS - found)}, "
+ f"unexpected={sorted(found - REQUIRED_ROWS)}"
)
medians = {}
@@ -134,7 +134,7 @@ def verify(path, expected_samples=3, require_wins=True):
def render(medians, title, selected=None):
- rows = set(medians) if selected is None else set(selected)
+ rows = REQUIRED_ROWS if selected is None else set(selected)
unknown = rows - set(medians)
if unknown:
raise VerificationError(f"unknown selected rows: {sorted(unknown)}")
@@ -164,11 +164,12 @@ def render(medians, title, selected=None):
def summary(medians):
- ratios = {row: result[2] for row, result in medians.items()}
+ ratios = {row: medians[row][2] for row in REQUIRED_ROWS}
narrowest = min(ratios, key=ratios.get)
widest = max(ratios, key=ratios.get)
return (
- f"PASS: 33/33 throughput rows; narrowest {narrowest}={ratios[narrowest]:.2f}x; "
+ f"PASS: {len(REQUIRED_ROWS)}/{len(REQUIRED_ROWS)} throughput rows; "
+ f"narrowest {narrowest}={ratios[narrowest]:.2f}x; "
f"widest {widest}={ratios[widest]:.2f}x; three samples per lane"
)
diff --git a/scripts/verify_throughput_test.py b/scripts/verify_throughput_test.py
index 0c44d4a..87fa203 100755
--- a/scripts/verify_throughput_test.py
+++ b/scripts/verify_throughput_test.py
@@ -25,7 +25,7 @@ def benchmark(row, engine, speed=1000, serial=False):
def transcript(omit=None, samples=3, losing=None, serial=False):
lines = []
- for row in sorted(verify.EXPECTED_ROWS):
+ for row in sorted(verify.REQUIRED_ROWS):
for engine in sorted(engines(row)):
if (row, engine) == omit:
continue
@@ -44,17 +44,22 @@ def verify_text(self, text):
def test_accepts_complete_winning_board_and_renders_markdown(self):
medians = self.verify_text(transcript())
- self.assertEqual(33, len(medians))
+ self.assertEqual(len(verify.REQUIRED_ROWS), len(medians))
+ self.assertIn(
+ f"PASS: {len(verify.REQUIRED_ROWS)}/{len(verify.REQUIRED_ROWS)} throughput rows",
+ verify.summary(medians),
+ )
table = verify.render(medians, "Test CPU")
self.assertIn("#### Test CPU", table)
+ self.assertIn("multi_N1_unicode_pair_miss_1_5mb", table)
self.assertIn("**2.0**", table)
self.assertIn("**2.00×**", table)
def test_accepts_gce_serial_tab_encoding(self):
- self.assertEqual(33, len(self.verify_text(transcript(serial=True))))
+ self.assertEqual(len(verify.REQUIRED_ROWS), len(self.verify_text(transcript(serial=True))))
def test_rejects_missing_row(self):
- first = sorted(verify.EXPECTED_ROWS)[0]
+ first = sorted(verify.REQUIRED_ROWS)[0]
text = "".join(
line
for line in transcript().splitlines(keepends=True)
@@ -63,6 +68,16 @@ def test_rejects_missing_row(self):
with self.assertRaisesRegex(verify.VerificationError, "row inventory differs"):
self.verify_text(text)
+ def test_rejects_missing_unicode_confirmation_row(self):
+ row = "multi/multi_N1_unicode_pair_miss_1_5mb"
+ text = "".join(
+ line
+ for line in transcript().splitlines(keepends=True)
+ if f"/{row.split('/', 1)[1]}/" not in line
+ )
+ with self.assertRaisesRegex(verify.VerificationError, "row inventory differs"):
+ self.verify_text(text)
+
def test_rejects_missing_required_engine(self):
row = "single/log_miss_1mb"
with self.assertRaisesRegex(verify.VerificationError, "missing engines"):
diff --git a/unicode_confirm.go b/unicode_confirm.go
new file mode 100644
index 0000000..bd0f01f
--- /dev/null
+++ b/unicode_confirm.go
@@ -0,0 +1,266 @@
+package casei
+
+import "unicode/utf8"
+
+const (
+ unicodePairConfirmMaxParts = 20
+ unicodePairConfirmPartSize = 10
+ unicodePairConfirmSkippedParts = 2
+
+ unicodePairConfirmSkippedAt = (unicodePairConfirmMaxParts - unicodePairConfirmSkippedParts) * unicodePairConfirmPartSize
+ unicodePairConfirmMaxLengthAt = unicodePairConfirmMaxParts * unicodePairConfirmPartSize
+ unicodePairConfirmAnchorAt = unicodePairConfirmMaxLengthAt + 1
+ unicodePairConfirmNAt = unicodePairConfirmMaxLengthAt + 2
+ unicodePairConfirmValidAt = unicodePairConfirmMaxLengthAt + 3
+ unicodePairConfirmPackedSize = unicodePairConfirmMaxLengthAt + 4
+ unicodePairConfirmMinAt = unicodePairConfirmPackedSize
+ unicodePairConfirmVariableSize = unicodePairConfirmPackedSize + 1
+ unicodePairConfirmVariableFlag = 2
+)
+
+// unicodePairConfirm is stable assembly input for one bounded literal. The
+// 204-byte fixed layout stores up to three one- or two-byte values plus their
+// source offset in each ten-byte part. A 205-byte variable layout instead
+// stores up to three three-byte-padded raw forms per part and adds the minimum
+// possible match width; its cursor advances by the form that actually matched.
+// Both layouts finish with maximum width, anchor offset, part count, and flags.
+type unicodePairConfirm string
+
+func (confirm unicodePairConfirm) valid() bool {
+ if (len(confirm) != unicodePairConfirmPackedSize && len(confirm) != unicodePairConfirmVariableSize) ||
+ confirm[unicodePairConfirmValidAt]&1 == 0 ||
+ confirm[unicodePairConfirmMaxLengthAt] == 0 || confirm[unicodePairConfirmAnchorAt] >= confirm[unicodePairConfirmMaxLengthAt] {
+ return false
+ }
+ if confirm.variable() {
+ parts := int(confirm[unicodePairConfirmNAt])
+ return len(confirm) == unicodePairConfirmVariableSize && parts > 0 && parts <= unicodePairConfirmMaxParts &&
+ confirm[unicodePairConfirmMinAt] > 0 && confirm[unicodePairConfirmMinAt] <= confirm[unicodePairConfirmMaxLengthAt]
+ }
+ if len(confirm) != unicodePairConfirmPackedSize {
+ return false
+ }
+ skipped := confirm.skippedN()
+ parts := int(confirm[unicodePairConfirmNAt])
+ return (skipped == 0 || skipped == unicodePairConfirmSkippedParts) && parts+skipped <= unicodePairConfirmMaxParts &&
+ (parts != 0 || skipped != 0)
+}
+
+func (confirm unicodePairConfirm) variable() bool {
+ return len(confirm) > unicodePairConfirmValidAt && confirm[unicodePairConfirmValidAt]&unicodePairConfirmVariableFlag != 0
+}
+
+func (confirm unicodePairConfirm) maxLength() int {
+ return int(confirm[unicodePairConfirmMaxLengthAt])
+}
+
+func (confirm unicodePairConfirm) minLength() int {
+ if confirm.variable() && len(confirm) == unicodePairConfirmVariableSize {
+ return int(confirm[unicodePairConfirmMinAt])
+ }
+ return confirm.maxLength()
+}
+
+func (confirm unicodePairConfirm) anchorAt() int {
+ return int(confirm[unicodePairConfirmAnchorAt])
+}
+
+func (confirm unicodePairConfirm) skippedN() int {
+ return int(confirm[unicodePairConfirmValidAt] >> 1)
+}
+
+// makeUnicodePairConfirm moves pair-pair's two raw tokens to trailing slots
+// when confirmAt is supplied. The vector transition proves those slots from
+// its low-six-bit tables plus UTF-8 byte classes; matchWidthAt still checks
+// every slot and remains a complete raw-token oracle for scalar replay.
+func makeUnicodePairConfirm(pattern string, anchorAt int, confirmAt ...int) unicodePairConfirm {
+ if anchorAt < 0 || anchorAt > 255 || len(pattern) > 255 || len(confirmAt) > 1 {
+ return ""
+ }
+
+ skippedAt := [unicodePairConfirmSkippedParts]int{}
+ skipN := 0
+ if len(confirmAt) != 0 {
+ if confirmAt[0] < 0 || confirmAt[0] > 255 || confirmAt[0] == anchorAt {
+ return ""
+ }
+ skippedAt[0], skippedAt[1] = anchorAt, confirmAt[0]
+ skipN = unicodePairConfirmSkippedParts
+ }
+
+ forms, _ := patternRawForms(pattern)
+ packed := make([]byte, unicodePairConfirmPackedSize)
+ at, parts, skipped := 0, 0, 0
+ for _, unit := range forms {
+ r, size := utf8.DecodeRuneInString(pattern[at:])
+ if r == utf8.RuneError && size == 1 || len(unit) == 0 || len(unit) > 3 {
+ return ""
+ }
+ width := len(unit[0])
+ if width < 1 || width > 2 || width != size {
+ return ""
+ }
+
+ var packedPart [unicodePairConfirmPartSize]byte
+ packedPart[6], packedPart[7], packedPart[8] = uint8(at), uint8(width), uint8(len(unit))
+ for i, form := range unit {
+ if len(form) != width {
+ return ""
+ }
+ value := uint16(form[0])
+ if width == 2 {
+ value |= uint16(form[1]) << 8
+ }
+ valueAt := i * 2
+ packedPart[valueAt], packedPart[valueAt+1] = uint8(value), uint8(value>>8)
+ }
+
+ isSkipped := false
+ for i := range skipN {
+ if at == skippedAt[i] {
+ isSkipped = true
+ break
+ }
+ }
+ if isSkipped {
+ if skipped == skipN {
+ return ""
+ }
+ partAt := unicodePairConfirmSkippedAt + skipped*unicodePairConfirmPartSize
+ copy(packed[partAt:], packedPart[:])
+ skipped++
+ } else {
+ if parts == unicodePairConfirmMaxParts-skipN {
+ return ""
+ }
+ partAt := parts * unicodePairConfirmPartSize
+ copy(packed[partAt:], packedPart[:])
+ parts++
+ }
+ at += size
+ }
+ if at != len(pattern) || parts+skipped == 0 || skipped != skipN {
+ return ""
+ }
+ packed[unicodePairConfirmMaxLengthAt] = uint8(at)
+ packed[unicodePairConfirmAnchorAt] = uint8(anchorAt)
+ packed[unicodePairConfirmNAt] = uint8(parts)
+ packed[unicodePairConfirmValidAt] = 1 | uint8(skipped<<1)
+ return unicodePairConfirm(string(packed))
+}
+
+// makeUnicodePairVariableConfirm records the same literal as a short sequence
+// of raw forms. Unlike the fixed-offset representation above, its confirmation
+// cursor advances by the width of the form that actually matched. The pair-pair
+// screen still fixes the start: makeUnicodePairAnchor only records anchors
+// before the first width-changing fold orbit.
+func makeUnicodePairVariableConfirm(pattern string, anchorAt int) unicodePairConfirm {
+ if anchorAt < 0 || anchorAt > 255 || !utf8.ValidString(pattern) {
+ return ""
+ }
+ forms, _ := patternRawForms(pattern)
+ if len(forms) == 0 || len(forms) > unicodePairConfirmMaxParts {
+ return ""
+ }
+ packed := make([]byte, unicodePairConfirmVariableSize)
+ minLength, maxLength := 0, 0
+ for part, unit := range forms {
+ if len(unit) == 0 || len(unit) > 3 {
+ return ""
+ }
+ minWidth, maxWidth := utf8.UTFMax+1, 0
+ partAt := part * unicodePairConfirmPartSize
+ for formIndex, form := range unit {
+ if len(form) == 0 || len(form) > 3 {
+ return ""
+ }
+ copy(packed[partAt+formIndex*3:], form)
+ if len(form) < minWidth {
+ minWidth = len(form)
+ }
+ if len(form) > maxWidth {
+ maxWidth = len(form)
+ }
+ }
+ packed[partAt+9] = byte(len(unit))
+ minLength += minWidth
+ maxLength += maxWidth
+ }
+ if minLength == maxLength || maxLength > 255 {
+ return ""
+ }
+ packed[unicodePairConfirmMaxLengthAt] = byte(maxLength)
+ packed[unicodePairConfirmAnchorAt] = byte(anchorAt)
+ packed[unicodePairConfirmNAt] = byte(len(forms))
+ packed[unicodePairConfirmValidAt] = 1 | unicodePairConfirmVariableFlag
+ packed[unicodePairConfirmMinAt] = byte(minLength)
+ return unicodePairConfirm(string(packed))
+}
+
+func (confirm unicodePairConfirm) matchesPartAt(haystack string, at, partAt int) bool {
+ value := uint16(haystack[at+int(confirm[partAt+6])])
+ if confirm[partAt+7] == 2 {
+ value |= uint16(haystack[at+int(confirm[partAt+6])+1]) << 8
+ }
+ if value == uint16(confirm[partAt])|uint16(confirm[partAt+1])<<8 {
+ return true
+ }
+ if confirm[partAt+8] >= 2 && value == uint16(confirm[partAt+2])|uint16(confirm[partAt+3])<<8 {
+ return true
+ }
+ return confirm[partAt+8] >= 3 && value == uint16(confirm[partAt+4])|uint16(confirm[partAt+5])<<8
+}
+
+func (confirm unicodePairConfirm) matchWidthAt(haystack string, at int) (int, bool) {
+ if !confirm.valid() || at < 0 || len(haystack)-at < confirm.minLength() {
+ return 0, false
+ }
+ start := at
+ if confirm.variable() {
+ for part := 0; part < int(confirm[unicodePairConfirmNAt]); part++ {
+ partAt := part * unicodePairConfirmPartSize
+ matched := false
+ for form := 0; form < int(confirm[partAt+9]); form++ {
+ formAt := partAt + form*3
+ width := 1
+ switch first := confirm[formAt]; {
+ case first >= 0xe0:
+ width = 3
+ case first >= 0xc0:
+ width = 2
+ }
+ if len(haystack)-at < width {
+ continue
+ }
+ equal := true
+ for i := 0; i < width; i++ {
+ equal = equal && haystack[at+i] == confirm[formAt+i]
+ }
+ if equal {
+ at += width
+ matched = true
+ break
+ }
+ }
+ if !matched {
+ return 0, false
+ }
+ }
+ return at - start, true
+ }
+ if len(haystack)-at < confirm.maxLength() {
+ return 0, false
+ }
+ for part := range int(confirm[unicodePairConfirmNAt]) {
+ if !confirm.matchesPartAt(haystack, at, part*unicodePairConfirmPartSize) {
+ return 0, false
+ }
+ }
+ for skipped := range confirm.skippedN() {
+ partAt := unicodePairConfirmSkippedAt + skipped*unicodePairConfirmPartSize
+ if !confirm.matchesPartAt(haystack, at, partAt) {
+ return 0, false
+ }
+ }
+ return confirm.maxLength(), true
+}
diff --git a/unicode_variable_confirm_test.go b/unicode_variable_confirm_test.go
new file mode 100644
index 0000000..89b6d5a
--- /dev/null
+++ b/unicode_variable_confirm_test.go
@@ -0,0 +1,122 @@
+package casei
+
+import (
+ "strings"
+ "testing"
+)
+
+func variableConfirmRenderings(forms [][]string, at int, prefix string, out *[]string) {
+ if at == len(forms) {
+ *out = append(*out, prefix)
+ return
+ }
+ for _, form := range forms[at] {
+ variableConfirmRenderings(forms, at+1, prefix+form, out)
+ }
+}
+
+func TestUnicodePairVariableConfirm(t *testing.T) {
+ const needle = "Шерлок Холмс"
+ plan := newSearchPlan([]string{needle})
+ confirm := plan.unicodePairConfirm()
+ if plan.unicodePairs[0].pairPair.valid == 0 || !confirm.valid() || !confirm.variable() {
+ t.Fatalf("variable confirmation was not compiled: pair=%+v confirm=%x", plan.unicodePairs[0].pairPair, string(confirm))
+ }
+ if confirm.minLength() >= confirm.maxLength() {
+ t.Fatalf("confirmation bounds = [%d,%d], want a width-changing range", confirm.minLength(), confirm.maxLength())
+ }
+
+ forms, _ := patternRawForms(needle)
+ var renderings []string
+ variableConfirmRenderings(forms, 0, "", &renderings)
+ for _, rendering := range renderings {
+ width, ok := confirm.matchWidthAt(rendering, 0)
+ if !ok || width != len(rendering) || !plan.matchesSingleAt(rendering, 0) {
+ t.Fatalf("confirmation rejected rendering %x", rendering)
+ }
+ }
+ nearMiss := strings.TrimSuffix(needle, "с") + "я"
+ if _, ok := confirm.matchWidthAt(nearMiss, 0); ok {
+ t.Fatalf("confirmation accepted near miss %x", nearMiss)
+ }
+
+ matcher := NewMatcher([]string{needle})
+ anchor := &plan.unicodePairs[0]
+ for _, offset := range []int{0, 1, 63, 64, 127, 128, 4095} {
+ for _, rendering := range renderings {
+ haystack := strings.Repeat("x", offset) + rendering + strings.Repeat("x", 256)
+ got, width, ok := plan.findWithWidth(haystack)
+ wantWidth := 0
+ if plan.chooseUnicodePairAnchor(haystack) != nil && unicodePairConfirmVectorEnabled() {
+ wantWidth = len(rendering)
+ }
+ if !ok || got != (Match{Pattern: 0, Start: offset}) || width != wantWidth {
+ t.Fatalf("offset %d rendering %x: findWithWidth=%+v,%d,%t", offset, rendering, got, width, ok)
+ }
+ // findUnicodePairConfirm is the VBMI route selected by
+ // findUnicodePairAnchor. Do not call it directly on hosts that
+ // cannot execute its full-block kernel.
+ if unicodePairConfirmVectorEnabled() {
+ got, width, ok = plan.findUnicodePairConfirm(haystack, anchor)
+ if !ok || got != (Match{Pattern: 0, Start: offset}) || width != len(rendering) {
+ t.Fatalf("offset %d rendering %x: direct confirmation=%+v,%d,%t", offset, rendering, got, width, ok)
+ }
+ }
+ calls := 0
+ if completed := matcher.Each(haystack, func(match Match, width int) bool {
+ calls++
+ if match != (Match{Pattern: 0, Start: offset}) || width != len(rendering) {
+ t.Errorf("offset %d rendering %x: Each yielded %+v,%d", offset, rendering, match, width)
+ }
+ return true
+ }); !completed || calls != 1 {
+ t.Fatalf("offset %d rendering %x: Each completed=%t calls=%d", offset, rendering, completed, calls)
+ }
+ }
+ }
+ // A conservative pair-pair survivor is not a match. Confirmation must
+ // continue within the same vector block and return the later exact one.
+ late := nearMiss + "x" + renderings[len(renderings)-1]
+ wantStart := len(nearMiss) + 1
+ if got, width, ok := plan.findUnicodePairConfirm(late, anchor); !ok || got != (Match{Pattern: 0, Start: wantStart}) || width != len(renderings[len(renderings)-1]) {
+ t.Fatalf("false candidate then match: direct confirmation=%+v,%d,%t", got, width, ok)
+ }
+
+ // A shorter rendering can begin after the final max-width-safe vector
+ // start; the scalar tail must still inspect it.
+ shortest := renderings[0]
+ for _, rendering := range renderings[1:] {
+ if len(rendering) < len(shortest) {
+ shortest = rendering
+ }
+ }
+ haystack := strings.Repeat("x", 4096) + shortest
+ wantWidth := 0
+ if unicodePairConfirmVectorEnabled() {
+ wantWidth = len(shortest)
+ }
+ if got, width, ok := plan.findWithWidth(haystack); !ok || got != (Match{Pattern: 0, Start: 4096}) || width != wantWidth {
+ t.Fatalf("short tail findWithWidth=%+v,%d,%t", got, width, ok)
+ }
+ if got, width, ok := plan.findUnicodePairConfirm(shortest, anchor); !ok || got != (Match{Pattern: 0}) || width != len(shortest) {
+ t.Fatalf("bare short rendering: direct confirmation=%+v,%d,%t", got, width, ok)
+ }
+ if got, ok := matcher.Find(shortest); !ok || got != (Match{Pattern: 0}) {
+ t.Fatalf("bare short rendering: Find=%+v,%t", got, ok)
+ }
+}
+
+func TestUnicodePairVariableConfirmRejectsMalformedPattern(t *testing.T) {
+ const needle = "Шер\x80лок Холмс"
+ plan := newSearchPlan([]string{needle})
+ if plan.unicodePairs[0].pairPair.valid == 0 {
+ t.Fatal("fixture did not compile a pair-pair screen")
+ }
+ if confirm := plan.unicodePairConfirm(); confirm.valid() {
+ t.Fatalf("malformed pattern compiled a variable confirmation: %x", string(confirm))
+ }
+ haystack := strings.Repeat("x", 97) + strings.ToUpper("Шер") + "\x80" + strings.ToUpper("лок Холмс")
+ if got, ok := plan.find(haystack); !ok || got != (Match{Pattern: 0, Start: 97}) {
+ t.Fatalf("fallback Find=%+v,%t", got, ok)
+ }
+}