|
REAL
Regular Expression Algorithmic Library — constexpr C++20 regex
|
Storage policy backing real::static_regex: compile-time, stateless.
More...
#include <storage.hpp>
Classes | |
| struct | measured |
| Everything build yields that is NOT a range, measured in ONE evaluation. More... | |
Public Types | |
| using | name_owner = borrowed_names |
| Name-resolution owner: empty, the tables having static storage duration. | |
| using | slot_storage = static_vec< std::size_t, slot_count > |
| Capture-slot storage, sized exactly to the program's slot count (no heap). | |
| using | il_guard_fields = static_il_guard_fields |
| IL guard fields for this storage — lifted to static_il_guard_fields, which carries the rationale; kept as a name here because wants_inner_literal reads next to it. | |
| using | no_il_guard_fields = static_no_il_guard_fields |
| No IL fields: the route is not compiled for this pattern (see wants_inner_literal). | |
| using | state_type = static_pike_scratch< scratch_code_tier(code_size), slot_count, wants_inner_literal > |
| This pattern's VM scratch — nothing but static_pike_scratch at this pattern's dimensions, so two patterns of the same shape name the same type. | |
Public Member Functions | |
| constexpr const program_view & | view () const |
| Returns a non-owning view of the compile-time program, by reference. | |
| constexpr std::string_view | pattern () const |
| Returns the pattern text. | |
| constexpr flags | compiled_flags () const |
Returns the flag set in force: constructor flags, plus a leading global-flags group's additions, minus its -removal – see real::basic_regex::compile_flags. | |
Static Public Attributes | |
| static constexpr bool | is_compile_time {true} |
| Selects the default constructor. | |
| static constexpr bool | viable {requires { typename std::bool_constant<(build(), true)>; }} |
| Whether build is a constant expression for this pattern. | |
| static constexpr measured | survey |
| The one measuring evaluation. Never emitted: nothing takes its address, and every member below reads it at compile time only. | |
| static constexpr flags | effective_flags |
The flag set in force: F plus what a leading (?imsxaU) group added, minus what a (?flags-flags) removal cleared. Mirrors dynamic_storage::compile, so the two storages report the same thing for the same pattern. Parsed ONCE – the two-call form this replaces ran the parser twice for one answer, the same defect as the seven-call measure above in miniature. | |
| static constexpr pattern_hints | hints {survey.hints} |
| Search hints. | |
| static constexpr std::size_t | code_size {survey.code_size} |
| Instruction count. | |
| static constexpr std::size_t | class_count {survey.class_count} |
| Distinct class count. | |
| static constexpr std::size_t | name_count {survey.name_count} |
| Named-group count. | |
| static constexpr std::size_t | cp_class_count {survey.cp_class_count} |
| Code-point class count (klass_cp). | |
| static constexpr std::size_t | cp_range_count {survey.cp_range_count} |
| Total code-point ranges. | |
| static constexpr std::uint16_t | slot_count {survey.slot_count} |
2*(groups+1). | |
| static constexpr std::array< instr, code_size > | code {take<instr, code_size>(build().code)} |
| The program. | |
| static constexpr std::array< char_class, class_count > | classes |
| Interned classes. | |
| static constexpr std::array< named_group, name_count > | names |
| Named groups. | |
| static constexpr std::array< cp_class, cp_class_count > | cp_classes |
| Code-point classes. | |
| static constexpr std::array< code_range, cp_range_count > | cp_ranges |
| Flat range buffer. | |
| static constexpr std::array< std::uint8_t,(class_count==0 ? 1 :class_count) *256 > | class_tables |
Flat byte-class membership tables, built at compile time: class_tables[i*256 + b]. Reuses the classes member rather than calling build again — an extra build() per table pushes the whole instantiation past clang's constexpr step budget. | |
| static constexpr std::array< std::uint8_t,(cp_class_count==0 ? 1 :cp_class_count) *256 > | cp_ascii_tables |
Flat ASCII tables for the code-point classes: cp_ascii_tables[i*256 + b]. | |
| static constexpr std::array< std::uint64_t,(cp_class_count==0 ? 1 :cp_class_count) *30 > | cp_page_tables |
| Two-byte-range membership bitmaps (U+0080..U+07FF) for the code-point classes, 30 words each. | |
| static constexpr bool | wants_inner_literal |
Whether the inner-literal route is worth compiling into this pattern's run(). | |
Static Private Member Functions | |
| static constexpr dynamic_program | build () |
| Returns the freshly built program (used for both measuring and filling). | |
| template<typename T , std::size_t N, typename Vec > | |
| static constexpr std::array< T, N > | take (const Vec &source) |
Copies the first N elements of v into a fixed array. | |
Static Private Attributes | |
| static constexpr program_view | view_ |
| The view itself, materialised once at compile time. See view. | |
Storage policy backing real::static_regex: compile-time, stateless.
Every array is a static constexpr member sized exactly by a measuring pass over the same compilation, so a static_regex object is stateless (sizeof 1) and matching allocates nothing.
| Pat | The pattern, as a real::fixed_string non-type parameter. |
| F | Compilation flags. |
| using real::detail::static_storage< Pat, F >::state_type = static_pike_scratch<scratch_code_tier (code_size), slot_count, wants_inner_literal> |
This pattern's VM scratch — nothing but static_pike_scratch at this pattern's dimensions, so two patterns of the same shape name the same type.
Nothing here depends on the pattern's value any more. The three byte-class table addresses that used to live in this type now travel in real::detail::program_view, which is where runtime program data belongs; they remain static constexpr arrays, so a constant-folding compiler still reaches them without a load. What that buys, and what it cost to establish, is in 10.1 The inlining budget.
|
inlinestaticconstexprprivate |
Returns the freshly built program (used for both measuring and filling).
Runs only at compile time (a static_regex instantiation), so it is invisible to the runtime coverage report; it is exercised by the constexpr static_asserts in tests/test_static.cpp and tests/test_constexpr.cpp.
|
inlineconstexpr |
Returns the flag set in force: constructor flags, plus a leading global-flags group's additions, minus its -removal – see real::basic_regex::compile_flags.
|
inlineconstexpr |
Returns the pattern text.
|
inlinestaticconstexprprivate |
Copies the first N elements of v into a fixed array.
| T | Element type. |
| N | Exact size (measured from build). |
| Vec | Source container type. |
| [in] | source | The source vector. |
|
inlineconstexpr |
Returns a non-owning view of the compile-time program, by reference.
Every field is a compile-time constant here — the spans point at static constexpr arrays and immut is null — so the whole view is one too, and handing back a reference costs nothing where returning by value copied the whole thing per call. 232 of its 432 bytes are pattern_hints, and view() is called once per search(): line-level profiling of a single [a-z]+ search put that one aggregate initialiser at 93 of the ~325 instructions the call spends, against 17 for the class scan itself. The measurement was taken at 408 bytes, before the three table bases moved into the view; the dynamic storage still returns by value and so still pays a construction of that size once per search — the one place this reasoning has not been applied.
|
staticconstexpr |
Flat byte-class membership tables, built at compile time: class_tables[i*256 + b]. Reuses the classes member rather than calling build again — an extra build() per table pushes the whole instantiation past clang's constexpr step budget.
tabulate<N>(f) here was written, measured and REFUSED. The loop below cannot be one pass: constant evaluation rejects indeterminate subobjects, so the array is zeroed and then overwritten — 2N element operations where a pack expansion needs N. The argument is sound and buys nothing. Compile time is indistinguishable between the two forms, with the direction flipping between paired runs; and bisecting -fconstexpr-steps to the failure point gives the SAME budget for both, so there is no headroom in it either — the compiler's own cost for a large pack cancels the halved element count. The loop stays: same speed, same budget, no helper to maintain.
|
staticconstexpr |
Interned classes.
|
staticconstexpr |
Flat ASCII tables for the code-point classes: cp_ascii_tables[i*256 + b].
|
staticconstexpr |
Code-point classes.
|
staticconstexpr |
Two-byte-range membership bitmaps (U+0080..U+07FF) for the code-point classes, 30 words each.
|
staticconstexpr |
Flat range buffer.
|
staticconstexpr |
The flag set in force: F plus what a leading (?imsxaU) group added, minus what a (?flags-flags) removal cleared. Mirrors dynamic_storage::compile, so the two storages report the same thing for the same pattern. Parsed ONCE – the two-call form this replaces ran the parser twice for one answer, the same defect as the seven-call measure above in miniature.
|
staticconstexpr |
Named groups.
|
staticconstexpr |
The one measuring evaluation. Never emitted: nothing takes its address, and every member below reads it at compile time only.
|
staticconstexpr |
Whether build is a constant expression for this pattern.
A requires expression is a SFINAE context: forming the bool_constant template argument needs a constant expression, and build throwing makes it one substitution failure rather than a hard error. That is what lets viable be asked instead of crashed into.
|
staticconstexprprivate |
The view itself, materialised once at compile time. See view.
|
staticconstexpr |
Whether the inner-literal route is worth compiling into this pattern's run().
A required literal at offset >= 1 is necessary but not sufficient. fixed_shape means the core already has an arithmetic-width scan for the whole pattern, and then memmem has nothing to add; without it the core falls to the general VM, which is what the literal sweep rescues. The shape of the result is the same on every pattern measured: a non-fixed-shape pattern gains by orders of magnitude on a subject with NO match – where the literal scan rejects the whole corpus and the general VM would walk it – and is neutral once matches are dense enough that the scan finds one immediately. A fixed_shape pattern gains nothing and pays for the attempt.
Excluding it HERE rather than at run time is what keeps the cost off the patterns that do not use the route: compiling the block into run() at all is measurable on a pattern with no inner literal, which never enters it.