From 62e1894eb09af84765d7b03c124a97d4d999e8bd Mon Sep 17 00:00:00 2001 From: H-Chris233 Date: Tue, 4 Nov 2025 13:03:03 +0000 Subject: [PATCH 1/2] feat: add precompiled matchers to improve performance --- src/enforcer.rs | 68 +++++++++++++++++++++++++++++++------- src/model/default_model.rs | 45 +++++++++++++++++++++++++ src/model/mod.rs | 3 ++ 3 files changed, 104 insertions(+), 12 deletions(-) diff --git a/src/enforcer.rs b/src/enforcer.rs index a997e5f1..155dfe41 100644 --- a/src/enforcer.rs +++ b/src/enforcer.rs @@ -14,6 +14,8 @@ use crate::{ Result, }; +use crate::model::DefaultModel; + #[cfg(any(feature = "logging", feature = "watcher"))] use crate::emitter::notify_logger_and_watcher; @@ -149,10 +151,21 @@ impl Enforcer { let mut eft_stream = self.eft.new_stream(&e_ast.value, max(policy_len, 1)); - let m_ast_compiled = self - .engine - .compile_expression(escape_eval(&m_ast.value)) - .map_err(Into::>::into)?; + let m_ast_compiled = if let Some(default_model) = + self.model.as_any().downcast_ref::() + { + default_model.get_compiled_matcher("m").ok_or_else(|| { + crate::error::Error::ModelError(crate::error::ModelError::M( + "Matcher 'm' not compiled".to_string(), + )) + })? + } else { + // Fallback to original compilation (for other Model implementations) + &self + .engine + .compile_expression(escape_eval(&m_ast.value)) + .map_err(Into::>::into)? + }; if policy_len == 0 { for token in p_ast.tokens.iter() { @@ -161,7 +174,7 @@ impl Enforcer { let eval_result = self .engine - .eval_ast_with_scope::(&mut scope, &m_ast_compiled)?; + .eval_ast_with_scope::(&mut scope, m_ast_compiled)?; let eft = if eval_result { EffectKind::Allow } else { @@ -189,7 +202,7 @@ impl Enforcer { let eval_result = self .engine - .eval_ast_with_scope::(&mut scope, &m_ast_compiled)?; + .eval_ast_with_scope::(&mut scope, m_ast_compiled)?; let eft = match p_ast.tokens.iter().position(|x| x == "p_eft") { Some(j) if eval_result => { let p_eft = &pvals[j]; @@ -278,10 +291,26 @@ impl Enforcer { let mut eft_stream = self.eft.new_stream(&e_ast.value, max(policy_len, 1)); - let m_ast_compiled = self - .engine - .compile_expression(escape_eval(&m_ast.value)) - .map_err(Into::>::into)?; + let m_ast_compiled = if let Some(default_model) = + self.model.as_any().downcast_ref::() + { + default_model.get_compiled_matcher(&ctx.m_type).ok_or_else( + || { + crate::error::Error::ModelError( + crate::error::ModelError::M(format!( + "Matcher '{}' not compiled", + ctx.m_type + )), + ) + }, + )? + } else { + // Fallback to original compilation (for other Model implementations) + &self + .engine + .compile_expression(escape_eval(&m_ast.value)) + .map_err(Into::>::into)? + }; if policy_len == 0 { for token in p_ast.tokens.iter() { @@ -290,7 +319,7 @@ impl Enforcer { let eval_result = self .engine - .eval_ast_with_scope::(&mut scope, &m_ast_compiled)?; + .eval_ast_with_scope::(&mut scope, m_ast_compiled)?; let eft = if eval_result { EffectKind::Allow } else { @@ -318,7 +347,7 @@ impl Enforcer { let eval_result = self .engine - .eval_ast_with_scope::(&mut scope, &m_ast_compiled)?; + .eval_ast_with_scope::(&mut scope, m_ast_compiled)?; let eft = match p_ast.tokens.iter().position(|x| x == "p_eft") { Some(j) if eval_result => { let p_eft = &pvals[j]; @@ -433,6 +462,13 @@ impl CoreApi for Enforcer { e.register_g_functions()?; + // 如果使用DefaultModel,编译matcher表达式 + if let Some(default_model) = + e.model.as_any_mut().downcast_mut::() + { + default_model.compile_matchers(&e.engine)?; + } + Ok(e) } @@ -534,6 +570,14 @@ impl CoreApi for Enforcer { async fn set_model(&mut self, m: M) -> Result<()> { self.model = m.try_into_model().await?; + + // 如果使用DefaultModel,重新编译matcher表达式 + if let Some(default_model) = + self.model.as_any_mut().downcast_mut::() + { + default_model.compile_matchers(&self.engine)?; + } + self.load_policy().await?; Ok(()) } diff --git a/src/model/default_model.rs b/src/model/default_model.rs index 4f6cfeac..5be2e925 100644 --- a/src/model/default_model.rs +++ b/src/model/default_model.rs @@ -19,14 +19,51 @@ use async_std::path::Path as ioPath; #[cfg(feature = "runtime-tokio")] use std::path::Path as ioPath; +use rhai::{Engine, AST}; use std::{collections::HashMap, sync::Arc}; #[derive(Clone, Default)] pub struct DefaultModel { pub(crate) model: HashMap, + /// 预编译的matcher表达式 - 在Model初始化时编译 + /// 键是完整的matcher名称(如\"m\", \"m2\", \"m3\") + compiled_matchers: HashMap, } impl DefaultModel { + /// 在Model加载完成后编译所有matcher表达式 + /// 在Enforcer使用前调用 + pub fn compile_matchers(&mut self, engine: &Engine) -> Result<()> { + self.compiled_matchers.clear(); + + // 只编译"m"段的matchers + if let Some(assertions) = self.model.get("m") { + for (key, assertion) in assertions { + let compiled = engine + .compile_expression(crate::util::escape_eval( + &assertion.value, + )) + .map_err(|e| { + crate::error::Error::ModelError( + crate::error::ModelError::M(format!( + "Failed to compile matcher '{}': {}", + key, e + )), + ) + })?; + + // 直接使用完整的matcher key作为HashMap键 + self.compiled_matchers.insert(key.clone(), compiled); + } + } + Ok(()) + } + + /// 获取预编译的matcher - O(1)查找,简洁直接 + #[inline] + pub fn get_compiled_matcher(&self, key: &str) -> Option<&AST> { + self.compiled_matchers.get(key) + } #[cfg(not(target_arch = "wasm32"))] pub async fn from_file>(p: P) -> Result { let cfg = Config::from_file(p).await?; @@ -444,6 +481,14 @@ impl Model for DefaultModel { s } + + fn as_any(&self) -> &dyn std::any::Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn std::any::Any { + self + } } #[cfg(test)] diff --git a/src/model/mod.rs b/src/model/mod.rs index 30a2540d..b551d1ac 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -73,4 +73,7 @@ pub trait Model: Send + Sync { field_values: Vec, ) -> (bool, Vec>); fn to_text(&self) -> String; + // 向下转换支持 - 用于性能优化 + fn as_any(&self) -> &dyn std::any::Any; + fn as_any_mut(&mut self) -> &mut dyn std::any::Any; } From acc2eb7004266c4824c7741f8a7903a82f2e6a13 Mon Sep 17 00:00:00 2001 From: H-Chris233 Date: Tue, 4 Nov 2025 13:23:38 +0000 Subject: [PATCH 2/2] fix:remove Chinese characters --- src/enforcer.rs | 4 ++-- src/model/default_model.rs | 14 +++++++------- src/model/mod.rs | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/enforcer.rs b/src/enforcer.rs index 155dfe41..c63cf36c 100644 --- a/src/enforcer.rs +++ b/src/enforcer.rs @@ -462,7 +462,7 @@ impl CoreApi for Enforcer { e.register_g_functions()?; - // 如果使用DefaultModel,编译matcher表达式 + // If using DefaultModel, compile matcher expressions if let Some(default_model) = e.model.as_any_mut().downcast_mut::() { @@ -571,7 +571,7 @@ impl CoreApi for Enforcer { async fn set_model(&mut self, m: M) -> Result<()> { self.model = m.try_into_model().await?; - // 如果使用DefaultModel,重新编译matcher表达式 + // If using DefaultModel, recompile matcher expressions if let Some(default_model) = self.model.as_any_mut().downcast_mut::() { diff --git a/src/model/default_model.rs b/src/model/default_model.rs index 5be2e925..afcc6d69 100644 --- a/src/model/default_model.rs +++ b/src/model/default_model.rs @@ -25,18 +25,18 @@ use std::{collections::HashMap, sync::Arc}; #[derive(Clone, Default)] pub struct DefaultModel { pub(crate) model: HashMap, - /// 预编译的matcher表达式 - 在Model初始化时编译 - /// 键是完整的matcher名称(如\"m\", \"m2\", \"m3\") + // Precompiled matcher expressions - compiled during Model initialization + // Keys are full matcher names (e.g., "m", "m2", "m3") compiled_matchers: HashMap, } impl DefaultModel { - /// 在Model加载完成后编译所有matcher表达式 - /// 在Enforcer使用前调用 + // Compiles all matcher expressions after Model loading is complete + // Should be called before Enforcer is used pub fn compile_matchers(&mut self, engine: &Engine) -> Result<()> { self.compiled_matchers.clear(); - // 只编译"m"段的matchers + // Only compile matchers from the 'm' section if let Some(assertions) = self.model.get("m") { for (key, assertion) in assertions { let compiled = engine @@ -52,14 +52,14 @@ impl DefaultModel { ) })?; - // 直接使用完整的matcher key作为HashMap键 + // Directly use the complete matcher key as HashMap key self.compiled_matchers.insert(key.clone(), compiled); } } Ok(()) } - /// 获取预编译的matcher - O(1)查找,简洁直接 + // Gets the precompiled matcher - O(1) lookup, simple and direct #[inline] pub fn get_compiled_matcher(&self, key: &str) -> Option<&AST> { self.compiled_matchers.get(key) diff --git a/src/model/mod.rs b/src/model/mod.rs index b551d1ac..bc32b8b9 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -73,7 +73,7 @@ pub trait Model: Send + Sync { field_values: Vec, ) -> (bool, Vec>); fn to_text(&self) -> String; - // 向下转换支持 - 用于性能优化 + // Downcast support for performance fn as_any(&self) -> &dyn std::any::Any; fn as_any_mut(&mut self) -> &mut dyn std::any::Any; }