diff --git a/casbin/.clang-format b/casbin/.clang-format
new file mode 100644
index 00000000..df0c5b87
--- /dev/null
+++ b/casbin/.clang-format
@@ -0,0 +1,25 @@
+BasedOnStyle: Google
+PointerAlignment: Right
+AccessModifierOffset: -4
+IndentWidth: 4
+MaxEmptyLinesToKeep: 1
+BreakBeforeBraces: Attach
+AllowShortFunctionsOnASingleLine: true
+AllowShortIfStatementsOnASingleLine: true
+AlignAfterOpenBracket: true
+IndentCaseLabels: true
+ObjCBlockIndentWidth: 4
+ObjCSpaceAfterProperty: true
+ColumnLimit: 0
+AlignTrailingComments: true
+SpaceAfterCStyleCast: false
+AlignOperands: true
+SpacesInSquareBrackets: false
+AlignConsecutiveDeclarations: false
+SpacesInContainerLiterals: false
+BreakConstructorInitializersBeforeComma: true
+AllowAllParametersOfDeclarationOnNextLine: false
+ContinuationIndentWidth: 4
+TabWidth: 4
+SpaceBeforeAssignmentOperators: true
+SpacesBeforeTrailingComments: 1
\ No newline at end of file
diff --git a/casbin/casbin.vcxproj b/casbin/casbin.vcxproj
index 6dd19d10..f11f7cf8 100644
--- a/casbin/casbin.vcxproj
+++ b/casbin/casbin.vcxproj
@@ -157,6 +157,7 @@
+
@@ -229,6 +230,7 @@
+
@@ -286,6 +288,9 @@
+
+
+
diff --git a/casbin/casbin.vcxproj.filters b/casbin/casbin.vcxproj.filters
index a3a2905a..88a6a79d 100644
--- a/casbin/casbin.vcxproj.filters
+++ b/casbin/casbin.vcxproj.filters
@@ -252,6 +252,9 @@
Source Files\persist\file_adapter
+
+ Source Files
+
@@ -464,5 +467,11 @@
Header Files\persist\file_adapter
+
+ Header Files
+
+
+
+
\ No newline at end of file
diff --git a/casbin/enforcer_cached.cpp b/casbin/enforcer_cached.cpp
new file mode 100644
index 00000000..35f4d6ef
--- /dev/null
+++ b/casbin/enforcer_cached.cpp
@@ -0,0 +1,213 @@
+/*
+* Copyright 2020 The casbin Authors. All Rights Reserved.
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+#pragma once
+
+#include "pch.h"
+
+#include "./enforcer_cached.h"
+#include "./persist/watcher_ex.h"
+#include "./persist/file_adapter/file_adapter.h"
+#include "./rbac/default_role_manager.h"
+#include "./effect/default_effector.h"
+#include "./exception/casbin_adapter_exception.h"
+#include "./exception/casbin_enforcer_exception.h"
+#include "./util/util.h"
+using namespace std;
+
+
+/**
+ * Enforcer is the default constructor.
+ */
+CachedEnforcer ::CachedEnforcer() {
+ this->enableCache = true;
+}
+
+/**
+ * Enforcer initializes an enforcer with a model file and a policy file.
+ *
+ * @param model_path the path of the model file.
+ * @param policyFile the path of the policy file.
+ */
+CachedEnforcer ::CachedEnforcer(string model_path, string policy_file): Enforcer(model_path, policy_file) {
+ this->enableCache = true;
+}
+
+/**
+ * Enforcer initializes an enforcer with a database adapter.
+ *
+ * @param model_path the path of the model file.
+ * @param adapter the adapter.
+ */
+CachedEnforcer ::CachedEnforcer(string model_path, shared_ptr adapter): Enforcer(model_path,adapter) {
+ this->enableCache = true;
+}
+
+/**
+ * Enforcer initializes an enforcer with a model and a database adapter.
+ *
+ * @param m the model.
+ * @param adapter the adapter.
+ */
+CachedEnforcer :: CachedEnforcer(shared_ptr m, shared_ptr adapter): Enforcer(m,adapter) {
+ this->enableCache = true;
+}
+
+/**
+ * Enforcer initializes an enforcer with a model.
+ *
+ * @param m the model.
+ */
+CachedEnforcer ::CachedEnforcer(shared_ptr m): Enforcer(m) {
+ this->enableCache = true;
+}
+
+/**
+ * Enforcer initializes an enforcer with a model file.
+ *
+ * @param model_path the path of the model file.
+ */
+CachedEnforcer ::CachedEnforcer(string model_path): Enforcer(model_path) {
+ this->enableCache = true;
+}
+
+/**
+ * Enforcer initializes an enforcer with a model file, a policy file and an enable log flag.
+ *
+ * @param model_path the path of the model file.
+ * @param policyFile the path of the policy file.
+ * @param enableLog whether to enable Casbin's log.
+ */
+CachedEnforcer :: CachedEnforcer(string model_path, string policy_file, bool enable_log): Enforcer(model_path,policy_file,enable_log) {
+ this->enableCache = true;
+}
+
+CachedEnforcer::CachedEnforcer(const CachedEnforcer& ce):Enforcer(ce){
+ this->m = ce.m;
+ this->enableCache = ce.enableCache;
+}
+
+CachedEnforcer::CachedEnforcer(CachedEnforcer&& ce):Enforcer(ce){
+ this->m = move(ce.m);
+ this->enableCache = ce.enableCache;
+}
+
+
+void CachedEnforcer::EnableCache(const bool& enableCache) {
+ this->enableCache = enableCache;
+}
+
+pair CachedEnforcer::getCachedResult(const string& key) {
+ locker.lock();
+ bool ok = m.count(key);
+ if (!ok) {
+ locker.unlock();
+ return pair(false, false);
+ }
+
+ pair res_ok(m[key], ok);
+ locker.unlock();
+ return res_ok;
+}
+
+void CachedEnforcer::setCachedResult(const string& key, const bool& res) {
+ locker.lock();
+ m[key] = res;
+ locker.unlock();
+}
+
+void CachedEnforcer::InvalidateCache() {
+ m.clear();
+}
+
+// Enforce decides whether a "subject" can access a "object" with the operation
+// "action", input parameters are usually: (sub, obj, act).
+bool CachedEnforcer ::Enforce(Scope scope) {
+ return EnforceWithMatcher("", scope);
+}
+
+// Enforce with a vector param,decides whether a "subject" can access a "object"
+// with the operation "action", input parameters are usually: (sub, obj, act).
+bool CachedEnforcer::Enforce(vector params) {
+ return EnforceWithMatcher("", params);
+}
+
+// Enforce with a map param,decides whether a "subject" can access a "object"
+// with the operation "action", input parameters are usually: (sub, obj, act).
+bool CachedEnforcer::Enforce(unordered_map params) {
+ return EnforceWithMatcher("", params);
+}
+
+// EnforceWithMatcher use a custom matcher to decides whether a "subject" can
+// access a "object" with the operation "action", input parameters are usually:
+// (matcher, sub, obj, act), use model matcher by default when matcher is "".
+bool CachedEnforcer ::EnforceWithMatcher(string matcher, Scope scope) {
+ return Enforcer::EnforceWithMatcher(matcher, scope);
+}
+
+// EnforceWithMatcher use a custom matcher to decides whether a "subject" can
+// access a "object" with the operation "action", input parameters are usually:
+// (matcher, sub, obj, act), use model matcher by default when matcher is "".
+bool CachedEnforcer::EnforceWithMatcher(string matcher, vector params) {
+ if (!enableCache) {
+ return Enforcer::EnforceWithMatcher(matcher,params);
+ }
+
+ string key;
+ for (auto r : params) {
+ key += r;
+ key += "$$";
+ }
+ key += matcher;
+ key += "$";
+
+ pair res_ok = getCachedResult(key);
+
+ if (res_ok.second) {
+ return res_ok.first;
+ }
+
+ bool res = Enforcer::EnforceWithMatcher(matcher,params);
+ setCachedResult(key, res);
+ return res;
+}
+
+// EnforceWithMatcher use a custom matcher to decides whether a "subject" can
+// access a "object" with the operation "action", input parameters are usually:
+// (matcher, sub, obj, act), use model matcher by default when matcher is "".
+bool CachedEnforcer::EnforceWithMatcher(string matcher, unordered_map params) {
+ if (!enableCache) {
+ return Enforcer::EnforceWithMatcher(matcher,params);
+ }
+
+ string key;
+ for (auto r : params) {
+ key += r.second;
+ key += "$$";
+ }
+ key += matcher;
+ key += "$";
+
+ pair res_ok = getCachedResult(key);
+
+ if (res_ok.second) {
+ return res_ok.first;
+ }
+
+ bool res = Enforcer::EnforceWithMatcher(matcher,params);
+ setCachedResult(key, res);
+ return res;
+}
\ No newline at end of file
diff --git a/casbin/enforcer_cached.h b/casbin/enforcer_cached.h
new file mode 100644
index 00000000..f019ad49
--- /dev/null
+++ b/casbin/enforcer_cached.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2020 The casbin Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CASBIN_CPP_ENFORCER_CACHED
+#define CASBIN_CPP_ENFORCER_CACHED
+
+#include
+
+#include "./enforcer.h"
+
+class CachedEnforcer : public Enforcer {
+public:
+ unordered_map m;
+ bool enableCache;
+ mutex locker;
+
+ CachedEnforcer(const CachedEnforcer& ce);
+ CachedEnforcer(CachedEnforcer&& ce);
+
+ void EnableCache(const bool& enableCache);
+ pair getCachedResult(const string& key);
+ void setCachedResult(const string& key, const bool& res);
+ void InvalidateCache();
+
+public:
+ /**
+ * Enforcer is the default constructor.
+ */
+ CachedEnforcer();
+ /**
+ * Enforcer initializes an enforcer with a model file and a policy file.
+ *
+ * @param model_path the path of the model file.
+ * @param policy_file the path of the policy file.
+ */
+ CachedEnforcer(string model_path, string policy_file);
+ /**
+ * Enforcer initializes an enforcer with a database adapter.
+ *
+ * @param model_path the path of the model file.
+ * @param adapter the adapter.
+ */
+ CachedEnforcer(string model_path, shared_ptr adapter);
+ /**
+ * Enforcer initializes an enforcer with a model and a database adapter.
+ *
+ * @param m the model.
+ * @param adapter the adapter.
+ */
+ CachedEnforcer(shared_ptr m, shared_ptr adapter);
+ /**
+ * Enforcer initializes an enforcer with a model.
+ *
+ * @param m the model.
+ */
+ CachedEnforcer(shared_ptr m);
+ /**
+ * Enforcer initializes an enforcer with a model file.
+ *
+ * @param model_path the path of the model file.
+ */
+ CachedEnforcer(string model_path);
+ /**
+ * Enforcer initializes an enforcer with a model file, a policy file and an enable log flag.
+ *
+ * @param model_path the path of the model file.
+ * @param policy_file the path of the policy file.
+ * @param enable_log whether to enable Casbin's log.
+ */
+ CachedEnforcer(string model_path, string policy_file, bool enable_log);
+
+ bool Enforce(Scope scope);
+ // Enforce with a vector param,decides whether a "subject" can access a
+ // "object" with the operation "action", input parameters are usually: (sub,
+ // obj, act).
+ bool Enforce(vector params);
+ // Enforce with a map param,decides whether a "subject" can access a "object"
+ // with the operation "action", input parameters are usually: (sub, obj, act).
+ bool Enforce(unordered_map params);
+ // EnforceWithMatcher use a custom matcher to decides whether a "subject" can
+ // access a "object" with the operation "action", input parameters are
+ // usually: (matcher, sub, obj, act), use model matcher by default when
+ // matcher is "".
+ bool EnforceWithMatcher(string matcher, Scope scope);
+ // EnforceWithMatcher use a custom matcher to decides whether a "subject" can
+ // access a "object" with the operation "action", input parameters are
+ // usually: (matcher, sub, obj, act), use model matcher by default when
+ // matcher is "".
+ bool EnforceWithMatcher(string matcher, vector params);
+ // EnforceWithMatcher use a custom matcher to decides whether a "subject" can
+ // access a "object" with the operation "action", input parameters are
+ // usually: (matcher, sub, obj, act), use model matcher by default when
+ // matcher is "".
+ bool EnforceWithMatcher(string matcher, unordered_map params);
+};
+#endif
\ No newline at end of file
diff --git a/test/.clang-format b/test/.clang-format
new file mode 100644
index 00000000..a37ae61b
--- /dev/null
+++ b/test/.clang-format
@@ -0,0 +1,27 @@
+# Visual Studio 生成的 .clang-format 文件
+
+BasedOnStyle: Google
+PointerAlignment: Right
+AccessModifierOffset: -4
+IndentWidth: 4
+MaxEmptyLinesToKeep: 1
+BreakBeforeBraces: Attach
+AllowShortFunctionsOnASingleLine: true
+AllowShortIfStatementsOnASingleLine: true
+AlignAfterOpenBracket: true
+IndentCaseLabels: true
+ObjCBlockIndentWidth: 4
+ObjCSpaceAfterProperty: true
+ColumnLimit: 0
+AlignTrailingComments: true
+SpaceAfterCStyleCast: false
+AlignOperands: true
+SpacesInSquareBrackets: false
+AlignConsecutiveDeclarations: false
+SpacesInContainerLiterals: false
+BreakConstructorInitializersBeforeComma: true
+AllowAllParametersOfDeclarationOnNextLine: true
+ContinuationIndentWidth: 4
+TabWidth: 4
+SpaceBeforeAssignmentOperators: true
+SpacesBeforeTrailingComments: 1
\ No newline at end of file
diff --git a/test/test.vcxproj b/test/test.vcxproj
index a2a151d6..99235b14 100644
--- a/test/test.vcxproj
+++ b/test/test.vcxproj
@@ -168,6 +168,7 @@
+
@@ -179,6 +180,9 @@
+
+
+
diff --git a/test/test.vcxproj.filters b/test/test.vcxproj.filters
index 4b5e33f9..4289d648 100644
--- a/test/test.vcxproj.filters
+++ b/test/test.vcxproj.filters
@@ -48,10 +48,16 @@
Source Files
+
+ Source Files
+
Header Files
+
+
+
\ No newline at end of file
diff --git a/test/test_enforcer_cached.cpp b/test/test_enforcer_cached.cpp
new file mode 100644
index 00000000..2dcd55f1
--- /dev/null
+++ b/test/test_enforcer_cached.cpp
@@ -0,0 +1,48 @@
+#pragma once
+
+#include "pch.h"
+
+#include
+
+using namespace std;
+
+namespace test_enforcer_cached
+{
+ TEST_CLASS(TestEnforcerCached)
+ {
+ public:
+
+ void testEnforceCache(CachedEnforcer& e, string sub,string obj,string act, bool res) {
+ Assert::AreEqual(res, e.Enforce({sub,obj,act}));
+ }
+
+
+
+ TEST_METHOD(TestCache) {
+ string model = "../../examples/basic_model.conf";
+ string policy = "../../examples/basic_policy.csv";
+ CachedEnforcer e = CachedEnforcer(model, policy);
+ testEnforceCache(e, "alice", "data1", "read", true);
+ testEnforceCache(e, "alice", "data1", "write", false);
+ testEnforceCache(e, "alice", "data2", "read", false);
+ testEnforceCache(e, "alice", "data2", "write", false);
+
+ // The cache is enabled, so even if we remove a policy rule, the decision
+ // for ("alice", "data1", "read") will still be true, as it uses the cached result.
+ e.RemovePolicy({"alice", "data1", "read"});
+ testEnforceCache(e, "alice", "data1", "read", true);
+ testEnforceCache(e, "alice", "data1", "write", false);
+ testEnforceCache(e, "alice", "data2", "read", false);
+ testEnforceCache(e, "alice", "data2", "write", false);
+
+ // Now we invalidate the cache, then all first-coming Enforce() has to be evaluated in real-time.
+ // The decision for ("alice", "data1", "read") will be false now.
+ e.InvalidateCache();
+ testEnforceCache(e, "alice", "data1", "read", false);
+ testEnforceCache(e, "alice", "data1", "write", false);
+ testEnforceCache(e, "alice", "data2", "read", false);
+ testEnforceCache(e, "alice", "data2", "write", false);
+ }
+
+ };
+}
\ No newline at end of file