This is an automated email from the ASF dual-hosted git repository.
bcall pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/master by this push:
new cfdcfbf95e Add flags option to Regex::exec (#12546)
cfdcfbf95e is described below
commit cfdcfbf95ebc8fac0894f847463053be80b249cf
Author: Bryan Call <[email protected]>
AuthorDate: Tue Oct 7 08:38:18 2025 -0700
Add flags option to Regex::exec (#12546)
---
include/tsutil/Regex.h | 5 +++--
src/tsutil/Regex.cc | 13 +++++++------
src/tsutil/unit_tests/test_Regex.cc | 37 +++++++++++++++++++++++++++++++++++++
3 files changed, 47 insertions(+), 8 deletions(-)
diff --git a/include/tsutil/Regex.h b/include/tsutil/Regex.h
index 8f2fc01205..a041fe7c98 100644
--- a/include/tsutil/Regex.h
+++ b/include/tsutil/Regex.h
@@ -36,6 +36,7 @@ enum REFlags {
RE_CASE_INSENSITIVE = 0x00000008u, ///< Ignore case (default: case
sensitive).
RE_UNANCHORED = 0x00000400u, ///< Unanchored (DFA defaults to
anchored).
RE_ANCHORED = 0x80000000u, ///< Anchored (Regex defaults to
unanchored).
+ RE_NOTEMPTY = 0x00000004u ///< Not empty (default: may match empty
string).
};
/// @brief Wrapper for PCRE2 match data.
@@ -124,7 +125,7 @@ public:
*
* It is safe to call this method concurrently on the same instance of @a
this.
*/
- bool exec(std::string_view subject) const;
+ bool exec(std::string_view subject, uint32_t flags = 0) const;
/** Execute the regular expression.
*
@@ -137,7 +138,7 @@ public:
* Each capture group takes 3 elements of @a ovector, therefore @a ovecsize
must
* be a multiple of 3 and at least three times the number of desired capture
groups.
*/
- int exec(std::string_view subject, RegexMatches &matches) const;
+ int exec(std::string_view subject, RegexMatches &matches, uint32_t flags =
0) const;
/// @return The number of capture groups in the compiled pattern.
int get_capture_count();
diff --git a/src/tsutil/Regex.cc b/src/tsutil/Regex.cc
index a0d548ad85..aa3f74cebd 100644
--- a/src/tsutil/Regex.cc
+++ b/src/tsutil/Regex.cc
@@ -31,9 +31,10 @@
#include <vector>
#include <mutex>
-static_assert(RE_CASE_INSENSITIVE == PCRE2_CASELESS, "Update
RE_CASE_INSERSITIVE for current PCRE2 version.");
-static_assert(RE_UNANCHORED == PCRE2_MULTILINE, "Update RE_MULTILINE for
current PCRE2 version.");
+static_assert(RE_CASE_INSENSITIVE == PCRE2_CASELESS, "Update
RE_CASE_INSENSITIVE for current PCRE2 version.");
+static_assert(RE_UNANCHORED == PCRE2_MULTILINE, "Update RE_UNANCHORED for
current PCRE2 version.");
static_assert(RE_ANCHORED == PCRE2_ANCHORED, "Update RE_ANCHORED for current
PCRE2 version.");
+static_assert(RE_NOTEMPTY == PCRE2_NOTEMPTY, "Update RE_NOTEMPTY for current
PCRE2 version.");
//----------------------------------------------------------------------------
namespace
@@ -296,20 +297,20 @@ Regex::compile(std::string_view pattern, std::string
&error, int &erroroffset, u
//----------------------------------------------------------------------------
bool
-Regex::exec(std::string_view subject) const
+Regex::exec(std::string_view subject, uint32_t flags) const
{
if (_Code::get(_code) == nullptr) {
return false;
}
RegexMatches matches;
- int count = this->exec(subject, matches);
+ int count = this->exec(subject, matches, flags);
return count > 0;
}
//----------------------------------------------------------------------------
int32_t
-Regex::exec(std::string_view subject, RegexMatches &matches) const
+Regex::exec(std::string_view subject, RegexMatches &matches, uint32_t flags)
const
{
auto code = _Code::get(_code);
@@ -317,7 +318,7 @@ Regex::exec(std::string_view subject, RegexMatches
&matches) const
if (code == nullptr) {
return 0;
}
- int count = pcre2_match(code, reinterpret_cast<PCRE2_SPTR>(subject.data()),
subject.size(), 0, 0,
+ int count = pcre2_match(code, reinterpret_cast<PCRE2_SPTR>(subject.data()),
subject.size(), 0, flags,
RegexMatches::_MatchData::get(matches._match_data),
RegexContext::get_instance()->get_match_context());
matches._size = count;
diff --git a/src/tsutil/unit_tests/test_Regex.cc
b/src/tsutil/unit_tests/test_Regex.cc
index 734b37ec2a..ebc0f1a764 100644
--- a/src/tsutil/unit_tests/test_Regex.cc
+++ b/src/tsutil/unit_tests/test_Regex.cc
@@ -190,3 +190,40 @@ TEST_CASE("Regex", "[libts][Regex]")
}
#endif
}
+
+TEST_CASE("Regex RE_NOTEMPTY flag behavior",
"[libts][Regex][flags][RE_NOTEMPTY]")
+{
+ // Pattern that only matches empty string
+ Regex r;
+ REQUIRE(r.compile("^$") == true);
+
+ SECTION("default exec matches empty subject")
+ {
+ // boolean overload
+ CHECK(r.exec(std::string_view("")) == true);
+
+ // matches overload should return 1 (one match - the whole subject)
+ RegexMatches matches;
+ CHECK(r.exec(std::string_view(""), matches) == 1);
+ CHECK(matches.size() == 1);
+ CHECK(matches[0] == std::string_view(""));
+ }
+
+ SECTION("RE_NOTEMPTY prevents empty matches")
+ {
+ // boolean overload with RE_NOTEMPTY should not match
+ CHECK(r.exec(std::string_view(""), RE_NOTEMPTY) == false);
+
+ // matches overload should return a negative value (PCRE2_ERROR_NOMATCH)
+ RegexMatches matches;
+ int rc = r.exec(std::string_view(""), matches, RE_NOTEMPTY);
+ CHECK(rc < 0);
+ }
+
+ SECTION("non-empty subject unaffected by RE_NOTEMPTY for this pattern")
+ {
+ // '^$' should not match 'a' in any case
+ CHECK(r.exec(std::string_view("a")) == false);
+ CHECK(r.exec(std::string_view("a"), RE_NOTEMPTY) == false);
+ }
+}