REAL
Regular Expression Algorithmic Library — constexpr C++20 regex
Loading...
Searching...
No Matches
storage.hpp
Go to the documentation of this file.
1
14#ifndef REAL_STORAGE_HPP
15#define REAL_STORAGE_HPP
16
17// Internal — do not include directly.
18// Users: #include <real/real.hpp> (or the documented opt-ins <real/dfa.hpp>, <real/std/regex.hpp>).
19
20#include "real/version.hpp"
21
22#include <array>
23#include <cassert>
24#include <cstddef>
25#include <cstdint>
26#include <stdexcept>
27#include <string>
28#include <string_view>
29
30#include "real/frontend/ast.hpp"
32#include "real/engine/pike.hpp"
33#include "real/core/program.hpp"
34
35namespace real {
36
45 template <std::size_t N>
47 {
48 char data[N] = {};
49
58 // NOLINTNEXTLINE(google-explicit-constructor,hicpp-explicit-conversions)
59 constexpr fixed_string(const char (&literal)[N])
60 {
61 for (std::size_t i = 0; i < N; ++i) {
62 data[i] = literal[i];
63 }
64 }
65
69 [[nodiscard]] constexpr std::string_view view() const
70 {
71 return {data, N - 1};
72 }
73 };
74
75 namespace detail {
76
89 template <typename T, std::size_t Cap>
91 {
92 public:
93
99 constexpr void push_back(const T& value)
100 {
101 if (size_ == Cap) {
102 throw std::length_error("static_vec overflow");
103 }
104 data_[size_] = value;
105 ++size_;
106 }
107
111 constexpr void clear()
112 {
113 size_ = 0;
114 }
115
122 constexpr void assign(std::size_t count,
123 const T& value)
124 {
125 if (count > Cap) {
126 throw std::length_error("static_vec overflow");
127 }
128 for (std::size_t i = 0; i < count; ++i) {
129 data_[i] = value;
130 }
131 size_ = count;
132 }
133
137 [[nodiscard]] constexpr std::size_t size() const
138 {
139 return size_;
140 }
141
145 [[nodiscard]] constexpr bool empty() const
146 {
147 return size_ == 0;
148 }
149
155 [[nodiscard]] constexpr T& operator[](std::size_t i)
156 {
157 return data_[i];
158 }
159
165 [[nodiscard]] constexpr const T& operator[](std::size_t i) const
166 {
167 return data_[i];
168 }
169
173 [[nodiscard]] constexpr T& back()
174 {
175 assert(size_ > 0 && "back() on an empty static_vec"); // debug precondition; no-op under NDEBUG
176 return data_[size_ - 1];
177 }
178
182 constexpr void pop_back()
183 {
184 assert(size_ > 0 && "pop_back() on an empty static_vec");
185 --size_;
186 }
187
188 private:
189
190 std::array<T, Cap> data_ {};
191 std::size_t size_ {};
192 };
193
218 template <typename T, std::size_t InlineCapacity>
220 {
221 static_assert(InlineCapacity > 0, "InlineCapacity must be positive");
222 // small_vec runs no element destructors — inline elements in particular are never
223 // destroyed (cleanup() only frees the heap block). That is correct only for
224 // trivially-destructible types, which is its sole use (POD-like VM state).
225 static_assert(std::is_trivially_destructible_v<T>,
226 "small_vec is for trivially-destructible types only");
227
228 // size_ and capacity_ are ALWAYS std::size_t — never a type narrowed on
229 // InlineCapacity. small_vec spills to the heap and routinely holds far more than
230 // its inline capacity (up to code_size in the VM: ~3·code_size epsilon entries, the
231 // live thread count of a wide alternation, the 2·(groups+1) capture slots). A
232 // uint8_t / uint16_t counter would truncate: at 256 / 65536, static_cast wraps the
233 // capacity to 0, reserve() then sees new_cap ≤ capacity_ and no-ops, so the buffer
234 // is rewritten in place and back() indexes out of bounds. The inline buffer
235 // dominates sizeof, so the wide counter is free.
236 std::size_t size_ {};
237 std::size_t capacity_ {InlineCapacity};
238 bool is_heap_ {};
239
249 {
250 T elems[InlineCapacity];
251 };
253 {
256
267 constexpr Storage() noexcept
268 {
269 if (std::is_constant_evaluated()) {
270 std::construct_at(&inline_buffer);
271 }
272 }
273
274 constexpr ~Storage() {}
275
276 // Rule of Five, made explicit. The union holds a possibly non-trivial T, so
277 // copy/move would be implicitly deleted anyway; small_vec manages copy, move
278 // and element lifetimes itself (is_heap_-aware) and never copies Storage by
279 // value. Declaring these deleted is also a safety guard: a defaulted copy
280 // would byte-copy the union and inherit the wrong active member (double-free).
281 Storage(const Storage&) = delete;
282 Storage& operator=(const Storage&) = delete;
283 Storage(Storage &&) = delete;
286
287 // Run-time cache of the active storage base (inline buffer or heap block), refreshed on
288 // every state change via \ref refresh_data. The hot accessors (operator[], back, push_back)
289 // index through it, so they avoid the per-access `is_heap_` branch that the profile showed
290 // dominating add_thread. NOT used during constant evaluation: a pointer to a subobject of
291 // *this is not a usable constant across copies, so the constexpr path keeps the is_heap_
292 // branch (guarded by std::is_constant_evaluated). static_regex uses static_vec, not
293 // small_vec, so this never participates in compile-time matching.
294 T* data_ {};
295
297 constexpr void refresh_data() noexcept
298 {
299 if (!std::is_constant_evaluated()) {
301 }
302 }
303
307 [[nodiscard]] constexpr T* inline_data() noexcept
308 {
309 return &storage_.inline_buffer.elems[0];
310 }
311
315 [[nodiscard]] constexpr const T* inline_data() const noexcept
316 {
317 return &storage_.inline_buffer.elems[0];
318 }
319
327 template <bool Move>
328 constexpr void transfer_range(const T * src,
329 std::size_t count,
330 T * dest)
331 {
332 if constexpr (std::is_trivially_copyable_v<T>) {
333 if (!std::is_constant_evaluated()) {
334 std::memcpy(dest, src, count * sizeof(T));
335 return;
336 }
337 }
338 // Element-wise transfer: the constexpr path, and the run-time path for a
339 // non-trivially-copyable T (the VM's POD element types take the memcpy above).
340 for (std::size_t i = 0; i < count; ++i) {
341 if constexpr (Move) {
342 std::construct_at(&dest[i], std::move(src[i]));
343 }
344 else {
345 std::construct_at(&dest[i], src[i]);
346 }
347 }
348 }
349
363 template <bool Move>
364 constexpr void transfer_inline_from(const small_vec& other)
365 {
366 const std::size_t inline_count {other.size_ <= InlineCapacity ? other.size_ : InlineCapacity};
367 transfer_range<Move>(other.inline_data(), inline_count, inline_data());
368 }
369
375 constexpr void cleanup() noexcept
376 {
377 if (std::is_constant_evaluated()) {
378 return;
379 }
380 if (is_heap_) {
381 ::operator delete(storage_.heap_ptr);
382 }
383 }
384
389 {
390 const std::size_t current {capacity_};
391 const std::size_t new_cap {(current > (std::size_t)-1 / 2) ? (std::size_t)-1 : current * 2};
392 reserve(new_cap);
393 }
394
395 public:
396
397 using value_type = T;
398 using size_type_ = std::size_t;
399
403 constexpr small_vec() noexcept
404 {
405 refresh_data(); // points data_ at the inline buffer (run time)
406 }
407
411 constexpr ~small_vec()
412 {
413 if (!std::is_constant_evaluated()) {
414 cleanup();
415 }
416 }
417
424 constexpr void push_back(const T& value)
425 {
426 if (size_ >= capacity_) {
427 if (std::is_constant_evaluated()) {
428 throw std::bad_alloc {};
429 }
431 }
432 if (std::is_constant_evaluated()) {
433 if (is_heap_) {
434 std::construct_at(&storage_.heap_ptr[size_], value);
435 }
436 else {
437 inline_data()[size_] = value;
438 }
439 }
440 else {
441 // size_ < capacity_ holds here (checked above); the analyzer cannot relate the active
442 // block's allocation size to size_. data_ is the active base (branchless).
443 // NOLINTNEXTLINE(clang-analyzer-security.ArrayBound)
444 std::construct_at(&data_[size_], value);
445 }
446 ++size_;
447 }
448
455 constexpr void assign(std::size_t count,
456 const T& value)
457 {
458 clear();
459 if (count > capacity_) {
460 if (std::is_constant_evaluated()) {
461 throw std::bad_alloc {};
462 }
463 reserve(count);
464 }
465 for (std::size_t i = 0; i < count; ++i) {
466 if (std::is_constant_evaluated()) {
467 // Compile time: the inline buffer is value-initialized, so assign through it;
468 // the heap path is never taken in a constant expression.
469 if (is_heap_) {
470 std::construct_at(&storage_.heap_ptr[i], value);
471 }
472 else {
473 inline_data()[i] = value;
474 }
475 }
476 else {
477 // Run time: the inline buffer is uninitialized (see \ref Storage), so this is the
478 // first write — placement-new begins each element's lifetime. data_ is the active
479 // base (inline or heap), branchless like push_back.
480 std::construct_at(&data_[i], value);
481 }
482 }
483 size_ = count;
484 }
485
489 [[nodiscard]] constexpr std::size_t size() const noexcept
490 {
491 return size_;
492 }
493
497 [[nodiscard]] constexpr bool empty() const noexcept
498 {
499 return size_ == 0;
500 }
501
507 [[nodiscard]] constexpr T& operator[](std::size_t i) noexcept
508 {
509 if (std::is_constant_evaluated()) {
510 return is_heap_ ? storage_.heap_ptr[i] : inline_data()[i];
511 }
512 return data_[i];
513 }
514
520 [[nodiscard]] constexpr const T& operator[](std::size_t i) const noexcept
521 {
522 if (std::is_constant_evaluated()) {
523 return is_heap_ ? storage_.heap_ptr[i] : inline_data()[i];
524 }
525 return data_[i];
526 }
527
531 constexpr void clear() noexcept
532 {
533 size_ = 0;
534 }
535
539 [[nodiscard]] constexpr T& back() noexcept
540 {
541 assert(size_ > 0 && "back() on an empty small_vec"); // debug precondition; no-op under NDEBUG
542 if (std::is_constant_evaluated()) {
543 return is_heap_ ? storage_.heap_ptr[size_ - 1] : inline_data()[size_ - 1];
544 }
545 return data_[size_ - 1];
546 }
547
551 [[nodiscard]] constexpr const T& back() const noexcept
552 {
553 assert(size_ > 0 && "back() on an empty small_vec");
554 if (std::is_constant_evaluated()) {
555 return is_heap_ ? storage_.heap_ptr[size_ - 1] : inline_data()[size_ - 1];
556 }
557 return data_[size_ - 1];
558 }
559
566 constexpr void pop_back() noexcept
567 {
568 assert(size_ > 0 && "pop_back() on an empty small_vec");
569 --size_;
570 }
571
577 constexpr void reserve(std::size_t new_capacity)
578 {
579 if (new_capacity <= capacity_) {
580 return;
581 }
582 if (std::is_constant_evaluated()) {
583 throw std::bad_alloc {};
584 }
585 T * new_data {static_cast<T*>(::operator new(new_capacity * sizeof(T)))};
586 const T* old_data {is_heap_ ? storage_.heap_ptr : inline_data()};
587 transfer_range<false>(old_data, size_, new_data);
588 if (is_heap_) {
589 ::operator delete(storage_.heap_ptr);
590 }
591 storage_.heap_ptr = new_data;
592 capacity_ = new_capacity;
593 is_heap_ = true;
594 refresh_data(); // run-time path only (constexpr threw above); data_ now points at the heap block
595 }
596
600 constexpr small_vec(small_vec&& other) noexcept
601 : size_(other.size_),
602 capacity_(other.capacity_),
603 is_heap_(other.is_heap_)
604 {
605 if (is_heap_) {
606 storage_.heap_ptr = other.storage_.heap_ptr;
607 other.storage_.heap_ptr = nullptr;
608 other.is_heap_ = false;
609 other.size_ = 0;
610 other.capacity_ = InlineCapacity;
611 }
612 else {
613 transfer_inline_from<true>(other);
614 }
615 refresh_data();
616 other.refresh_data(); // other is now empty/inline
617 }
618
624 constexpr small_vec& operator=(small_vec&& other) noexcept
625 {
626 if (this != &other) {
627 cleanup();
628 size_ = other.size_;
629 capacity_ = other.capacity_;
630 is_heap_ = other.is_heap_;
631 if (is_heap_) {
632 storage_.heap_ptr = other.storage_.heap_ptr;
633 other.storage_.heap_ptr = nullptr;
634 other.is_heap_ = false;
635 other.size_ = 0;
636 other.capacity_ = InlineCapacity;
637 }
638 else {
639 transfer_inline_from<true>(other);
640 }
641 refresh_data();
642 other.refresh_data(); // other is now empty/inline
643 }
644 return *this;
645 }
646
650 constexpr small_vec(const small_vec& other)
651 : size_(other.size_),
652 capacity_(other.capacity_)
653 {
654 if (other.is_heap_) {
655 if (std::is_constant_evaluated()) {
656 throw std::bad_alloc {}; // dynamic heap path not for constexpr (static_regex uses static_vec)
657 }
658 storage_.heap_ptr = static_cast<T*>(::operator new(other.capacity_ * sizeof(T)));
659 transfer_range<false>(other.storage_.heap_ptr, other.size_, storage_.heap_ptr);
660 is_heap_ = true;
661 capacity_ = other.capacity_;
662 }
663 else {
664 transfer_inline_from<false>(other);
665 }
666 refresh_data();
667 }
668
674 constexpr small_vec& operator=(const small_vec& other)
675 {
676 if (this != &other) {
677 cleanup();
678 size_ = other.size_;
679 if (other.is_heap_) {
680 storage_.heap_ptr = static_cast<T*>(::operator new(other.capacity_ * sizeof(T)));
681 transfer_range<false>(other.storage_.heap_ptr, other.size_, storage_.heap_ptr);
682 is_heap_ = true;
683 capacity_ = other.capacity_;
684 }
685 else {
686 is_heap_ = false;
687 capacity_ = InlineCapacity;
688 transfer_inline_from<false>(other);
689 }
690 refresh_data();
691 }
692 return *this;
693 }
694 };
695
703 {
704 static constexpr bool is_compile_time {};
713 basic_thread_list<small_vec<std::int32_t, 64>,
714 small_vec<std::size_t, 256>,
715 std::vector<std::uint64_t>>,
716 small_vec<eps_entry, 32>>
717 {
720 std::optional<lazy_dfa> fwd_dfa;
721 std::optional<reverse_dfa> rev_dfa;
722 const void* dfa_program {nullptr};
723 std::optional<reverse_dfa> il_prefix_rev;
724 const void* il_prefix_for {nullptr};
725 const void* il_text {nullptr};
726 bool il_abandoned {false};
727 };
728
729 std::string pattern_text;
732
737
745 static constexpr dynamic_storage compile(std::string_view pattern,
746 flags compile_flags)
747 {
748 const ast tree {detail::parse(pattern, compile_flags)};
749 const flags effective {compile_flags | tree.inline_flags};
750 return {.pattern_text = std::string(pattern),
751 .program = detail::compile(tree, effective),
752 .effective_flags = effective};
753 }
754
758 [[nodiscard]] constexpr program_view view() const
759 {
761 pv.immut = &immut_; // the router builds/uses the per-regex cache through the view
762 return pv;
763 }
764
768 [[nodiscard]] constexpr std::string_view pattern() const
769 {
770 return pattern_text;
771 }
772
776 [[nodiscard]] constexpr flags compiled_flags() const
777 {
778 return effective_flags;
779 }
780 };
781
792 template <fixed_string Pat, flags F = flags::none>
794 {
795 static constexpr bool is_compile_time {true};
796
797 private:
798
806 static constexpr dynamic_program build()
807 {
808 const ast tree {detail::parse(Pat.view(), F)};
809 dynamic_program prog {detail::compile(tree, F | tree.inline_flags)};
810 if (!prog.lookarounds.empty()) {
811 // Honest absence: the constexpr sub-VM is a measured follow-up. A clear compile
812 // error (this throw, evaluated at compile time) beats a silent miscompile.
813 throw regex_error("static_regex does not support lookarounds yet (use real::regex)", 0);
814 }
815 return prog;
816 }
817
826 template <typename T, std::size_t N, typename Vec>
827 static constexpr std::array<T, N> take(const Vec& source)
828 {
829 std::array<T, N> result {};
830 for (std::size_t i = 0; i < N; ++i) {
831 result[i] = source[i];
832 }
833 return result;
834 }
835
836 public:
837
838 static constexpr flags effective_flags {F | detail::parse(Pat.view(), F).inline_flags};
839 static constexpr pattern_hints hints {build().hints};
840 static constexpr std::size_t code_size {build().code.size()};
841 static constexpr std::size_t class_count {build().classes.size()};
842 static constexpr std::size_t name_count {build().names.size()};
843 static constexpr std::size_t cp_class_count {build().cp_classes.size()};
844 static constexpr std::size_t cp_range_count {build().cp_ranges.size()};
845 static constexpr std::uint16_t slot_count {build().slot_count};
846
847 static constexpr std::array<instr, code_size> code {take<instr, code_size>(build().code)};
848 static constexpr std::array<char_class, class_count> classes =
849 take<char_class, class_count>(build().classes);
850 static constexpr std::array<named_group, name_count> names =
851 take<named_group, name_count>(build().names);
852 static constexpr std::array<cp_class, cp_class_count> cp_classes =
853 take<cp_class, cp_class_count>(build().cp_classes);
854 static constexpr std::array<code_range, cp_range_count> cp_ranges =
855 take<code_range, cp_range_count>(build().cp_ranges);
856
861 // OPT D1: worst-case live capture blocks — every reference (a DFS stack frame or a thread in either
862 // list) could point to a distinct block; freed blocks recycle through the pool's free list, so the
863 // pool never grows past this. The stack is (3*code_size)+4, each list up to code_size threads.
864 static constexpr std::size_t max_blocks {(5 * code_size) + 8};
865
875 basic_thread_list<static_vec<std::int32_t, code_size>,
876 static_vec<std::size_t, code_size>,
877 static_vec<std::uint64_t, code_size>>,
878 static_vec<eps_entry, (3 * code_size) + 4>>
879 {
883 };
884
888 [[nodiscard]] constexpr program_view view() const
889 {
890 return {.code = code,
891 .classes = classes,
892 .names = names,
893 .lookarounds = {}, // static_regex rejects lookarounds at compile (always empty)
894 .cp_classes = cp_classes,
895 .cp_ranges = cp_ranges,
896 .prefix_code = {}, // IL: static storage builds no prefix program (the route is dynamic-only)
897 .prefix_classes = {},
898 .prefix_cp_classes = {},
899 .prefix_cp_ranges = {},
900 .slot_count = slot_count,
903 .hints = hints};
904 }
905
909 [[nodiscard]] constexpr std::string_view pattern() const
910 {
911 return Pat.view();
912 }
913
917 [[nodiscard]] constexpr flags compiled_flags() const
918 {
919 return effective_flags;
920 }
921 };
922 } // namespace detail
923} // namespace real
924
925#endif // REAL_STORAGE_HPP
Pattern text → AST, via a constexpr recursive-descent parser.
Small-buffer-optimized vector for the dynamic hot paths.
Definition storage.hpp:220
bool is_heap_
True once spilled to the heap.
Definition storage.hpp:238
constexpr void transfer_inline_from(const small_vec &other)
Transfers other's inline elements into this vector's inline buffer.
Definition storage.hpp:364
constexpr void assign(std::size_t count, const T &value)
Resizes to count copies of value.
Definition storage.hpp:455
constexpr bool empty() const noexcept
Returns true if empty.
Definition storage.hpp:497
std::size_t size_type_
Size type (for std-container API compat).
Definition storage.hpp:398
constexpr small_vec() noexcept
Constructs an empty vector in the inline state.
Definition storage.hpp:403
constexpr void refresh_data() noexcept
Refreshes data_ to the active storage base (run time only).
Definition storage.hpp:297
constexpr const T & back() const noexcept
Returns const reference to the last element. Precondition: the vector is non-empty.
Definition storage.hpp:551
constexpr void reserve(std::size_t new_capacity)
Ensures capacity for at least new_capacity elements (heap-backed).
Definition storage.hpp:577
T value_type
Element type.
Definition storage.hpp:397
constexpr T & back() noexcept
Returns reference to the last element. Precondition: the vector is non-empty.
Definition storage.hpp:539
constexpr const T * inline_data() const noexcept
Returns const pointer to the inline buffer.
Definition storage.hpp:315
constexpr ~small_vec()
Destroys elements and frees any heap block.
Definition storage.hpp:411
constexpr std::size_t size() const noexcept
Returns the number of elements.
Definition storage.hpp:489
union real::detail::small_vec::Storage storage_
constexpr void cleanup() noexcept
Frees the heap block, if any (run-time only). T is trivially destructible (see the class static_asser...
Definition storage.hpp:375
constexpr T & operator[](std::size_t i) noexcept
Returns reference to the element at i.
Definition storage.hpp:507
void extend_capacity()
Doubles the capacity (saturating), spilling to the heap as needed.
Definition storage.hpp:388
constexpr void transfer_range(const T *src, std::size_t count, T *dest)
Copies or moves count elements from src to dest.
Definition storage.hpp:328
constexpr small_vec & operator=(small_vec &&other) noexcept
Move assignment.
Definition storage.hpp:624
constexpr void push_back(const T &value)
Appends value, growing to the heap if the inline buffer is full.
Definition storage.hpp:424
constexpr small_vec(const small_vec &other)
Copy constructor (needed for vector<match_result> in find_all).
Definition storage.hpp:650
constexpr void clear() noexcept
Removes all elements (capacity and heap state unchanged).
Definition storage.hpp:531
constexpr small_vec(small_vec &&other) noexcept
Move constructor: steals other's heap block or moves inline elements.
Definition storage.hpp:600
std::size_t capacity_
Current capacity.
Definition storage.hpp:237
constexpr T * inline_data() noexcept
Returns pointer to the inline buffer.
Definition storage.hpp:307
std::size_t size_
Number of elements in use.
Definition storage.hpp:236
constexpr const T & operator[](std::size_t i) const noexcept
Returns const reference to the element at i.
Definition storage.hpp:520
constexpr void pop_back() noexcept
Removes the last element. Precondition: the vector is non-empty.
Definition storage.hpp:566
constexpr small_vec & operator=(const small_vec &other)
Copy assignment.
Definition storage.hpp:674
Fixed-capacity vector backed by an inline array (no heap).
Definition storage.hpp:91
constexpr void pop_back()
Removes the last element. Precondition: the vector is non-empty.
Definition storage.hpp:182
constexpr void clear()
Removes all elements (capacity unchanged).
Definition storage.hpp:111
constexpr const T & operator[](std::size_t i) const
Returns const reference to the element at i.
Definition storage.hpp:165
std::size_t size_
Number of elements in use.
Definition storage.hpp:191
std::array< T, Cap > data_
Inline element storage.
Definition storage.hpp:190
constexpr std::size_t size() const
Returns the number of elements.
Definition storage.hpp:137
constexpr T & operator[](std::size_t i)
Returns reference to the element at i.
Definition storage.hpp:155
constexpr void assign(std::size_t count, const T &value)
Resizes to count copies of value.
Definition storage.hpp:122
constexpr void push_back(const T &value)
Appends value.
Definition storage.hpp:99
constexpr T & back()
Returns reference to the last element. Precondition: the vector is non-empty.
Definition storage.hpp:173
constexpr bool empty() const
Returns true if empty.
Definition storage.hpp:145
AST → NFA program, via Thompson construction.
constexpr dynamic_program compile(const ast &tree, flags compile_flags)
Compiles tree to an NFA program (convenience over compiler).
Definition compiler.hpp:901
constexpr ast parse(std::string_view pattern, flags initial_flags=flags::none)
Parses pattern into an ast (convenience over parser).
Definition ast.hpp:1853
constexpr bool has_flag(flags value, flags flag)
Tests whether flag is set in value.
Definition program.hpp:92
flags
Compilation flags, mirroring Python's re.I, re.M and re.S.
Definition program.hpp:42
@ none
No flags.
@ bytes
Binary mode: . and [^…] match raw bytes, not codepoints.
@ ascii
ASCII mode (re.A): \d \w \s \b stay ASCII and icase folds ASCII only, even in text mode....
The Pike VM — a Thompson NFA simulation — and its fast paths.
Compiled form of a pattern and the public flags / error types.
A parsed pattern: the node pool plus side tables.
Definition ast.hpp:164
flags inline_flags
Flags from a leading (?ims).
Definition ast.hpp:168
Reusable VM scratch state.
Definition pike.hpp:236
Owning, heap-allocated program: the storage backing real::regex.
Definition program.hpp:368
std::vector< cp_class > cp_classes
Match-time code-point classes (for klass_cp).
Definition program.hpp:373
pattern_hints hints
Search-acceleration hints.
Definition program.hpp:382
std::uint16_t slot_count
2 * (capture groups + 1).
Definition program.hpp:379
std::vector< instr > code
The instruction stream (main program + lookaround sub-program regions).
Definition program.hpp:369
constexpr program_view view() const
Returns a non-owning program_view over this program.
Definition program.hpp:392
std::vector< code_range > cp_ranges
Flat range buffer the cp_class slices index into.
Definition program.hpp:374
std::vector< char_class > classes
Interned character classes.
Definition program.hpp:370
std::vector< named_group > names
Named capture groups.
Definition program.hpp:371
VM scratch state: SBO thread lists, working slots and eps stack.
Definition storage.hpp:717
capture_pool pool
OPT D1: copy-on-write capture blocks (heap-backed).
Definition storage.hpp:719
std::optional< lazy_dfa > fwd_dfa
OPT lazy-DFA: forward pass (cache persists across a find_iter).
Definition storage.hpp:720
const void * il_prefix_for
IL: the prefix program il_prefix_rev was built for.
Definition storage.hpp:724
std::optional< reverse_dfa > il_prefix_rev
IL: the inner-literal prefix reverse DFA (built once per program).
Definition storage.hpp:723
std::optional< reverse_dfa > rev_dfa
OPT lazy-DFA: the reverse start-finder.
Definition storage.hpp:721
lookaround_scratch lookaround
Isolated sub-scratch for bounded lookaround evaluation.
Definition storage.hpp:718
const void * il_text
IL: the haystack il_abandoned refers to.
Definition storage.hpp:725
bool il_abandoned
IL: a linearity guard tripped on this haystack — stay on the core.
Definition storage.hpp:726
const void * dfa_program
The program the DFAs were built for.
Definition storage.hpp:722
Storage policy backing real::regex: heap, sized once at run time.
Definition storage.hpp:703
static constexpr dynamic_storage compile(std::string_view pattern, flags compile_flags)
Parses and compiles pattern with flags compile_flags.
Definition storage.hpp:745
std::string pattern_text
The original pattern text.
Definition storage.hpp:729
dynamic_program program
The compiled program.
Definition storage.hpp:730
constexpr program_view view() const
Returns a non-owning view of the compiled program.
Definition storage.hpp:758
constexpr flags compiled_flags() const
Returns the effective flags (constructor flags merged with (?ims)).
Definition storage.hpp:776
flags effective_flags
Constructor flags merged with any (?ims).
Definition storage.hpp:731
constexpr std::string_view pattern() const
Returns the original pattern text.
Definition storage.hpp:768
static constexpr bool is_compile_time
Selects the runtime constructor.
Definition storage.hpp:704
detail::regex_immutables immut_
Per-regex lazy-DFA/one-pass cache, built once (thread-safe) and shared by every search on this regex ...
Definition storage.hpp:736
Reusable, isolated scratch for one level of lookaround evaluation (dynamic only).
Definition pike.hpp:279
Search-acceleration hints extracted from a compiled program.
Definition program.hpp:267
regex_immutables * immut
Per-regex DFA/one-pass cache (dynamic storage only; else null).
Definition program.hpp:361
std::span< const instr > code
The instruction stream (main + lookaround regions).
Definition program.hpp:347
The per-regex immutable cache the router shares across every find_iter on a regex: the byte- program ...
Definition onepass.hpp:502
Active member (inline buffer or heap pointer) per is_heap_ state.
Definition storage.hpp:249
VM scratch state, all fixed-capacity (zero heap).
Definition storage.hpp:879
basic_capture_pool< static_vec< std::size_t, max_blocks *slot_count >, static_vec< std::int32_t, max_blocks >, static_vec< std::uint32_t, max_blocks > > pool
COW capture blocks (zero heap).
Definition storage.hpp:882
Storage policy backing real::static_regex: compile-time, stateless.
Definition storage.hpp:794
static constexpr std::size_t name_count
Named-group count.
Definition storage.hpp:842
static constexpr bool is_compile_time
Selects the default constructor.
Definition storage.hpp:795
static constexpr std::size_t max_blocks
Definition storage.hpp:864
static constexpr std::array< instr, code_size > code
The program.
Definition storage.hpp:847
static constexpr std::array< char_class, class_count > classes
Interned classes.
Definition storage.hpp:848
constexpr flags compiled_flags() const
Returns the effective flags.
Definition storage.hpp:917
static constexpr std::uint16_t slot_count
2*(groups+1).
Definition storage.hpp:845
static constexpr dynamic_program build()
Returns the freshly built program (used for both measuring and filling).
Definition storage.hpp:806
constexpr program_view view() const
Returns a non-owning view of the compile-time program.
Definition storage.hpp:888
static constexpr std::size_t class_count
Distinct class count.
Definition storage.hpp:841
static constexpr std::array< cp_class, cp_class_count > cp_classes
Code-point classes.
Definition storage.hpp:852
static constexpr std::array< named_group, name_count > names
Named groups.
Definition storage.hpp:850
static constexpr std::size_t code_size
Instruction count.
Definition storage.hpp:840
static constexpr flags effective_flags
Flags merged with (?ims).
Definition storage.hpp:838
static constexpr std::array< T, N > take(const Vec &source)
Copies the first N elements of v into a fixed array.
Definition storage.hpp:827
constexpr std::string_view pattern() const
Returns the pattern text.
Definition storage.hpp:909
static constexpr pattern_hints hints
Search hints.
Definition storage.hpp:839
static constexpr std::size_t cp_range_count
Total code-point ranges.
Definition storage.hpp:844
static constexpr std::array< code_range, cp_range_count > cp_ranges
Flat range buffer.
Definition storage.hpp:854
static constexpr std::size_t cp_class_count
Code-point class count (klass_cp).
Definition storage.hpp:843
A fixed-size string usable as a non-type template parameter.
Definition storage.hpp:47
constexpr std::string_view view() const
Returns a view of the string, excluding the trailing NUL.
Definition storage.hpp:69
constexpr fixed_string(const char(&literal)[N])
Captures a string literal.
Definition storage.hpp:59
char data[N]
The captured characters, including the trailing NUL.
Definition storage.hpp:48
inline_block inline_buffer
Inline storage (when not heap).
Definition storage.hpp:254
constexpr ~Storage()
Destruction handled by cleanup.
Definition storage.hpp:274
Storage(const Storage &)=delete
constexpr Storage() noexcept
Starts in the inline state.
Definition storage.hpp:267
Storage & operator=(Storage &&)=delete
T * heap_ptr
Heap storage (when is_heap_).
Definition storage.hpp:255
Storage & operator=(const Storage &)=delete
REAL's version macros and the C++20 language-standard guard.