/src/trafficserver/include/tsutil/Regex.h
Line | Count | Source |
1 | | /** @file |
2 | | |
3 | | A brief file description |
4 | | |
5 | | @section license License |
6 | | |
7 | | Licensed to the Apache Software Foundation (ASF) under one |
8 | | or more contributor license agreements. See the NOTICE file |
9 | | distributed with this work for additional information |
10 | | regarding copyright ownership. The ASF licenses this file |
11 | | to you under the Apache License, Version 2.0 (the |
12 | | "License"); you may not use this file except in compliance |
13 | | with the License. You may obtain a copy of the License at |
14 | | |
15 | | http://www.apache.org/licenses/LICENSE-2.0 |
16 | | |
17 | | Unless required by applicable law or agreed to in writing, software |
18 | | distributed under the License is distributed on an "AS IS" BASIS, |
19 | | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
20 | | See the License for the specific language governing permissions and |
21 | | limitations under the License. |
22 | | */ |
23 | | |
24 | | #pragma once |
25 | | |
26 | | #include <cstdint> |
27 | | #include <memory> |
28 | | #include <string_view> |
29 | | #include <string> |
30 | | #include <vector> |
31 | | |
32 | | /// @brief Match flags for regular expression evaluation. |
33 | | /// |
34 | | /// @internal These values are copied from pcre2.h, to avoid having to include it. The values are checked (with |
35 | | /// static_assert) in Regex.cc against PCRE2 named constants, in case they change in future PCRE2 releases. |
36 | | enum REFlags { |
37 | | RE_CASE_INSENSITIVE = 0x00000008u, ///< Ignore case (by default, matches are case sensitive). |
38 | | RE_UNANCHORED = 0x00000400u, ///< Unanchored (@a DFA defaults to anchored). |
39 | | RE_ANCHORED = 0x80000000u, ///< Anchored (@a Regex defaults to unanchored). |
40 | | RE_ENDANCHORED = 0x20000000u, ///< Anchored at the subject end; with RE_ANCHORED, requires a whole-subject match. |
41 | | RE_NOTEMPTY = 0x00000004u, ///< Not empty (by default, matches may match empty string). |
42 | | /// Require the match to consume the entire subject. When set, a successful pcre2 match |
43 | | /// that does not span [0, subject.size()) is reported as @c RE_ERROR_NOMATCH. Implemented |
44 | | /// as a post-match length check so JIT remains eligible on all supported PCRE2 versions. |
45 | | RE_FULL_MATCH = 0x10000000u, |
46 | | }; |
47 | | |
48 | | /// @brief Error codes returned by regular expression operations. |
49 | | /// |
50 | | /// @internal As with REFlags, these values are copied from pcre2.h, to avoid having to include it. |
51 | | enum REErrors { |
52 | | RE_ERROR_NOMATCH = -1, ///< No match found. |
53 | | RE_ERROR_NULL = -51 ///< NULL code or subject was passed. |
54 | | }; |
55 | | |
56 | | /// @brief Wrapper for PCRE2 match data. |
57 | | class RegexMatches |
58 | | { |
59 | | friend class Regex; |
60 | | |
61 | | public: |
62 | | /** Construct a new RegexMatches object. |
63 | | * |
64 | | * @param size The number of matches to allocate space for. |
65 | | */ |
66 | | RegexMatches(uint32_t size = DEFAULT_MATCHES); |
67 | | ~RegexMatches(); |
68 | | |
69 | | /** Get the match at the given index. |
70 | | * |
71 | | * @return The match at the given index, or an empty view. |
72 | | */ |
73 | | std::string_view operator[](size_t index) const; |
74 | | /** Get the ovector pointer for the capture groups. Don't use this unless you know what you are doing. |
75 | | * |
76 | | * @return ovector pointer. |
77 | | */ |
78 | | size_t *get_ovector_pointer(); |
79 | | int32_t size() const; |
80 | | |
81 | | private: |
82 | | constexpr static uint32_t DEFAULT_MATCHES = 10; |
83 | | static void *malloc(size_t size, void *caller); |
84 | | static void free(void *p, void *caller); |
85 | | std::string_view _subject; |
86 | | char _buffer[24 + 96 + 28 * DEFAULT_MATCHES]; // 24 bytes for the general context, 96 bytes overhead, 28 bytes per match. |
87 | | size_t _buffer_bytes_used = 0; |
88 | | int32_t _size = 0; |
89 | | |
90 | | /// @internal This effectively wraps a void* so that we can avoid requiring the pcre2.h include for the user of the Regex |
91 | | /// API (see Regex.cc). |
92 | | struct _MatchData; |
93 | | class _MatchDataPtr |
94 | | { |
95 | | friend struct _MatchData; |
96 | | |
97 | | private: |
98 | | void *_ptr = nullptr; |
99 | | }; |
100 | | _MatchDataPtr _match_data; |
101 | | }; |
102 | | |
103 | | /// @brief Wrapper for PCRE2 match context |
104 | | /// |
105 | | /// @internal This instance is not tied to any Regex and can be used with one of the Regex::exec overloads. |
106 | | class RegexMatchContext |
107 | | { |
108 | | friend class Regex; |
109 | | |
110 | | public: |
111 | | /** Construct a new RegexMatchContext object. |
112 | | */ |
113 | | RegexMatchContext(); |
114 | | ~RegexMatchContext(); |
115 | | |
116 | | /// uses pcre2_match_context_copy for a deep copy. |
117 | | RegexMatchContext(RegexMatchContext const &orig); |
118 | | RegexMatchContext &operator=(RegexMatchContext const &orig); |
119 | | |
120 | | RegexMatchContext(RegexMatchContext &&) = default; |
121 | | RegexMatchContext &operator=(RegexMatchContext &&) = default; |
122 | | |
123 | | /** Limits the amount of backtracking that can take place. |
124 | | * Any regex exec call that fails will return PCRE2_ERROR_MATCHLIMIT(-47) |
125 | | */ |
126 | | void set_match_limit(uint32_t limit); |
127 | | |
128 | | private: |
129 | | /// @internal This wraps a void* so to avoid requiring a pcre2 include. |
130 | | struct _MatchContext; |
131 | | struct _MatchContextPtr { |
132 | | void *_ptr = nullptr; |
133 | | }; |
134 | | |
135 | | _MatchContextPtr _match_context; |
136 | | }; |
137 | | |
138 | | /// @brief Wrapper for PCRE2 regular expression. |
139 | | class Regex |
140 | | { |
141 | | public: |
142 | 0 | Regex() = default; |
143 | | /** Deep copy constructor. |
144 | | * |
145 | | * Creates a new Regex object with a deep copy of the compiled pattern. |
146 | | * Uses pcre2_code_copy() to duplicate the compiled pattern without |
147 | | * requiring the original pattern string. |
148 | | * |
149 | | * @param other The Regex object to copy from. |
150 | | */ |
151 | | Regex(Regex const &other); |
152 | | /** Deep copy assignment operator. |
153 | | * |
154 | | * Replaces the current compiled pattern with a deep copy of the other's pattern. |
155 | | * |
156 | | * @param other The Regex object to copy from. |
157 | | * @return Reference to this object. |
158 | | */ |
159 | | Regex &operator=(Regex const &other); |
160 | | Regex(Regex &&that) noexcept; |
161 | | Regex &operator=(Regex &&other); |
162 | | ~Regex(); |
163 | | |
164 | | /** Compile the @a pattern into a regular expression. |
165 | | * |
166 | | * @param pattern Source pattern for regular expression (null terminated). |
167 | | * @param flags Compilation flags. |
168 | | * @return @a true if compiled successfully, @a false otherwise. |
169 | | * |
170 | | * @a flags should be the bitwise @c or of @c REFlags values. |
171 | | */ |
172 | | bool compile(std::string_view pattern, uint32_t flags = 0); |
173 | | |
174 | | /** Compile the @a pattern into a regular expression. |
175 | | * |
176 | | * @param pattern Source pattern for regular expression (null terminated). |
177 | | * @param error String to receive error message. |
178 | | * @param erroffset Pointer to integer to receive error offset. |
179 | | * @param flags Compilation flags. |
180 | | * @return @a true if compiled successfully, @a false otherwise. |
181 | | * |
182 | | * @a flags should be the bitwise @c or of @c REFlags values. |
183 | | */ |
184 | | bool compile(std::string_view pattern, std::string &error, int &erroffset, unsigned flags = 0); |
185 | | |
186 | | /** Execute the regular expression. |
187 | | * |
188 | | * @param subject String to match against. |
189 | | * @return @c true if the pattern matched, @a false if not. |
190 | | * |
191 | | * It is safe to call this method concurrently on the same instance of @a this. |
192 | | */ |
193 | | bool exec(std::string_view subject) const; |
194 | | |
195 | | /** Execute the regular expression. |
196 | | * |
197 | | * @param subject String to match against. |
198 | | * @param flags Match flags (e.g., RE_NOTEMPTY). |
199 | | * @return @c true if the pattern matched, @a false if not. |
200 | | * |
201 | | * It is safe to call this method concurrently on the same instance of @a this. |
202 | | */ |
203 | | bool exec(std::string_view subject, uint32_t flags) const; |
204 | | |
205 | | /** Execute the regular expression. |
206 | | * |
207 | | * @param subject String to match against. |
208 | | * @param matches Place to store the capture groups. |
209 | | * @return @c The number of capture groups. < 0 if an error occurred. 0 if the number of Matches is too small. |
210 | | * |
211 | | * It is safe to call this method concurrently on the same instance of @a this. |
212 | | * |
213 | | * Each capture group takes 3 elements of @a ovector, therefore @a ovecsize must |
214 | | * be a multiple of 3 and at least three times the number of desired capture groups. |
215 | | */ |
216 | | int exec(std::string_view subject, RegexMatches &matches) const; |
217 | | |
218 | | /** Execute the regular expression. |
219 | | * |
220 | | * @param subject String to match against. |
221 | | * @param matches Place to store the capture groups. |
222 | | * @param flags Match flags (e.g., RE_NOTEMPTY). |
223 | | * @param optional context Match context (set matching limits). |
224 | | * @return @c The number of capture groups. < 0 if an error occurred. 0 if the number of Matches is too small. |
225 | | * |
226 | | * It is safe to call this method concurrently on the same instance of @a this. |
227 | | * |
228 | | * Each capture group takes 3 elements of @a ovector, therefore @a ovecsize must |
229 | | * be a multiple of 3 and at least three times the number of desired capture groups. |
230 | | */ |
231 | | int exec(std::string_view subject, RegexMatches &matches, uint32_t flags, |
232 | | RegexMatchContext const *const matchContext = nullptr) const; |
233 | | |
234 | | /** Error string for exec failure. |
235 | | * |
236 | | * @param int return code from exec call. |
237 | | */ |
238 | | static std::string get_error_string(int rc); |
239 | | |
240 | | /// @return The number of capture groups in the compiled pattern, -1 for fail. |
241 | | int32_t get_capture_count() const; |
242 | | |
243 | | /// @return number of highest back references, -1 for fail. |
244 | | int32_t get_backref_max() const; |
245 | | |
246 | | /// @return Is the compiled pattern empty? |
247 | | bool empty() const; |
248 | | |
249 | | private: |
250 | | /// @internal This effectively wraps a void* so that we can avoid requiring the pcre2.h include for the user of the Regex |
251 | | /// API (see Regex.cc). |
252 | | struct _Code; |
253 | | class _CodePtr |
254 | | { |
255 | | friend struct _Code; |
256 | | |
257 | | private: |
258 | | void *_ptr = nullptr; |
259 | | }; |
260 | | _CodePtr _code; |
261 | | }; |
262 | | |
263 | | /** Deterministic Finite state Automata container. |
264 | | * |
265 | | * This contains a set of patterns (which may be of size 1) and matches if any of the patterns |
266 | | * match. |
267 | | */ |
268 | | class DFA |
269 | | { |
270 | | public: |
271 | | DFA() = default; |
272 | | ~DFA(); |
273 | | |
274 | | /// @return The number of patterns successfully compiled. |
275 | | int32_t compile(const std::string_view pattern, unsigned flags = 0); |
276 | | /// @return The number of patterns successfully compiled. |
277 | | int32_t compile(const std::string_view *const patterns, int npatterns, unsigned flags = 0); |
278 | | /// @return The number of patterns successfully compiled. |
279 | | int32_t compile(const char *const *patterns, int npatterns, unsigned flags = 0); |
280 | | |
281 | | /** Match @a str against the internal patterns. |
282 | | * |
283 | | * @param str String to match. |
284 | | * @return Index of the matched pattern, -1 if no match. |
285 | | */ |
286 | | int32_t match(std::string_view str) const; |
287 | | |
288 | | private: |
289 | | struct Pattern { |
290 | 0 | Pattern(Regex &&rxp, std::string &&s) : _re(std::move(rxp)), _p(std::move(s)) {} |
291 | | Regex _re; ///< The compile pattern. |
292 | | std::string _p; ///< The original pattern. |
293 | | }; |
294 | | |
295 | | /** Compile @a pattern and add it to the pattern set. |
296 | | * |
297 | | * @param pattern Regular expression to compile. |
298 | | * @param flags Regular expression compilation flags. |
299 | | * @return @c true if @a pattern was successfully compiled, @c false if not. |
300 | | */ |
301 | | bool build(std::string_view pattern, unsigned flags = 0); |
302 | | |
303 | | std::vector<Pattern> _patterns; |
304 | | bool _full_match = false; ///< Apply RE_FULL_MATCH on every match() call. |
305 | | }; |