# Performance and risk analysis

Regex Tools provides one deliberately scoped analysis implementation:
ECMAScript 2025 patterns parsed into the application-owned normalized AST and
executed by the browser's native `RegExp` runtime. The analyser does not apply
ECMAScript findings to PCRE2, Python, Java or any other flavour.

Analysis is advisory. The UI uses “potential risk”, “possibly” and “observed
under selected limits”. It never turns an empty finding list into “safe”,
“guaranteed linear” or a vulnerability proof.

## Static analysis

The application-owned `regex-tools-ecmascript-heuristics` analyser walks at
most 20,000 normalized nodes and returns at most 100 findings. Each finding
contains:

- flavour and UTF-16 source range;
- stable rule identifier;
- static or dynamically observed provenance;
- explanation and example risk;
- low, medium or high confidence;
- limitations;
- a suggested investigation.

The current rules review nested and nullable repetition, ambiguous repeated
alternatives, overlapping alternative prefixes, wildcards and backreferences
inside repetition, repeated lookaround bodies, unanchored amplification,
capture count, nesting depth and replacement-output expansion. These are
structural heuristics. They do not model the native engine's complete compiled
program, optimizations or calling context.

## Dynamic growth probes

The user defines a bounded subject family:

```text
prefix + repeatedFragment.repeat(repetitions) + suffix
```

The byte and UTF-16 size are calculated before the generated string is
allocated. The repeated fragment is limited to 4,096 UTF-16 units, each affix
to 16,384 units, the run to 24 samples, and repetitions to 10,000,000. The UI
defaults to a 1 MiB generated-subject cap even though the central interactive
hard cap remains 16 MiB.

Every probe executes in a dedicated worker. The supervisor terminates and
discards that worker on per-sample timeout, cancellation or crash. Runs also
stop at:

- the selected repetition bound;
- the selected generated-byte bound;
- the selected step bound;
- the 30-second aggregate wall bound;
- a selected normalized-growth threshold.

The growth threshold compares the time ratio with the input-size ratio for two
adjacent samples. Sub-millisecond measurements below a noise floor do not
trigger it. A threshold observation characterizes only those generated inputs
and that browser. Timeout, crash, compile rejection and non-match remain
distinct states.

The UI renders time and outcome in separate plots and keeps the exact sample
table alongside them.

## Benchmark method

A benchmark begins with a fresh analysis worker. Worker creation and its
identity response form the separate cold-start measurement. The first native
engine use is retained as a cold sample. Configured warm-up samples are
discarded; configured measured samples produce the warm statistics.

Each sample measures these phases separately:

- `RegExp` construction;
- first `exec`;
- bounded all-match iteration;
- native string replacement when its conservative output bound is acceptable;
- all-match throughput and match count.

All-match iteration retains only counts, not values or captures. It follows the
same explicit scan-all, global, sticky and Unicode zero-length advancement
semantics as the workbench. The application-added indices flag remains in the
effective flags so the measured operation reflects interactive execution.

Replacement timing is skipped when match collection reaches its bound or a
conservative estimate can exceed 8 Mi UTF-16 units. This prevents a benchmark
from using an unbounded native replacement merely to obtain a timing.
It is also skipped for explicit scan-all with sticky `y`: native
`String.replace` processes only one sticky match, while the workbench's
explicit scan-all operation can process a contiguous sequence. Reporting that
different operation as the same benchmark would be misleading.

Warm metrics include sample count, minimum, conventional median, nearest-rank
p95 and maximum. Milliseconds are shown to two decimal places, with values
below 0.01 ms labelled accordingly; this does not imply nanosecond accuracy.
The result retains subject size, match count, effective flags, browser runtime
identity and engine identity.

Defaults:

| Setting                 | Default | Hard bound |
| ----------------------- | ------: | ---------: |
| Warm-up samples         |       3 |        100 |
| Measured samples        |      15 |      1,000 |
| Per-sample timeout      |     2 s |       10 s |
| Aggregate wall time     |    30 s |       30 s |
| Matches per sample      |  10,000 |     10,000 |
| Replacement output gate |   8 MiU |      8 MiU |

One subject is not a complete performance characterization. Results from
different algorithms, pattern ports, browser engines, WebAssembly runtimes or
machines are not directly comparable. Trace-enabled execution is never used
for benchmark results.

## Privacy and persistence

Patterns, subjects, generated inputs, findings and benchmark samples remain in
the browser. No analysis request is uploaded. Analysis settings and results are
currently ephemeral and are not added to project JSON or IndexedDB; this also
keeps large benchmark subjects out of persisted projects by default.
