Skip to content

Commit 9c7b326

Browse files
authored
feat: Benchmarks for exprtk evaluator (#189)
* feat: exprtk evaluator support RBAC with domain Signed-off-by: stonex <[email protected]> * feat: exprtk evaluator support rbac with pattern Signed-off-by: stonex <[email protected]> * feat: add benchmark for exprtk evaluator Signed-off-by: stonex <[email protected]> * perf: only compile once in Eval when eval string don't change. Signed-off-by: stonex <[email protected]> * fix: repair regex function and add function judge whether's null Signed-off-by: stonex <[email protected]> * fix: clean all symbol table to avoid haning pointer in exprtk. Signed-off-by: stonex <[email protected]> * chore: use switch for select Signed-off-by: stonex <[email protected]>
1 parent dc1499d commit 9c7b326

File tree

7 files changed

+199
-54
lines changed

7 files changed

+199
-54
lines changed

casbin/enforcer.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ bool Enforcer::m_enforce(const std::string& matcher, std::shared_ptr<IEvaluator>
102102
m_log.LogPrint("Policy Rule: ", p_vals);
103103
if(p_tokens.size() != p_vals.size())
104104
return false;
105-
m_func_map.evalator->Clean(m_model->m["p"]);
105+
m_func_map.evalator->Clean(m_model->m["p"], false);
106106
m_func_map.evalator->InitialObject("p");
107107
for(int j = 0 ; j < p_tokens.size() ; j++) {
108108
size_t index = p_tokens[j].find("_");
@@ -173,7 +173,7 @@ bool Enforcer::m_enforce(const std::string& matcher, std::shared_ptr<IEvaluator>
173173
} else {
174174
// Push initial value for p in symbol table
175175
// If p don't in symbol table, the evaluate result will be invalid.
176-
m_func_map.evalator->Clean(m_model->m["p"]);
176+
m_func_map.evalator->Clean(m_model->m["p"], false);
177177
m_func_map.evalator->InitialObject("p");
178178
for(int j = 0 ; j < p_tokens.size() ; j++) {
179179
size_t index = p_tokens[j].find("_");
@@ -367,6 +367,11 @@ void Enforcer::SetWatcher(std::shared_ptr<Watcher> watcher) {
367367
watcher->SetUpdateCallback(func);
368368
}
369369

370+
// SetWatcher sets the current evaluator.
371+
void Enforcer::SetEvaluator(std::shared_ptr<IEvaluator> evaluator) {
372+
this->m_evalator = evaluator;
373+
}
374+
370375
// GetRoleManager gets the current role manager.
371376
std::shared_ptr<RoleManager> Enforcer ::GetRoleManager() {
372377
return this->rm;

casbin/model/evaluator.cpp

Lines changed: 45 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -20,32 +20,45 @@
2020

2121
namespace casbin {
2222
bool ExprtkEvaluator::Eval(const std::string& expression_string) {
23-
expression.register_symbol_table(symbol_table);
24-
// replace (&& -> and), (|| -> or)
25-
auto replaced_string = std::regex_replace(expression_string, std::regex("&&"), "and");
26-
replaced_string = std::regex_replace(replaced_string, std::regex("\\|{2}"), "or");
27-
// replace string "" -> ''
28-
replaced_string = std::regex_replace(replaced_string, std::regex("\""), "\'");
23+
if (this->expression_string_ != expression_string) {
24+
this->expression_string_ = expression_string;
25+
// replace (&& -> and), (|| -> or)
26+
auto replaced_string = std::regex_replace(expression_string, std::regex("&&"), "and");
27+
replaced_string = std::regex_replace(replaced_string, std::regex("\\|{2}"), "or");
28+
// replace string "" -> ''
29+
replaced_string = std::regex_replace(replaced_string, std::regex("\""), "\'");
30+
31+
return parser.compile(replaced_string, expression);
32+
}
2933

30-
return parser.compile(replaced_string, expression);
34+
return this->parser.error_count() == 0;
3135
}
3236

33-
void ExprtkEvaluator::InitialObject(std::string identifier) {
37+
void ExprtkEvaluator::InitialObject(const std::string& identifier) {
3438
// symbol_table.add_stringvar("");
3539
}
3640

37-
void ExprtkEvaluator::PushObjectString(std::string target, std::string proprity, const std::string& var) {
41+
void ExprtkEvaluator::PushObjectString(const std::string& target, const std::string& proprity, const std::string& var) {
3842
auto identifier = target + "." + proprity;
39-
this->symbol_table.add_stringvar(identifier, const_cast<std::string&>(var));
43+
44+
if (!symbol_table.symbol_exists(identifier)) {
45+
identifiers_[identifier] = std::make_unique<std::string>("");
46+
this->symbol_table.add_stringvar(identifier, *identifiers_[identifier]);
47+
}
48+
symbol_table.get_stringvar(identifier)->ref() = var;
4049
}
4150

42-
void ExprtkEvaluator::PushObjectJson(std::string target, std::string proprity, const nlohmann::json& var) {
51+
void ExprtkEvaluator::PushObjectJson(const std::string& target, const std::string& proprity, const nlohmann::json& var) {
4352
auto identifier = target + "." + proprity;
4453
// this->symbol_table.add_stringvar(identifier, const_cast<std::string&>(var));
4554
}
4655

4756
void ExprtkEvaluator::LoadFunctions() {
48-
57+
AddFunction("keyMatch", ExprtkFunctionFactory::GetExprtkFunction(ExprtkFunctionType::KeyMatch, 2));
58+
AddFunction("keyMatch2", ExprtkFunctionFactory::GetExprtkFunction(ExprtkFunctionType::KeyMatch2, 2));
59+
AddFunction("keyMatch3", ExprtkFunctionFactory::GetExprtkFunction(ExprtkFunctionType::KeyMatch3, 2));
60+
AddFunction("regexMatch", ExprtkFunctionFactory::GetExprtkFunction(ExprtkFunctionType::RegexMatch, 2));
61+
AddFunction("ipMatch", ExprtkFunctionFactory::GetExprtkFunction(ExprtkFunctionType::IpMatch, 2));
4962
}
5063

5164
void ExprtkEvaluator::LoadGFunction(std::shared_ptr<RoleManager> rm, const std::string& name, int narg) {
@@ -69,31 +82,29 @@ namespace casbin {
6982
}
7083

7184
bool ExprtkEvaluator::GetBoolen() {
72-
return expression.value();
85+
return bool(this->expression);
7386
}
7487

7588
float ExprtkEvaluator::GetFloat() {
7689
return expression.value();
7790
}
7891

79-
void ExprtkEvaluator::Clean(AssertionMap& section) {
80-
for (auto& [assertion_name, assertion]: section.assertion_map) {
81-
std::vector<std::string> raw_tokens = assertion->tokens;
82-
83-
for(int j = 0 ; j < raw_tokens.size() ; j++) {
84-
size_t index = raw_tokens[j].find("_");
85-
std::string token = raw_tokens[j].substr(index + 1);
86-
auto identifier = assertion_name + "." + token;
87-
if (symbol_table.get_stringvar(identifier) != nullptr) {
88-
symbol_table.remove_stringvar(identifier);
89-
}
90-
}
92+
void ExprtkEvaluator::Clean(AssertionMap& section, bool after_enforce) {
93+
if (after_enforce == false) {
94+
return;
9195
}
96+
97+
this->symbol_table.clear();
98+
this->expression_string_ = "";
99+
this->Functions.clear();
100+
this->identifiers_.clear();
92101
}
93102

94103
void ExprtkEvaluator::AddFunction(const std::string& func_name, std::shared_ptr<exprtk_func_t> func) {
95-
this->Functions.push_back(func);
96-
symbol_table.add_function(func_name, *func);
104+
if (func != nullptr) {
105+
this->Functions.push_back(func);
106+
symbol_table.add_function(func_name, *func);
107+
}
97108
}
98109

99110
void ExprtkEvaluator::PrintSymbol() {
@@ -104,21 +115,24 @@ namespace casbin {
104115
for (auto& var: var_list) {
105116
printf(" %s: %s\n" , var.c_str(), symbol_table.get_stringvar(var)->ref().c_str());
106117
}
118+
printf("Current error: %s\n", parser.error().c_str());
119+
// printf("Current exprsio string: %s\n", parser.current_token);
120+
printf("Current value: %d\n", bool(this->expression));
107121
}
108122

109123
bool DuktapeEvaluator::Eval(const std::string& expression) {
110124
return casbin::Eval(scope, expression);
111125
}
112126

113-
void DuktapeEvaluator::InitialObject(std::string identifier) {
127+
void DuktapeEvaluator::InitialObject(const std::string& identifier) {
114128
PushObject(scope, identifier);
115129
}
116130

117-
void DuktapeEvaluator::PushObjectString(std::string target, std::string proprity, const std::string& var) {
131+
void DuktapeEvaluator::PushObjectString(const std::string& target, const std::string& proprity, const std::string& var) {
118132
PushStringPropToObject(scope, target, var, proprity);
119133
}
120134

121-
void DuktapeEvaluator::PushObjectJson(std::string target, std::string proprity, const nlohmann::json& var) {
135+
void DuktapeEvaluator::PushObjectJson(const std::string& target, const std::string& proprity, const nlohmann::json& var) {
122136
PushObject(scope, proprity);
123137
PushObjectPropFromJson(scope, var, proprity);
124138
PushObjectPropToObject(scope, target, proprity);
@@ -183,7 +197,7 @@ namespace casbin {
183197
return casbin::GetFloat(scope);
184198
}
185199

186-
void DuktapeEvaluator::Clean(AssertionMap& section) {
200+
void DuktapeEvaluator::Clean(AssertionMap& section, bool after_enforce) {
187201
if (scope != nullptr) {
188202
for (auto& [assertion_name, assertion]: section.assertion_map) {
189203
std::vector<std::string> raw_tokens = assertion->tokens;

include/casbin/enforcer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,8 @@ class Enforcer : public IEnforcer {
127127
void SetAdapter(std::shared_ptr<Adapter> adapter);
128128
// SetWatcher sets the current watcher.
129129
void SetWatcher(std::shared_ptr<Watcher> watcher);
130+
// SetWatcher sets the current watcher.
131+
void SetEvaluator(std::shared_ptr<IEvaluator> evaluator);
130132
// GetRoleManager gets the current role manager.
131133
std::shared_ptr<RoleManager> GetRoleManager();
132134
// SetRoleManager sets the current role manager.

include/casbin/model/evaluator.h

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@ namespace casbin {
3434
std::list<std::string> func_list;
3535
virtual bool Eval(const std::string& expression) = 0;
3636

37-
virtual void InitialObject(std::string target) = 0;
37+
virtual void InitialObject(const std::string& target) = 0;
3838

39-
virtual void PushObjectString(std::string target, std::string proprity, const std::string& var) = 0;
39+
virtual void PushObjectString(const std::string& target, const std::string& proprity, const std::string& var) = 0;
4040

41-
virtual void PushObjectJson(std::string target, std::string proprity, const nlohmann::json& var) = 0;
41+
virtual void PushObjectJson(const std::string& target, const std::string& proprity, const nlohmann::json& var) = 0;
4242

4343
virtual void LoadFunctions() = 0;
4444

@@ -52,23 +52,28 @@ namespace casbin {
5252

5353
virtual float GetFloat() = 0;
5454

55-
virtual void Clean(AssertionMap& section) = 0;
55+
virtual void Clean(AssertionMap& section, bool after_enforce = true) = 0;
5656
};
5757

5858
class ExprtkEvaluator : public IEvaluator {
5959
private:
60+
std::string expression_string_;
6061
symbol_table_t symbol_table;
6162
expression_t expression;
6263
parser_t parser;
6364
std::vector<std::shared_ptr<exprtk_func_t>> Functions;
65+
std::unordered_map<std::string, std::unique_ptr<std::string>> identifiers_;
6466
public:
67+
ExprtkEvaluator() {
68+
this->expression.register_symbol_table(this->symbol_table);
69+
};
6570
bool Eval(const std::string& expression);
6671

67-
void InitialObject(std::string target);
72+
void InitialObject(const std::string& target);
6873

69-
void PushObjectString(std::string target, std::string proprity, const std::string& var);
74+
void PushObjectString(const std::string& target, const std::string& proprity, const std::string& var);
7075

71-
void PushObjectJson(std::string target, std::string proprity, const nlohmann::json& var);
76+
void PushObjectJson(const std::string& target, const std::string& proprity, const nlohmann::json& var);
7277

7378
void LoadFunctions();
7479

@@ -82,7 +87,7 @@ namespace casbin {
8287

8388
float GetFloat();
8489

85-
void Clean(AssertionMap& section);
90+
void Clean(AssertionMap& section, bool after_enforce = true);
8691

8792
void PrintSymbol();
8893

@@ -103,11 +108,11 @@ namespace casbin {
103108

104109
bool Eval(const std::string& expression);
105110

106-
void InitialObject(std::string target);
111+
void InitialObject(const std::string& target);
107112

108-
void PushObjectString(std::string target, std::string proprity, const std::string& var);
113+
void PushObjectString(const std::string& target, const std::string& proprity, const std::string& var);
109114

110-
void PushObjectJson(std::string target, std::string proprity, const nlohmann::json& var);
115+
void PushObjectJson(const std::string& target, const std::string& proprity, const nlohmann::json& var);
111116

112117
void LoadFunctions();
113118

@@ -121,7 +126,7 @@ namespace casbin {
121126

122127
float GetFloat();
123128

124-
void Clean(AssertionMap& section);
129+
void Clean(AssertionMap& section, bool after_enforce = true);
125130
// For duktape
126131
void AddFunction(const std::string& func_name, Function f, Index nargs);
127132

include/casbin/model/exprtk_config.h

Lines changed: 89 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121

2222
#include "casbin/exprtk/exprtk.hpp"
2323
#include "casbin/rbac/role_manager.h"
24+
#include "casbin/rbac/default_role_manager.h"
25+
#include "casbin/util/util.h"
2426

2527
namespace casbin {
2628
using numerical_type = float;
@@ -98,19 +100,101 @@ namespace casbin {
98100
}
99101
};
100102

103+
104+
struct ExprtkOtherFunction : public exprtk::igeneric_function<numerical_type>
105+
{
106+
typedef typename exprtk::igeneric_function<numerical_type>::generic_type
107+
generic_type;
108+
109+
typedef typename generic_type::scalar_view scalar_t;
110+
typedef typename generic_type::vector_view vector_t;
111+
typedef typename generic_type::string_view string_t;
112+
113+
typedef typename exprtk::igeneric_function<numerical_type>::parameter_list_t
114+
parameter_list_t;
115+
private:
116+
casbin::MatchingFunc func_;
117+
public:
118+
ExprtkOtherFunction(const std::string& idenfier, casbin::MatchingFunc func)
119+
: exprtk::igeneric_function<numerical_type>(idenfier), func_(func)
120+
{}
121+
122+
ExprtkOtherFunction()
123+
: exprtk::igeneric_function<numerical_type>("ss")
124+
{}
125+
126+
inline numerical_type operator()(parameter_list_t parameters) {
127+
bool res = false;
128+
129+
// check value cnt
130+
if (parameters.size() != 2) {
131+
return numerical_type(res);
132+
}
133+
134+
// check value type
135+
for (std::size_t i = 0; i < parameters.size(); ++i) {
136+
generic_type& gt = parameters[i];
137+
138+
if (generic_type::e_scalar == gt.type) {
139+
return numerical_type(res);
140+
}
141+
else if (generic_type::e_vector == gt.type) {
142+
return numerical_type(res);
143+
}
144+
}
145+
146+
std::string name1 = exprtk::to_str(string_t(parameters[0]));
147+
std::string name2 = exprtk::to_str(string_t(parameters[1]));
148+
149+
if(this->func_ == nullptr)
150+
res = name1 == name2;
151+
else {
152+
res = this->func_(name1, name2);
153+
}
154+
155+
return numerical_type(res);
156+
}
157+
};
158+
101159
enum class ExprtkFunctionType {
160+
Unknown,
102161
Gfunction,
162+
KeyMatch,
163+
KeyMatch2,
164+
KeyMatch3,
165+
RegexMatch,
166+
IpMatch,
103167
};
104168

105169
class ExprtkFunctionFactory {
106170
public:
107171
static std::shared_ptr<exprtk_func_t> GetExprtkFunction(ExprtkFunctionType type, int narg, std::shared_ptr<RoleManager> rm = nullptr) {
108-
if (type == ExprtkFunctionType::Gfunction) {
109-
std::string idenfier(narg, 'S');
110-
return std::make_shared<ExprtkGFunction>(idenfier, rm);
111-
} else {
112-
return nullptr;
172+
std::string idenfier(narg, 'S');
173+
std::shared_ptr<exprtk_func_t> func = nullptr;
174+
switch (type) {
175+
case ExprtkFunctionType::Gfunction:
176+
func = std::make_shared<ExprtkGFunction>(idenfier, rm);
177+
break;
178+
case ExprtkFunctionType::KeyMatch:
179+
func.reset(new ExprtkOtherFunction(idenfier, KeyMatch));
180+
break;
181+
case ExprtkFunctionType::KeyMatch2:
182+
func.reset(new ExprtkOtherFunction(idenfier, KeyMatch));
183+
break;
184+
case ExprtkFunctionType::KeyMatch3:
185+
func.reset(new ExprtkOtherFunction(idenfier, KeyMatch));
186+
break;
187+
case ExprtkFunctionType::IpMatch:
188+
func.reset(new ExprtkOtherFunction(idenfier, KeyMatch));
189+
break;
190+
case ExprtkFunctionType::RegexMatch:
191+
func.reset(new ExprtkOtherFunction(idenfier, KeyMatch));
192+
break;
193+
default:
194+
func = nullptr;
113195
}
196+
197+
return func;
114198
}
115199
};
116200
}

0 commit comments

Comments
 (0)