diff --git a/benches/sendprop.rs b/benches/sendprop.rs index 99362b2..0d28590 100644 --- a/benches/sendprop.rs +++ b/benches/sendprop.rs @@ -19,43 +19,43 @@ use tf_demo_parser::{Demo, DemoParser, MatchState, MessageType, MessageTypeAnaly pub struct SendPropAnalyser; -// impl MessageHandler for SendPropAnalyser { -// type Output = Vec; -// -// fn does_handle(message_type: MessageType) -> bool { -// false -// } -// -// fn into_output(self, state: &ParserState) -> Self::Output { -// state -// .send_tables -// .iter() -// .map(|v| ParseSendTable { -// name: v.name.clone(), -// props: v.props.clone(), -// needs_decoder: v.needs_decoder, -// }) -// .collect() -// } -// } -// -// fn flatten_bench(input_file: &str, b: &mut Bencher) { -// let file = fs::read(input_file).expect("Unable to read file"); -// let demo = Demo::new(&file); -// let stream = demo.get_stream(); -// let (_, send_tables) = DemoParser::new_with_analyser(stream.clone(), SendPropAnalyser) -// .parse() -// .unwrap(); -// b.iter(|| { -// let flat: Vec<_> = send_tables -// .iter() -// .map(|table| table.flatten_props(&send_tables)) -// .collect(); -// test::black_box(flat); -// }); -// } -// -// #[bench] -// fn sendprop_test_gully(b: &mut Bencher) { -// flatten_bench("data/gully.dem", b); -// } +impl MessageHandler for SendPropAnalyser { + type Output = Vec; + + fn does_handle(message_type: MessageType) -> bool { + false + } + + fn into_output(self, state: &ParserState) -> Self::Output { + state + .send_tables + .iter() + .map(|v| ParseSendTable { + name: v.name.clone(), + props: v.raw_props.clone(), + needs_decoder: v.needs_decoder, + }) + .collect() + } +} + +fn flatten_bench(input_file: &str, b: &mut Bencher) { + let file = fs::read(input_file).expect("Unable to read file"); + let demo = Demo::new(&file); + let stream = demo.get_stream(); + let (_, send_tables) = DemoParser::new_with_analyser(stream.clone(), SendPropAnalyser) + .parse() + .unwrap(); + b.iter(|| { + let flat: Vec<_> = send_tables + .iter() + .map(|table| table.flatten_props(&send_tables)) + .collect(); + test::black_box(flat); + }); +} + +#[bench] +fn sendprop_test_gully(b: &mut Bencher) { + flatten_bench("data/gully.dem", b); +} diff --git a/src/demo/packet/datatable.rs b/src/demo/packet/datatable.rs index 759e67b..4301b6e 100644 --- a/src/demo/packet/datatable.rs +++ b/src/demo/packet/datatable.rs @@ -10,6 +10,7 @@ use serde::{Deserialize, Serialize}; use std::cmp::min; +use std::convert::TryFrom; use std::rc::Rc; #[derive(BitRead, Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd, Display, FromStr)] @@ -121,14 +122,14 @@ impl ParseSendTable { } impl ParseSendTable { - pub fn flatten_props(&self, tables: &[ParseSendTable]) -> Vec { + pub fn flatten_props(&self, tables: &[ParseSendTable]) -> Result> { let mut flat = Vec::with_capacity(32); - self.get_all_props(tables, &self.get_excludes(tables), &mut flat); + self.get_all_props(tables, &self.get_excludes(tables), &mut flat)?; // sort often changed props before the others let mut start = 0; for i in 0..flat.len() { - if flat[i].flags.contains(SendPropFlag::ChangesOften) { + if flat[i].changes_often { if i != start { flat.swap(i, start); } @@ -136,7 +137,7 @@ impl ParseSendTable { } } - flat + Ok(flat) } fn get_excludes<'a>(&'a self, tables: &'a [ParseSendTable]) -> Vec { @@ -161,36 +162,40 @@ impl ParseSendTable { &self, tables: &[ParseSendTable], excludes: &[SendPropIdentifier], - props: &mut Vec, - ) { + props: &mut Vec, + ) -> Result<()> { let mut local_props = Vec::new(); - self.get_all_props_iterator_props(tables, excludes, &mut local_props, props); + self.get_all_props_iterator_props(tables, excludes, &mut local_props, props)?; props.extend_from_slice(&local_props); + Ok(()) } fn get_all_props_iterator_props( &self, tables: &[ParseSendTable], excludes: &[SendPropIdentifier], - local_props: &mut Vec, - props: &mut Vec, - ) { + local_props: &mut Vec, + props: &mut Vec, + ) -> Result<()> { self.props .iter() .filter(|prop| !prop.is_exclude()) .filter(|prop| !excludes.iter().any(|exclude| *exclude == prop.identifier())) - .for_each(|prop| { + .map(|prop| { if let Some(table) = prop.get_data_table(tables) { if prop.flags.contains(SendPropFlag::Collapsible) { - table.get_all_props_iterator_props(tables, excludes, local_props, props); + table.get_all_props_iterator_props(tables, excludes, local_props, props)?; } else { - table.get_all_props(tables, excludes, props); + table.get_all_props(tables, excludes, props)?; } } else { - local_props.push(prop.clone()); + local_props.push(SendPropDefinition::try_from(prop)?); } + Ok(()) }) + .collect::>()?; + Ok(()) } } @@ -198,6 +203,7 @@ impl ParseSendTable { pub struct SendTable { pub name: SendTableName, pub needs_decoder: bool, + pub raw_props: Vec, pub flattened_props: Vec, } diff --git a/src/demo/parser/state.rs b/src/demo/parser/state.rs index d3870a3..8c28e6c 100644 --- a/src/demo/parser/state.rs +++ b/src/demo/parser/state.rs @@ -11,11 +11,10 @@ use crate::demo::packet::datatable::{ }; use crate::demo::packet::stringtable::StringTableEntry; -use crate::demo::sendprop::{SendProp, SendPropDefinition}; +use crate::demo::sendprop::SendProp; use crate::nullhasher::NullHasherBuilder; use crate::{Result, Stream}; use std::cell::RefCell; -use std::convert::TryFrom; #[derive(Default, Clone)] pub struct DemoMeta { @@ -106,25 +105,23 @@ impl<'a> ParserState { let flat_props: Vec<_> = parse_tables .iter() .map(|table| table.flatten_props(&parse_tables)) - .collect(); + .collect::>>()?; let mut send_tables: FnvHashMap = parse_tables .into_iter() .zip(flat_props.into_iter()) .map(|(parse_table, flat)| { - Ok(( + ( parse_table.name.clone(), SendTable { name: parse_table.name, needs_decoder: parse_table.needs_decoder, - flattened_props: flat - .into_iter() - .map(|raw| SendPropDefinition::try_from(raw)) - .collect::, _>>()?, + raw_props: parse_table.props, + flattened_props: flat, }, - )) + ) }) - .collect::>()?; + .collect(); self.server_classes = server_classes; diff --git a/src/demo/sendprop.rs b/src/demo/sendprop.rs index 47169a0..736f0f8 100644 --- a/src/demo/sendprop.rs +++ b/src/demo/sendprop.rs @@ -16,6 +16,7 @@ use std::convert::{TryFrom, TryInto}; use fnv::FnvHasher; use std::fmt; use std::hash::{Hash, Hasher}; +use std::num::NonZeroU64; use std::rc::Rc; #[derive( @@ -347,16 +348,18 @@ impl FloatDefinition { #[derive(Debug, Clone)] pub struct SendPropDefinition { + pub changes_often: bool, pub identifier: SendPropIdentifier, pub parse_definition: SendPropParseDefinition, } -impl TryFrom for SendPropDefinition { +impl TryFrom<&RawSendPropDefinition> for SendPropDefinition { type Error = MalformedSendPropDefinitionError; - fn try_from(definition: RawSendPropDefinition) -> std::result::Result { - let parse_definition = (&definition).try_into()?; + fn try_from(definition: &RawSendPropDefinition) -> std::result::Result { + let parse_definition = definition.try_into()?; Ok(SendPropDefinition { + changes_often: definition.flags.contains(SendPropFlag::ChangesOften), parse_definition, identifier: definition.identifier(), }) diff --git a/tests/sendprops.rs b/tests/sendprops.rs index 9fe9874..375ec45 100644 --- a/tests/sendprops.rs +++ b/tests/sendprops.rs @@ -1,34 +1,50 @@ use std::fs; use test_case::test_case; +use fnv::FnvHashMap; use std::collections::{HashMap, HashSet}; use tf_demo_parser::demo::packet::datatable::{ParseSendTable, SendTableName, ServerClass}; use tf_demo_parser::demo::parser::MessageHandler; +use tf_demo_parser::demo::sendprop::{SendPropIdentifier, SendPropName}; use tf_demo_parser::{Demo, DemoParser, MessageType, ParserState}; +#[derive(Default)] pub struct SendPropAnalyser { tables: Vec, + prop_names: FnvHashMap, } impl SendPropAnalyser { pub fn new() -> Self { - SendPropAnalyser { tables: Vec::new() } + SendPropAnalyser::default() } } impl MessageHandler for SendPropAnalyser { - type Output = Vec; + type Output = ( + Vec, + FnvHashMap, + ); fn does_handle(_message_type: MessageType) -> bool { false } fn handle_data_tables(&mut self, tables: &[ParseSendTable], _server_classes: &[ServerClass]) { + for table in tables { + for prop_def in &table.props { + self.prop_names.insert( + prop_def.identifier(), + (prop_def.owner_table.clone(), prop_def.name.clone()), + ); + } + } + self.tables = tables.to_vec() } fn into_output(self, _state: &ParserState) -> Self::Output { - self.tables + (self.tables, self.prop_names) } } @@ -36,7 +52,7 @@ impl MessageHandler for SendPropAnalyser { fn flatten_test(input_file: &str, snapshot_file: &str) { let file = fs::read(input_file).expect("Unable to read file"); let demo = Demo::new(&file); - let (_, send_tables) = + let (_, (send_tables, prop_names)) = DemoParser::new_with_analyser(demo.get_stream(), SendPropAnalyser::new()) .parse() .expect("Failed to parse"); @@ -47,8 +63,12 @@ fn flatten_test(input_file: &str, snapshot_file: &str) { table.name.clone(), table .flatten_props(&send_tables) + .unwrap() .into_iter() - .map(|prop| format!("{}.{}", prop.owner_table, prop.name)) + .map(|prop| { + let (table_name, prop_name) = &prop_names[&prop.identifier]; + format!("{}.{}", table_name, prop_name) + }) .collect(), ) })