diff --git a/Cargo.toml b/Cargo.toml index e60dbc6..83c3fe6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,4 +11,9 @@ regex = "1.0.5" unicase = "2.2.0" [dev-dependencies] +criterion = "0.2.0" tempfile = "3.0.0" + +[[bench]] +name = "eval" +harness = false diff --git a/benches/eval.rs b/benches/eval.rs new file mode 100644 index 0000000..8071f51 --- /dev/null +++ b/benches/eval.rs @@ -0,0 +1,136 @@ +#[macro_use] +extern crate criterion; +extern crate loot_condition_interpreter; + +use criterion::Criterion; +use loot_condition_interpreter::{Expression, GameType, State}; + +fn generate_active_plugins() -> Vec { + let mut vec: Vec = (0..255).map(|i| format!("Blank{}.esm", i)).collect(); + vec.push("Blank.esm".into()); + vec +} + +fn generate_plugin_versions() -> Vec<(String, String)> { + let mut vec: Vec<(String, String)> = (0..255) + .map(|i| (format!("Blank{}.esm", i), "5".to_string())) + .collect(); + vec.push(("Blank.esm".into(), "5".into())); + vec +} + +fn criterion_benchmark(c: &mut Criterion) { + c.bench_function("Expression.eval() file(path)", |b| { + let state = State::new(GameType::Tes4, ".".into(), ".".into()); + let expression = Expression::parse("file(\"Cargo.toml\")").unwrap().1; + + b.iter(|| { + assert!(expression.eval(&state).unwrap()); + }); + }); + + c.bench_function("Expression.eval() file(regex)", |b| { + let state = State::new(GameType::Tes4, ".".into(), ".".into()); + let expression = Expression::parse("file(\"Cargo.*\")").unwrap().1; + + b.iter(|| { + assert!(expression.eval(&state).unwrap()); + }); + }); + + c.bench_function("Expression.eval() active(path)", |b| { + let state = State::new( + GameType::Tes4, + "testing-plugins/Oblivion/Data".into(), + ".".into(), + ).with_active_plugins(&generate_active_plugins()); + + let expression = Expression::parse("active(\"Blank.esm\")").unwrap().1; + + b.iter(|| { + assert!(expression.eval(&state).unwrap()); + }); + }); + + c.bench_function("Expression.eval() active(regex)", |b| { + let state = State::new( + GameType::Tes4, + "testing-plugins/Oblivion/Data".into(), + ".".into(), + ).with_active_plugins(&generate_active_plugins()); + + let expression = Expression::parse("active(\"Blank.*\")").unwrap().1; + + b.iter(|| { + assert!(expression.eval(&state).unwrap()); + }); + }); + + c.bench_function("Expression.eval() many()", |b| { + let state = State::new(GameType::Tes4, ".".into(), ".".into()); + let expression = Expression::parse("many(\"Cargo.*\")").unwrap().1; + + b.iter(|| { + assert!(expression.eval(&state).unwrap()); + }); + }); + + c.bench_function("Expression.eval() many_active()", |b| { + let state = State::new( + GameType::Tes4, + "testing-plugins/Oblivion/Data".into(), + ".".into(), + ).with_active_plugins(&generate_active_plugins()); + + let expression = Expression::parse("many_active(\"Blank.*\")").unwrap().1; + + b.iter(|| { + assert!(expression.eval(&state).unwrap()); + }); + }); + + c.bench_function("Expression.eval() checksum()", |b| { + let state = State::new( + GameType::Tes4, + "testing-plugins/Oblivion/Data".into(), + ".".into(), + ); + let expression = Expression::parse("checksum(\"Blank.esm\", 374E2A6F)") + .unwrap() + .1; + + b.iter(|| { + assert!(expression.eval(&state).unwrap()); + }); + }); + + c.bench_function("Expression.eval() version(plugin)", |b| { + let state = State::new( + GameType::Tes4, + "testing-plugins/Oblivion/Data".into(), + ".".into(), + ).with_plugin_versions(&generate_plugin_versions()); + + let expression = Expression::parse("version(\"Blank.esm\", \"5.0\", ==)") + .unwrap() + .1; + + b.iter(|| { + assert!(expression.eval(&state).unwrap()); + }); + }); + + c.bench_function("Expression.eval() version(executable)", |b| { + let state = State::new(GameType::Tes4, ".".into(), ".".into()); + let expression = Expression::parse( + "version(\"loot_api-0.13.8-0-g47797cc_dev-win32/loot_api.dll\", \"0.13.8.0\", ==)", + ).unwrap() + .1; + + b.iter(|| { + assert!(expression.eval(&state).unwrap()); + }); + }); +} +criterion_group!(benches, criterion_benchmark); +criterion_main!(benches); diff --git a/src/lib.rs b/src/lib.rs index 82edaff..72b465b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,6 +52,7 @@ impl From for Error { } } +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum GameType { Tes4, Tes5, @@ -84,6 +85,7 @@ impl GameType { } } +#[derive(Debug)] pub struct State { game_type: GameType, /// Game Data folder path. @@ -100,6 +102,39 @@ pub struct State { condition_cache: RwLock>, } +impl State { + pub fn new(game_type: GameType, data_path: PathBuf, loot_path: PathBuf) -> Self { + State { + game_type, + data_path, + loot_path, + active_plugins: HashSet::default(), + crc_cache: RwLock::default(), + plugin_versions: HashMap::default(), + condition_cache: RwLock::default(), + } + } + + pub fn with_plugin_versions, V: ToString>( + mut self, + plugin_versions: &[(T, V)], + ) -> Self { + self.plugin_versions = plugin_versions + .iter() + .map(|(p, v)| (p.as_ref().to_lowercase(), v.to_string())) + .collect(); + self + } + + pub fn with_active_plugins>(mut self, active_plugins: &[T]) -> Self { + self.active_plugins = active_plugins + .into_iter() + .map(|s| s.as_ref().to_lowercase()) + .collect(); + self + } +} + /// Compound conditions joined by 'or' #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] pub struct Expression(Vec);