C++20  ·  header-only  ·  constexpr

Regular expressions that can't blow up.

A linear-time, ReDoS-safe regex engine for C++20 — drop-in for std::regex, RE2, Python re, and the Rust regex crate, plus a regexp-shaped Go binding. Every divergence documented per surface. Match untrusted input without ever falling off a backtracking cliff.

$ brew install RECHE23/sci/real-regex
runtime vs. input length (a+)+$  · worst case
real — O(n) backtracking — O(2ⁿ)
what you get

Linear time, including bounded lookarounds.

linear-time

It can't blow up

Every match is O(n·m) — no backtracking, so no catastrophic path: ReDoS-safe by construction.

drop-in

Drop in anywhere

Drop-ins for std::regex, RE2, Python re, the Rust regex crate, a regexp-shaped Go API, and a frozen C ABI — one linear engine underneath, each surface's divergences documented.

constexpr

Compile-time, zero-dependency

Hold a real::static_regex as a constexpr object; the pattern lives in the type, a few dozen headers, nothing to link. real::regex allocates — it is not a constexpr variable.

the difference

One input. Two outcomes.

The pattern below is harmless-looking and perfectly valid. On a long run of as ending in the wrong byte, a backtracking engine explores an exponential number of paths. real walks it once.

(a+)+$  matched against  "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!"

Same regex, same string. The only difference is whether the engine can be forced off a cliff by input it didn't choose — the difference between a feature and a denial-of-service.

std::regex / PCREbacktracking
≈ 2³⁰ steps
reallinear
≈ 30 steps

How it compares →

quickstart

Familiar on the surface. Safe underneath.

If you know std::regex, re or the regex crate, you already know this API. The Go binding is regexp-shaped (MatchString, Find, Split).

drop-in + a constexpr match
// The pattern lives in the type — parsed & compiled at compile time.
#include <real/real.hpp>
constexpr real::static_regex<R"((\w+)@(\w+))"> email;
static_assert(email.search("info@example.com")[2] == "example");

// every snippet verified against the current public API

install

Four ecosystems, one engine.

C++ · header-only

$ brew install RECHE23/sci/real-regex

Python · PyPI

$ pip install real-regex

Rust · crates.io

$ cargo add real-regex

Go · module

$ go get github.com/RECHE23/real-regex/bindings/go
drop-in, honestly

Compatible — and it tells you where it isn't.

The std::regex and RE2 layers document every intentional divergence, down to per-standard-library behavior. No silent surprises: the contract says exactly what the engine does, and CI enforces it.

Read the compatibility contract →
how it works

Thompson's construction, done at compile time.

A pattern becomes an NFA program, matched by a Pike VM with a lazy-DFA fast path and a SIMD prefilter — the classic linear-time pipeline, in modern constexpr C++.

patternNFA programPike VM · lazy-DFAO(n) match