Skip to content

Commit 789f3e0

Browse files
authored
feat: Improve performance of built-in-functions via static regexes (#249)
1 parent abf9092 commit 789f3e0

File tree

1 file changed

+39
-26
lines changed

1 file changed

+39
-26
lines changed

casbin/util/built_in_functions.cpp

+39-26
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,24 @@
3434

3535
namespace casbin {
3636

37+
namespace {
38+
static const std::regex capturingColonNonSlashRegex("(.*?):[^/]+(.*?)");
39+
static const std::regex enclosedPlaceHolderRegex("(.*?)\\{[^/]+?\\}(.*?)");
40+
41+
std::string PrepareWildCardMatching(const std::string& value) {
42+
static const std::regex pattern("/\\*");
43+
return std::regex_replace(value, pattern, "/.*");
44+
}
45+
46+
std::string EscapeCurlyBraces(const std::string& value) {
47+
static const std::regex curlyBraceOpenPattern("\\{");
48+
static const std::regex curlyBraceClosePattern("\\}");
49+
50+
std::string intermediate = std::regex_replace(value, curlyBraceOpenPattern, "\\{");
51+
return std::regex_replace(intermediate, curlyBraceClosePattern, "\\}");
52+
}
53+
}
54+
3755
// KeyMatch determines whether key1 matches the pattern of key2 (similar to RESTful path), key2 can contain a *.
3856
// For example, "/foo/bar" matches "/foo/*"
3957
bool KeyMatch(const std::string& key1, const std::string& key2) {
@@ -67,10 +85,9 @@ std::string KeyGet(const std::string& key1, const std::string& key2) {
6785
// KeyMatch2 determines whether key1 matches the pattern of key2 (similar to RESTful path), key2 can contain a *.
6886
// For example, "/foo/bar" matches "/foo/*", "/resource1" matches "/:resource"
6987
bool KeyMatch2(const std::string& key1, const std::string& key2) {
70-
std::string k2 = regex_replace(key2, std::regex("/\\*"), "/.*");
71-
k2 = regex_replace(k2, std::regex("(.*?):[^/]+(.*?)"), "$1[^/]+$2");
72-
k2 = regex_replace(k2, std::regex("\\{"), "\\{");
73-
k2 = regex_replace(k2, std::regex("\\}"), "\\}");
88+
std::string k2 = PrepareWildCardMatching(key2);
89+
k2 = std::regex_replace(k2, capturingColonNonSlashRegex, "$1[^/]+$2");
90+
k2 = EscapeCurlyBraces(k2);
7491

7592
if (!k2.compare("*"))
7693
k2 = "(.*)";
@@ -82,17 +99,16 @@ bool KeyMatch2(const std::string& key1, const std::string& key2) {
8299
// For example, "/resource1" matches "/:resource"
83100
// if the path_var == "resource", then "resource1" will be returned
84101
std::string KeyGet2(const std::string& key1, const std::string& key2, const std::string& path_var) {
85-
std::string k2 = regex_replace(key2, std::regex("/\\*"), "/.*");
102+
static const std::regex colonAnyButSlashPattern(":[^/]+");
103+
std::string k2 = PrepareWildCardMatching(key2);
86104

87105
std::vector<std::string> keys;
88-
std::regex keys_regex(":[^/]+");
89-
for (std::sregex_iterator it(k2.begin(), k2.end(), keys_regex), end_it; it != end_it; ++it) {
106+
for (std::sregex_iterator it(k2.begin(), k2.end(), colonAnyButSlashPattern), end_it; it != end_it; ++it) {
90107
keys.push_back(it->str());
91108
}
92109

93-
k2 = regex_replace(k2, std::regex("(.*?):[^/]+(.*?)"), "$1([^/]+)$2");
94-
k2 = regex_replace(k2, std::regex("\\{"), "\\{");
95-
k2 = regex_replace(k2, std::regex("\\}"), "\\}");
110+
k2 = std::regex_replace(k2, capturingColonNonSlashRegex, "$1([^/]+)$2");
111+
k2 = EscapeCurlyBraces(k2);
96112
if (!k2.compare("*"))
97113
k2 = "(.*)";
98114
k2 = "^" + k2 + "$";
@@ -110,10 +126,9 @@ std::string KeyGet2(const std::string& key1, const std::string& key2, const std:
110126
// KeyMatch3 determines whether key1 matches the pattern of key2 (similar to RESTful path), key2 can contain a *.
111127
// For example, "/foo/bar" matches "/foo/*", "/resource1" matches "/{resource}"
112128
bool KeyMatch3(const std::string& key1, const std::string& key2) {
113-
std::string k2 = regex_replace(key2, std::regex("/\\*"), "/.*");
114-
k2 = regex_replace(k2, std::regex("(.*?)\\{[^/]+?\\}(.*?)"), "$1[^/]+$2");
115-
k2 = regex_replace(k2, std::regex("\\{"), "\\{");
116-
k2 = regex_replace(k2, std::regex("\\}"), "\\}");
129+
std::string k2 = PrepareWildCardMatching(key2);
130+
k2 = std::regex_replace(k2, enclosedPlaceHolderRegex, "$1[^/]+$2");
131+
k2 = EscapeCurlyBraces(k2);
117132

118133
return RegexMatch(key1, "^" + k2 + "$");
119134
}
@@ -122,17 +137,16 @@ bool KeyMatch3(const std::string& key1, const std::string& key2) {
122137
// For example, "project/proj_project1_admin/" matches "project/proj_{project}_admin/"
123138
// if the pathVar == "project", then "project1" will be returned
124139
std::string KeyGet3(const std::string& key1, const std::string& key2, const std::string& path_var) {
125-
std::string k2 = regex_replace(key2, std::regex("/\\*"), "/.*");
140+
static const std::regex placeHolderPattern("\\{[^/]+?\\}");
141+
std::string k2 = PrepareWildCardMatching(key2);
126142

127143
std::vector<std::string> keys;
128-
std::regex keys_regex("\\{[^/]+?\\}");
129-
for (std::sregex_iterator it(k2.begin(), k2.end(), keys_regex), end_it; it != end_it; ++it) {
144+
for (std::sregex_iterator it(k2.begin(), k2.end(), placeHolderPattern), end_it; it != end_it; ++it) {
130145
keys.push_back(it->str());
131146
}
132147

133-
k2 = regex_replace(k2, std::regex("(.*?)\\{[^/]+?\\}(.*?)"), "$1([^/]+?)$2");
134-
k2 = regex_replace(k2, std::regex("\\{"), "\\{");
135-
k2 = regex_replace(k2, std::regex("\\}"), "\\}");
148+
k2 = std::regex_replace(k2, enclosedPlaceHolderRegex, "$1([^/]+?)$2");
149+
k2 = EscapeCurlyBraces(k2);
136150
if (!k2.compare("*"))
137151
k2 = "(.*)";
138152
k2 = "^" + k2 + "$";
@@ -153,16 +167,15 @@ std::string KeyGet3(const std::string& key1, const std::string& key2, const std:
153167
// "/parent/123/child/456" does not match "/parent/{id}/child/{id}"
154168
// But KeyMatch3 will match both.
155169
bool KeyMatch4(const std::string& key1, const std::string& key2) {
156-
std::string k2 = regex_replace(key2, std::regex("/\\*"), "/.*");
170+
std::string k2 = PrepareWildCardMatching(key2);
157171

158172
std::vector<std::string> tokens;
159-
std::regex tokens_regex("\\{([^/]+)\\}");
173+
static std::regex tokens_regex("\\{([^/]+)\\}");
160174
for (std::sregex_iterator it(k2.begin(), k2.end(), tokens_regex), end_it; it != end_it; ++it)
161175
tokens.push_back(it->str());
162176

163-
k2 = regex_replace(k2, std::regex("(.*?)\\{[^/]+?\\}(.*?)"), "$1([^/]+)$2");
164-
k2 = regex_replace(k2, std::regex("\\{"), "\\{");
165-
k2 = regex_replace(k2, std::regex("\\}"), "\\}");
177+
k2 = std::regex_replace(k2, enclosedPlaceHolderRegex, "$1([^/]+)$2");
178+
k2 = EscapeCurlyBraces(k2);
166179
k2 = "^" + k2 + "$";
167180
std::smatch matches;
168181
std::regex_match(key1.begin(), key1.end(), matches, std::regex(k2));
@@ -187,7 +200,7 @@ bool KeyMatch4(const std::string& key1, const std::string& key2) {
187200
// RegexMatch determines whether key1 matches the pattern of key2 in regular expression.
188201
bool RegexMatch(const std::string& key1, const std::string& key2) {
189202
std::regex regex_s(key2);
190-
return regex_match(key1, regex_s);
203+
return std::regex_match(key1, regex_s);
191204
}
192205

193206
// IPMatch determines whether IP address ip1 matches the pattern of IP address ip2, ip2 can be an IP address or a CIDR pattern.

0 commit comments

Comments
 (0)