Coverage Report

Created: 2026-09-07 06:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
};