diff --git a/src/filters/fb_network_builder.rs b/src/filters/fb_network_builder.rs index 63e0f52a..34f7c7a3 100644 --- a/src/filters/fb_network_builder.rs +++ b/src/filters/fb_network_builder.rs @@ -45,7 +45,7 @@ impl<'a> FlatSerialize<'a, EngineFlatBuilder<'a>> for &NetworkFilter { network_filter: &NetworkFilter, builder: &mut EngineFlatBuilder<'a>, ) -> WIPOffset> { - let opt_domains = network_filter.opt_domains.as_ref().map(|v| { + let opt_domains = network_filter.get_opt_domains().map(|v| { let mut o: Vec = v .iter() .map(|x| builder.get_or_insert_unique_domain_hash(x)) @@ -55,7 +55,7 @@ impl<'a> FlatSerialize<'a, EngineFlatBuilder<'a>> for &NetworkFilter { FlatSerialize::serialize(o, builder) }); - let opt_not_domains = network_filter.opt_not_domains.as_ref().map(|v| { + let opt_not_domains = network_filter.get_opt_not_domains().map(|v| { let mut o: Vec = v .iter() .map(|x| builder.get_or_insert_unique_domain_hash(x)) @@ -66,8 +66,7 @@ impl<'a> FlatSerialize<'a, EngineFlatBuilder<'a>> for &NetworkFilter { }); let modifier_option = network_filter - .modifier_option - .as_ref() + .get_modifier_option() .map(|s| builder.create_string(s)); let hostname = network_filter @@ -75,14 +74,11 @@ impl<'a> FlatSerialize<'a, EngineFlatBuilder<'a>> for &NetworkFilter { .as_ref() .map(|s| builder.create_string(s)); - let tag = network_filter - .tag - .as_ref() - .map(|s| builder.create_string(s)); + let tag = network_filter.get_tag().map(|s| builder.create_string(s)); - let patterns = if network_filter.filter.iter().len() > 0 { + let patterns = if network_filter.get_filter().iter().len() > 0 { let offsets: Vec> = network_filter - .filter + .get_filter() .iter() .map(|s| builder.create_string(s)) .collect(); @@ -92,9 +88,8 @@ impl<'a> FlatSerialize<'a, EngineFlatBuilder<'a>> for &NetworkFilter { }; let raw_line = network_filter - .raw_line - .as_ref() - .map(|v| builder.create_string(v.as_str())); + .get_raw_line() + .map(|v| builder.create_string(v)); let network_filter = fb::NetworkFilter::create( builder.raw_builder(), @@ -261,7 +256,7 @@ impl NetworkRulesBuilder { FilterId::Exceptions } else if filter.is_important() { FilterId::Importants - } else if filter.tag.is_some() && !filter.is_redirect() { + } else if filter.get_tag().is_some() && !filter.is_redirect() { // `tag` + `redirect` is unsupported for now. FilterId::TaggedFiltersAll } else if (filter.is_redirect() && filter.also_block_redirect()) diff --git a/src/filters/network.rs b/src/filters/network.rs index 921b8c96..73ce2b36 100644 --- a/src/filters/network.rs +++ b/src/filters/network.rs @@ -304,8 +304,9 @@ impl From<&request::RequestType> for NetworkFilterMask { } } -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, Default)] pub enum FilterPart { + #[default] Empty, Simple(String), AnyOf(Vec), @@ -371,21 +372,25 @@ impl FilterPart { } } +#[derive(Debug, Clone, Serialize, Deserialize, Default)] +pub struct NetworkFilterExtra { + pub raw_line: Option, // 8 + pub opt_domains: Option>, // 24 + pub opt_not_domains: Option>, // 24 + pub modifier_option: Option, // 24 + pub(crate) tag: Option, // 24 + pub filter: FilterPart, // 32 +} #[derive(Debug, Clone, Serialize, Deserialize)] pub struct NetworkFilter { - pub mask: NetworkFilterMask, - pub filter: FilterPart, - pub opt_domains: Option>, - pub opt_not_domains: Option>, + pub mask: NetworkFilterMask, // 4 (8) /// Used for `$redirect`, `$redirect-rule`, `$csp`, and `$removeparam` - only one of which is /// supported per-rule. - pub modifier_option: Option, - pub hostname: Option, - pub(crate) tag: Option, + pub hostname: Option, // 24 - pub raw_line: Option>, + pub id: Hash, // 8 - pub id: Hash, + pub extra: Option>, } // TODO - restrict the API so that this is always true - i.e. lazy-calculate IDs from actual data, @@ -432,6 +437,70 @@ fn validate_options(options: &[NetworkFilterOption]) -> Result<(), NetworkFilter } impl NetworkFilter { + pub fn get_raw_line(&self) -> Option<&str> { + if let Some(extra) = &self.extra { + extra.raw_line.as_deref() + } else { + None + } + } + + pub fn get_modifier_option(&self) -> Option<&str> { + if let Some(extra) = &self.extra { + extra.modifier_option.as_deref() + } else { + None + } + } + + pub fn get_opt_domains(&self) -> Option<&[Hash]> { + if let Some(extra) = &self.extra { + extra.opt_domains.as_deref() + } else { + None + } + } + + pub fn get_opt_not_domains(&self) -> Option<&[Hash]> { + if let Some(extra) = &self.extra { + extra.opt_not_domains.as_deref() + } else { + None + } + } + + pub fn add_raw_line(&mut self, line: String) { + if let Some(extra) = &mut self.extra { + extra.raw_line = Some(line); + } else { + self.extra.get_or_insert_default().raw_line = Some(line); + } + } + + pub fn get_tag(&self) -> Option<&str> { + if let Some(extra) = &self.extra { + extra.tag.as_deref() + } else { + None + } + } + + pub fn get_filter(&self) -> &FilterPart { + if let Some(extra) = &self.extra { + &extra.filter + } else { + &FilterPart::Empty + } + } + + pub fn set_filter(&mut self, filter: FilterPart) { + if let Some(extra) = &mut self.extra { + extra.filter = filter; + } else { + self.extra.get_or_insert_default().filter = filter; + } + } + pub fn parse(line: &str, debug: bool, _opts: ParseOptions) -> Result { let parsed = AbstractNetworkFilter::parse(line)?; @@ -795,23 +864,31 @@ impl NetworkFilter { // Finally, apply any explicitly negated request types mask &= !cpt_mask_negative; + let mut extra: Option = None; + if debug + || opt_domains.is_some() + || opt_not_domains.is_some() + || tag.is_some() + || modifier_option.is_some() + || filter.is_some() + { + let extra_val = extra.get_or_insert_with(|| NetworkFilterExtra::default()); + if debug { + extra_val.raw_line = Some(String::from(line)); + } + extra_val.opt_domains = opt_domains; + extra_val.opt_not_domains = opt_not_domains; + extra_val.tag = tag; + extra_val.modifier_option = modifier_option; + extra_val.filter = filter + .map(|f| FilterPart::Simple(f)) + .unwrap_or(FilterPart::Empty); + } + Ok(NetworkFilter { - filter: if let Some(simple_filter) = filter { - FilterPart::Simple(simple_filter) - } else { - FilterPart::Empty - }, hostname: hostname_decoded?, mask, - opt_domains, - opt_not_domains, - tag, - raw_line: if debug { - Some(Box::new(String::from(line))) - } else { - None - }, - modifier_option, + extra: extra.map(|e| Box::new(e)), id: utils::fast_hash(line), }) } @@ -856,23 +933,23 @@ impl NetworkFilter { let mut mask = self.mask; mask.set(NetworkFilterMask::BAD_FILTER, false); compute_filter_id( - self.modifier_option.as_deref(), + self.get_modifier_option(), mask, - self.filter.string_view().as_deref(), + self.get_filter().string_view().as_deref(), self.hostname.as_deref(), - self.opt_domains.as_ref(), - self.opt_not_domains.as_ref(), + self.get_opt_domains(), + self.get_opt_not_domains(), ) } pub fn get_id(&self) -> Hash { compute_filter_id( - self.modifier_option.as_deref(), + self.get_modifier_option(), self.mask, - self.filter.string_view().as_deref(), + self.get_filter().string_view().as_deref(), self.hostname.as_deref(), - self.opt_domains.as_ref(), - self.opt_not_domains.as_ref(), + self.get_opt_domains(), + self.get_opt_not_domains(), ) } @@ -881,11 +958,11 @@ impl NetworkFilter { // If there is only one domain and no domain negation, we also use this // domain as a token. - if self.opt_domains.is_some() - && self.opt_not_domains.is_none() - && self.opt_domains.as_ref().map(|d| d.len()) == Some(1) + if self.get_opt_domains().is_some() + && self.get_opt_not_domains().is_none() + && self.get_opt_domains().map(|d| d.len()) == Some(1) { - if let Some(domains) = self.opt_domains.as_ref() { + if let Some(domains) = self.get_opt_domains() { if let Some(domain) = domains.first() { tokens.push(*domain) } @@ -893,7 +970,7 @@ impl NetworkFilter { } // Get tokens from filter - match &self.filter { + match self.get_filter() { FilterPart::Simple(f) => { if !self.is_complete_regex() { let skip_last_token = @@ -919,7 +996,7 @@ impl NetworkFilter { } if tokens.is_empty() && self.mask.contains(NetworkFilterMask::IS_REMOVEPARAM) { - if let Some(removeparam) = &self.modifier_option { + if let Some(removeparam) = self.get_modifier_option() { if VALID_PARAM.is_match(removeparam) { let mut param_tokens = utils::tokenize(&removeparam.to_ascii_lowercase()); tokens.append(&mut param_tokens); @@ -929,10 +1006,12 @@ impl NetworkFilter { // If we got no tokens for the filter/hostname part, then we will dispatch // this filter in multiple buckets based on the domains option. - if tokens.is_empty() && self.opt_domains.is_some() && self.opt_not_domains.is_none() { - self.opt_domains - .as_ref() - .unwrap_or(&vec![]) + if tokens.is_empty() + && self.get_opt_domains().is_some() + && self.get_opt_not_domains().is_none() + { + self.get_opt_domains() + .unwrap_or(&[]) .iter() .map(|&d| vec![d]) .collect() @@ -957,8 +1036,8 @@ impl NetworkFilterMaskHelper for NetworkFilter { impl fmt::Display for NetworkFilter { fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - match self.raw_line.as_ref() { - Some(r) => write!(f, "{}", r.clone()), + match self.get_raw_line() { + Some(r) => write!(f, "{}", r), None => write!(f, "NetworkFilter"), } } @@ -977,11 +1056,11 @@ impl NetworkMatchable for NetworkFilter { check_excluded_domains, check_included_domains, check_options, check_pattern, }; check_options(self.mask, request) - && check_included_domains(self.opt_domains.as_deref(), request) - && check_excluded_domains(self.opt_not_domains.as_deref(), request) + && check_included_domains(self.get_opt_domains(), request) + && check_excluded_domains(self.get_opt_not_domains(), request) && check_pattern( self.mask, - self.filter.iter(), + self.get_filter().iter(), self.hostname.as_deref(), (self as *const NetworkFilter) as u64, request, @@ -1004,8 +1083,8 @@ fn compute_filter_id( mask: NetworkFilterMask, filter: Option<&str>, hostname: Option<&str>, - opt_domains: Option<&Vec>, - opt_not_domains: Option<&Vec>, + opt_domains: Option<&[Hash]>, + opt_not_domains: Option<&[Hash]>, ) -> Hash { let mut hash: Hash = (5408 * 33) ^ Hash::from(mask.bits()); diff --git a/src/network_filter_list.rs b/src/network_filter_list.rs index 980d5157..4d912a43 100644 --- a/src/network_filter_list.rs +++ b/src/network_filter_list.rs @@ -28,8 +28,8 @@ impl From<&NetworkFilter> for CheckResult { fn from(filter: &NetworkFilter) -> Self { Self { filter_mask: filter.mask, - modifier_option: filter.modifier_option.clone(), - raw_line: filter.raw_line.clone().map(|v| *v), + modifier_option: filter.get_modifier_option().map(|v| v.to_string()), + raw_line: filter.get_raw_line().map(|v| v.to_string()), } } } diff --git a/src/optimizer.rs b/src/optimizer.rs index ff1c760c..0ccba73e 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -11,8 +11,8 @@ trait Optimization { } pub fn is_filter_optimizable_by_patterns(filter: &NetworkFilter) -> bool { - filter.opt_domains.is_none() - && filter.opt_not_domains.is_none() + filter.get_opt_domains().is_none() + && filter.get_opt_not_domains().is_none() && !filter.is_hostname_anchor() && !filter.is_redirect() && !filter.is_csp() @@ -92,13 +92,13 @@ impl Optimization for SimplePatternGroup { // if any filter is empty (meaning matches anything), the entire combiation matches anything if filters .iter() - .any(|f| matches!(f.filter, FilterPart::Empty)) + .any(|f| matches!(f.get_filter(), FilterPart::Empty)) { - filter.filter = FilterPart::Empty + filter.set_filter(FilterPart::Empty); } else { let mut flat_patterns: Vec = Vec::with_capacity(filters.len()); for f in filters { - match &f.filter { + match &f.get_filter() { FilterPart::Empty => (), FilterPart::Simple(s) => flat_patterns.push(s.clone()), FilterPart::AnyOf(s) => flat_patterns.extend_from_slice(s), @@ -106,11 +106,11 @@ impl Optimization for SimplePatternGroup { } if flat_patterns.is_empty() { - filter.filter = FilterPart::Empty; + filter.set_filter(FilterPart::Empty); } else if flat_patterns.len() == 1 { - filter.filter = FilterPart::Simple(flat_patterns[0].clone()) + filter.set_filter(FilterPart::Simple(flat_patterns[0].clone())) } else { - filter.filter = FilterPart::AnyOf(flat_patterns) + filter.set_filter(FilterPart::AnyOf(flat_patterns)) } } @@ -121,13 +121,8 @@ impl Optimization for SimplePatternGroup { .mask .set(NetworkFilterMask::IS_COMPLETE_REGEX, is_complete_regex); - if base_filter.raw_line.is_some() { - filter.raw_line = Some(Box::new( - filters - .iter() - .flat_map(|f| f.raw_line.clone()) - .join(" <+> "), - )) + if base_filter.get_raw_line().is_some() { + filter.add_raw_line(filters.iter().flat_map(|f| f.get_raw_line()).join(" <+> ")) } filter