| //===----------------------------------------------------------------------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| /// | |
| /// \file | |
| /// Pool for Regular Expression AST nodes (Class Definitions). | |
| /// | |
| //===----------------------------------------------------------------------===// | |
| #ifndef LLVM_LIBC_SRC___SUPPORT_REGEX_REGEX_EXPR_POOL_H | |
| #define LLVM_LIBC_SRC___SUPPORT_REGEX_REGEX_EXPR_POOL_H | |
| #include "src/__support/CPP/expected.h" | |
| #include "src/__support/macros/config.h" | |
| #include "src/__support/regex/regex_ast.h" | |
| #include <stddef.h> | |
| namespace LIBC_NAMESPACE_DECL { | |
| namespace regex { | |
| /// An arena-based pool for Regular Expression AST nodes. | |
| /// | |
| /// This class manages the allocation and hash-consing of Expr nodes. All | |
| /// nodes created through this pool are owned by it and will be freed when | |
| /// the pool is destroyed. Hash-consing ensures that identical expressions | |
| /// are represented by the same pointer, enabling fast comparison and | |
| /// derivative normalization. | |
| class ExprPool { | |
| /// Internal storage block for AST nodes. | |
| /// | |
| /// Blocks are allocated on demand to avoid large contiguous allocations | |
| /// and are linked together in a list for cleanup. | |
| /// TODO: Consider adopting cpp::forward_list for block management once | |
| /// it is available in LLVM-libc. | |
| struct Block { | |
| /// Number of Expr nodes stored in each block. | |
| static constexpr size_t BLOCK_SIZE = 256; | |
| /// The actual storage for Expr nodes. | |
| Expr nodes[BLOCK_SIZE]; | |
| /// Pointer to the next block in the chain. | |
| Block *next = nullptr; | |
| /// Number of nodes currently used in this block. | |
| size_t used = 0; | |
| }; | |
| /// The first block in the allocation chain. | |
| Block *head = nullptr; | |
| /// The block currently being used for new node allocations. | |
| Block *current = nullptr; | |
| /// Total number of nodes allocated across all blocks. | |
| size_t node_count = 0; | |
| /// The size of the hash table used for hash-consing (interning) expression | |
| /// nodes. Choosing 0x4000 (16,384) is the smallest power of two that keeps | |
| /// the load factor below 70% when the pool reaches its limit of 10,000 nodes | |
| /// (peak load factor is ~61%). Using a power of two allows the compiler to | |
| /// optimize the modulo indexing into an efficient bitwise AND, while the low | |
| /// load factor minimizes collisions and guarantees O(1) average interning | |
| /// time. | |
| static constexpr size_t HASH_TABLE_SIZE = 0x4000; | |
| /// The maximum number of nodes allowed in the pool to prevent memory | |
| /// exhaustion during compilation of highly complex or maliciously crafted | |
| /// regular expressions. A limit of 10,000 nodes provides a sufficient budget | |
| /// for most practical regexes while keeping the peak memory footprint | |
| /// manageable (approx. 320KB-500KB depending on architecture). | |
| static constexpr size_t MAX_NODE_LIMIT = 10000; | |
| /// Hash table storing pointers to unique Expr nodes. | |
| Expr **hashtable = nullptr; | |
| /// Core hash-consing function (Interning). | |
| /// | |
| /// Guarantees that for any two identical structural definitions of an Expr, | |
| /// this function will return the same pointer. This enables O(1) structural | |
| /// equality via pointer comparison. | |
| /// | |
| /// \param e A structural definition (proto-node) to intern. | |
| /// \returns A pointer to the unique, stable instance in the arena, | |
| /// or REG_ESPACE on failure. | |
| cpp::expected<Expr *, int> intern(const Expr &e); | |
| public: | |
| ExprPool(); | |
| ~ExprPool(); | |
| // TODO: Use fluent interface (and_then, transform) for these factories once | |
| // implemented in cpp::expected. | |
| /// Returns an EmptySet node. | |
| cpp::expected<Expr *, int> empty_set(); | |
| /// Returns an EmptyStr node. | |
| cpp::expected<Expr *, int> empty_str(); | |
| /// Creates or returns an existing Literal node for the given character. | |
| cpp::expected<Expr *, int> make_lit(char c); | |
| /// Normalizing factory for Concatenation (L · R). | |
| /// | |
| /// Applies algebraic simplifications before interning: | |
| /// - (Ø · R) or (R · Ø) => Ø | |
| /// - (ε · R) or (R · ε) => R | |
| cpp::expected<Expr *, int> make_concat(Expr *l, Expr *r); | |
| /// Normalizing factory for Alternation (L | R). | |
| /// | |
| /// Applies algebraic simplifications before interning: | |
| /// - (Ø | R) or (R | Ø) => R | |
| /// - (R | R) => R (Idempotency) | |
| cpp::expected<Expr *, int> make_alt(Expr *l, Expr *r); | |
| }; | |
| } // namespace regex | |
| } // namespace LIBC_NAMESPACE_DECL | |
| #endif // LLVM_LIBC_SRC___SUPPORT_REGEX_REGEX_EXPR_POOL_H |